Adding multiple tools, richer state, and human-in-the-loop safety

Tool registry pattern in Python

You want your agent to pick between multiple tools at run time; a tool registry is a clean way to do this.

At its core, a tool registry is just a mapping from a tool name the model uses in text (like "search_web") to a callable function plus metadata your code understands.

Basic structure

Use a dict where keys are tool names (strings) and values are objects holding:

  • The Python function to call
  • A human-readable description
  • Optional metadata: argument schema, risk level, etc.
pythonfrom typing import Callable, Dict, Any class Tool: def __init__(self, func: Callable, description: str, risky: bool = False): self.func = func self.description = description self.risky = risky # Example tool functions def add_numbers(a: float, b: float) -> float: return a + b def reverse_text(text: str) -> str: return text[::-1] # Registry TOOLS: Dict[str, Tool] = { "add_numbers": Tool( func=add_numbers, description="Add two numbers and return the sum" ), "reverse_text": Tool( func=reverse_text, description="Reverse the given text" ), }

The model chooses between tools by name. Your prompt tells it:

  • What tools exist
  • What each tool does
  • How to call them (the format you expect)

Example tool section in the prompt:

textYou can use these tools: 1. add_numbers(a, b) Use this to add two numbers. 2. reverse_text(text) Use this to reverse a piece of text. When you want to use a tool, answer in this exact JSON: {"tool": "tool_name", "args": { ... }}

At run time:

  1. The model responds with a JSON specifying "tool": "add_numbers" or "reverse_text".
  2. Your code looks up that name in TOOLS.
  3. You call the appropriate function with the provided arguments.
pythonimport json def run_tool_from_model_output(raw_output: str) -> Any: data = json.loads(raw_output) tool_name = data["tool"] args = data.get("args", {}) if tool_name not in TOOLS: raise ValueError(f"Unknown tool {tool_name}") tool = TOOLS[tool_name] return tool.func(**args)

This design supports at least two tools and scales easily: new tool → new function + new Tool entry, without changing the core loop.

Keep tool names short, unambiguous, and stable; changing names later breaks older prompts or logs.

1 / 5