Yasmin — WhatsApp AI ordering agent
An LLM agent that creates real orders, with deterministic code guards that contain the model's own failure modes.
- orders every two days
- ~100
- guards, each from a real incident
- 12
- specialist agents + one validator voice
- 3 + 1
Problem
A burger restaurant took WhatsApp orders by hand: someone read each chat and retyped the order into a separate system. Wiring the APIs was the easy part. The real problem was making a tool-calling LLM reliable enough to handle money — a hallucinated product ID or an unconfirmed payment method is not a cosmetic bug, it is a wrong order or lost revenue.
Solution / architecture
A multi-agent system in n8n: a thin orchestrator that routes through deterministic gates, domain specialists (orders, support, after-sales) with their own tools, and a final Validator that rewrites every reply in one voice. Logic lives in the specialist and voice lives in the Validator, so tuning tone never risks breaking order logic. Between the model and the vendor API sits a layer of code guards.
Impact
- Twelve deterministic guards, each born from a production incident — all rejecting with an actionable instruction for the agent, not an error code it would paraphrase badly to the customer.
- Root-caused invalid product IDs: the vendor API exposed an undocumented internal field with the same numeric shape as real IDs. Two prompt fixes had already failed; the fix was a code guard.
- Found a silent failure that had existed for months — one flow branch never wired — by auditing connections after every other hypothesis was ruled out with evidence.
- Reverse-engineered the vendor's undocumented pricing with real orders: two fields we sent were silently dropped, and the documented change field was a different one.
- An interceptor replaces the message whenever the agent announces an order that was never created. It has already caught a false confirmation live.
- Failure tests (invalid credentials, unreachable host) on isolated clones of the flow — graceful degradation proven without touching the live service.
Read the full story
The principle that came out of it, and that I now apply by default: proof of a conversation event must live outside the model. Making a field required does not make the question happen. One order went out as credit for a customer who had said debit, because the model filled the required field with a plausible value. The fix was not a better prompt — the model's value is now ignored, and the payment method is derived from the customer's own words through a fixed mapping the agent cannot forge.
The same idea moved a business event out of an LLM classifier's hands: tapping “Good” on the satisfaction survey was being classified as a greeting. It became an exact-match gate.
On the vendor side, two real orders proved that the total and fee fields we sent were discarded and recalculated from the vendor's catalogue. The costliest failure mode of an undocumented API is the wrong field name that throws no error and simply does nothing. A delivery charge the platform would not let us register was solved by decoupling the billing zone from the real delivery location.