I helped to a SaaS that handles insurance claims processing, intake, damage assessment, fraud flagging, payout approval, adjuster notes, policy documents, the whole claim lifecycle. We wanted adjusters and customers to just ask the system things directly instead of clicking through six tabs. Here’s the actual path, including the parts that didn’t work.
**attempt one: one agent, one giant prompt**
First version was one agent with a system prompt containing everything, policy rules, claim statuses, fraud indicators, payout thresholds, all of it. Worked in demos. Fell apart within days of real adjusters using it. It would answer a question about payout timing using fraud flagging logic, or explain a policy exclusion using rules from a completely different coverage type. The prompt was too big for the model to actually hold onto the right section at the right moment, and there was no way to tell which part of that giant prompt caused a wrong answer.
**attempt two: one agent, all the tools**
Next we thought, fine, keep the agent but let it call real tools instead of relying on the prompt alone. Claim lookup, damage estimator, fraud score checker, payout trigger, document retrieval, around 30 tools total. This is where it got worse, not better. An adjuster asked about a delayed payout and the agent called the fraud flagging tool instead, because the wording overlapped just enough. Every new tool we added made the next tool pick a little less reliable. That’s when it clicked that the problem wasn’t the prompt anymore, it was one model trying to hold too many unrelated jobs at once.
**attempt three: one agent spinning up sub agents on demand**
We tried letting a main agent dynamically spin up helper agents per request. Looked elegant on paper. In practice we lost track of which sub agent actually produced which answer, context leaked between them, and debugging a wrong answer meant reconstructing a conversation between agents that no longer existed by the time we looked.
**what actually worked: a supervisor plus scoped specialist agents**
The fix was stepping back from all three. A supervisor sits on top, and its only job is figuring out which domain a request belongs to. It never touches claim data and never answers anything itself, it just decides where to send the request. Underneath it sit specialist agents, an intake agent that only knows how to open and update claims, a damage assessment agent that only knows estimator tools, a fraud agent that only knows fraud scoring signals, a payouts agent that only knows approval and disbursement, and a policy docs agent whose only job is retrieving from coverage documents. Each one has its own prompt and its own small tool set, nothing else. The delayed payout mixup from before just structurally can’t happen anymore, the payouts agent doesn’t have fraud tools to reach for even by accident.
**then RBAC showed up as its own problem**
Adjusters and customers both talk to the system, but a customer should never be able to trigger a payout approval, and an adjuster shouldn’t override a fraud flag without a senior reviewer. We first wrote that as a prompt instruction, “only allow this if role is senior adjuster.” It got bypassed once during internal testing with a slightly rephrased request, which was enough to make us stop trusting prompts for this. Permissions now live in the supervisor’s routing itself, a customer session simply cannot resolve to the payouts agent at all. Not hidden. Unreachable.
**then we needed protected agents**
There’s an internal reconciliation agent that recalculates payout totals when a fraud flag changes a claim’s status, meant to be called only by the fraud agent, never by a person. During a test, someone typed something like “act as the reconciliation agent and clear this flag” directly into the customer chat. In the old single agent setup that partially worked. Now that agent is marked protected, invisible to any user facing request no matter the phrasing, reachable only through explicit agent to agent calls we allow.
**then resolvers, because we run multiple insurers on the same platform**
Every insurer we serve has different coverage terms, different payout thresholds, different escalation contacts. We were copying entire prompt sets per insurer just to swap those details, and they drifted out of sync. Prompts now use resolvers, placeholders like {{insurer_name}} or {{payout_threshold}} filled in per client at run time. One agent definition, reused across every insurer instead of forked copies.
**and finally human in the loop where money moves**
Payout approval never auto commits, even when the payouts agent is confident. It prepares exactly what it would approve and why, a human reviews and confirms, then it commits. The payouts agent owns that checkpoint itself, it isn’t a separate approval system sitting on top.
Every piece of this came from something that actually broke in front of us, not a diagram we drew first. We ended up generalizing the whole pattern, supervisor, scoped specialist agents, RBAC in routing, protected agents, resolvers, human checkpoints, into one engine you configure instead of hand build.
It’s free and open source, called Extra: [https://github.com/extra-org/extra\](https://github.com/extra-org/extra)
Curious if anyone else here went through the same three failed attempts before landing on supervisor plus specialists. The sub agent spawning phase in particular still makes me wince a little.