9.3 Tools, function calling and action spaces

Standard applied practice as of August 2026 — the least settled area in this subject, so the topic's resources carry the current state

What this is and why it exists

Tools are how a model acts on the world, and most tool failures are not model failures — they are description failures. A tool's name, its parameters and its description are read by the model as a prompt, and a vague one produces confident misuse. This topic is designing that contract, making tools safe to retry, and the single most important safety control in an agent that acts: a person in front of anything that cannot be undone.

The vocabulary

  • Tool — a function the model may request, with a described purpose and arguments.
  • Action space — the complete set of tools available to an agent.
  • Tool selection — the model choosing which tool to call.
  • Idempotent — safe to call more than once with the same effect as calling once.
  • Idempotency key — a caller-supplied identifier making a repeat recognisable.
  • Side effect — a change to the world outside the agent.
  • Confirmation gate — a required human approval before an action proceeds.
  • Dry run — showing what would happen without doing it.

The mental model

A tool description is a prompt, and it should be written as one. The model sees only the name, the parameter names and types, and the description — never your implementation. So write for a competent colleague who cannot see the code: what the tool does, when to use it, when not to use it, what each argument means, what units and formats are expected, and what it returns.

Four rules make descriptions work. Say when not to use it, since the boundary between two tools is where selection fails and the negative case is what draws it. Name units and formats explicitly — a date argument without a format, an amount without a currency and a duration without a unit are all guesses waiting to happen. Describe the return value, because the model plans its next step from what it expects to receive. And keep the set small: a handful of well-described tools is used far more accurately than thirty overlapping ones, and if the list is long, group them behind a smaller set or select a relevant subset per task.

Tool selection failures are almost always naming failures. Two tools that sound similar get confused; one that sounds general gets used for everything; one whose name describes the implementation rather than the purpose gets ignored. The fix is in the naming, not the model: make names describe the purpose in the user's terms, make similar tools unmistakably different — search-orders and search-products rather than query and lookup — and merge two tools into one with a parameter where they genuinely do the same thing to different data. When you see a wrong tool chosen, read the two descriptions side by side and ask how a stranger would tell them apart; the answer is nearly always visible immediately.

Idempotency is what makes retries safe, and retries are unavoidable. Networks fail, requests time out, an agent loses its transcript and starts again. If a tool is safe to call twice, all of that is recoverable. If it is not, a timeout on a payment call leaves you unable to retry safely and unable to know whether it went through.

The design rules are ordinary and worth stating. Reads are naturally idempotent — call them freely. Make writes idempotent with a key: the caller supplies an identifier and the server, on seeing a repeat, returns the original result rather than acting again, which turns "did it happen?" into a question with an answer. Make deletes tolerant: deleting something already gone should succeed quietly rather than erroring, since an error there causes exactly the retry loop you do not want. And decide retry behaviour per tool rather than globally — retrying a read is free, retrying a non-idempotent write is a duplicate, and a blanket retry policy applied to both is how duplicate orders get created.

Two more habits. Return errors the model can act on: "the customer id was not found — check the format, which is eight digits" leads to a correction, while "Error 500" leads to a retry loop. And keep tool results small, because everything returned lands in the transcript and is paid for on every subsequent step; return the fields needed, not the whole record.

Then the control this topic exists for: a person in front of anything irreversible.

The model will eventually call the wrong tool with confident arguments. Not because it is broken — because it is a probabilistic system operating on ambiguous instructions, and over enough runs the unlikely happens. Design as though it will, because the alternative is discovering it in production on a customer's account.

So classify every tool by reversibility and gate accordingly. Reversible and cheap — reads, drafts, sandbox writes — no gate, let the agent work. Reversible with effort — creating a record, updating a status — no gate, and log it so it can be found and undone. Irreversible or outward-facing — sending a message, publishing, paying, deleting, anything touching a third party — always a gate, meaning a person sees exactly what will happen and approves it before it happens.

Three properties make a gate real rather than decorative. It shows the actual arguments, formatted for a person: this message, to this address, with this text; this amount, to this account. A gate that says "the agent wants to send an email" without showing which email is a button people learn to press. It is enforced outside the model, in your code, so no instruction reaching the model can bypass it. And it fails closed: if the approval is not obtained, nothing happens.

Two additions that pay for themselves. Dry runs: a mode showing what the agent would do without doing it, which is how you build confidence in a new agent and how you debug one that misbehaves. And spending and volume limits per task, so even approved actions cannot run away — the agent that sends one email correctly can send four thousand equally correctly.

The general rule for the whole topic, which the security lesson makes structural: every tool you expose is a capability an injected instruction can use. Ask of each one what happens if a document the agent reads tells it to call this with the worst possible arguments. If the answer is unacceptable, the tool is wrong, and no description fixes it.

What you should now be able to explain or do

Write a tool description as a prompt, including when not to use it, units and formats, and the return value. Keep the action space small and diagnose a wrong selection by reading descriptions side by side. Make reads, writes and deletes safe to retry, using an idempotency key, and set retry behaviour per tool. Return actionable errors and small results. Classify tools by reversibility and gate the irreversible ones. Build a gate that shows real arguments, is enforced outside the model and fails closed. Add dry runs and per-task limits. Apply the worst-arguments test.

Check yourself

In the naming and descriptions, not the model. Read the two side by side and ask how a stranger would tell them apart — make the names describe purpose in the user's terms, and say in each when not to use it.

An idempotency key: the caller supplies an identifier and the server returns the original result on a repeat rather than acting again. That turns "did my timed-out request go through?" into a question with an answer.

Because erroring causes exactly the retry loop you are trying to survive. Tolerant deletes make recovery ordinary instead of dangerous.

It shows the actual arguments formatted for a person, it is enforced in your code outside the model so no instruction can bypass it, and it fails closed. A gate saying only "the agent wants to send an email" is a button people learn to press.

What happens if a document the agent reads instructs it to call this with the worst possible arguments. If that answer is unacceptable, the tool is wrong — a description cannot fix a capability.

Go deeper

Back to Tools, function calling and action spaces: work through the checklist