02Deep dive · routing
Multi-agent routing and the supervisor that guards the money
Five specialists, one conversation, one wallet. Why routing by intent wasn't enough on its own, and what happened when the supervisor started calling every number on the screen a lie.
The problem
One WhatsApp thread, one customer on the other side, and behind it a team of specialists: reception, scheduling, sales, billing and, when the case calls for it, a person. The customer doesn't know which one is talking, and doesn't need to. The system decides, message by message, protecting one thing above everything else: the money. Route it wrong and reception quotes a price nobody approved. Supervise it wrong and the customer gets charged the wrong amount.
Constraints
- 01One wallet, many voices. Whoever answers, the total, the price and the payment code have to be the company's real numbers, never a value improvised to keep the conversation flowing.
- 02Routing can't ask the customer. "Press 1 for sales" is exactly the friction this product exists to remove; intent has to come out of the message itself.
- 03A specialist doesn't step outside its role. Reception answers, but doesn't sell — crossing that line is how invented prices get out.
- 04On money, fail closed. If a guard can't confirm a number is real, the message doesn't go.
Architecture
Supervisor
reads each message, routes by intent
Reception
answers, never sells
Scheduling
books, confirms, reminds
Sales
quotes, builds the cart, Pix
Billing
issues and settles through the bank
Copilot
decides alongside the person
Human
takes hard cases, full context
Routing by intent alone wasn't enough. "I want 10 crates" isn't a question reception can answer: a quantity stuck to a unit is already a buying signal. So the router learned to read the shape of the message, not just its subject — a quantity next to a unit goes to Sales even when the sentence looks like small talk. And before any reply with a value in it leaves, the money supervisor re-checks the price, the total and the payment code against what the tools actually returned.
The hard part
The money supervisor was the safety net, and it nearly strangled the thing it was protecting. In its first version it flagged as a lie any number the agent hadn't just fetched. And then it passed along a stale total itself.
The problem was timing. The supervisor's prompt was built with the cart value read before that turn's tools had run. Inside the same turn the agent applied the volume discount, so the supervisor compared the correct discounted total against the number it had cached earlier and called the agent a liar. The fix became a rule: any value that can change mid-turn comes back in the tool result instead of being read once at the start. After that, it checks against what the tool just returned.
Trade-offs
- Every supervisor pass costs a second and a model call, on every reply involving money. One wrong charge costs more than a month of that latency.
- Inferred routing gets it wrong sometimes. When it's a close call I push it to Sales on purpose: losing a sale is worse than reception politely fetching a salesperson.
- Several specialists is more prompt to maintain than one do-everything agent. But that separation is what makes each one auditable, and that is what makes the guards possible.
What I'd do differently
The routing rules grew as one special case after another: quantity and unit, then one more, then another. It works and it's tested, but every new signal means editing a function that only gets fatter. I'd turn each signal into a named predicate, testable on its own, so the router reads almost like a table of "this shape of message means this intent". The day another specialist comes in, it would be adding a row instead of threading a new condition through the ones already there.