Tool calling, properly: designing the surface an agent acts through
An agent is only as good as its tools. Here is what a good tool contract looks like.
An agent is a model plus a set of tools plus a policy about when it may use them. Most of the quality is in the second part, and most teams spend their attention on the first.
One tool, one verb
A "manage subscription" tool that upgrades, downgrades, pauses and cancels depending on parameters is four tools sharing a name. The model has to infer which one it is invoking from parameter combinations, and it will occasionally infer wrong in a way that cancels something.
Split them. Error rates fall, permission scoping becomes possible, and the tool register becomes something a risk function can read.
Type everything, and constrain it
Free-text parameters are where bad calls come from. Where a value is one of a known set, make it an enum. Where it is a date, make it a date with a format. Where it has a valid range, state the range.
The model sees the schema. A constrained schema is instruction that cannot be ignored, which is a categorically better mechanism than a sentence in a prompt.
Name things for a reader who has never seen the system
Tool and parameter names are read by a model with no access to your internal conventions. A tool called proc_adj_v2 with a parameter fl is a guessing game.
Write descriptions for a new joiner: what it does, when to use it, when not to use it, and what happens as a side effect. The "when not to use it" line is the most valuable and the most often omitted.
Return what changed
A tool that returns success: true tells the agent nothing it can verify. Return the resulting state — the new plan, the new balance, the created ticket id.
This lets the agent confirm its own work, and it makes the trace far more useful when someone reads it eighteen months later.
Make failure modes explicit and distinct
"Insufficient permission", "record not found", "record locked by another process" and "validation failed on field X" require different recoveries. A generic error string means the agent guesses, and guessing after a failure is where retry storms come from.
Type the errors. Say which are retryable.
Scope permissions per tool, not per agent
An agent that reads billing and writes tickets holds two scopes. They should be grantable and revocable independently, and the trace should record which was exercised.
Per-agent permissions collapse into a superset that nobody can reason about within about two quarters.
No escape hatches
The general-purpose tool — run arbitrary SQL, call any internal endpoint, execute a shell command — will be added for flexibility during development and will be used for exactly the things you did not want.
It also destroys the governance property that matters: that someone can read the tool register and enumerate everything the agent can do. Remove it before production. We did, after learning why.
Idempotency
Agents retry. Networks fail mid-call. A tool that creates a duplicate refund on retry is a defect waiting for a bad afternoon.
Take an idempotency key on every state-changing tool, and honour it.
Keep the set small
Beyond roughly twenty to thirty tools, selection accuracy degrades noticeably regardless of model.
If you need more surface area, compose: a small set of high-level tools that internally orchestrate, rather than a flat list of eighty primitives. The agent's job is deciding what to do, not assembling low-level calls.
- Agents
- Tools
- Engineering