Tool Calling: Making Agents Act
How agents choose and invoke external functions reliably — schema design, argument validation, and error recovery.
From text to action
A language model alone can only produce text. Tool calling is what turns it into an agent: the model emits a structured request, your code executes it, and the result goes back into context.
model → { name: "search_orders", args: { customer_id: "C1024" } }
your code → executes → returns rows
model → reasons over rows → answers
The model never executes anything itself. Your code does. The model only decides what to call and with which arguments.
Schema design is the whole game
The tool description is a prompt. A poorly-described tool gets called wrong, or not at all.
Write descriptions that state the action and the boundary:
Weak: get_data — fetches data.
Strong: get_order_history — Returns order rows for one customer, newest first. Accepts a customer id, not an email or name. Returns at most 50 rows; use the before param to page further back.
Three rules that prevent most failures:
- Enumerate the enum. If a param accepts
day|week|month, list them. Do not write “a time period”. - Say what is NOT accepted. “Accepts a customer id, not an email” eliminates a whole class of bad calls.
- State the limits. Max rows, default sort, pagination direction. The model cannot guess your API contract.
Handling failures without loops of death
Tool calls fail for predictable reasons: invalid arguments, transient errors, permission denials. Return the failure to the model as data, not as an exception that kills the turn.
{ ok: false, error: "customer_id C1024 not found", hint: "ids look like C1000-C9999" }
The model can usually self-correct from a descriptive error. It cannot self-correct from a stack trace.
Guard against infinite retry: cap tool calls per turn, and detect repeats of identical (name, args) pairs. If the same failing call appears twice, stop and surface the problem to the human.
Common failure modes
| Symptom | Likely cause |
|---|---|
| Tool never called | Description lacks trigger keywords the user would use |
| Wrong parameters every time | Enum not enumerated; units not stated |
| Calls the wrong tool of several | Tool descriptions overlap; sharpen the differences |
| Retries forever on error | Errors returned as exceptions, not as inspectable data |
| Hallucinates the result | Result never actually fed back into context |