Study guides / CCAR-P / Domain 2

Solution Design & Architecture · Lesson 4 of 6

2.4 - Multi-Agent Systems and Orchestration Strategy

Decide whether multi-agent is justified, choose an orchestration topology, design what context crosses agent boundaries, propagate errors so failures are visible, and account for the token cost multiplier.

A multi-agent system adds a layer of model-directed delegation on top of everything in lesson 2.3, and Anthropic's own numbers show it is expensive. The architect's job is therefore sequenced: first decide whether multi-agent is justified, then choose a topology, then design the context that crosses each boundary and the way failures travel back. Most exam scenarios in this area are solved by tracing a symptom (missing coverage, unsourced claims, a silently absent section, a runaway bill) to the boundary where the design broke.

Is multi-agent justified?

Anthropic's guidance names three situations where separate agents consistently help. Context protection: a subagent works in its own context window, and only its final message returns to the parent, so bulky intermediate material (dozens of files read, large lookups) never accumulates in the main conversation. Parallelization: independent subtasks finish in the time of the slowest rather than the sum. Specialization: separate agents with focused tools and prompts, warranted when a single agent has a large tool set, gets confused across unrelated domains, or degrades on old tasks when new tools are added.

The cost side is large. Anthropic's research-system write-up reports that agents typically use about 4× the tokens of chat interactions and multi-agent systems about 15×; a separate Anthropic post on when to use multi-agent systems cites 3 to 10× versus a single agent. The figures depend on workload and measurement, so treat them as an order of magnitude and measure your own. The research write-up says multi-agent systems fit valuable tasks with heavy parallelization, information beyond a single context window and many complex tools, and are a poor fit for most coding tasks and for work where all agents need the same context or have many dependencies. Anthropic's cost guidance adds that an orchestrator only pays off when there is bulk to hand off; for one dependent chain, or work that fits in a single context, it pays for a plan, a handoff and a merge that a single model gets for free. Teams have also found that better prompting of one agent matched an elaborate multi-agent build.

Common exam distractor

"Split it into specialist agents for modularity" and "add more agents to fix a quality problem" are the recurring wrong answers. Modularity is not one of the justifications, and more agents multiply cost and handoff loss. The defensible answer states which of context protection, parallelization or specialization applies, shows evidence from an eval, and accounts for the token multiple. If none applies, keep the single agent.

Choosing a topology

TopologyControlUse whenWatch for
Orchestrator-workers (hub-and-spoke)A lead decomposes at runtime, delegates, synthesisesSubtasks cannot be predicted in advance: research, multi-file changesThe lead's decomposition is a single point of failure; a synchronous lead blocks on its slowest worker
Parallel fan-outYour code launches N independent workers (sectioning or voting)Subtasks are known and independent, or you want diverse attemptsAggregation logic; cost scales with N
Sequential handoffOne agent finishes and passes the work on (triage to specialist)Distinct stages need different tools or promptsEach handoff degrades fidelity, so pass structured state
Generator and verifierA separate agent checks the first agent's workYou need an independent check without full implementation contextVerifiers can pass outputs without thorough testing; give explicit criteria
Nested delegationSubagents spawn subagentsVery wide fan-outCap depth, concurrency and spend

"Handoff" is common industry vocabulary rather than a pattern name from the Anthropic documents reviewed for this lesson, so reason from its mechanics: each transfer loses context. Hub-and-spoke routing all communication through the coordinator gives observability, uniform error handling and controlled information flow. In current Agent SDK versions a subagent can itself spawn subagents, so strict hub-and-spoke is a design choice you make for those three properties, not a product limit; the SDK provides a nesting-depth limit (default 3 layers below the main agent), a concurrency limit (default 20 subagents at once) and a query-level spend cap to bound the tree. These limits are documented for recent SDK releases (TypeScript v0.3.219 and Python v0.2.127 or later), so check your version before relying on them.

Context passing across the boundary

A subagent's context starts fresh (a fork, which inherits the parent conversation, is the documented exception). In the Agent SDK the only channel from parent to subagent is the prompt string of the Agent tool call (the tool appears as Agent in current versions and Task in older ones, so match both when detecting invocations); the subagent also gets its own system prompt, project instructions and tool definitions, but not the parent's conversation history, tool results or system prompt. Only the subagent's final message comes back, and the parent may summarise it. From this follow the design rules:

