Tool Use and Function Calling
What is Tool Use?
Tool use allows LLMs to call external functions, APIs, or tools to accomplish tasks beyond their native capabilities. This extends the model's abilities to interact with the real world.
Defining Tools
from typing import Callable, Dict, Any
from pydantic import BaseModel
class ToolDefinition(BaseModel):
name: str
description: str
parameters: Dict[str, Any]
function: Callable
class ToolRegistry:
def __init__(self):
self.tools: Dict[str, ToolDefinition] = {}
def register(self, tool: ToolDefinition):
self.tools[tool.name] = tool
def get_tool(self, name: str) -> ToolDefinition:
return self.tools.get(name)
def to_openai_format(self):
return [
{
"type": "function",
"function": {
"name": tool.name,
"description": tool.description,
"parameters": tool.parameters
}
}
for tool in self.tools.values()
]
# Example: Register a search tool
search_tool = ToolDefinition(
name="web_search",
description="Search the web for information",
parameters={
"type": "object",
"properties": {
"query": {"type": "string", "description": "Search query"}
},
"required": ["query"]
},
function=lambda query: search_web(query)
)
Function Calling with OpenAI
import openai
def function_call_example():
tools = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get current weather for a location",
"parameters": {
"type": "object",
"properties": {
"location": {"type": "string"},
"unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}
},
"required": ["location"]
}
}
}
]
response = openai.ChatCompletion.create(
model="gpt-4",
messages=[{"role": "user", "content": "What's the weather in NYC?"}],
tools=tools,
tool_choice="auto"
)
# Check if function call was requested
if response['choices'][0]['message'].get('tool_calls'):
tool_call = response['choices'][0]['message']['tool_calls'][0]
function_name = tool_call['function']['name']
arguments = json.loads(tool_call['function']['arguments'])
# Execute the function
result = execute_function(function_name, arguments)
# Return result to model
return result
Tool Execution Loop
class ToolAgent:
def __init__(self, llm, tool_registry):
self.llm = llm
self.registry = tool_registry
self.max_iterations = 5
def run(self, user_query: str) -> str:
messages = [{"role": "user", "content": user_query}]
tools = self.registry.to_openai_format()
for _ in range(self.max_iterations):
response = self.llm.chat(
messages=messages,
tools=tools,
tool_choice="auto"
)
message = response['choices'][0]['message']
if not message.get('tool_calls'):
return message['content']
# Execute tool calls
for tool_call in message['tool_calls']:
result = self.execute_tool(tool_call)
messages.append({
"role": "tool",
"tool_call_id": tool_call['id'],
"content": str(result)
})
return "Max iterations reached"
def execute_tool(self, tool_call):
name = tool_call['function']['name']
args = json.loads(tool_call['function']['arguments'])
tool = self.registry.get_tool(name)
return tool.function(**args)
Tool Design Best Practices
- Clear Descriptions: Make tool purpose obvious to the LLM
- Proper Validation: Validate inputs before execution
- Error Handling: Return meaningful error messages
- Idempotency: Design tools to be safely re-runnable
Summary
Tool use transforms LLMs from text generators into capable assistants that can interact with external systems and accomplish real-world tasks.
Next: We'll explore multi-agent systems.