Chapter 15 — Why Multiple Agents?
Intake, Resolution, Policy, and Outreach are built. The temptation is to make every stage a separate agent — more messages, credentials, failure modes, and eval work. That only pays when the split enforces a real boundary.
For O-1001, Policy decides whether a refund or reship proposal satisfies the rule pack. Outreach drafts or sends only after that verdict and only with the right permission. They do not need a debate to reach truth; policy runs against evidence and deterministic rules.
Intake facts → Resolution track → Policy verdict → Outreach draft → approve → send
This chapter adds the decision test: split for permissions, context isolation, blast radius, or eval surface — not because “agents feel advanced” or to dodge the FSM from Chapter 7.
First principles
The valid reasons are operational, not theatrical:
| Reason | ShopOps example |
|---|---|
| Permissions / SoD | Outreach can send; Policy cannot; Intake cannot |
| Context isolation | Policy rules pinned without CRM novel-length notes |
| Model routing | Tiny classifier vs larger strategy narrative (Ch 34) |
| Latency / parallelism | Independent reads fan out |
| Org / trust boundary | Vendor contact gateway vs in-house policy |
| Blast-radius isolation | Compromised contact tools cannot read full order + payment PII |
| Eval surface | Score policy agent on rule fixtures alone |
These are not valid reasons:
- “Agents feel more advanced”
- Roleplay personas without capability differences
- Belief that debate yields truth
- Dodging a clear FSM (Ch 7) by chatting
The key test is blast radius: if one worker’s credentials leak, what can it still read, change, or send? If the answer is “everything,” the split did not isolate anything.
Concrete example
Policy separated from Outreach:
- Policy mounts: rule pack, proposal read, verdict write to ledger.
- Outreach mounts: draft/send channel adapters, reads verdicts.
- Neither needs the other’s full prompt.
- A single-process mode can still call the same functions in order —
mode=single|multiis a deployment switch, not a different product.
Diagram
flowchart TD
Q{Need multi-agent?} --> P[Different permissions?]
P -->|yes| Split[Split]
P -->|no| C[Different context / model / org boundary?]
C -->|yes| Split
C -->|no| E[Need separate eval surface?]
E -->|yes| Split
E -->|no| Mod[Keep modules in one agent]
Split --> SoD[Enforce SoD in authz + mounts]
Mod --> FSM[Prefer FSM / orchestrated stages]
Implementation
from shopops.orchestrator import (
Orchestrator,
default_supervisor,
mode_config,
)
cfg = mode_config("multi")
assert cfg["separation_of_duties"] is True
orch = Orchestrator(default_supervisor())
orch.create_case("O-1001", topology=cfg["topology"], order_total_cents=240_00)
# Drive stages: intake → resolution → policy → outreach
while orch.cases["O-1001"].status == "open":
orch.tick("O-1001")
assert orch.cases["O-1001"].status in {"completed", "failed", "awaiting_approval"}
# Same tools/functions, narrower topology
cfg_single = mode_config("single")
orch2 = Orchestrator(default_supervisor())
orch2.create_case("A-1002", topology=cfg_single["topology"])
orch2.tick("A-1002")
The workers are ordinary functions. “Multi-agent” here means separate contracts, capabilities, and ledger producers — optionally separate processes later (Ch 32). You can refactor an unjustified split back to modules without changing tool semantics.
Failure modes
- Persona proliferation — ten agents, one shared API key.
- Consensus theatre — majority vote on send window. send window are a clock, not an opinion.
- Chat as integration bus — unbounded transcripts as “state.”
- Split without eval — you cannot tell which agent regressed.
- Duplicate writes — two agents both “own” contact (Ch 17).
Production considerations
- Write the SoD matrix before the prompts (architecture.md).
- Prefer one orchestrated case worker with staged modules until a valid split reason appears.
- When you split, split credentials and tool mounts, not only system prompts.
- Measure: illegal send rate, cross-capability attempts, eval flake rate. If multi-agent worsens them, roll back.
- Industry writeups on multi-agent failures are mostly about coordination and authority — treat them as systems lessons, not model-quality nits. [VERIFY SOURCE when citing a specific postmortem.]
Worked decision: Policy vs Outreach
Ask the split questions in order for ShopOps contact:
- Permissions? Yes — Outreach may draft/send under policy; Policy must not hold send credentials.
- Context? Yes — rule packs should stay pinned; CRM novels dilute them (Ch 3).
- Eval? Yes — Policy can go green on 100% deterministic fixtures before any model assist (Ch 38).
- Org boundary? Sometimes — if a vendor owns the email gateway, Outreach is already a trust boundary.
Resolution vs Intake is weaker: often same process, different modules, until Profile’s data plane must not see draft copy. Default to modules; graduate to agents when a row in the table lights up.
Anti-pattern autopsy: five personas in one process, one API key, shared scratchpad. You paid the coordination tax (Ch 16–17) and gained zero blast-radius reduction. Refactor back.
Cost of a bad split
Every extra agent process multiplies: credentials to rotate, prompts to version, eval surfaces to maintain, and conflict surfaces on the ledger. That cost is justified when SoD or blast radius demands it. It is not justified for “the Resolution persona sounds cooler.”
A useful review question in design docs: If we merged these two agents into functions tomorrow, which invariant would we lose? If the answer is only “roleplay flavor,” merge them. If the answer is “Outreach’s send credential would sit beside Policy’s rule pack in one mount,” keep the split — and prove it with authz tests (Ch 27).
Single-process multi-agent
mode=multi does not require Kubernetes on day one. Four workers as functions behind contracts already buy SoD if tool mounts and credentials differ. Process isolation comes later when blast radius or org boundaries demand it (Ch 32–33). Do not confuse “we named four agents” with “we isolated four trust domains.”
Chapter summary
- Split for permissions, context, models, latency, org boundaries, isolation, eval — not roleplay.
- Debate ≠ truth; clocks and rule packs are not committee topics.
mode=single|multishould share tool semantics.- Blast radius is the test of isolation.
- Modules first; agents when boundaries are real.
- SoD lives in authz and mounts.
- Unjustified splits should be easy to undo.
- Orchestration comes next (Ch 18); communication and conflict before that (Ch 16–17).
Exercises
- Take a design with five persona agents; rewrite as modules. List which splits you kept and why.
- Draw the SoD matrix for Intake / Resolution / Policy / Outreach — read vs write vs send.
- Implement a negative test: Outreach worker cannot append Profile-only ledger keys (extend ledger ACLs in a later chapter; stub the test now).
- Find one public multi-agent failure writeup; map it to a row in the valid/invalid table. [VERIFY SOURCE]
References
- Anthropic. Building Effective Agents. 2024. [VERIFY URL]
- Industry multi-agent failure writeups — cite specifically per claim. [VERIFY SOURCE]
- OWASP LLM Top 10 — excessive agency / permission themes. [VERIFY edition]