Orchestrator-Workers Pattern
Central orchestrator delegating to specialized sub-agents.
Learning Objectives
- Design hub-and-spoke agent architectures
- Implement task decomposition and delegation
- Manage results aggregation from workers
Orchestrator-Workers Pattern
The orchestrator-workers pattern is one of the most powerful and frequently tested agentic architectures on the CCA-F exam. It introduces a central orchestrator agent that dynamically breaks down complex tasks, delegates subtasks to specialized worker agents, and aggregates their results into a coherent final output. Unlike prompt chaining, where the developer defines the exact sequence of steps, the orchestrator itself decides how to decompose the task — making this pattern genuinely agentic.
Hub-and-Spoke Architecture
The orchestrator-workers pattern follows a hub-and-spoke model. The orchestrator is the hub — it receives the original task, analyzes what needs to be done, creates a plan, and then dispatches work to spoke agents (the workers). Each worker is specialized: it has its own system prompt, its own set of tools, and its own focused scope. Workers do not communicate with each other directly — all coordination flows through the orchestrator.
This architecture provides several benefits:
- Separation of concerns: Each worker focuses on a narrow domain, reducing prompt complexity and improving output quality.
- Scalability: New workers can be added without modifying the orchestrator's core logic — just register a new specialist.
- Parallel execution: Independent subtasks can be dispatched to workers concurrently, reducing wall-clock time.
- Error isolation: A failure in one worker does not necessarily crash the entire system — the orchestrator can retry, substitute, or work around the failure.
Task Decomposition Strategies
The orchestrator's most critical responsibility is task decomposition — deciding what to delegate and to whom. There are several approaches:
LLM-Driven Decomposition
The orchestrator uses an LLM call to analyze the task and produce a structured plan. This is the most flexible approach — the orchestrator can handle novel tasks it has never seen before — but it introduces the risk of poor decomposition (e.g., missing subtasks or creating unnecessary ones).
Template-Based Decomposition
For well-understood task types, the orchestrator uses predefined templates. For example, a "write a blog post" task always decomposes into: research → outline → draft → edit. The template approach is more predictable but less adaptable to novel inputs.
Hybrid Approach
Use templates for known task types and fall back to LLM-driven decomposition for unfamiliar tasks. The orchestrator first classifies the task (routing!), then applies the appropriate decomposition strategy.
Worker Specialization
Each worker agent should have a clearly defined scope:
- Focused system prompt: The worker's instructions should describe exactly what it does and nothing more. A research worker should not be instructed to also format output — that's a different worker's job.
- Relevant tools only: Give each worker only the tools it needs. A search worker gets the web search tool. A code writer gets the file system tool. Giving every worker every tool leads to confusion and misuse.
- Clear input/output contract: Define what the worker expects as input and what format its output should be in. This makes the orchestrator's aggregation step predictable.
Results Aggregation
After workers complete their subtasks, the orchestrator must combine their outputs into a coherent final result. Common aggregation strategies include:
- Concatenation: Simply joining outputs (e.g., sections of a report written by different workers).
- Synthesis: Using an LLM call to read all worker outputs and produce a unified summary or answer.
- Structured merge: Combining JSON outputs from workers into a single data structure (e.g., merging search results from multiple sources).
- Quality selection: If multiple workers attempted the same task (voting pattern), selecting the best output based on evaluation criteria.
Code Example: Research Orchestrator with Workers
The following example shows an orchestrator that breaks down a research question into sub-questions, delegates each to a research worker, and then synthesizes the findings.
import anthropic
import json
import concurrent.futures
client = anthropic.Anthropic()
def llm(prompt: str, system: str = "") -> str:
kwargs = {"model": "claude-sonnet-4-5-20250514", "max_tokens": 2048,
"messages": [{"role": "user", "content": prompt}]}
if system:
kwargs["system"] = system
return client.messages.create(**kwargs).content[0].text.strip()
# ---------- Orchestrator ----------
def decompose_question(question: str) -> list[str]:
"""The orchestrator breaks the question into sub-questions."""
prompt = f"""Break down the following research question into 3-5 specific,
independent sub-questions that would fully answer the original question when
combined. Return a JSON array of strings.
Research question: {question}
Respond with a valid JSON array only."""
raw = llm(prompt, system="You are a research planning assistant.")
raw = raw.removeprefix("```json").removeprefix("```").removesuffix("```").strip()
return json.loads(raw)
# ---------- Research Worker ----------
def research_worker(sub_question: str) -> dict:
"""A specialized worker that researches a single sub-question."""
answer = llm(
f"Research and answer this question in 2-3 paragraphs: {sub_question}",
system="""You are a thorough research analyst. Provide factual, detailed
answers with specific examples where possible. If you are uncertain about
something, say so explicitly."""
)
return {"question": sub_question, "findings": answer}
# ---------- Synthesis Worker ----------
def synthesize(original_question: str, findings: list[dict]) -> str:
"""Combine all research findings into a coherent answer."""
findings_text = ""
for i, f in enumerate(findings, 1):
findings_text += f"\n\nSub-question {i}: {f['question']}\n{f['findings']}"
return llm(
f"""Based on the following research findings, write a comprehensive answer
to the original question.
Original question: {original_question}
Research findings:{findings_text}
Provide a well-structured, coherent answer that synthesizes all findings.""",
system="You are a research synthesis expert."
)
# ---------- Run the orchestrator ----------
def orchestrated_research(question: str) -> str:
print(f"[Orchestrator] Decomposing: {question}")
sub_questions = decompose_question(question)
print(f"[Orchestrator] Created {len(sub_questions)} sub-questions")
# Dispatch to workers in parallel
findings = []
with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
futures = {executor.submit(research_worker, sq): sq for sq in sub_questions}
for future in concurrent.futures.as_completed(futures):
result = future.result()
print(f"[Worker] Completed: {result['question'][:60]}...")
findings.append(result)
# Synthesize results
print("[Orchestrator] Synthesizing findings...")
return synthesize(question, findings)
# Example
answer = orchestrated_research(
"What are the key considerations for deploying multi-agent AI systems in production?"
)
print(answer)Orchestrator-Workers vs. Prompt Chaining
A common exam question asks when to choose orchestrator-workers over prompt chaining:
- Use prompt chaining when the steps are known in advance, are sequential, and don't require dynamic planning. Chaining is simpler and more predictable.
- Use orchestrator-workers when the task requires dynamic decomposition (the steps aren't known until the input is analyzed), when subtasks are independent and can be parallelized, or when different subtasks require fundamentally different capabilities.
Exam Tip: The orchestrator-workers pattern is tested heavily on the CCA-F exam. Know when the overhead is justified: if the subtasks are always the same and always sequential, a prompt chain is better. The orchestrator pattern shines when the decomposition itself is dynamic — when the model needs to look at the input and decide what steps are needed. Watch for scenario questions that describe novel or variable inputs requiring adaptive task planning.
Key Takeaways
Orchestrator-workers use a hub-and-spoke model where a central agent decomposes tasks and delegates to specialized workers. The orchestrator decides what to do — workers decide how to do it.
Worker specialization is critical: each worker should have a focused system prompt, relevant tools only, and a clear input/output contract. Avoid giving every worker every capability.
Task decomposition can be LLM-driven (flexible but risky), template-based (predictable but rigid), or hybrid. Choose based on how variable your inputs are.
Choose orchestrator-workers over prompt chaining when the decomposition is dynamic, subtasks can be parallelized, or workers need fundamentally different capabilities.