Invoking tools dynamically

In earlier lessons, we defined tools as plain Python functions. That gives an agent concrete capabilities.

What we need next is the ability to choose between those capabilities at runtime. This is what allows an agent to behave differently based on its current state, rather than following a fixed script.

In practical programs, including classical agents, this pattern shows up whenever behavior must be selected programmatically and executed on demand.

Storing available tools in data structures

To select tools dynamically, we first need a way to store them.

In Python, functions are values, which means they can be placed inside data structures like dictionaries or lists.

A common approach is to map a tool name to the function that implements it.

def generate_planet_page(state):
    return f"<h1>{state['planet']}</h1>"

def generate_index_page(state):
    return "<h1>Planets</h1>"

tools = {
    "planet_page": generate_planet_page,
    "index_page": generate_index_page,
}

This turns a collection of functions into a simple lookup table that the agent can reason about.

Selecting a tool based on agent decisions

Once tools are stored, selecting one becomes a data problem instead of a control-flow problem.

The agent’s decision logic produces a key, and that key identifies the tool to use.

decision = "planet_page"
tool = tools[decision]

The selection logic can be as simple or as structured as needed, but the important point is that no if or elif chain is required to choose the behavior.

Invoking a selected tool programmatically

After a tool is selected, invoking it is no different from calling any other function.

The only difference is that the function was retrieved dynamically.

result = tool(state)

From Python’s perspective, there is nothing special about this call.

The indirection exists only in how the function was chosen.

Passing state and inputs to a tool

Tools rarely operate in isolation.

They usually need access to agent state or other inputs produced earlier in the program.

That information is passed explicitly as arguments.

state = {"planet": "Mars"}
html = tool(state)

This keeps tools pure and predictable: all required inputs are visible at the call site.

Receiving and handling tool results

Tools return values just like any other function.

The agent receives the result and decides what to do next.

page_html = tool(state)

The returned value might be content, a status indicator, or structured data.

What matters is that the result flows back into the agent’s control logic rather than disappearing into side effects.

Conclusion

At this point, we can store tools, select them dynamically, invoke them, and handle their results.

This completes the core mechanical loop needed for tool-based behavior in a classical agent.

We now have a clean separation between deciding what to do and executing how it is done, which is exactly the orientation we need before introducing success, failure, and recovery.