One Schema Per Tool: Stopping a Malformed Refund Call Before It Runs
A malformed call is worse than a bad answer
A wrong answer from a support agent is a follow-up question. A malformed issue_refund call is a broken transaction and a support ticket about you. AI agent tool schema validation is what makes the second impossible — every tool call well-formed, every time, with no exceptions.
The two-schema drift problem
The model needs a function spec describing each tool. The endpoint that executes the call needs its own validation. If those two are maintained separately, they drift — a field renamed in one, a type tightened in the other, an enum value added to the spec but not the handler. The failures are intermittent and hard to trace, because most calls still work.
The single-schema pattern
Define each tool once, as one typed schema, and generate both the model's function spec and the executing endpoint's validation from it. There's no second definition to keep in sync. On our support agent project each tool is a single typed model shared verbatim between the function spec sent to the model and the endpoint that runs it — the one hard requirement on the build was that every tool call be schema-valid without exception, and this is the design that met it.
Strict schema enforcement
Modern models support constrained decoding that guarantees the emitted call matches the schema — an invalid call can't be produced in the first place, rather than being caught after. With strict enforcement on a tightly specified schema, the model's schema-compliance rate on tool calls goes to effectively 100%.
Validate again server-side
Trust nothing. The schema constrains the model; a second check on the endpoint catches anything else — a replayed call, a client bug, a schema version mismatch during a deploy. Two checks, one schema.
What the schema should encode
The tighter the schema, the less the model can get wrong: required fields, enum values for reasons and statuses, types, numeric ranges. A create_return schema that only accepts a valid order ID format, a reason from a fixed list, and a positive quantity has removed most of the ways a call can be malformed before the model ever runs.
What a good tool schema looks like
Take create_return. A loose version accepts an order ID string, an items field, and a free-text reason. A tight version accepts an order ID matching your ID format, items as a non-empty list of line references that must exist on that order, quantity as a positive integer no greater than what was purchased, and reason as an enum of the six reason codes your returns process actually uses — no free-text field for the model to fill with a paragraph. The tight version has removed most of the ways the call can be wrong before the model runs.
Test the schema before you ship
Fuzz the tool: feed the model prompts designed to elicit edge-case calls — a missing field, a wrong type, an out-of-range quantity, an invalid reason — and confirm that every emitted call is valid or the model declines gracefully. Add "schema changed" to the deploy checklist, so a field rename gets the same testing as a code change. On our support-agent project the schema is a versioned artefact, and a change to it is treated as a release, not a config tweak.
Where this stops being right
- A very small, read-only tool set doesn't have the drift problem at meaningful scale — the discipline still helps, but the payoff is smaller.
- Strict enforcement can occasionally block a valid-but-unusual call — handle that with a fallback path, not by loosening the schema.
- The schema is code. Version it like the prompt, and treat a schema change as a deploy that needs testing.
FAQ
What does "schema-valid every time" actually require? Constrained decoding so the model can't emit an invalid call, a single schema shared between the model spec and the executing endpoint, and a server-side re-check.
Can the model still call the wrong tool? Schema validity is about the call being well-formed. Whether the agent should call a tool is a separate decision, handled by confidence thresholds.
Where does the schema live? As a typed definition — a Pydantic model, for example — that both the function spec and the endpoint are generated from.
ISTRALLEN builds support agents where one typed schema per tool spans the model spec and the endpoint, so a malformed call can't execute; see AI for E-commerce.