The pipeline dies at night with a stack trace inside a tool function: KeyError: 'ORD-2024-TUE', ValueError: invalid date '31/06/2026', or a database lookup
returning zero rows. Worse variants do not crash at all: the tool is called with a
customer id that exists but is the wrong customer, and the bot confidently reads
one person's data to another. The tool code passes every hand-written test.
Any tool-use build where the model's arguments flow straight into the function —
FUNCTIONS[name](**args) with nothing in between. Common right after module 3.8's
first working loop, when the happy path works and the guard rails feel like
tomorrow's job. Triggers include users mentioning ids from memory, dates in Indian
day-first format when the schema silently expects ISO, and item or customer names
the model "completed" to something plausible. Linked module: 3.8 (Tool use /
function calling).
The model writes arguments the same way it writes prose: by predicting plausible
text. A schema shapes the format of the call, but nothing in the model checks that
an id exists, a date is real, or a quantity is sane — plausible and valid are
different properties. Confirm it by reading the failing transcript: the call in
tool_calls will look reasonable and match the schema's types, yet name a value
your system has never contained. Then classify the failure: malformed argument
(wrong type or format — the schema was too loose or ignored), or hallucinated
argument (well-formed value that does not exist — only your data can catch this).
The crash is the friendly version; the wrong-customer read is the same bug with a
worse outcome, and it produces no stack trace at all.
Install a validate-then-execute gate in the loop, and keep it in code. Check the call against three things before running anything: the schema (types, required fields, enums, ranges), an allowlist of tool names, and existence in your real data — does this id, item, or customer actually exist. On failure, execute nothing; return the problem to the model as the tool result, written so it can correct course: "customer_id 'C-1042-B' not found; ids look like 'C-####'" beats "invalid input". Wrap the execution itself in try/except so an unexpected error also becomes a tool result instead of a crash. Re-run the night's failing inputs; every bad call should now end as a polite correction or an honest "not found".
Never expose a raw function to the loop — every tool goes through the same validator wrapper, so a new tool cannot skip the gate by accident. Tighten schemas until they carry enums, formats, and ranges, because every constraint you encode is one the model tends to respect. Keep an adversarial test set of calls with fake ids, day-first dates, negative quantities, and near-miss names, and run it on every change. For any tool that reads person-specific data, add an ownership check — the requesting user must match the record — since that is the variant that leaks data silently.
Q: "Your model calls tools with arguments that pass the schema but crash the backend or fetch the wrong record. Walk me through your defence." Strong answer: name the gap between plausible and valid, give the three-layer gate — schema checks, allowlist, existence checks against real data — and describe returning a self-explanatory error to the model instead of raising. Bonus for naming the silent variant: a valid-looking id that belongs to the wrong customer, and the ownership check that stops it.