多代理系统
构建协作式的多 Agent 系统,实现任务分发与结果整合
📚 学习目标
学完这篇文章后,你将能够:
- 理解多代理协作的优势与适用场景
- 掌握“主管模式(Supervisor)”架构
- 实现“层级代理(Hierarchical)”系统
- 设计代理间的切换与消息传递机制
前置知识
在开始学习之前,建议先阅读:
你需要了解:
- 单一 Agent 的局限性(Context 窗口、职责混淆)
- 模块化设计思想
1 为什么需要多代理?
就像软件开发中的“微服务”或“单一职责原则”一样,将复杂的 AI 任务拆解给多个专用的 Agent 可以:
- 提高准确性:专精的 Prompt 和工具集让 Agent 在特定领域表现更好。
- 易于维护:可以独立调试和优化每个 Agent。
- 突破限制:避免单一 System Prompt 过于复杂导致的混乱。
2 主管模式 (Supervisor)
这是最常见的多代理模式。由一个主管 Agent 负责接收用户需求,并路由给具体的工兵 Agent。
架构图
关键代码实现
需要一个特殊的路由节点(Supervisor)来决定下一个是谁。
下面给出两种常见实现方式:
- 简单版(路由 + conditional edges):主管只输出“下一站是谁”。
- 工程版(预构建 supervisor):把路由与汇总做成一个可复用组件。
方式 A:conditional edges(易理解)
import { Annotation, StateGraph, START, END } from '@langchain/langgraph';
import { ChatOpenAI } from '@langchain/openai';
import { HumanMessage, SystemMessage } from '@langchain/core/messages';
const State = Annotation.Root({
messages: Annotation<Array<{ role: string; content: string }>>({
reducer: (a, b) => a.concat(b),
default: () => [],
}),
next: Annotation<'coder' | 'writer' | 'finish'>({
default: () => 'finish',
}),
draft: Annotation<string>({
default: () => '',
}),
});
const supervisorModel = new ChatOpenAI({
model: 'gpt-4o-mini',
temperature: 0,
});
const supervisorNode = async (state: typeof State.State) => {
const decision = await supervisorModel.invoke([
new SystemMessage(
'你是主管。请输出 next 的值:coder / writer / finish。\n- coder:写代码\n- writer:写文档\n- finish:结束\n只输出一个单词。',
),
...state.messages.map((m) => new HumanMessage(m.content)),
]);
const raw = String(decision.content).trim().toLowerCase();
const next = raw.includes('coder')
? 'coder'
: raw.includes('writer')
? 'writer'
: 'finish';
return { next };
};
const coderNode = async (state: typeof State.State) => ({
draft: state.draft + '\n// coder: implement TODO...\n',
});
const writerNode = async (state: typeof State.State) => ({
draft: state.draft + '\n// writer: explain design...\n',
});
const routeNext = (state: typeof State.State) => {
if (state.next === 'coder') return 'coder';
if (state.next === 'writer') return 'writer';
return END;
};
export const multiAgentApp = new StateGraph(State)
.addNode('supervisor', supervisorNode)
.addNode('coder', coderNode)
.addNode('writer', writerNode)
.addEdge(START, 'supervisor')
.addConditionalEdges('supervisor', routeNext)
.addEdge('coder', 'supervisor')
.addEdge('writer', 'supervisor')
.compile();代码解析:
supervisorNode的职责很窄:只做决策,不做产出。coder/writer产出写回draft,然后回到 supervisor 再决策。- 通过
recursionLimit给循环上限(避免 supervisor 一直分派)。
方式 B:预构建 Supervisor(更省心)
如果你使用了对应的预构建能力,可以把多个 agent 注册给 supervisor,让它自动做选择与汇总(具体 API 以你当前版本为准)。
主管模式的结果汇总
当多个工兵返回内容后,建议由主管统一汇总与裁剪,避免回答风格不一致:
const summarizerNode = async (state: typeof State.State) => {
const prompt = `请把以下草稿汇总为清晰的最终答复:\n${state.draft}`;
const summary = await supervisorModel.invoke([new HumanMessage(prompt)]);
return { draft: String(summary.content) };
};
const app = new StateGraph(State)
.addNode('supervisor', supervisorNode)
.addNode('coder', coderNode)
.addNode('writer', writerNode)
.addNode('summarize', summarizerNode)
.addEdge(START, 'supervisor')
.addConditionalEdges('supervisor', routeNext)
.addEdge('coder', 'supervisor')
.addEdge('writer', 'supervisor')
.addEdge('supervisor', 'summarize')
.addEdge('summarize', END)
.compile();💡 设计提示
如果工兵产出较多,可以让 supervisor 输出结构化数据(如 JSON),再由 summarize 统一渲染为最终文本。
轻量通信协议
为了让 supervisor 更稳定地产生决策,可以约定统一的消息格式:
const decisionSchema = z.object({
next: z.enum(['coder', 'writer', 'finish']),
reason: z.string(),
});
const decision = await supervisorModel
.withStructuredOutput(decisionSchema)
.invoke([
new SystemMessage('请输出 next 和 reason 字段'),
...state.messages.map((m) => new HumanMessage(m.content)),
]);
return { next: decision.next };ℹ️ 说明
结构化输出能降低“口头回答”导致的解析失败,尤其适合多代理路由场景。
3 接力模式 (Handoffs)
代理之间直接传递控制权,类似于接力赛。这通常通过特殊的工具调用来实现。
示例场景
用户先联系“销售 Agent”,确定意向后,销售 Agent 将用户“转接”给“技术支持 Agent”。
// 定义一个工具,用于转移控制权
const transferToSupport = tool(
() => 'Successfully transferred to Support Agent',
{ name: 'transfer_to_support', description: 'Transfer user to support team' },
);在路由逻辑里:
如果是 transfer_to_support 工具被调用 -> 路由到 Support Node。
💡 提示
接力模式更像“业务流程”,主管模式更像“任务调度”。如果你的流程天然是 A->B->C,接力往往更直观。
路由示例:基于工具调用切换代理
import { END, StateGraph } from '@langchain/langgraph';
const routeByTool = (state: typeof State.State) => {
const last = state.messages[state.messages.length - 1];
const hasTransfer = last?.tool_calls?.some(
(call) => call.name === 'transfer_to_support',
);
if (hasTransfer) return 'support';
return END;
};
const app = new StateGraph(State)
.addNode('sales', salesNode)
.addNode('support', supportNode)
.addEdge(START, 'sales')
.addConditionalEdges('sales', routeByTool)
.addEdge('support', END)
.compile();ℹ️ 说明
通过“工具调用”来显式表达“转交”意图,可以避免模型口头说“转交”却没有触发真实路由的情况。
4 共享状态与隔离状态
在多代理系统中,状态管理变得尤为重要。
| 状态类型 | 说明 | 例子 |
|---|---|---|
| Global State | 所有代理可见,用于传递最终结果 | 消息历史、最终报告 |
| Private State | 仅特定代理可见,避免干扰 | 代理内部的思维链(Scratchpad) |
在 LangGraph 中,可以通过**子图(Subgraph)**来实现状态隔离(下一章会详细讲)。
一个常见的状态拆分策略
- 全局状态:任务目标、最终输出、全局摘要
- 私有状态:每个 agent 的 scratchpad、临时推理结果
const GlobalState = Annotation.Root({
goal: Annotation<string>(),
summary: Annotation<string>(),
});
const AgentPrivateState = Annotation.Root({
messages: Annotation<BaseMessage[]>({
reducer: messagesStateReducer,
default: () => [],
}),
scratchpad: Annotation<string>({
default: () => '',
}),
});💡 提示
如果所有代理共用同一份 messages,很容易出现“上下文污染”。优先把每个代理的消息放在自己的子图内。
5 模式对比:什么时候用哪个?
| 模式 | 核心特点 | 适合场景 | 代价 |
|---|---|---|---|
| 主管模式 | 中心化路由与汇总 | 任务不确定、需要动态分配 | 主管 prompt 设计成本 |
| 接力模式 | 明确的流程链路 | 售前->售后、申请->审批->执行 | 可扩展性受流程限制 |
| 层级模式 | 主管下面还有小主管 | 大型任务拆分(团队协作) | 状态隔离与接口设计更复杂 |
| 群体/并行 | 多个 agent 同时做子任务 | 多角度分析、并行检索 | 结果合并与冲突处理 |
6 层级代理(Hierarchical)
当任务规模很大时,主管下面可能还有“子主管”,形成层级结构:
适用场景
- 大型任务拆解(如“生成完整产品方案”)
- 需要多层汇总与审校
💡 设计要点
- 每层只做“决策或汇总”,避免职责重叠。
- 上层尽量处理“抽象信息”,下层处理“具体内容”。
7 并行协作(群体模式)
当你希望“多角度并行分析”时,可以让多个 agent 同时执行,再合并结果:
const collectResults = (state: typeof State.State) => ({
draft: [state.resultA, state.resultB, state.resultC].join('\n'),
});
const app = new StateGraph(State)
.addNode('agentA', agentA)
.addNode('agentB', agentB)
.addNode('agentC', agentC)
.addNode('merge', collectResults)
.addEdge(START, 'agentA')
.addEdge(START, 'agentB')
.addEdge(START, 'agentC')
.addEdge('agentA', 'merge')
.addEdge('agentB', 'merge')
.addEdge('agentC', 'merge')
.compile();⚠️ 注意
并行模式的关键是“结果合并策略”,建议统一输出格式或使用结构化输出。
8 常见坑:多代理最容易翻车的 3 件事
- 共享 messages:所有 agent 读写同一份 messages,导致上下文污染。
- 职责不清:工兵 agent 既要“做事”又要“决定下一步”,会与主管冲突。
- 循环失控:主管不断分派任务(或者 agent 不断调用工具)。
对应解决思路:
- 用子图隔离每个 agent 的 messages
- 主管只做“选择”,工兵只做“产出”
- 设置
recursionLimit/ retry_count,必要时引入人工审批
💡 练习题
- 设计题:设计一个“软件开发团队”的多代理系统,包含产品经理、架构师、程序员和测试员。请画出他们的协作流程图。
点击查看参考思路
可以采用“主管模式”:由 PM 代理拆分需求,架构师代理给出技术方案,程序员代理产出代码,测试代理验证结果,最后由主管汇总输出。
- 思考题:在主管模式中,如果“工兵 Agent”完成了任务,它应该把结果直接给用户,还是还给主管?由什么因素决定?
点击查看答案
取决于是否需要统一口径与一致性:若需要统一风格、去重和最终审核,应回传给主管;如果任务完全独立且无需合并,可直接响应用户。
-
操作题:为你的多代理系统添加
recursionLimit,避免主管反复分派。点击查看答案
在调用
app.invoke或app.stream时传recursionLimit:const result = await multiAgentApp.invoke( { messages }, { recursionLimit: 5 }, ); -
设计题:选择一个“接力模式”场景,并说明为什么比主管模式更合适。
点击查看答案
例如“售前 -> 售后 -> 运维”的固定流程,步骤清晰且顺序固定,接力模式能直观表达转交关系。
-
操作题:设计一个并行模式,让 3 个 agent 同时收集信息并合并结果。
点击查看答案
让多个 agent 输出结构化内容,再由 merge 节点统一拼接或做去重合并。
✅ 总结
本章要点:
- 多代理系统通过分工协作解决复杂问题。
- 主管模式适合中心化调度,接力模式适合流程化协作。
- 合理的 Prompt 设计让代理明确自己的职责边界。
下一步:如何优雅地隔离代理状态?让我们学习子图。
登录以继续阅读
解锁完整文档、代码示例及更多高级功能。