Multi-Tool Registry¶
This notebook builds a small tool registry — a dict keyed by tool
name — over several SimpleFunctionTool instances, then shows how to
dispatch an incoming ToolCall to the correct tool by name.
Coming up in Chapter 4: you'll build an
LLMAgentthat manages a collection of tools and routes each tool call the LLM makes to the right one automatically. The registry pattern you see here is exactly how it does that under the hood.
No LLM is involved — this is pure Chapter 2 tool mechanics.
# Uncomment the line below to install `llm-agents-from-scratch` from PyPI
# !pip install llm-agents-from-scratch
Defining the Tools¶
We define four simple utility functions and wrap each in a
SimpleFunctionTool.
import math
from llm_agents_from_scratch.tools import SimpleFunctionTool
def celsius_to_fahrenheit(celsius: float) -> float:
"""Convert a temperature from Celsius to Fahrenheit."""
return celsius * 9 / 5 + 32
def word_count(text: str) -> int:
"""Count the number of words in a string."""
return len(text.split())
def circle_area(radius: float) -> float:
"""Calculate the area of a circle given its radius."""
return round(math.pi * radius**2, 4)
def reverse_string(text: str) -> str:
"""Reverse the characters of a string."""
return text[::-1]
tools = [
SimpleFunctionTool(func=celsius_to_fahrenheit),
SimpleFunctionTool(func=word_count),
SimpleFunctionTool(func=circle_area),
SimpleFunctionTool(func=reverse_string),
]
for t in tools:
print(f" {t.name}")
celsius_to_fahrenheit word_count circle_area reverse_string
Building the Registry¶
A registry is just a dict keyed by tool name. You'll see this exact
one-liner again in Chapter 4 when we build the LLM agent.
tools_registry = {t.name: t for t in tools}
print("Registry keys:", list(tools_registry.keys()))
Registry keys: ['celsius_to_fahrenheit', 'word_count', 'circle_area', 'reverse_string']
Dispatching Tool Calls¶
Given a list of ToolCall objects, look up each tool by name and invoke it.
This is the core dispatch pattern that an LLM agent will later automate. For
now we drive it manually so the mechanics are fully visible.
from llm_agents_from_scratch.data_structures import ToolCall
tool_calls = [
ToolCall(
tool_name="celsius_to_fahrenheit",
arguments={"celsius": 100.0},
),
ToolCall(
tool_name="word_count",
arguments={"text": "the quick brown fox jumps over the lazy dog"},
),
ToolCall(
tool_name="circle_area",
arguments={"radius": 5.0},
),
ToolCall(
tool_name="reverse_string",
arguments={"text": "agents"},
),
]
for tc in tool_calls:
tool = tools_registry[tc.tool_name]
result = tool(tc)
print(f" {tc.tool_name}({tc.arguments}) → {result.content}")
celsius_to_fahrenheit({'celsius': 100.0}) → 212.0
word_count({'text': 'the quick brown fox jumps over the lazy dog'}) → 9
circle_area({'radius': 5.0}) → 78.5398
reverse_string({'text': 'agents'}) → stnega
Handling an Unknown Tool Name¶
When a tool name isn't in the registry, rather than raising an exception,
we should instead return a ToolCallResult with error=True so that the
information reaches the LLM. With that context the LLM can understand what
went wrong and decide what to do next, such as correcting a misspelled tool
name or choosing a different tool entirely. (You'll learn about LLM tool-calling
in the next chapter!)
import json
from llm_agents_from_scratch.data_structures import ToolCallResult
def dispatch(tc: ToolCall) -> ToolCallResult:
"""Dispatch a ToolCall, returning an error result for unknown tools."""
tool = tools_registry.get(tc.tool_name)
if tool is None:
return ToolCallResult(
tool_call_id=tc.id_,
content=json.dumps(
{
"error_type": "UnknownToolError",
"message": (
f"Tool '{tc.tool_name}' not found. "
f"Available: {list(tools_registry.keys())}"
),
},
),
error=True,
)
return tool(tc)
unknown_tc = ToolCall(
tool_name="get_weather",
arguments={"city": "London"},
)
result = dispatch(unknown_tc)
print(f"error: {result.error}")
print(f"content: {result.content}")
error: True
content: {"error_type": "UnknownToolError", "message": "Tool 'get_weather' not found. Available: ['celsius_to_fahrenheit', 'word_count', 'circle_area', 'reverse_string']"}