Frontier Engineering
← Multi-Agent Architectures

Orchestrator–Worker

Also known as: supervisor, manager agent, lead-subagent

A lead agent decomposes a task, dispatches independent subtasks to fresh workers, and synthesizes their returns into one answer.

10 min read

The problem

Some tasks are too large for one context window and too parallel for one sequential loop. Researching six vendors, auditing forty files, or reconciling five data sources in a single agent means the context fills with the residue of earlier subtasks, later reasoning degrades under the accumulated noise, and everything happens strictly one step at a time. Worse, an error in step two silently contaminates every step after it.

The pattern

Split the roles. An orchestrator owns the objective, decomposes it into independent subtasks, and never does the work itself. Each subtask goes to a worker with a clean context, a narrow brief, and its own tools. Workers return compact structured results rather than transcripts. The orchestrator synthesizes those results, decides whether to dispatch another round, and produces the final answer.

Use when

  • The task decomposes into subtasks that genuinely do not depend on each other
  • Total work exceeds what fits usefully in one context window
  • Subtasks are search- or analysis-heavy, where parallelism buys real wall-clock time
  • You want failure isolation: one bad subtask should not poison the whole run

Avoid when

  • The task is inherently sequential — each step needs the previous step's output
  • A single agent with good tools already succeeds; this multiplies token cost several-fold
  • Subtasks need to share evolving state, which workers with isolated contexts cannot do
  • Latency matters more than depth and the orchestration round-trips dominate

Tradeoffs

  • GainEffective context scales with worker count; each worker reasons on a clean slate

    CostToken spend multiplies — commonly 4–15× a single-agent run for the same objective

  • GainIndependent subtasks run concurrently, collapsing wall-clock time

    CostWorkers cannot see each other's findings, so they duplicate work and miss cross-cutting insights

  • GainA failed worker is contained and retryable without restarting the run

    CostDecomposition quality becomes the dominant failure mode, and it is hard to evaluate


Orchestrator–worker is the multi-agent shape that most often earns its cost, because it exploits the one thing a single agent cannot buy at any price: several clean context windows working at once.

The orchestrator holds the objective; the workers hold the tools. Nothing crosses between workers.

The orchestrator does not do the work

The most common implementation mistake is an orchestrator that also has the domain tools and starts using them. It fills its own context with detail, loses the plot on the objective, and the workers become decoration. Give the orchestrator exactly two capabilities: dispatch a worker, and finish. Everything else belongs to workers.

ORCHESTRATOR_SYSTEM = """You coordinate a team of workers. You never do research or
write code yourself.

For each subtask you dispatch, write a brief containing:
- the exact question the worker must answer
- what a complete answer looks like
- what is out of scope for this worker

Dispatch independent subtasks together in one turn. Workers cannot see each other
or talk to each other, so never assume shared knowledge between them, and never
split one indivisible question across two workers.

When you have enough to answer the objective, call finish."""

ORCHESTRATOR_TOOLS = [dispatch_worker, finish]  # deliberately nothing else

Briefs, not prompts

Worker quality tracks brief quality almost linearly. A vague brief — "look into pricing" — produces a worker that wanders, returns a wall of text, and overlaps with two other workers. A brief that names the question, the deliverable shape, and the boundary produces something the orchestrator can actually compose. Making the brief a structured object rather than a free-text string forces the orchestrator to fill in the parts it would otherwise skip.

class Brief(BaseModel):
    question: str            # the single question this worker answers
    deliverable: str         # what a complete answer contains
    out_of_scope: list[str]  # what another worker is handling
    max_steps: int = 12


class WorkerResult(BaseModel):
    answer: str                    # compact; not a transcript
    sources: list[str]
    confidence: Literal["high", "medium", "low"]
    unresolved: str | None         # what it could not determine


async def run_worker(brief: Brief) -> WorkerResult:
    # Fresh context. The orchestrator's history is deliberately NOT inherited.
    return await agent_loop(
        system=WORKER_SYSTEM,
        task=brief.model_dump_json(),
        tools=WORKER_TOOLS,
        max_steps=brief.max_steps,
        response_schema=WorkerResult,
    )


async def dispatch_round(briefs: list[Brief]) -> list[WorkerResult]:
    results = await asyncio.gather(*(run_worker(b) for b in briefs), return_exceptions=True)
    return [
        r if isinstance(r, WorkerResult)
        else WorkerResult(answer="", sources=[], confidence="low", unresolved=str(r))
        for r in results
    ]
warning

Workers returning full transcripts defeats the entire pattern. If a worker's 40k-token exploration comes back verbatim, the orchestrator's context fills exactly as a single agent's would — you paid for parallelism and got none of the context savings. Enforce a structured, bounded return type.

Sizing the fan-out

Orchestrators over-decompose when left to themselves, spawning a worker per trivial lookup. Constrain the fan-out explicitly and scale it to the objective: a simple comparison needs two or three workers, a broad survey ten. State the guidance in the system prompt and enforce a hard ceiling in code, because token cost scales linearly with worker count and an unbounded orchestrator is a budget incident.

  • Orchestrator holds the objective and the synthesis; workers hold the tools
  • Briefs are structured — question, deliverable, out-of-scope — never free text
  • Workers return compact structured results, never transcripts
  • Cap fan-out per round and total rounds; enforce in code, not just in the prompt
  • Run a second round only when the first leaves a named, specific gap
tip

Log every brief alongside its result. When output quality is poor, the cause is visible in that pairing far more often than in the worker's internals — usually two briefs that overlap, or one that asked for something unanswerable.