Agentic AIPythonverifiedVerified

Tool Use Agent Pattern in Python

Augment an LLM with callable external tools — APIs, code interpreters, databases — so it can take actions and retrieve real-time information beyond its training data.

How to Implement the Tool Use Agent Pattern in Python

1Step 1: Define the tool and message types

from dataclasses import dataclass
from typing import Any, Callable, Awaitable


@dataclass
class ToolDefinition:
    name: str
    description: str
    parameters: dict[str, dict[str, str]]


@dataclass
class ToolResult:
    tool_name: str
    output: str
    is_error: bool


@dataclass
class AgentMessage:
    role: str  # "user" | "assistant" | "tool"
    content: str
    tool_name: str | None = None


ToolHandler = Callable[[dict[str, Any]], Awaitable[str]]

2Step 2: Create the ToolRegistry for registration and execution

class ToolRegistry:
    def __init__(self) -> None:
        self._tools: dict[str, tuple[ToolDefinition, ToolHandler]] = {}

    def register(self, definition: ToolDefinition, handler: ToolHandler) -> None:
        self._tools[definition.name] = (definition, handler)

    def get_definitions(self) -> list[ToolDefinition]:
        return [defn for defn, _ in self._tools.values()]

    async def execute(self, name: str, params: dict[str, Any]) -> ToolResult:
        entry = self._tools.get(name)
        if entry is None:
            return ToolResult(name, f"Unknown tool: {name}", is_error=True)
        try:
            output = await entry[1](params)
            return ToolResult(name, output, is_error=False)
        except Exception as exc:
            return ToolResult(name, str(exc), is_error=True)

3Step 3: Implement the tool-use loop

async def tool_use_loop(
    user_message: str,
    registry: ToolRegistry,
    llm: Callable[
        [list[AgentMessage], list[ToolDefinition]],
        Awaitable[dict[str, Any]],
    ],
) -> str:
    messages: list[AgentMessage] = [AgentMessage("user", user_message)]

    for _ in range(10):
        response = await llm(messages, registry.get_definitions())
        messages.append(AgentMessage("assistant", response["content"]))

        tool_call = response.get("tool_call")
        if tool_call is None:
            return response["content"]

        result = await registry.execute(tool_call["name"], tool_call["params"])
        messages.append(AgentMessage("tool", result.output, result.tool_name))

    return "Max iterations reached"

Tool Use Agent Pattern Architecture

hourglass_empty

Rendering diagram...

lightbulb

Tool Use Agent Pattern in the Real World

A lawyer (the LLM) in a courtroom knows the law but needs a paralegal team (the tools) to pull case files, run searches, and retrieve exhibits. The lawyer directs which file to fetch, the paralegal returns it, and the lawyer integrates that information into their argument — the lawyer's intelligence is amplified by the support staff's ability to reach into the real world.