Error propagation

Failures inside a worker must reach the coordinator in a form it can act on. A workable contract carries four things: the failure type (transient, validation, business rule, permission), what was attempted (query, parameters, target), partial results gathered before the failure, and alternatives the worker can suggest. Two anti-patterns break systems: silent suppression, returning an empty result marked as success, so the coordinator never retries and the report silently omits an area; and workflow termination, killing the whole run because one worker timed out and discarding the work of the others. Keep two situations apart. An access failure (timeout, connection error, denied permission) means the query did not run and may deserve a retry. A valid empty result means the query ran and found nothing, which is the answer and needs no retry.

Let workers recover transient problems locally (retry, fallback source) and escalate only what they cannot fix, always with attempted action and partial results. In the final output, add coverage annotations that say which areas are well supported and which are limited and why. Anthropic's experience is that telling an agent when a tool is failing and letting it adapt works well, combined with retries and checkpoints so a long run resumes rather than restarts. Note also that in the Agent SDK an API error that ends a subagent early, such as a rate limit, is not delivered as its result, so the coordinator needs a rule for a worker that never reports.

Key concept: shared nothing except the brief and the reply

Subagents share nothing with the coordinator or with each other except the prompt the coordinator writes and the message they return. Every multi-agent failure can be located at one of those two channels or at the decomposition: missing scope points to the coordinator's decomposition, missing citations or context to the brief or the structured reply, invisible gaps to error propagation, and surprise cost to unbounded fan-out.

Exam traps

Practice question

A team proposes replacing a working single-agent support assistant (six tools, meets most eval targets) with five agents: triage, order, billing, tone and a supervisor, 'for modularity'. Evals show the current agent fails mainly on rare multi-step billing disputes. Which recommendation is best?

  • A Adopt the five-agent design because modular systems are easier to maintain, extend and test, each agent can be owned by a different team, and the supervisor keeps the whole conversation flow under central control

    Modularity is not one of the reasons multi-agent pays off, and the design would multiply token cost and add handoff loss while the evidence points to a narrow failure class.

  • B Adopt the five agents but let them message each other directly instead of routing through the supervisor, to avoid coordinator latency, reduce the number of model calls and let specialists hand work over quickly

    Direct agent-to-agent traffic bypasses the coordinator's observability, error handling and control over context, and it does not address the lack of a justification for splitting.

  • C Replace the agent with a single call to a larger model and no tools, since coordination overhead is the real problem and a stronger model can answer billing and order questions from its own knowledge

    Order and billing data are live system records that need tools or code lookups, and nothing shows a model-capability gap. It also abandons a design that already meets most targets.

  • D Keep the single agent and target the billing-dispute failures first (prompt, tools or a specialist route); add subagents only if evals show context bloat, tool confusion or a parallelism need, and measure the token multiple Correct

    It starts from evidence, addresses the observed failure and applies the justifications (context protection, parallelization, specialization) only when they are demonstrated. Cost is measured, not assumed.

Build exercise: Test whether multi-agent earns its cost

Advanced · 2.5 hours

