前言
2025 年 AI Agent 已經不稀奇了,真正開始捲的是 Multi-Agent — 讓多個 Agent 自己協調工作、分工合作。
單一 Agent 能力再強,碰到複雜任務還是會遇到瓶頸:context window 塞不下、一個 prompt 要兼顧搜尋 + 撰寫 + 校稿 + 生圖 + 語音合成,品質很難穩定。把任務拆給多個專精的 Agent,每個只做一件事,反而更可控。
但問題來了:多個 Agent 之間怎麼協調? 這篇整理兩種主流架構模式的差異,並用我實際做的 Podcast 自動生成管線當例子,分享為什麼我最後選了 Supervisor 模式。
兩種主流架構模式
在規劃 Multi-Agent 系統時,通常會碰到兩種設計模式:
1. Network / Peer-to-Peer(點對點協作)
Agent 之間直接互相溝通,沒有中心控制者。每個 Agent 可以自行決定要找誰對話、傳遞什麼資訊。
┌─────────┐ ┌─────────┐
│ Agent A │◄───►│ Agent B │
└────┬────┘ └────┬────┘
│ │
▼ ▼
┌─────────┐ ┌─────────┐
│ Agent C │◄───►│ Agent D │
└─────────┘ └─────────┘
(每個 Agent 都可以跟任意 Agent 溝通)
適合場景:開放性討論、腦力激盪、多觀點辯論 風險:流程難預測、容易陷入無限迴圈、Debug 困難
2. Supervisor(主管模式)
有一個中央 Supervisor Agent 負責評估目前狀態,決定下一步要派誰上場。Worker Agent 執行完後回報結果,由 Supervisor 決定後續動作。
┌────────────┐
│ Supervisor │
│ (協調者) │
└─────┬──────┘
┌──────────┼──────────┐
▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐
│Worker A │ │Worker B │ │Worker C │
│(搜尋) │ │(撰寫) │ │(校稿) │
└─────────┘ └─────────┘ └─────────┘
(Worker 只跟 Supervisor 溝通,彼此不直接對話)
適合場景:有明確流程的任務管線、需要品質把關、要可追蹤進度 優勢:流程可控、分工明確、容錯好設計
比較表
面向 | Network (P2P) | Supervisor |
|---|---|---|
控制方式 | 去中心化,Agent 自行協商 | 中央控制,Supervisor 分派 |
流程可預測性 | ❌ 低,路徑不固定 | ✅ 高,有明確的狀態機 |
Debug 難度 | 😰 高,訊息流向複雜 | 😌 低,每步都有 log |
適合任務 | 開放性討論、創意發想 | 流程明確的生產管線 |
擴展性 | 新增 Agent 需考慮通訊拓撲 | 新增 Worker 只需註冊到 Supervisor |
容錯設計 | 各 Agent 自行處理 | Supervisor 統一管理降級策略 |
為什麼我選 Supervisor 模式
核心原因:Supervisor 只需要知道「誰能做什麼」,而 Worker 只需要知道「怎麼把這件事做好」。
這個分工非常乾淨:
-
Supervisor 負責「流程編排」— 誰先做、誰後做、失敗了怎麼辦
-
Worker 負責「專業執行」— 搜新聞就搜新聞、寫稿就寫稿、校稿就校稿
這跟軟體工程的 單一職責原則(SRP) 一樣 — 每個 Agent 只做一件事,做好就回報。
而且在實務上,大多數 AI 自動化任務都有明確的前後依賴關係(搜尋完才能寫、寫完才能校),Supervisor 模式天然適合這種管線型工作流。
跨 Agent 怎麼協調?共享狀態是唯一溝通管道
在 Supervisor 模式裡,Agent 之間不直接通訊。所有協調都透過一個共享的 State 物件:
┌─────────────────────────────────────────────┐
│ 共享狀態(Shared State) │
│ │
│ rawSources ← Research 寫入 │
│ podcastMarkdown ← Writer 寫入 │
│ editorFeedback ← Editor 寫入 │
│ coverImagePath ← CoverArt 寫入 │
│ audioPath ← TTS 寫入 │
│ errors ← 所有 Agent 可寫入(append 模式) │
│ │
│ Supervisor 讀取 State → 決定下一步 │
└─────────────────────────────────────────────┘
每個 Agent 的溝通方式:
-
讀:從 State 拿到自己需要的輸入(例如 Writer 讀
rawSources) -
寫:把自己的產出寫回 State(例如 Writer 寫
podcastMarkdown) -
不需要知道:前一步是誰做的、下一步是誰接手
這樣的好處是 Agent 完全解耦 — 你可以隨時替換任何一個 Agent 的實作(例如把 Gemini Writer 換成 Claude Writer),只要輸入輸出的 State 欄位不變,其他 Agent 完全不受影響。
LangGraph State 管理機制:Annotation 與 Reducer
LangGraph 用 Annotation 來定義共享狀態,這是整個 Multi-Agent 協調的基礎。
核心概念:每個欄位可以有自己的 reducer
一般的 State 管理是「後寫覆蓋前寫」,但在 Multi-Agent 場景下這樣會出問題。例如 Agent A 記了一個 error,Agent B 又記了一個 error,如果沒有 reducer,B 的 error 會把 A 的覆蓋掉。
LangGraph 的解法是讓每個欄位定義自己的 reducer(合併策略):
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: () => ({}),
}),
})
Reducer 運作方式
Reducer 類型 | 行為 | 適用場景 |
|---|---|---|
無(預設) | 後寫覆蓋前寫 | 單一 Agent 產出的欄位( |
append | 新值追加到陣列 | 多個 Agent 都可能寫入的欄位( |
merge | 合併物件 key | 計數器、狀態追蹤( |
custom | 自訂邏輯 | 任何特殊需求 |
為什麼這很重要?
沒有 reducer 的 Multi-Agent 系統,最常見的 bug 就是狀態被意外覆蓋。想像這個場景:
-
CoverArt Agent 和 TTS Agent 平行執行
-
CoverArt 遇到錯誤,寫入
errors: [{ agent: 'coverArt', message: '...' }] -
TTS 也遇到錯誤,寫入
errors: [{ agent: 'tts', message: '...' }] -
沒有 append reducer → TTS 的 error 覆蓋 CoverArt 的 error → 你永遠不知道封面生成失敗了
有了 append reducer,兩個 error 都會被保留。這是 Multi-Agent 系統 State 管理的基本功。
設計概念:用 LangGraph StateGraph 實作 Supervisor
我用 LangGraph 的 StateGraph 來實作 Supervisor。核心思路是:
-
共享狀態(State):所有 Agent 讀寫同一個 State 物件(上面講的 Annotation)
-
節點(Node):每個 Agent 是一個節點
-
邊(Edge):定義節點之間的執行順序
-
條件路由(Conditional Edge):Supervisor 根據 State 決定下一步走哪條路
START → Research → Writer → Editor ─┬─→ Publisher → [CoverArt + TTS] → END
▲ │
│ (score < 60, retry ≤ 2)
└───────────────┘
回饋迴路:帶著問題清單重寫
實作案例:Podcast 自動生成管線
我用這個架構做了一個「科技新聞 Podcast 自動生成器」,6 個 Agent 各司其職:
Agent | 職責 | 使用技術 |
|---|---|---|
🔍 ResearchAgent | 搜尋最新 AI 新聞(8-12 則) | Claude CLI + web_search |
✍️ WriterAgent | 將新聞素材寫成 Podcast 文章 | Gemini 3.1 Pro |
📝 EditorAgent | 品質評分 + 清理 AI 痕跡 | Gemini 3.1 Pro + regex |
📦 PublisherAgent | 組裝 frontmatter、寫入 MDX | Filesystem |
🎨 CoverArtAgent | 生成封面圖 | Gemini Image + sharp |
🎙️ TTSAgent | 生成多角色對話語音 | Gemini + VoAI TTS |
完整架構圖
┌─────────────────────────────────────────────────────────────────────┐
│ 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 │
└─────────────────────────────────────────────────────────────────────┘
程式碼重點解析
1. 共享狀態定義(LangGraph Annotation)
所有 Agent 讀寫同一個 State,用 Annotation 定義資料結構與 reducer:
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: () => ({}),
}),
})
💡 重點:errors 用 append reducer,不會被後面的 Agent 覆蓋掉前面的錯誤紀錄。
2. StateGraph 組裝:節點 + 邊 + 條件路由
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()
}
💡 重點:addConditionalEdges 就是 Supervisor 的核心 — 根據目前 State 決定下一步走哪條路。
3. Worker Agent 的實作(以 WriterAgent 為例)
每個 Worker 只做一件事:接收 State → 執行任務 → 回寫結果:
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',
}
}
Worker 不需要知道自己是第幾次被叫、前後是誰,它只管「把稿子寫好」。
回饋迴路:品質守門員模式
這是 Supervisor 模式最強大的地方 — 可以設計回饋迴路(Feedback Loop)。
Writer ──寫稿──▶ Editor ──評分──┬──▶ ✅ 通過 → Publisher
│
❌ 不合格(score < 60)
│
帶回饋重寫(最多 2 次)
│
└──▶ 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'
}
類比:Coding Agent 場景
同樣的模式可以套在程式碼審查上:
Coding Agent ──提交代碼──▶ Supervisor Agent ──檢查──┬──▶ ✅ 通過 → Merge
│
❌ 資安漏洞 / 不符規定
│
帶回饋改程式碼(最多 N 次)
│
└──▶ Coding Agent
Supervisor 只需要評估「這段 code 有沒有問題」,Coding Agent 只需要「根據回饋修正」。兩邊職責清楚,不會互相干擾。
容錯與降級策略
在 Multi-Agent 系統裡,不是每個 Agent 都同樣重要。好的 Supervisor 要設計分級容錯:
失敗情境 | 處理方式 | 嚴重度 |
|---|---|---|
Research 搜尋失敗 | Writer 用 LLM 自身知識生成 | ⚠️ 降級 |
Editor 多次不合格 | 帶警告繼續發布 | ⚠️ 降級 |
CoverArt 生成失敗 | 文章照常發布(無封面) | 💡 可接受 |
TTS 合成失敗 | 文章照常發布(無音檔) | 💡 可接受 |
Publisher 寫入失敗 | 中止管線 | 🔴 硬性失敗 |
💡 設計原則:核心流程(Research → Write → Edit → Publish)失敗要降級處理,附加功能(Cover、TTS)失敗不應影響主線。
注意事項 / PS
實務踩坑
-
共享狀態要用 reducer:如果多個 Agent 同時寫入
errors,不用 append reducer 會互相覆蓋。LangGraph 的Annotationreducer 是救命的 -
平行執行要注意獨立性:CoverArt 和 TTS 可以平行,是因為它們沒有資料依賴。如果有依賴就必須串行
-
回饋迴路要設上限:沒有
MAX_RETRIES的回饋迴路 = 無限迴圈。我設 2 次,超過就帶警告繼續 -
不同 Agent 可以用不同模型:Research 用 Claude(搜尋能力強)、Writer/Editor 用 Gemini(便宜 + 長 context)、TTS 用專門的語音 API,混搭反而效果更好
效能參考
模式 | 耗時 | 說明 |
|---|---|---|
純文字(skip-tts + skip-cover) | ~2 分鐘 | Research → Writer → Editor → Publisher |
含封面 | ~3 分鐘 | 加上 CoverArt 生成 |
完整管線 | ~4 分鐘 | CoverArt + TTS 平行執行 |
什麼時候不該用 Supervisor
-
如果任務本身就是開放性的(例如多 Agent 辯論、創意發想),Supervisor 反而會限制 Agent 的自主性
-
如果只有 2 個 Agent 且流程是線性的,直接串接就好,不需要搬出 Supervisor 架構
結論
Multi-Agent 的核心不是「Agent 越多越好」,而是分工明確 + 流程可控。
Supervisor 模式的三個設計原則:
-
Supervisor 管流程,Worker 管執行 — 單一職責、各司其職
-
回饋迴路保品質 — Editor/Reviewer 當守門員,不合格就打回去
-
分級容錯 — 核心流程降級、附加功能 graceful fail
如果你也在規劃 Multi-Agent 系統,建議先想清楚「這個任務有沒有明確的前後順序」。有的話,Supervisor 模式是最穩的起點。




























留言