NorthGradient
Start reading
Introduction to LLM Agents Browse lessons

Introduction to LLM Agents · Introduction to LLM Agents · 4 min read

Calling the tool

The LLM returns a decision: call get_weather with {"city": "Berlin"}. That decision is just text. Your code is responsible for translating it into an actual Python function call and capturing what comes back.

The LLM decides. Your code executes. The result goes back to the LLM in the next step.

Dispatching the tool call

We need a function that takes a tool name and a JSON argument string, looks up the corresponding Python function, parses the arguments, and calls it:

import json

# A registry mapping tool names to the actual Python functions
TOOL_REGISTRY = {
    "get_weather": get_weather,
    "get_news":    get_news,
}

def call_tool(tool_name: str, arguments_json: str) -> str:
    """Execute a tool by name with the given JSON arguments.

    Returns the tool's output as a string, or an error message
    if the tool name is unknown or the call fails.
    """
    if tool_name not in TOOL_REGISTRY:
        return f"Error: unknown tool '{tool_name}'"

    # arguments_json is a string like '{"city": "Berlin"}'
    # json.loads converts it to a Python dict: {"city": "Berlin"}
    arguments = json.loads(arguments_json)

    # ** unpacks the dict as keyword arguments: get_weather(city="Berlin")
    result = TOOL_REGISTRY[tool_name](**arguments)
    return result

Putting it together so far

With the reasoning step from lesson 2 and this dispatcher, we can already handle one tool call:

task = "What is the weather like in Berlin right now?"

# Step 1: LLM decides which tool to call
decision = reasoning_step(task)
print(decision)
# {"tool_name": "get_weather", "arguments": '{"city": "Berlin"}', "call_id": "call_abc123"}

# Step 2: we execute the tool
if isinstance(decision, dict):
    result = call_tool(decision["tool_name"], decision["arguments"])
    print(result)
    # Berlin: Overcast, 14°C

Two steps: reason, then act. The loop is not closed yet because we have not fed the result back to the LLM. That is the next lesson.

Why a registry instead of if/else

You could dispatch tool calls with a chain of if/else statements:

# Fragile: grows linearly, breaks if tool name has a typo
if tool_name == "get_weather":
    result = get_weather(**arguments)
elif tool_name == "get_news":
    result = get_news(**arguments)

The registry approach is cleaner for two reasons. First, adding a new tool requires one line in TOOL_REGISTRY, not a new branch in a conditional. Second, unknown tool names produce an explicit error message rather than silently doing nothing.

In the next lesson, we feed the tool result back to the LLM to get the final answer, completing the loop.