Background
By 2025, AI Agents are nothing new. The real competition has shifted to Multi-Agent systems โ letting multiple agents coordinate, divide work, and collaborate autonomously.
No matter how capable a single agent is, it hits a wall on complex tasks: the context window overflows, and trying to pack search + writing + editing + image generation + speech synthesis into one prompt makes quality unpredictable. Breaking the task into multiple specialized agents, each doing one thing, actually gives you more control.
But that raises the question: how do multiple agents coordinate? This post compares the two dominant architectural patterns and explains why I ended up choosing the Supervisor model for my podcast auto-generation pipeline.
Two Dominant Architectural Patterns
When designing a Multi-Agent system, you typically encounter two design patterns:
1. Network / Peer-to-Peer
Agents communicate directly with each other โ no central controller. Each agent can decide on its own who to talk to and what to pass along.
โโโโโโโโโโโ โโโโโโโโโโโ
โ Agent A โโโโโโบโ Agent B โ
โโโโโโฌโโโโโ โโโโโโฌโโโโโ
โ โ
โผ โผ
โโโโโโโโโโโ โโโโโโโโโโโ
โ Agent C โโโโโโบโ Agent D โ
โโโโโโโโโโโ โโโโโโโโโโโ
๏ผๆฏๅ Agent ้ฝๅฏไปฅ่ทไปปๆ Agent ๆบ้๏ผ
Best for: open-ended discussions, brainstorming, multi-perspective debates. Risks: unpredictable flow, easy to fall into infinite loops, difficult to debug.
2. Supervisor
A central Supervisor Agent evaluates the current state and decides which agent to dispatch next. Worker agents execute their task and report back; the Supervisor decides what happens next.
โโโโโโโโโโโโโโ
โ Supervisor โ
โ (ๅ่ชฟ่
) โ
โโโโโโโฌโโโโโโโ
โโโโโโโโโโโโผโโโโโโโโโโโ
โผ โผ โผ
โโโโโโโโโโโ โโโโโโโโโโโ โโโโโโโโโโโ
โWorker A โ โWorker B โ โWorker C โ
โ(ๆๅฐ) โ โ(ๆฐๅฏซ) โ โ(ๆ ก็จฟ) โ
โโโโโโโโโโโ โโโโโโโโโโโ โโโโโโโโโโโ
๏ผWorker ๅช่ท Supervisor ๆบ้๏ผๅฝผๆญคไธ็ดๆฅๅฐ่ฉฑ๏ผ
Best for: task pipelines with clear steps, quality gates, and trackable progress. Advantages: predictable flow, clear separation of concerns, straightforward fault tolerance.
Comparison
Dimension | Network (P2P) | Supervisor |
|---|---|---|
Control | Decentralized, agents negotiate | Centralized, Supervisor dispatches |
Predictability | โ Low, paths are dynamic | โ High, explicit state machine |
Debug difficulty | ๐ฐ High, complex message flows | ๐ Low, every step is logged |
Best task type | Open-ended discussion, creative work | Production pipelines with defined steps |
Extensibility | Adding agents requires rethinking topology | Just register a new Worker with the Supervisor |
Fault tolerance | Each agent handles its own errors | Supervisor owns the degradation strategy |
Why I Chose the Supervisor Pattern
The core reason: the Supervisor only needs to know "who can do what," and Workers only need to know "how to do this one thing well."
That separation is clean:
- Supervisor handles "orchestration" โ who goes first, who goes next, what happens on failure.
- Workers handle "specialized execution" โ search is search, writing is writing, editing is editing.
This mirrors the Single Responsibility Principle (SRP) in software engineering โ each agent does exactly one thing and reports back when done.
In practice, most AI automation tasks have clear sequential dependencies (you need the search results before you can write, and the draft before you can edit). The Supervisor pattern is a natural fit for this kind of pipeline workflow.
How Agents Coordinate: Shared State Is the Only Communication Channel
In the Supervisor pattern, agents never communicate directly with each other. All coordination flows through a shared State object:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ ๅ
ฑไบซ็ๆ
๏ผShared State๏ผ โ
โ โ
โ rawSources โ Research ๅฏซๅ
ฅ โ
โ podcastMarkdown โ Writer ๅฏซๅ
ฅ โ
โ editorFeedback โ Editor ๅฏซๅ
ฅ โ
โ coverImagePath โ CoverArt ๅฏซๅ
ฅ โ
โ audioPath โ TTS ๅฏซๅ
ฅ โ
โ errors โ ๆๆ Agent ๅฏๅฏซๅ
ฅ๏ผappend ๆจกๅผ๏ผ โ
โ โ
โ Supervisor ่ฎๅ State โ ๆฑบๅฎไธไธๆญฅ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Each agent's interaction pattern is simple:
- Read: pull its required input from State (e.g., Writer reads
rawSources) - Write: push its output back to State (e.g., Writer writes
podcastMarkdown) - Doesn't need to know: who ran before it, or who runs next
This means agents are fully decoupled. You can swap any agent's implementation at any time (e.g., replace a Gemini Writer with a Claude Writer) โ as long as the State field contracts remain the same, no other agent is affected.
LangGraph State Management: Annotations and Reducers
LangGraph uses Annotation to define shared state โ this is the foundation of all Multi-Agent coordination.
Core concept: each field can have its own reducer
Ordinary state management is "last write wins," but that breaks down in Multi-Agent scenarios. For example, if Agent A records an error and Agent B records an error, without a reducer B's error would overwrite A's.
LangGraph's solution is to let each field define its own reducer (merge strategy):
export const PodcastAnnotation = Annotation.Root({
// ๆฎ้ๆฌไฝ๏ผๅพๅฏซ่ฆ่๏ผ้ ่จญ่ก็บ๏ผ
date: Annotation<string>,
podcastMarkdown: Annotation<string>,
// append reducer๏ผๆฐๅผ่ฟฝๅ ๅฐ้ฃๅ๏ผไธ่ฆ่่ๅผ
errors: Annotation<AgentError[], AgentError[]>({
reducer: (existing, update) => [...existing, ...update],
default: () => [],
}),
// merge reducer๏ผๅไฝต็ฉไปถ็ key-value
retryCount: Annotation<Record<string, number>>({
reducer: (existing, update) => ({ ...existing, ...update }),
default: () => ({}),
}),
})
How reducers work
Reducer type | Behavior | Use case |
|---|---|---|
None (default) | Last write wins | Fields written by a single agent ( |
append | New value appended to array | Fields multiple agents may write ( |
merge | Merge object keys | Counters, state tracking ( |
custom | Any custom logic | Special requirements |
Why does this matter?
The most common bug in Multi-Agent systems without reducers is state being silently overwritten. Consider this scenario:
- CoverArt Agent and TTS Agent run in parallel.
- CoverArt hits an error and writes
errors: [{ agent: 'coverArt', message: '...' }]. - TTS also hits an error and writes
errors: [{ agent: 'tts', message: '...' }]. - Without an append reducer, TTS's error overwrites CoverArt's โ you'll never know the cover generation failed.
With an append reducer, both errors are preserved. This is table-stakes for Multi-Agent state management.
Design Concept: Implementing Supervisor with LangGraph StateGraph
I use LangGraph's StateGraph to implement the Supervisor. The core ideas are:
- Shared State: all agents read from and write to the same State object (using
Annotationas described above). - Nodes: each agent is a node.
- Edges: define execution order between nodes.
- Conditional Edges: the Supervisor reads State to decide which path to take next.
START โ Research โ Writer โ Editor โโฌโโ Publisher โ [CoverArt + TTS] โ END
โฒ โ
โ (score < 60, retry โค 2)
โโโโโโโโโโโโโโโโโ
ๅ้ฅ่ฟด่ทฏ๏ผๅธถ่ๅ้กๆธ
ๅฎ้ๅฏซ
Implementation: Podcast Auto-Generation Pipeline
I built a "Tech News Podcast Auto-Generator" using this architecture. Six agents, each with a dedicated role:
Agent | Responsibility | Technology |
|---|---|---|
๐ ResearchAgent | Search for the latest AI news (8-12 items) | Claude CLI + web_search |
โ๏ธ WriterAgent | Turn news sources into a podcast article | Gemini 3.1 Pro |
๐ EditorAgent | Quality scoring + removing AI artifacts | Gemini 3.1 Pro + regex |
๐ฆ PublisherAgent | Assemble frontmatter and write MDX file | Filesystem |
๐จ CoverArtAgent | Generate cover image | Gemini Image + sharp |
๐๏ธ TTSAgent | Generate multi-voice dialogue audio | Gemini + VoAI TTS |
Full Architecture Diagram
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ LangGraph StateGraph โ
โ (Supervisor ๅ่ชฟ่
) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ โโโโโโโโโโโโ โโโโโโโโโโโโ โโโโโโโโโโโโ โโโโโโโโโโโโโโโโ โ
โ โ Research โโโโโถโ Writer โโโโโถโ Editor โโโโโถโ Publisher โ โ
โ โ Agent โ โ Agent โโโโโโ Agent โ โ Agent โ โ
โ โ โ โ โๅ้ฅ โ โ โ โ โ
โ โ Claude โ โ Gemini โ่ฟด่ทฏ โ Gemini โ โ Filesystem โ โ
โ โ CLI + โ โ 3.1 Pro โ(โค2) โ 3.1 Pro โ โ MDX ๅฏซๅ
ฅ โ โ
โ โweb_searchโ โ โ โ โ โ โ โ
โ โโโโโโโโโโโโ โโโโโโโโโโโโ โโโโโโโโโโโโ โโโโโโโโฌโโโโโโโโ โ
โ โ โ
โ โโโโโโโโโโโโดโโโโโ โ
โ โ ๅนณ่กๅท่ก โ โ
โ โโโโโโผโโโโโ โโโโโโโโผโโโ โ
โ โCoverArt โ โ TTS โ โ
โ โ Agent โ โ Agent โ โ
โ โ Gemini โ โ Gemini โ โ
โ โ Image + โ โ + VoAI โ โ
โ โ sharp โ โ โ โ
โ โโโโโโฌโโโโโ โโโโโโฌโโโโโ โ
โ โผ โผ โ
โ END END โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Key Code Walkthrough
1. Shared State Definition (LangGraph Annotation)
All agents read and write the same State. Annotation defines the data structure and reducers:
import { Annotation } from '@langchain/langgraph'
export const PodcastAnnotation = Annotation.Root({
// โโโ ่ผธๅ
ฅ โโโ
date: Annotation<string>,
config: Annotation<PodcastConfig>,
// โโโ ๅ้ๆฎต็ขๅบ โโโ
rawSources: Annotation<NewsSource[]>, // Research ๅฏซๅ
ฅ
podcastMarkdown: Annotation<string>, // Writer ๅฏซๅ
ฅ
editorFeedback: Annotation<EditorFeedback>, // Editor ๅฏซๅ
ฅ
cleanedContent: Annotation<string>, // Editor ๅฏซๅ
ฅ
coverImagePath: Annotation<string | null>, // CoverArt ๅฏซๅ
ฅ
audioPath: Annotation<string | null>, // TTS ๅฏซๅ
ฅ
// โโโ ๆงๅถๆฌไฝ โโโ
errors: Annotation<AgentError[]>({
reducer: (existing, update) => [...existing, ...update],
default: () => [],
}),
retryCount: Annotation<Record<string, number>>({
reducer: (existing, update) => ({ ...existing, ...update }),
default: () => ({}),
}),
})
Key point: errors uses an append reducer so later agents can't overwrite earlier error records.
2. StateGraph Assembly: Nodes + Edges + Conditional Routing
import { StateGraph, END } from '@langchain/langgraph'
export function buildGraph() {
const graph = new StateGraph(PodcastAnnotation)
// ่จปๅ็ฏ้ป๏ผๆฏๅ Agent ้ฝๆฏไธๅ function๏ผ
.addNode('research', researchAgent)
.addNode('writer', writerAgent)
.addNode('editor', editorAgent)
.addNode('publisher', publisherAgent)
.addNode('coverArt', coverArtAgent)
.addNode('tts', ttsAgent)
// ้ ๅบๅท่ก
.addEdge('__start__', 'research')
.addEdge('research', 'writer')
.addEdge('writer', 'editor')
// ๆขไปถ่ทฏ็ฑ๏ผEditor ๆฑบๅฎ่ฆ้ๅฏซ้ๆฏๆพ่ก
.addConditionalEdges('editor', routeAfterEditor, {
publisher: 'publisher',
writerRetry: 'writerRetry',
})
// Publisher ๅพ๏ผๅนณ่กๅท่ก CoverArt + TTS
.addConditionalEdges('publisher', routeAfterPublisher, {
coverArt: 'coverArt',
tts: 'tts',
end: 'end',
})
.addEdge('coverArt', END)
.addEdge('tts', END)
return graph.compile()
}
Key point: addConditionalEdges is the Supervisor's core mechanism โ it reads the current State and decides which path to take next.
3. Worker Agent Implementation (WriterAgent as an Example)
Each Worker does exactly one thing: receive State, execute the task, write back the result:
export async function writerAgent(
state: PodcastState
): Promise<Partial<PodcastState>> {
const model = await createVertexModel({
model: state.config.writerModel,
temperature: 0.8,
})
// ๅฆๆๆฏ retry๏ผๅธถๅ
ฅ Editor ็ๅ้ฅ
let feedbackContext = ''
if (state.editorFeedback && !state.editorFeedback.passed) {
feedbackContext = state.editorFeedback.issues
.map(i => `- ${i}`)
.join('\n')
}
const prompt = buildWriterPrompt(state.date, newsContext) + feedbackContext
const response = await model.invoke([{ role: 'user', content: prompt }])
return {
podcastMarkdown: response.content.trim(),
currentStep: 'writing_done',
}
}
The Worker doesn't need to know which iteration it's on, who ran before it, or who runs next. It just focuses on producing a good draft.
Feedback Loop: The Quality Gatekeeper Pattern
This is the most powerful aspect of the Supervisor pattern โ you can design feedback loops.
Writer โโdraftโโโถ Editor โโscoreโโโฌโโโถ โ
Pass โ Publisher
โ
โ Fail (score < 60)
โ
Rewrite with feedback (max 2 retries)
โ
โโโโถ Writer
function routeAfterEditor(state: PodcastState): string {
// ้้ โ ไธไธๆญฅ
if (state.editorFeedback?.passed) return 'publisher'
// ๆฒ้้ไฝ้ๆ้่ฉฆๆฌกๆธ โ ๅธถๅ้ฅ้ๅฏซ
const retries = state.retryCount?.['writer'] ?? 0
if (retries < MAX_WRITER_RETRIES) return 'writerRetry'
// ่ถ
้้่ฉฆๆฌกๆธ โ ๅธถ่ญฆๅ็นผ็บ
return 'publisher'
}
Analogy: Coding Agent Scenario
The same pattern applies to code review:
Coding Agent โโsubmit codeโโโถ Supervisor Agent โโreviewโโโฌโโโถ โ
Pass โ Merge
โ
โ Security issue / violation
โ
Fix with feedback (max N retries)
โ
โโโโถ Coding Agent
The Supervisor only evaluates "does this code have problems," and the Coding Agent only "fixes based on feedback." Responsibilities are clear and don't bleed into each other.
Fault Tolerance and Degradation Strategies
In a Multi-Agent system, not every agent is equally critical. A well-designed Supervisor needs tiered fault tolerance:
Failure scenario | Handling | Severity |
|---|---|---|
Research search fails | Writer generates from LLM's own knowledge | โ ๏ธ Degrade |
Editor fails repeatedly | Publish with warning | โ ๏ธ Degrade |
CoverArt generation fails | Article publishes without cover | ๐ก Acceptable |
TTS synthesis fails | Article publishes without audio | ๐ก Acceptable |
Publisher write fails | Abort the pipeline | ๐ด Hard failure |
Design principle: failures in the core pipeline (Research โ Write โ Edit โ Publish) need graceful degradation; failures in supplementary features (Cover, TTS) should not impact the main flow.
Notes and Gotchas
Practical pitfalls
- Use reducers for shared state: if multiple agents write to
errorssimultaneously without an append reducer, they'll overwrite each other. LangGraph'sAnnotationreducer is a lifesaver. - Parallel agents must be truly independent: CoverArt and TTS can run in parallel because they have no data dependency on each other. If there is a dependency, sequential execution is required.
- Always cap feedback loops: a feedback loop without
MAX_RETRIESis an infinite loop. I set mine to 2; after that, the pipeline continues with a warning. - Different agents can use different models: Research uses Claude (strong search reasoning), Writer/Editor use Gemini (cheap + long context), TTS uses a dedicated voice API. Mixing models often yields better results than using one model for everything.
Performance reference
Mode | Time | Description |
|---|---|---|
Text only (skip-tts + skip-cover) | ~2 min | Research โ Writer โ Editor โ Publisher |
With cover | ~3 min | Adds CoverArt generation |
Full pipeline | ~4 min | CoverArt + TTS running in parallel |
When not to use Supervisor
- If the task is inherently open-ended (multi-agent debates, creative brainstorming), Supervisor actually limits agent autonomy.
- If you only have 2 agents and the flow is linear, just chain them directly โ no need to bring in the full Supervisor architecture.
Conclusion
The core of Multi-Agent isn't "more agents = better." It's clear division of labor + predictable flow.
Three design principles for the Supervisor pattern:
- Supervisor owns orchestration, Workers own execution โ single responsibility, each does its job.
- Feedback loops ensure quality โ the Editor/Reviewer acts as gatekeeper, sending work back when it doesn't pass.
- Tiered fault tolerance โ core pipeline degrades gracefully; supplementary features fail gracefully.
If you're planning a Multi-Agent system, I'd suggest first asking yourself: "does this task have a clear sequential order?" If yes, the Supervisor pattern is the most stable starting point.



























Comments