intermediatearchitecture-patternsworkfloworchestrator-workersdecompositionpatterns

Chapter 4 of 5

Orchestrator-Workers

The workflow that blurs into agency — a central LLM dynamically breaks down the task, delegates to workers, and synthesizes. Subtasks aren't predefined.

Orchestrator-Workers

In the orchestrator-workers workflow, a central LLM dynamically breaks down tasks, delegates them to worker LLMs, and synthesizes their results. (source)

              ┌→ [worker: file A] ─┐
[task] → [orchestrator] ┼→ [worker: file B] ─┼→ [synthesize] → output
              └→ [worker: file C] ─┘

How it differs from parallelization

This is the key distinction, and it's easy to miss:

  • Parallelization fans out to predefined subtasks. You wrote down the branches in advance.
  • Orchestrator-workers fans out to subtasks the orchestrator decides on, based on the specific input.

Topographically they look similar — one input, many workers, one aggregated output. The difference is who decides the workers: you (parallelization) or the model (orchestrator-workers).

When to use it

This pattern fits complex tasks where you can't predict the subtasks.

In coding, for example: the number of files that need to change, and the nature of the change in each, depends on the task. A "refactor the auth module" task might touch 2 files or 12 — you can't know until someone looks. The orchestrator looks, decides which workers to spin up, and each worker handles one file.

# The orchestrator decides the plan
plan = orchestrator_llm(f"Break this task into file-level subtasks: {task}")
# plan = [{"file": "auth/login.ts", "change": "..."}, {"file": "auth/token.ts", "change": "..."}]

# Workers execute in parallel, each on one file
results = [worker_llm(subtask) for subtask in plan]

# Synthesize — the orchestrator (or you) merge and verify
final = merge_and_verify(results)

Other examples:

  • Search tasks that gather and analyze information from multiple sources, where the relevant sources depend on the query.
  • Any task where the work unit is "go figure out what needs doing, then do the pieces."

The bridge to full agents

Orchestrator-workers is the pattern where a workflow starts to feel like an agent — the model is making decisions about what to do. But it's still bounded: it decides the breakdown once, executes, and stops. A full agent (next course) loops this — decide, act, observe, decide again. If orchestrator-workers is a fan-out, an agent is a fan-out that keeps fanning until the goal is met.

When NOT to use it

If you can predict the subtasks, use plain parallelization. Orchestrator-workers adds an LLM call (the orchestrator's planning step) and a synthesis step. That's worth it when the planning genuinely varies; it's waste when the plan is always the same.

# If the plan is always the same, just write it:
results = [worker_a(task), worker_b(task), worker_c(task)]  # parallelization
# Don't pay an orchestrator to rediscover a fixed plan.

What you've now seen

You now know four workflows — chaining, routing, parallelization, orchestrator-workers — plus the augmented LLM they all stand on. The fifth workflow, evaluator-optimizer, is the one that turns a workflow into a loop, and it's where the next course picks up.

The thread connecting all of them: the path is partly or fully written down, and complexity is added only when it demonstrably helps. That discipline is what separates a system that ships from one that spirals.


Next: Designing Tools for Agents (ACI) — the unsung skill that determines whether any of these patterns actually work.