You'll practice:

  1. Pick a research-style task over a folder of documents (for example: compare three vendors' security policies). Write 8 test queries and run a single agent on them, recording quality, total tokens and wall-clock time.

    Without a baseline you cannot tell whether multi-agent improved anything or only raised the bill.

    You should see: A table of 8 queries with a quality score, token total and duration for the single agent.

    Hints
    1. What would 'better' mean for these queries, and how will you score it consistently?
    2. Write a short rubric (coverage of each vendor, every claim has a source, no unsupported claims) and score each answer 0 to 2 on each item.
    3. Use the Agent SDK with allowed_tools=['Read', 'Grep', 'Glob'] and no agents defined; read total_cost_usd from the final ResultMessage as a cost proxy alongside duration.
  2. Define two subagents with AgentDefinition, each with a specific description, a system prompt and a read-only tools list, and include the Agent tool in the coordinator's allowed tools.

    Restricting tools and describing when each agent applies is what lets the coordinator delegate sensibly and limits the blast radius.

    You should see: A run in which the coordinator delegates at least one subtask and messages from inside a subagent carry a parent_tool_use_id.

    Hints
    1. What makes a description specific enough that the coordinator chooses the right agent?
    2. State when to use the agent and what it returns; give each agent only the tools it needs.
    3. agents = {'policy-reader': AgentDefinition(description='Reads one vendor security policy and returns structured findings. Use for per-vendor extraction.', prompt='Extract findings on encryption, access control and incident response. Return JSON: a findings list of objects with claim, source_file, quote and confidence.', tools=['Read', 'Grep', 'Glob'])}
      options = ClaudeAgentOptions(allowed_tools=['Read', 'Grep', 'Glob', 'Agent'], agents=agents)
  3. Write the coordinator's delegation brief template (objective, output format, tools and sources guidance, task boundaries) and require each subagent to return findings with claim, source and confidence rather than prose.

    The brief is the only channel into the subagent and the structured reply is the only channel out. Vague briefs cause duplicated work; stripped metadata causes unsourced claims.

    You should see: A template with all four elements, and subagent outputs that always include a source for each claim.

    Hints
    1. If two subagents received your brief for different vendors, would they produce comparable outputs?
    2. State the objective and boundary (only this vendor, only these folders), the exact output schema, and what to do when nothing is found.
    3. Brief: 'Objective: extract security findings for {vendor}. Sources: only files under docs/{vendor}/. Output: JSON list of {claim, source_file, quote, confidence}. Boundary: do not compare vendors. If a topic is not covered, return an entry with status not_found rather than omitting it.'
  4. Define an error contract (failure type, attempted action, partial results, alternatives) and inject failures: make one vendor's folder unreadable and give another an empty topic. Check that the final report distinguishes an access failure from a valid empty result and annotates coverage.

    This tests the two anti-patterns directly: silent suppression and workflow termination, and the access-failure versus empty-result distinction.

    You should see: A final report that states which vendor could not be read and why, states which topics were searched and found empty, and still contains the successful vendors' findings.

    Hints
    1. What must the coordinator know to decide between retrying and reporting a gap?
    2. Add the contract to the brief, and add a rule: on an unreadable source return status access_failed with the attempted path; on a successful search with no hits return status empty.
    3. Error reply shape: {'status': 'access_failed', 'failure_type': 'permission', 'attempted': 'Read docs/vendorB/', 'partial_results': [], 'alternatives': ['ask for access', 'use vendorB public trust page']}. Valid empty: {'status': 'empty', 'searched': 'incident response in docs/vendorA/'}.
  5. Run the multi-agent version on the same 8 queries with bounds: a query-level spend cap and limits on subagent nesting depth and concurrency. Record quality, tokens or cost, and time.

    Cost multiplication is the central risk. Bounds turn it from a surprise into a design parameter.

    You should see: A second table beside the baseline, and evidence that the limits are enforced (for example a budget-capped result subtype if you set the cap low).

    Hints
    1. Which of the three limits would stop a runaway fan-out soonest?
    2. Set max_budget_usd on the options, and pass the two documented environment variables through env for depth and concurrency (these need a recent SDK release; check the subagents page for the minimum version).
    3. options = ClaudeAgentOptions(allowed_tools=['Read', 'Grep', 'Glob', 'Agent'], agents=agents, env={'CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH': '1', 'CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTS': '5'}, max_budget_usd=2.0)
      # on each message: if isinstance(message, ResultMessage): print(message.subtype, message.total_cost_usd)
  6. Write a decision record: is multi-agent justified for this task? Cite which of context protection, parallelization or specialization applied, the measured quality gain, and the token multiple.

    The task statement is about design justification. A defensible 'no' is as valid as a 'yes' when the numbers say so.

    You should see: A record with the baseline and multi-agent numbers side by side, the justification category, and the conditions under which you would revisit the decision.

    Hints
    1. Did the extra agents fix a failure the baseline actually had?
    2. Compare quality per token across the two runs, and name the justification you can prove from the eval rather than the one you expected.
    3. Example: Multi-agent raised rubric score from [a] to [b] at [x] times the cost; benefit came from parallel per-vendor reading (parallelization). Decision: adopt for comparisons across 4 or more vendors, single agent otherwise. Revisit if worker outputs stop preserving sources.

Sources