Tool Calling: From Chat to Agency
Tool calling (or function calling) is the mechanism that allows an LLM to interact with deterministic software systems. It transforms the model from a text generator into a reasoning engine for API orchestration.
1. Structured Definitions with Pydantic
In production, tools should be defined using typed schemas to ensure validation at the boundary. Pydantic is the industry standard for this in the Python ecosystem.
Concrete Example: Tool Schema
```python
from pydantic import BaseModel, Field
class GetWeatherArgs(BaseModel):
"""Retrieves current weather for a location."""
location: str = Field(description="The city and state, e.g. San Francisco, CA")
unit: str = Field(
default="celsius",
enum=["celsius", "fahrenheit"],
description="Temperature unit"
)
The model sees the JSON schema representation:
print(GetWeatherArgs.model_json_schema())
```
2. The Execution Loop
A robust agentic loop does not crash on a malformed tool call; it uses the error as feedback to the model.
Error-Handling & Hallucination Mitigation
Models often hallucinate parameters or call non-existent tools. The loop must handle these gracefully.
```python
def agent_loop(user_input):
messages = [{"role": "user", "content": user_input}]
for _ in range(5): # Limit to 5 iterations
response = llm.chat(messages, tools=my_tools)
if not response.tool_calls:
return response.content # Final Answer
for call in response.tool_calls:
try:
1. Validate against schema
args = validate_tool_args(call.name, call.args)
2. Execute actual code
result = execute_tool(call.name, args)
messages.append({"role": "tool", "name": call.name, "content": result})
except Exception as e:
3. Feed error back to LLM for self-correction
error_msg = f"Error in tool '{call.name}': {str(e)}. Please correct your arguments."
messages.append({"role": "tool", "name": call.name, "content": error_msg})
return "Failed to complete task within iteration limit."
```
3. Common Hallucination Patterns
1. **Missing Required Arguments:** The model calls `search_database()` without a `query` string.
- *Fix:* Return the validation error: `{"error": "Field 'query' is required"}`.
2. **Imaginary Tools:** The model calls `send_slack_message()` when only `send_email()` is available.
- *Fix:* Return: `{"error": "Tool not found. Available tools: [send_email, search_db]"}`.
3. **Type Mismatch:** The model passes a string `"100"` where an integer `100` is required.
- *Fix:* Use Pydantic's strict mode or automatic coercion, and report failures.
4. Security: The Human-in-the-Loop (HITL)
Mutating tools (deleting files, sending money, making public posts) must require explicit approval.
**Pattern: Pre-Execution Interception**
1. Model emits a `ToolCall` for a "Write" operation.
2. The Orchestrator pauses the loop.
3. The UI presents the tool arguments to the user: "Confirm: Send $500 to User B?"
4. If confirmed, the loop continues. If denied, the orchestrator feeds "Action cancelled by user" back to the LLM.
5. Multi-Tool Composition
Sophisticated agents can call multiple tools in parallel (Parallel Tool Calling) or in sequence (Chaining).
**Example Sequence:**
1. `search_docs(query="authentication")` -> Returns doc IDs.
2. `read_file(path="auth_docs.md")` -> Returns content.
3. `summarize_text(text="...")` -> Returns final answer.
The orchestrator must maintain a clean **State Object** that accumulates these observations, ensuring the context doesn't exceed the model's effective reasoning window.