What this system is, and its 5 parts
Think of this as how you ask one question and get back a properly cited briefing from many sources at once. A coordinator splits the question into the obvious sub-questions (visual arts, music, writing, film, performing arts. Not just the first one that comes to mind), spawns a small team of researchers in parallel, each works alone in their lane, then a separate fact-checker reconciles anything they disagree on, and a final writer turns the verified findings into a single readable report with citations. The whole point is that one big agent thinking by itself misses things; a small team with the right division of labour does not.
Coordinator Agent
the hub of hub-and-spokeReceives the user query, performs SEMANTIC decomposition (not lexical) into all relevant sub-domains, spawns research subagents in parallel with explicit task prompts, awaits all results, routes findings to verification, hands verified claims to synthesis. Owns every cross-subagent communication path.
ConfigurationDecomposition is the load-bearing step. For 'impact of AI on creative industries' the coordinator must enumerate visual + music + writing + film + performing arts, not stop at the first sub-domain. Spawn pattern: asyncio.gather (Python) / Promise.all (TS).Concept: subagents →
Research Subagent (parallel)
scoped tools, isolated contextOne subagent per sub-domain. Receives an explicit task prompt. No inherited history, no parent context. Runs research with a narrow tool whitelist (Read, WebSearch, Bash). Returns structured findings JSON: {claim, sources: [{url, date, confidence}]}. Never editorialises; reports facts as stated.
Configurationsystem: "You are a research specialist. Find authoritative sources. Return JSON {findings: [{claim, sources: [...]}]}." tools: [Read, WebSearch, Bash]. messages: [{role: "user", content: task_from_coordinator}].Concept: tool-calling →Verification Subagent
fact-check + reconcile contradictionsCross-checks all claims from research subagents. When two sources conflict (45% Pew vs 12% McKinsey), preserves both with their context and attribution rather than picking the 'more likely' one. Returns verified claims with confidence scores; the verification step is what protects the report from misinformation.
ConfigurationInput: pooled claims from all research subagents. Output: {verifications: [{claim, verified, confidence, sources_reconciled, notes}]}. Notes field captures the context that explains apparent contradictions (different timeframes, definitions, populations).Concept: evaluation →Synthesis Subagent
read-only narrative generatorReceives verified claims + the coordinator's narrative prompt. Writes a cohesive Markdown report with inline citations [1], [2]. CRITICAL: tools restricted to Read only. No WebSearch, no Bash. This prevents re-research and keeps synthesis focused on stitching the verified facts into a story.
Configurationsystem: "You are a synthesis specialist. Read verified findings and write a cited narrative. Do NOT research." tools: [Read]. Input: {verified_claims, narrative_prompt}. Output: Markdown with [n] citations.Concept: context-window →Error Propagation Layer
structured timeout contextWhen a subagent times out or hits a dead end, returns structured error context the coordinator can act on: {status: 'timeout', query, partial_results, alternatives}. Coordinator inspects status_code and either retries with a narrower scope, accepts partial data, or transparently marks the gap in the final report.
ConfigurationOn timeout: {status: 'timeout', query, partial, alternatives: ['narrower query', 'different keywords', ...]}. On no_results: {status: 'no_results', query, alternatives}. Never return [] as success. Silence loses the failure context.Concept: structured-outputs →The problem
- Complete coverage of the research topic. Every relevant sub-domain enumerated, none silently dropped.
- Reconciled contradictions preserved with attribution, not flattened into one 'most likely' number.
- Cited final report that traces every claim to a verifiable source and acknowledges data gaps.
- Coordinator decomposes 'creative industries' into only visual arts. Misses music, writing, film, performing arts.
- Web-search subagent times out and returns empty results as success. Coordinator treats as 'no info' instead of 'needs retry'.
- Synthesis picks the 'more likely' statistic between 45% (Pew) and 12% (McKinsey). Drops the conflict, ships misinformation.
- ✓ Topic-coverage gap rate = 0 (decomposition reviewed before spawn)
- ✓ Timeout-as-empty-results rate = 0 (structured error context required)
- ✓ Contradiction-preservation rate = 100% (both stats + sources retained)
- ✓ Subagent-to-subagent direct call rate = 0 (all routes through coordinator)
One run, traced end to end
8 steps to production
Build the coordinator's semantic decomposition
The decomposition step is where most coverage failures actually originate. Analyse the topic semantically and enumerate ALL relevant sub-domains before spawning anything. For 'creative industries', that means visual arts AND music AND writing AND film AND performing arts. Not the first one that comes to mind. The decomposition is the coordinator's load-bearing responsibility.
Concept: subagents →from typing import List
def decompose_query(query: str) -> List[str]:
"""Semantic decomposition. Enumerate all relevant sub-domains.
The exam-question distractor is to blame subagents for narrow
coverage when the coordinator's decomposition was the bug.
"""
q = query.lower()
if "creative industries" in q:
domains = [
"visual arts (digital art, graphic design, photography)",
"music production and composition",
"writing (novels, journalism, screenwriting)",
"film and video production",
"performing arts (theater, dance)",
]
elif "healthcare" in q:
domains = [
"clinical decision support",
"medical imaging and diagnostics",
"drug discovery and trials",
"patient-facing communication",
"administrative + revenue cycle",
]
else:
# Generic fallback. STILL decompose, never single-shot
domains = [
f"{query}. Recent academic literature",
f"{query}. Industry case studies",
f"{query}. Empirical adoption data",
]
return [f"Find AI impact on {d}" for d in domains]Define subagent system prompts and tool whitelists
Every subagent gets its own system prompt + scoped tool list. Research subagents get [Read, WebSearch, Bash]; verification gets [Read, WebSearch, Bash] + a fact-check rubric; synthesis gets [Read] only. That read-only restriction is the architectural detail that prevents synthesis from re-researching mid-narrative.
Concept: tool-calling →Spawn research subagents in parallel
All research subagents fire at once via async fan-out. Latency is max(subagents), not sum. The whole point of the architecture. Each subagent receives an explicit task prompt with the context it needs; nothing is inherited from the coordinator's history. Cost: N separate API calls. Worth it.
Concept: agentic-loops →Return structured error context, never silence
When a subagent times out or hits no results, the WORST thing it can do is return []. The coordinator then can't tell whether 'no info exists' or 'we never got the data'. A critical distinction for the final report. Always return a structured error: status + query + partial_results + alternatives. The coordinator inspects status_code and decides: retry, narrow, or transparently mark the gap.
Concept: structured-outputs →Run the verification subagent and preserve contradictions
Pool all claims from research subagents and pass them to a single verification subagent. When two sources conflict (45% Pew vs 12% McKinsey), the verification subagent's job is NOT to pick a winner. It is to preserve both with their context (different definitions, different timeframes, different populations) and attribute each to its source. Picking one is misinformation; preserving both is journalism.
Concept: evaluation →Run the synthesis subagent with READ-ONLY tools
Synthesis is the final step. It receives verified claims + the coordinator's narrative prompt, and emits Markdown with inline citations. The crucial detail: the synthesis subagent's tool list is [Read] only. No WebSearch, no Bash. That restriction prevents it from re-researching mid-narrative (a common failure mode where synthesis fact-checks itself again and inflates latency 3-5×).
Concept: context-window →Route ALL communication through the coordinator
If subagent B needs a finding from subagent A, the answer is NOT to call A from B. The answer is: A finishes, returns to coordinator, coordinator passes the finding into B's task prompt. This single rule preserves isolation (each subagent has clean context), parallelism (when dependencies allow), and visibility (the coordinator owns the whole orchestration graph).
Concept: subagents →Cap parallelism and add retry budgets
Parallel fan-out has diminishing returns past 5-7 subagents. API concurrency limits, context-window contention on the coordinator side, and rate-limit backpressure all kick in. Cap concurrency, set a retry budget per subagent (typically 2 retries with narrowed queries), and emit telemetry: spawn count, parallel max, retry rate, partial-data rate. These metrics are how you tune the system in production.
Concept: evaluation →9 decisions the exam turns into distractors
Tune subagent prompts or upgrade their model
Audit the coordinator's decomposition first. It almost always lives there
Empty list [], marked as success
Structured error: {status: 'timeout', query, partial_results, alternatives}
Synthesis picks the 'more likely' one and drops the other
Verification preserves both with attribution + context (different definitions, different timeframes)
A calls B directly with the finding
A returns to coordinator; coordinator passes A's finding into B's task prompt
Coordinator decomposes 'creative industries' into visual arts only; report misses music, writing, film, performing arts. Subagents finished successfully. The bug is upstream.
Fix the coordinator's semantic decomposition. Enumerate ALL relevant sub-domains before spawning. The decomposition step is the coordinator's load-bearing responsibility.
Web-search subagent times out, returns []. Coordinator treats as 'no information exists' instead of 'timed out'. Final report has a silent gap.
Return structured error: {status: 'timeout', query, partial_results, alternatives}. Coordinator inspects status_code, retries with a narrower scope, or marks the gap transparently in the report.
Synthesis subagent calls verify_fact for 100 claims sequentially. 80 are simple (Wikipedia), 20 complex. Total 60+ seconds for what should be 10.
Scope verify_fact narrowly: simple-claim batch verification (parallel, ~3s) + dedicated complex-verification subagent (parallel, pre-synthesis). Synthesis assumes facts are pre-verified.
Two sources conflict (45% any-use Pew vs 12% daily-use McKinsey). Synthesis picks the 'more likely' one; the other is dropped. Report is misinformation.
Preserve both at the verification step with sources_reconciled + notes explaining the apparent conflict. Synthesis presents both with attribution. Reader sees both numbers and why they differ.
Researcher A (papers) directly hands a finding to Researcher B (web). Isolation breaks; parallelism degrades to sequential; coordinator loses visibility.
Route everything through the coordinator. A returns to coordinator; coordinator constructs B's task prompt with A's finding embedded. Hub-and-spoke is non-negotiable.
Cost & latency
3 subagents × ~20K input + ~2K output ≈ $0.06; 5 subagents ≈ $0.10. Parallel: latency = max(subagents) ≈ 3-5s, cost = sum.
Reads pooled findings (~15K) + cross-checks (~10K) + emits verifications (~1K) ≈ $0.04. Single pass, serial.
Reads verified findings (~5K) + generates Markdown narrative (~3K). Read-only tools keep cost low; no re-research.
Narrowed retries (~10K input + 1K output). Cap at 2 retries per subagent to bound cost; beyond that, accept partial data and mark the gap.
Decompose ~0.5s + parallel research ~5s + verification ~3s + synthesis ~3s + coordinator overhead. Subagents in parallel save ~10s vs sequential.
Check every gate before release
5 exam-pattern questions
Work through one question at a time, check the architecture, then move through the set.
A research system decomposes 'impact of AI on creative industries' into three subtopics: visual arts, music, writing. The web-search subagent finds results for all three. The synthesis subagent produces a report covering only visual arts. Why?
Frequently asked
Should subagents run in parallel or in sequence?
Parallel whenever possible. Independent research tasks (visual arts, music, writing) all run at once via asyncio.gather / Promise.all. Cost: N separate API calls. Latency: max(N) ≈ 5-8s, not sum. Sequence only when there's a true data dependency (B needs A's output). And even then, the coordinator handles the chaining; subagents never call each other.
Can subagents inherit the coordinator's conversation history?
No. Subagents are isolated by design. That's the architectural win. The coordinator passes context explicitly in the subagent's task prompt: User asked: [query]. Key context so far: [pinned facts]. Your task: [focused research goal]. Subagent starts fresh with only what's in the task. Inheriting history defeats parallelism and bloats per-subagent context cost.
What happens if multiple subagents return partial / timeout results?
Coordinator collects what came back, invokes synthesis with a narrative-prompt note: Research is incomplete due to timeouts on [X, Y]. The report should acknowledge gaps in those areas explicitly. Transparency beats silence. The reader sees a report that says 'we got these 3 sub-domains; the other 2 timed out' rather than a confidently-misleading report missing 2 whole sub-domains.
Should the synthesis subagent have web-search access?
No. Read-only is the architectural detail. Synthesis stitches verified findings into a narrative; it does not re-research. If synthesis needs to verify a fact mid-sentence, that's a sign verification should have been broader upstream. Fix the verification phase, not the synthesis tool list. Read-only also caps the latency and cost of synthesis predictably.
How do we handle contradictions surfaced by research subagents?
Don't resolve them at the subagent level. Pass conflicting findings to the verification subagent with sources intact. Verification reconciles: preserves both with attribution + a notes field explaining the conflict (different timeframes, definitions, populations, methodologies). Synthesis then presents both with context. The reader gets transparency; the system avoids fabricating false certainty.
Can a subagent spawn another subagent (nested)?
In theory yes; in practice avoid it. Nested subagents increase latency, complicate context flow, and obscure the orchestration graph from the coordinator. Keep the hierarchy shallow: coordinator → leaf subagents. If you need 'meta-research' (one subagent's job is to figure out what to research), have the coordinator do that decomposition step explicitly.
What's the maximum number of subagents to run in parallel?
No hard limit, diminishing returns past 5-7. API concurrency limits, rate-limit backpressure, and context-window contention on the coordinator side all start kicking in. Use a bounded semaphore (MAX_PARALLEL = 5), measure latency at different fan-outs, and tune to your workload. For 10+ tasks, consider Batch API or sequential task chains.
