04. Tool Registry
Chapter 4 of 24 · 15 min
The tool registry is a mapping layer. It connects the string names the LLM uses to actual Python callables, along with the schemas that tell the LLM what each tool expects.
A well-designed registry supports three operations: registration, retrieval by name, and schema generation for LLM input.
from dataclasses import dataclass
from typing import Any, Callable, Awaitable
@dataclass
class Tool:
name: str
description: str
parameters: dict[str, Any] # JSON Schema compatible
handler: Callable[..., Awaitable[Any]]
class ToolRegistry:
def __init__(self):
self._tools: dict[str, Tool] = {}
def register(
self,
name: str,
description: str,
parameters: dict[str, Any],
handler: Callable[..., Awaitable[Any]]
) -> None:
if name in self._tools:
raise ValueError(f"Tool '{name}' already registered")
self._tools[name] = Tool(name, description, parameters, handler)
def get(self, name: str) -> Tool:
if name not in self._tools:
raise KeyError(f"Tool '{name}' not found. Registered: {list(self._tools.keys())}")
return self._tools[name]
def schemas(self) -> list[dict[str, Any]]:
"""Generate tool schemas for LLM function calling."""
return [
{
"type": "function",
"function": {
"name": tool.name,
"description": tool.description,
"parameters": tool.parameters
}
}
for tool in self._tools.values()
]
Decorator pattern for registration:
def tool(registry: ToolRegistry, name: str, description: str, parameters: dict[str, Any]):
def decorator(func: Callable[..., Awaitable[Any]]):
registry.register(name, description, parameters, func)
return func
return decorator
# Usage
registry = ToolRegistry()
@tool(registry, name="get_weather", description="Get current weather for a location", parameters={
"type": "object",
"properties": {
"location": {"type": "string", "description": "City name"}
},
"required": ["location"]
})
async def get_weather(location: str) -> str:
# Implementation
return f"Sunny, 72°F in {location}"
Failure mode: schema mismatch. If your schema says a parameter is required but the handler has a default, the LLM may pass null and your handler crashes. Keep schemas and function signatures in sync—ideally derive schemas from type hints.
EXERCISE
Define five tools your target agent needs. Write their schemas (name, description, parameters) and the handler signatures. Check for schema-signature mismatches.