Multi-agent patterns

Orchestrator and worker, critic and reflection, context isolation and hand-off protocols — and why more agents rarely means more capability.

The patterns worth knowing

Splitting one agent into several is an engineering technique, not an intelligence upgrade. Every additional agent is another model call, another failure point and another log stream. Use a pattern only when it buys something specific.

PatternStructureWhat it buysWhat it costs
Orchestrator and workersOne planner dispatches isolated subtasksContext isolation; independent subtasks run in parallelFan-out multiplies tokens and latency; aggregation can lose detail
Critic and reflectionGenerator plus a reviewer that scores against criteriaCatches errors a single pass missesExtra call per round; a vague critic approves everything
PipelineFixed sequence of specialistsEach stage gets a narrow prompt and toolsetRigid; no adaptation when input is unexpected
DebateAgents argue, then a judge decidesUseful for judgement under ambiguityExpensive, non-deterministic, hard to evaluate
Hand-offOne agent transfers control to anotherFocus per phase, without one giant promptThe receiving agent loses everything the sender knew

The single real benefit is context isolation. A worker that sees only its own subtask and its own tools makes fewer mistakes than one carrying the entire conversation. If your split does not isolate context, it is just extra calls.

Orchestrator and workers

def orchestrate(goal, max_workers=4):
    subtasks = plan_subtasks(goal)                 # model call, schema-validated
    subtasks = subtasks[:max_workers]

    results = parallel_map(
        lambda t: run_worker(t),                   # each gets a FRESH context
        subtasks,
    )
    return synthesise(goal, results)               # one call, all results in

def run_worker(subtask):
    # no history, no other subtasks, only the tools this task needs
    tools = select_tools(subtask["tool_scope"])
    return agent(subtask["instruction"], tools=tools, max_steps=4)
  • Give each worker only the tools it needs. Tool selection inside a worker is where most cross-contamination happens.
  • Cap the fan-out. Four workers at eight steps each is thirty-two model calls, and provider rate limits will make the wall-clock time worse than running sequentially.
  • Make workers return structured results with a stable shape, so the synthesis step reads data rather than prose.
  • Keep the orchestration decision outside the workers. A worker that can spawn more workers is a recursion you did not budget for.
  • Parallel writes need coordination: two workers updating the same record will fight. Serialise writes through the orchestrator.
def orchestrator_with_writes(goal):
    reads = [t for t in plan_subtasks(goal) if t["kind"] == "read"]
    reads_done = parallel_map(run_worker, reads[:4])

    writes = plan_writes(goal, reads_done)         # planned with real results
    for w in writes:                               # serialised, never parallel
        run_worker(w)
    return synthesise(goal, reads_done)

Critic and reflection

A critic works only if it has explicit criteria and the authority to fail the output. A critic asked whether the answer looks good will nearly always say yes, because agreement is the cheapest continuation.

CRITERIA = [
    "Every claim cites a source id returned by a tool",
    "No number appears that is not present in a tool result",
    "The question asked in the goal is answered directly in the first sentence",
]

def generate_then_review(goal, max_rounds=2):
    draft = agent(goal)
    for _ in range(max_rounds):
        verdict = critic(goal=goal, draft=draft, criteria=CRITERIA)
        # verdict: {"failures": [{"criterion": 0, "evidence": "...", "fix": "..."}]}
        if not verdict["failures"]:
            return draft
        draft = revise(goal, draft, verdict["failures"])
    return draft                       # cap reached: return best effort, flagged
  • Number the criteria and make the critic cite the criterion index. Vague complaints cannot be acted on.
  • Cap the rounds. Reflection loops converge slowly and are the most common cause of runaway cost.
  • Use the critic as a gate, not a co-author: it should describe what is wrong, and the generator should fix it.
  • Log every verdict. The failures the critic repeatedly finds tell you what to fix in the generator's prompt.
⚠️
Multi-agent designs multiply cost, latency and the number of places a failure can hide, while making the trace much harder to read. Exhaust the single-agent options first: a clearer goal, better tool descriptions, a checklist in the prompt. Most tasks that look like they need a second agent need a better first one.

FAQ

How many agents is too many?
If you cannot draw the control flow, or if any agent can spawn agents without a fixed budget, it is already too many. Two or three with clear roles and a fixed fan-out is a practical ceiling for most production work.
Do agents need to talk to each other?
Rarely. Structured results passed through an orchestrator are easier to validate, log and evaluate than free-form agent-to-agent conversation. Keep the dialogue out of the system unless the dialogue is the product.

Planning and task decomposition Evaluating agents

Last refreshed 2026-09-18.