Lesson 3 of 6 in Agentic Architecture & Orchestration
1.3 · Orchestration Patterns
Once you know you're building an agentic system, the next question is how to structure it. There are four canonical orchestration patterns: chaining, routing, parallelization, and orchestrator-workers. Each fits a specific problem shape. The exam tests whether you can look at a scenario and match it to the right pattern — and whether you can reject architectures that look sophisticated but solve the wrong problem.
1.3.1 Prompt chaining — sequential dependency
Break a task into ordered steps, each with its own prompt. The output of step N feeds step N+1. You use chaining when the pipeline has clear handoffs and you can verify (or improve) each stage before it feeds the next.
Chaining is the workflow-shaped pattern. Steps are known in advance; you wrote them. The value comes from letting each step have a focused prompt, focused inputs, and focused evaluation — versus one megaprompt trying to do everything.
# Content generation chain outline = llm(prompt=OUTLINE_PROMPT, input=brief) draft = llm(prompt=DRAFT_PROMPT, input=outline) edited = llm(prompt=EDIT_PROMPT, input=draft) final_html = llm(prompt=FORMAT_PROMPT, input=edited)
1.3.2 Routing — one-of-N specialization
A classifier prompt inspects the incoming request and dispatches it to one of several specialized downstream prompts. Use routing when input types vary widely enough that a single 'do-everything' prompt would compromise on each — accuracy on one class comes at the cost of accuracy on another.
The typical shape is a small, cheap classifier at the top and larger specialist prompts (or entire agents) below. This is different from an orchestrator that plans dynamically: the router picks EXACTLY ONE downstream lane, based on a known taxonomy.
# Support router category = classify(request) # billing | technical | returns | account if category == "billing": return billing_agent(request) elif category == "technical": return technical_agent(request) elif category == "returns": return returns_workflow(request) elif category == "account": return account_agent(request)
1.3.3 Parallelization — sectioning and voting
Two flavours. Sectioning splits a task into INDEPENDENT chunks, runs them in parallel, and aggregates. Use for embarrassingly-parallel work: processing 50 files, reviewing 10 sections of a contract, generating slides for 8 topics.
Voting runs the SAME task multiple times and combines the results — usually via majority vote or a consensus rule. Voting is the right answer when reliability matters more than cost: content moderation, safety filtering, high-stakes classification. You trade compute for correctness.
# Sectioning: independent work in parallel results = parallel_map(review_file, files) # each independent report = aggregate(results) # Voting: same work, N times verdicts = [classify(item) for _ in range(5)] final = majority_vote(verdicts)
1.3.4 Orchestrator-workers — dynamic decomposition
An orchestrator LLM PLANS the task at runtime and dispatches worker LLMs (usually subagents) to execute the pieces. Workers return summaries; the orchestrator synthesizes. This is the pattern behind multi-agent research systems, dynamic troubleshooting agents, and open-ended investigation tools.
This is where 'orchestrator-workers' diverges sharply from a workflow: in a workflow YOU wrote the steps; in orchestrator-workers the ORCHESTRATOR writes them at runtime based on what the task actually needs.
1.3.5 Parallel dispatch: emit multiple Task calls in one turn
When you spawn subagents via the Agent SDK's Task tool, don't dispatch them one at a time. Emit MULTIPLE Task tool_use blocks in the SAME assistant response — the SDK executes them in parallel, cutting total wall-clock time roughly by the number of workers. Serial dispatch is a common performance mistake in coordinator prompts, and the exam tests it directly.
# ❌ Serial: three round trips, three wait times
assistant turn 1: Task(agent="research_a")
→ wait → results back
assistant turn 2: Task(agent="research_b")
→ wait → results back
assistant turn 3: Task(agent="research_c")
# ✅ Parallel: one round trip, workers run concurrently
assistant turn 1:
Task(agent="research_a", ...)
Task(agent="research_b", ...)
Task(agent="research_c", ...)
# → tool_results come back together after all three finish1.3.6 Picking the right pattern
The decision framework: sequential dependency → chain. Known input variety with distinct downstream paths → route. Independent parallel subtasks → parallelize (sectioning). Same task needing high reliability → parallelize (voting). Unknown decomposition, the plan itself must be decided at runtime → orchestrator-workers. Real systems mix these: a router's downstream branches can each be chains, agents, or workflows in their own right.
Takeaways
- Chain for sequential dependency with knowable steps
- Route when input types are known and each needs a specialized downstream prompt
- Sectioning for speed on independent subtasks; voting for reliability on the same task
- Orchestrator-workers only when the plan itself must be decided at runtime
- Parallel Task calls in one response are the SDK-native way to fan out subagents
Exam traps
Practice scenario
A research assistant needs to answer open-ended questions like 'How has EV charging infrastructure evolved in Europe over the past 3 years?' The final answer requires investigating regulations, manufacturers, charging networks, and consumer adoption — but which subtopics matter varies per question. Which orchestration pattern fits best?