Symptoms and causes
| Symptom | Usual cause |
|---|---|
| Model answers from memory instead of calling the tool | Tool description doesn't say when to use it; tool_choice not set where a call is required |
| Calls the wrong tool | Two tools with overlapping names and descriptions |
| Arguments invalid (missing field, wrong type, made-up IDs) | Loose schema; no validation; the model never saw a valid ID |
| API error: tool result missing / unexpected message order | Your code didn't send back a result for every tool call id |
| Agent loops calling the same tool | Tool returns an error the model can't act on; no step limit |
1. Write tool definitions for the model
{
"name": "get_ticket_status",
"description": "Get the current status, assignee and last update of ONE support ticket. Use when the user asks about a specific ticket number like INC0012345. Do not use for searching tickets.",
"parameters": {
"type": "object",
"properties": {
"ticket_id": {"type": "string", "pattern": "^INC[0-9]{7}$", "description": "Ticket number, e.g. INC0012345"}
},
"required": ["ticket_id"],
"additionalProperties": false
}
}
- Say when to use it and when not to.
- Use enums, patterns and required fields. Enable strict schema mode where the provider supports it.
- Fewer tools work better. With more than ~15–20, group them or route to sub-agents.
2. Validate every call in code
import json, jsonschema
args = json.loads(call.arguments)
jsonschema.validate(args, TOOLS[call.name]["parameters"])
On failure, don't crash. Return a clear error as the tool result so the model can correct itself:
{"error": "ticket_id must look like INC0012345; got '12345'"}
3. Return results correctly
- Every tool call id gets exactly one result message, in the same turn, including when parallel calls happen.
- Keep results small and structured. Don't return a 2 MB API response.
- Include enough for the next step (IDs, status) and nothing secret.
4. Stop loops
MAX_STEPS = 8
for step in range(MAX_STEPS):
resp = llm.chat(messages, tools=tools)
if not resp.tool_calls:
break
...
else:
return "I couldn't complete this automatically; a human will follow up."
Also detect repeats: the same tool with the same arguments twice in a row → stop and escalate.
5. Test
Create a set of user requests with the expected tool and arguments, and measure tool selection accuracy and argument validity on each model or prompt change.
Warning
Never execute a tool call that changes something (refund, restart, delete) just because the model asked. Validate permissions for the end user, and require confirmation for impactful actions.