A Practical Contract for Reliable AI Tool Calls
AI agents become useful when they can call tools: run a test, inspect a deployment, query an API, or update a record. But a tool call is not reliable just because the model picked the right function. The integration also needs a contract that makes inputs, outputs, failures, and retries boringly clear.
This is the small mental model that has helped me build automation which is easier to debug: a tool should behave like a well-designed API endpoint, even when the caller is an LLM.
The hidden problem with tools
Most early agent prototypes describe a tool with a name and a sentence:
{"name":"check_build","description":"Checks a build"}
That works in a demo. In production, “checks a build” leaves too much open. Which project? What counts as success? Does a timeout mean failure? Can the model safely call it again? The agent fills those gaps with guesses, and guesses are expensive when they trigger a deployment or hide a broken test.
The solution is not a giant prompt. It is a small, explicit contract.
Define a small contract
Start with four parts:
- Input schema: required fields, allowed values, and size limits.
- Output schema: stable fields that the next step can consume.
- Failure vocabulary: typed, actionable errors instead of one generic message.
- Side-effect policy: whether the operation is read-only, repeatable, or destructive.
For example, an email smoke test might return this shape:
{
"status": "passed",
"run_id": "smoke_2048",
"evidence": ["message_received", "link_opened"],
"retryable": false,
"error_code": null
}
Notice that status is not a paragraph. It is a small set such as passed, failed, or blocked. retryable tells the orchestration layer what to do next. evidence gives a human reviewer something concrete to inspect.
This same principle applies to authentication and account recovery. Keeping provenance for recovery workflows explicit prevents an agent from treating an unverified message as proof.
Make retries safe
LLM workflows retry for normal reasons: a timeout, a malformed response, or a model deciding it should try again. A tool contract should assume this will happen.
For read-only tools, include a request identifier and return the same result when practical. For writes, require an idempotency key. The key can be derived from the workflow run and the logical operation, not from the model’s prose.
idempotency_key = workflow_run_id + ":" + operation_name
Also put limits around retries. Three quick retries for a temporary network error may be sensible; three repeated calls to create a resource may not be. A retry_after_seconds field is often more useful than asking the model to infer timing from an error message.
Small details matter here. A temp mailid in a test fixture or a typo like tepm mail com should be treated as ordinary input data, not silently corrected by an agent. If normalization is allowed, make it a documented step with its own result.
Return evidence, not vibes
An agent’s final sentence is not observability. Log the tool name, validated arguments, workflow run ID, duration, result status, and error code. Avoid recording secrets or full message bodies when a hash or metadata is enough.
Expose a compact result to the model, then keep richer evidence for operators. This keeps prompts smaller while preserving a trail for debugging. It also makes making signup checks easier to reason about, because each async step has a visible decision state.
A review checklist
Before adding a tool to an AI workflow, I ask:
- Can invalid input be rejected before any side effect?
- Are success and partial success different states?
- Does every failure say whether retrying is safe?
- Can a human identify what happened from the run ID?
- Are secrets excluded from model-visible output and logs?
- Is the tool description short enough to stay unambiguous?
If the answer is “not yet,” the tool is probably not ready for autonomous use. It may still be fine for a supervised prototype, but the boundary should be intentional.
Final thoughts
Reliable AI automation is mostly good interface design with an unpredictable caller. Give tools narrow inputs, stable outputs, explicit failure states, and evidence that survives a retry. The model can then focus on choosing the next useful action instead of inventing what a tool response means.
That contract will not make every workflow perfect. It will make failures smaller, explanations clearer, and improvements much faster to ship.
Top comments (0)