📖 本章学习目标
完成本章后,你将能够:
- ✅ 理解为什么需要多 Agent 系统及其适用场景
- ✅ 掌握串行、并行、委托三种 Agent 协作模式
- ✅ 使用 LangGraph 实现 Supervisor 模式的多 Agent 系统
- ✅ 实现子 Agent 的资源隔离和权限控制
- ✅ 设计高效的多 Agent 通信机制
- ✅ 避免多 Agent 系统的常见陷阱
一、为什么需要多 Agent
单个 Agent 就像一个全能工程师,但"全能"往往意味着"平均"。在复杂任务中,让专门的 Agent 各司其职,往往比一个大而全的 Agent 效果更好。
1、单 Agent 的局限性
问题 1:工具过多导致选择困难
typescript
// 一个 Agent 拥有 20+ 个工具
const agent = createAgent({
model: "openai:gpt-4o",
tools: [
searchTool, fetchWebpageTool, calculatorTool,
emailTool, calendarTool, fileReaderTool,
codeExecutorTool, databaseTool, // ...更多
],
});
- LLM 需要在众多工具中选择,容易选错
- Prompt 中包含大量工具定义,消耗 Token
- 不同领域的工具可能产生冲突
问题 2:角色混乱
typescript
systemPrompt: `你是客服助手,同时也是代码专家,还能做数据分析...`
- 角色定位不清晰,回答风格不一致
- 专业深度不够,每个领域都"懂一点但不精"
- 难以维护和优化
问题 3:资源浪费
- 每次调用都加载所有工具和知识库
- 即使用户只问客服问题,也加载了代码执行器
- 成本和延迟都不必要地增加
2、多 Agent 的优势
有点类似在一家公司:
- ❌ 单 Agent:一个员工既要接客服电话,又要写代码,还要做财务报表
- ✅ 多 Agent:客服专员、软件工程师、财务分析师各司其职
显然,分工协作效率更高、专业性更强。
核心优势对比:
| 维度 | 单 Agent | 多 Agent |
|---|---|---|
| 专业性 | 通用但浅显 | 专域深耕 |
| 可维护性 | 修改一处影响全局 | 模块化独立优化 |
| 扩展性 | 添加功能需重构 | 即插即用新 Agent |
| 成本控制 | 始终加载全部资源 | 按需激活 |
| 错误隔离 | 一处错误影响全局 | 故障局限在单个 Agent |
3、多 Agent 的典型应用场景
场景 1:任务分解
示例:内容创作工作流
搜集资料"] --> B["作家 Agent
撰写初稿"] B --> C["编辑 Agent
润色校对"] C --> D["审核 Agent
质量把关"] D --> E["最终文章"]
优势:
- 每个 Agent 专注自己的环节
- 可以并行处理多个任务
- 质量问题容易定位到具体环节
场景 2:角色分工
示例:软件开发团队
| Agent 角色 | 职责 | 工具集 |
|---|---|---|
| 产品经理 Agent | 需求分析、功能规划 | 用户调研工具、竞品分析 |
| 架构师 Agent | 技术选型、系统设计 | 架构图生成、技术文档 |
| 开发工程师 Agent | 代码编写、单元测试 | 代码编辑器、测试框架 |
| 测试工程师 Agent | Bug 检测、性能测试 | 自动化测试、性能分析 |
场景 3:专域隔离
示例:企业服务平台
typescript
// 不同部门有独立的 Agent 和知识库
const agents = {
customerService: {
tools: [productKnowledgeBase, orderQuery],
knowledgeBase: "customer-faq",
},
technicalSupport: {
tools: [apiDocs, troubleshootingGuide],
knowledgeBase: "tech-manuals",
},
billing: {
tools: [invoiceGenerator, paymentProcessor],
knowledgeBase: "billing-policies",
},
};
优势:
- 数据安全:客服无法访问财务数据
- 权限控制:不同 Agent 有不同的访问权限
- 知识隔离:避免信息混淆
二、多 Agent 的三种协作模式
根据任务特点,选择合适的协作模式至关重要。
1、串行模式(Serial)
适用场景: 任务有明确的先后顺序,后一步依赖前一步的结果。
流程图:
第一步"] -->|传递结果| B["Agent B
第二步"] B -->|传递结果| C["Agent C
第三步"] C --> D["最终结果"] style A fill:#e8f4fd,stroke:#1890ff style B fill:#fff7e6,stroke:#fa8c16 style C fill:#f6ffed,stroke:#52c41a
典型用例:
- 研究 → 写作 → 编辑 → 审核
- 需求分析 → 系统设计 → 代码实现 → 测试
实现示例
typescript
import { StateGraph, MessagesAnnotation } from "@langchain/langgraph";
import { createAgent } from "langchain";
// 创建三个专门的 Agent
const researcher = createAgent({
model: "openai:gpt-4o",
tools: [searchTool, fetchWebpageTool],
systemPrompt: "你是研究员,负责搜集信息和资料。输出结构化的研究报告。",
});
const writer = createAgent({
model: "openai:gpt-4o",
tools: [],
systemPrompt: "你是写作专家,基于提供的资料撰写文章。",
});
const editor = createAgent({
model: "openai:gpt-4o",
tools: [],
systemPrompt: "你是编辑,负责润色文章,确保语言流畅、逻辑清晰。",
});
// 构建串行工作流
const workflow = new StateGraph(MessagesAnnotation)
.addNode("research", async (state) => {
const result = await researcher.invoke({ messages: state.messages });
return { messages: result.messages };
})
.addNode("write", async (state) => {
const result = await writer.invoke({ messages: state.messages });
return { messages: result.messages };
})
.addNode("edit", async (state) => {
const result = await editor.invoke({ messages: state.messages });
return { messages: result.messages };
})
.addEdge("__start__", "research")
.addEdge("research", "write")
.addEdge("write", "edit")
.addEdge("edit", "__end__");
const serialApp = workflow.compile();
// 执行
const result = await serialApp.invoke({
messages: [{ role: "user", content: "写一篇关于 AI 发展趋势的文章" }],
});
执行流程:
- Researcher 搜集 AI 发展趋势的最新资料
- Writer 基于资料撰写初稿
- Editor 润色和优化文章
- 返回最终版本
优点:
- ✅ 流程清晰,易于理解
- ✅ 每步结果可单独检查
- ✅ 便于调试和优化
缺点:
- ❌ 总耗时 = 各步骤耗时之和
- ❌ 某一步失败会影响整个流程
2、并行模式(Parallel)
适用场景: 多个子任务可以独立执行,最后汇总结果。
流程图:
子任务 A"] Coordinator --> B2["Agent 2
子任务 B"] Coordinator --> B3["Agent 3
子任务 C"] B1 --> Merge["结果汇总"] B2 --> Merge B3 --> Merge Merge --> Final["最终结果"] style Coordinator fill:#fff7e6,stroke:#fa8c16 style B1 fill:#e8f4fd,stroke:#1890ff style B2 fill:#e8f4fd,stroke:#1890ff style B3 fill:#e8f4fd,stroke:#1890ff
典型用例:
- 同时查询多个数据源
- 并行分析不同维度的数据
- 多语言翻译
实现示例
typescript
import { StateGraph, Annotation } from "@langchain/langgraph";
import { createAgent } from "langchain";
// 定义包含多个子任务结果的状态
const ParallelState = Annotation.Root({
messages: Annotation<BaseMessage[]>({
reducer: (current, next) => [...current, ...next],
default: () => [],
}),
taskResults: Annotation<Record<string, string>>({
reducer: (current, next) => ({ ...current, ...next }),
default: () => ({}),
}),
});
// 创建专门的 Agent
const weatherAgent = createAgent({
model: "openai:gpt-4o",
tools: [getWeather],
systemPrompt: "你是天气专家,负责查询天气信息。",
});
const trafficAgent = createAgent({
model: "openai:gpt-4o",
tools: [getTraffic],
systemPrompt: "你是交通专家,负责查询路况信息。",
});
const eventsAgent = createAgent({
model: "openai:gpt-4o",
tools: [getLocalEvents],
systemPrompt: "你是活动专家,负责查询当地活动信息。",
});
// 并行执行多个 Agent
async function parallelNode(state: typeof ParallelState.State) {
const userQuery = state.messages.at(-1)?.content;
// 并行执行三个 Agent(Promise.all 自动并发)
const [weatherResult, trafficResult, eventsResult] = await Promise.all([
weatherAgent.invoke({
messages: [{ role: "user", content: `查询北京的天气:${userQuery}` }],
}),
trafficAgent.invoke({
messages: [{ role: "user", content: `查询北京的路况:${userQuery}` }],
}),
eventsAgent.invoke({
messages: [{ role: "user", content: `查询北京的活动:${userQuery}` }],
}),
]);
// 汇总结果
return {
taskResults: {
weather: weatherResult.messages.at(-1)?.content,
traffic: trafficResult.messages.at(-1)?.content,
events: eventsResult.messages.at(-1)?.content,
},
};
}
// 汇总节点
async function mergeNode(state: typeof ParallelState.State) {
const { weather, traffic, events } = state.taskResults;
const summaryPrompt = `请整合以下信息,给用户一个综合建议:
天气情况:
${weather}
路况信息:
${traffic}
当地活动:
${events}
请给出出行建议。`;
const summarizer = new ChatOpenAI({ model: "gpt-4o" });
const response = await summarizer.invoke(summaryPrompt);
return {
messages: [new AIMessage(response.content as string)],
};
}
// 构建并行工作流
const workflow = new StateGraph(ParallelState)
.addNode("parallel", parallelNode)
.addNode("merge", mergeNode)
.addEdge("__start__", "parallel")
.addEdge("parallel", "merge")
.addEdge("merge", "__end__");
const parallelApp = workflow.compile();
执行流程:
- 并行启动三个 Agent(天气、交通、活动)
- 等待所有 Agent 完成
- 汇总结果并生成综合建议
优点:
- ✅ 大幅缩短总耗时(取决于最慢的子任务)
- ✅ 提高系统吞吐量
- ✅ 子任务之间互不影响
缺点:
- ❌ 需要处理结果合并逻辑
- ❌ 某个子任务失败需要容错机制
3、委托模式(Delegation / Supervisor)
适用场景: 需要一个"主管"来协调多个"专家",动态决定下一步交给谁处理。
流程图:
决策路由"] -->|"委托"| Worker1["专家 Agent 1
研究员"] Supervisor -->|"委托"| Worker2["专家 Agent 2
作家"] Supervisor -->|"委托"| Worker3["专家 Agent 3
编辑"] Worker1 -->|"汇报"| Supervisor Worker2 -->|"汇报"| Supervisor Worker3 -->|"汇报"| Supervisor Supervisor -->|"任务完成"| End["结束"] style Supervisor fill:#fff7e6,stroke:#fa8c16,stroke-width:3px style Worker1 fill:#e8f4fd,stroke:#1890ff style Worker2 fill:#e8f4fd,stroke:#1890ff style Worker3 fill:#e8f4fd,stroke:#1890ff
典型用例:
- 复杂的研究任务(需要多次切换研究方向)
- 客户服务(根据问题类型转接不同专家)
- 项目管理(动态分配任务给不同团队)
完整实现
这是最灵活也是最常用的多 Agent 模式,让我们详细实现。
第一步:定义状态
typescript
import { StateGraph, MessagesAnnotation, Annotation } from "@langchain/langgraph";
// 定义包含路由信息的状态
const SupervisorState = Annotation.Root({
// 继承默认的消息历史
...MessagesAnnotation.spec,
// 下一个要执行的 Agent 名称
nextAgent: Annotation<string | "FINISH">({
reducer: (_, next) => next, // 直接覆盖
default: () => "FINISH", // 默认结束
}),
// 任务进度追踪
taskProgress: Annotation<string>({
reducer: (_, next) => next,
default: () => "",
}),
});
第二步:创建专门的 Agent
typescript
import { createAgent } from "langchain";
// 研究员 Agent:负责搜集信息
const researchAgent = createAgent({
model: "openai:gpt-4o",
tools: [searchTool, fetchWebpageTool, wikipediaTool],
systemPrompt: `你是资深研究员,擅长搜集和整理信息。
职责:
1. 根据任务要求搜索相关信息
2. 从多个来源验证信息准确性
3. 输出结构化的研究报告
输出格式:
- 关键发现(3-5 条)
- 数据来源
- 待确认的问题`,
});
// 作家 Agent:负责撰写内容
const writerAgent = createAgent({
model: "openai:gpt-4o",
tools: [readFileTool, writeFileTool],
systemPrompt: `你是专业作家,擅长将信息整合成易读的文章。
职责:
1. 基于研究员提供的资料撰写文章
2. 确保文章结构清晰、逻辑连贯
3. 语言生动、引人入胜
注意:不要编造事实,所有内容必须基于提供的资料。`,
});
// 编辑 Agent:负责质量把关
const editorAgent = createAgent({
model: "openai:gpt-4o",
tools: [],
systemPrompt: `你是资深编辑,负责审核和优化文章质量。
审核标准:
1. 事实准确性:是否有错误或遗漏
2. 逻辑连贯性:段落之间是否衔接自然
3. 语言表达:是否简洁、准确、生动
4. 格式规范:标题、段落、标点是否正确
输出:修改后的文章 + 修改说明`,
});
第三步:实现 Supervisor 节点
typescript
import { ChatOpenAI } from "@langchain/openai";
const chatModel = new ChatOpenAI({ model: "gpt-4o" });
// Supervisor 决定下一步交给谁
async function supervisorNode(state: typeof SupervisorState.State) {
// 构建决策 Prompt
const decisionPrompt = `你是任务协调员。根据当前进展,决定下一步应该交给谁处理。
可用选项:
- research:研究员(需要搜集更多信息时)
- writer:作家(需要撰写或修改文章时)
- editor:编辑(需要审核和优化质量时)
- FINISH:任务完成(用户对结果满意时)
当前任务进度:
${state.taskProgress || "尚未开始"}
最近对话:
${state.messages.slice(-5).map(m => `${m.role}: ${m.content}`).join("\n")}
请只回复一个单词(research/writer/editor/FINISH),不要解释。`;
const response = await chatModel.invoke([
{ role: "system", content: decisionPrompt },
]);
const nextAgent = response.content?.toString().trim().toUpperCase() || "FINISH";
console.log(`[Supervisor] 下一步:${nextAgent}`);
return {
nextAgent: nextAgent.toLowerCase(),
taskProgress: `${state.taskProgress}\n[${new Date().toLocaleTimeString()}] 决策:${nextAgent}`,
};
}
第四步:实现 Worker 节点
typescript
// 研究员节点
async function researchNode(state: typeof SupervisorState.State) {
console.log("[Researcher] 开始研究工作...");
const result = await researchAgent.invoke({
messages: state.messages
});
console.log("[Researcher] 研究完成");
return {
messages: result.messages,
nextAgent: "supervisor", // 完成后返回给 Supervisor
};
}
// 作家节点
async function writerNode(state: typeof SupervisorState.State) {
console.log("[Writer] 开始写作...");
const result = await writerAgent.invoke({
messages: state.messages
});
console.log("[Writer] 写作完成");
return {
messages: result.messages,
nextAgent: "supervisor",
};
}
// 编辑节点
async function editorNode(state: typeof SupervisorState.State) {
console.log("[Editor] 开始审核...");
const result = await editorAgent.invoke({
messages: state.messages
});
console.log("[Editor] 审核完成");
return {
messages: result.messages,
nextAgent: "supervisor",
};
}
第五步:构建工作流
typescript
// 条件路由函数
function routeFromSupervisor(state: typeof SupervisorState.State) {
const next = state.nextAgent;
if (next === "finish") {
return "__end__"; // 任务完成
}
// 返回对应的节点名称
return next; // "research" / "writer" / "editor"
}
// 构建多 Agent 图
const workflow = new StateGraph(SupervisorState)
// 添加节点
.addNode("supervisor", supervisorNode)
.addNode("research", researchNode)
.addNode("writer", writerNode)
.addNode("editor", editorNode)
// 定义边
.addEdge("__start__", "supervisor") // 入口 → Supervisor
// Supervisor 的条件路由
.addConditionalEdges("supervisor", routeFromSupervisor, {
research: "research",
writer: "writer",
editor: "editor",
__end__: "__end__",
})
// Worker 完成后返回 Supervisor
.addEdge("research", "supervisor")
.addEdge("writer", "supervisor")
.addEdge("editor", "supervisor");
// 编译为可执行应用
const multiAgentApp = workflow.compile();
第六步:执行与监控
typescript
// 执行多 Agent 系统
const result = await multiAgentApp.invoke({
messages: [{
role: "user",
content: "写一篇关于量子计算最新进展的深度报道"
}],
});
// 查看执行过程
console.log("最终结果:");
console.log(result.messages.at(-1)?.content);
console.log("\n任务进度日志:");
console.log(result.taskProgress);
典型的执行轨迹:
bash
[Supervisor] 下一步:RESEARCH
[Researcher] 开始研究工作...
[Researcher] 研究完成
[Supervisor] 下一步:WRITER
[Writer] 开始写作...
[Writer] 写作完成
[Supervisor] 下一步:EDITOR
[Editor] 开始审核...
[Editor] 审核完成
[Supervisor] 下一步:FINISH
优点:
- ✅ 高度灵活,可根据情况动态调整
- ✅ 易于扩展新的专家 Agent
- ✅ Supervisor 可以学习优化路由策略
缺点:
- ❌ Supervisor 本身可能成为瓶颈
- ❌ 需要精心设计决策逻辑
- ❌ 可能出现循环委托(A→B→A→B...)
三、子 Agent 的资源隔离
在多 Agent 系统中,子 Agent 应该只能访问它被授权的工具和数据,这是安全性和可维护性的关键。
1、为什么要资源隔离?
风险场景:
| 风险 | 示例 | 后果 |
|---|---|---|
| 权限滥用 | 客服 Agent 调用删除数据库工具 | 数据丢失 |
| 信息泄露 | 普通用户 Agent 访问财务数据 | 隐私泄露 |
| 资源竞争 | 多个 Agent 同时写入同一文件 | 数据损坏 |
| 成本失控 | 某个 Agent 无限调用付费 API | 费用暴涨 |
2、实现工具隔离
方法 1:静态工具配置
最简单的方式:创建 Agent 时只传入它需要的工具。
typescript
// 研究员 Agent:只有搜索相关工具
const researchAgent = createAgent({
model: "openai:gpt-4o",
tools: [
searchTool, // ✅ 允许
fetchWebpageTool, // ✅ 允许
wikipediaTool, // ✅ 允许
],
// ❌ 没有文件写入权限
// ❌ 没有数据库访问权限
// ❌ 没有邮件发送权限
});
// 作家 Agent:只有文件操作工具
const writerAgent = createAgent({
model: "openai:gpt-4o",
tools: [
readFileTool, // ✅ 允许
writeFileTool, // ✅ 允许
],
// ❌ 不能搜索网络
// ❌ 不能执行代码
});
// 主管 Agent:只有协调工具
const supervisorAgent = createAgent({
model: "openai:gpt-4o",
tools: [
delegateToResearch, // ✅ 委托给研究员
delegateToWriter, // ✅ 委托给作家
finalizeReport, // ✅ 完成任务
],
// ❌ 没有直接操作数据的权限
});
方法 2:动态工具配置
根据上下文动态决定可用的工具。
typescript
import { createAgent } from "langchain";
// 创建基础 Agent(不传工具)
const flexibleAgent = createAgent({
model: "openai:gpt-4o",
tools: [], // 初始为空
});
// 根据用户角色动态提供工具
async function invokeWithDynamicTools(userRole: string, query: string) {
let availableTools = [];
switch (userRole) {
case "admin":
availableTools = [
searchTool,
databaseQueryTool,
deleteUserTool, // ⚠️ 危险操作
systemConfigTool,
];
break;
case "editor":
availableTools = [
searchTool,
readFileTool,
writeFileTool,
];
break;
case "viewer":
availableTools = [
searchTool,
readFileTool,
];
break;
}
// 调用时传入工具
const result = await flexibleAgent.invoke(
{ messages: [{ role: "user", content: query }] },
{ configurable: { tools: availableTools } }
);
return result;
}
执行流程:
3、实现数据隔离
方法 1:命名空间隔离
不同的 Agent 使用不同的向量数据库命名空间。
typescript
import { PineconeStore } from "@langchain/pinecone";
// 研究员的知识库(公开信息)
const researchVectorStore = await PineconeStore.fromExistingIndex(
embeddings,
{
pineconeIndex: index,
namespace: "public-research", // 命名空间隔离
}
);
// 财务 Agent 的知识库(敏感信息)
const financeVectorStore = await PineconeStore.fromExistingIndex(
embeddings,
{
pineconeIndex: index,
namespace: "confidential-finance", // 不同的命名空间
}
);
// 研究员 Agent 只能访问公开知识库
const researchAgent = createAgent({
model: "openai:gpt-4o",
tools: [createRetrieverTool(researchVectorStore)],
});
// 财务 Agent 只能访问财务知识库
const financeAgent = createAgent({
model: "openai:gpt-4o",
tools: [createRetrieverTool(financeVectorStore)],
});
方法 2:元数据过滤
在检索时根据 Agent 身份过滤结果。
typescript
// 文档中添加权限标签
await vectorStore.addDocuments([
new Document({
pageContent: "财务报告内容...",
metadata: {
source: "finance-report.pdf",
allowedAgents: ["finance", "supervisor"], // 只有特定 Agent 能访问
sensitivity: "high",
},
}),
new Document({
pageContent: "产品说明书...",
metadata: {
source: "product-manual.pdf",
allowedAgents: ["research", "writer", "supervisor"], // 更多 Agent 能访问
sensitivity: "low",
},
}),
]);
// 检索时过滤
function createRestrictedRetriever(agentName: string) {
return vectorStore.asRetriever({
filter: {
allowedAgents: { $in: [agentName] }, // 只返回该 Agent 有权访问的文档
},
});
}
// 研究员只能访问公开文档
const researchRetriever = createRestrictedRetriever("research");
// 财务可以访问财务文档
const financeRetriever = createRestrictedRetriever("finance");
4、实现执行隔离
防止某个 Agent 占用过多资源或执行时间过长。
(1)超时控制
typescript
import { createMiddleware } from "langchain";
// 为每个 Agent 设置执行超时
const timeoutMiddleware = createMiddleware({
name: "TimeoutControl",
wrapModelCall: async (request, handler) => {
const timeout = 30000; // 30 秒超时
try {
// 使用 Promise.race 实现超时
const result = await Promise.race([
handler(request),
new Promise((_, reject) =>
setTimeout(() => reject(new Error("执行超时")), timeout)
),
]);
return result;
} catch (error) {
if ((error as Error).message === "执行超时") {
console.warn(`[Timeout] Agent 执行超过 ${timeout}ms,已终止`);
throw new Error("Agent 执行超时,请稍后重试");
}
throw error;
}
},
});
// 应用到特定 Agent
const researchAgent = createAgent({
model: "openai:gpt-4o",
tools: [searchTool],
middleware: [timeoutMiddleware],
});
(2 )资源配额
typescript
// 跟踪每个 Agent 的资源使用
const resourceQuota = new Map<string, {
apiCalls: number;
tokensUsed: number;
lastReset: Date;
}>();
const quotaMiddleware = createMiddleware({
name: "ResourceQuota",
beforeModel: async (request) => {
const agentId = getAgentId(request);
const quota = resourceQuota.get(agentId) || {
apiCalls: 0,
tokensUsed: 0,
lastReset: new Date(),
};
// 每小时重置配额
if (Date.now() - quota.lastReset.getTime() > 3600000) {
quota.apiCalls = 0;
quota.tokensUsed = 0;
quota.lastReset = new Date();
}
// 检查配额
if (quota.apiCalls >= 100) {
throw new Error(`${agentId} 已达到 API 调用上限(100次/小时)`);
}
if (quota.tokensUsed >= 100000) {
throw new Error(`${agentId} 已达到 Token 使用上限(100K/小时)`);
}
// 更新配额
resourceQuota.set(agentId, {
...quota,
apiCalls: quota.apiCalls + 1,
});
return request;
},
afterModel: async (response) => {
const agentId = getAgentId(response);
const quota = resourceQuota.get(agentId)!;
// 更新 Token 使用量
quota.tokensUsed += response.usage?.total_tokens || 0;
resourceQuota.set(agentId, quota);
return response;
},
});
四、多 Agent 通信机制
Agent 之间如何高效、可靠地传递信息,是多 Agent 系统的关键。
1、通过消息历史通信(推荐)
最简单也最常用的方式:所有 Agent 共享同一个消息历史。
typescript
// 所有 Agent 都看到完整的对话历史
const result = await multiAgentApp.invoke({
messages: [
{ role: "user", content: "写一篇关于 AI 的文章" },
// Researcher 的输出
{ role: "assistant", content: "我找到了以下资料..." },
// Writer 的输出
{ role: "assistant", content: "基于资料,我写了初稿..." },
// Editor 的输出
{ role: "assistant", content: "我优化了文章..." },
],
});
优点:
- ✅ 简单直观
- ✅ 每个 Agent 都能看到完整上下文
- ✅ 天然支持多轮对话
缺点:
- ❌ 消息历史可能很长,消耗 Token
- ❌ 无关信息可能干扰 Agent 判断
2、通过状态字段通信
对于结构化数据,使用自定义状态字段更高效。
typescript
const CollaborationState = Annotation.Root({
...MessagesAnnotation.spec,
// 研究结果
researchFindings: Annotation<string[]>({
reducer: (current, next) => [...current, ...next],
default: () => [],
}),
// 文章草稿
articleDraft: Annotation<string | null>({
reducer: (_, next) => next,
default: () => null,
}),
// 编辑意见
editComments: Annotation<string[]>({
reducer: (current, next) => [...current, ...next],
default: () => [],
}),
});
// Researcher 写入研究结果
async function researchNode(state: typeof CollaborationState.State) {
const findings = await doResearch();
return {
researchFindings: findings, // 写入结构化数据
messages: [new AIMessage("研究完成")],
};
}
// Writer 读取研究结果
async function writerNode(state: typeof CollaborationState.State) {
const findings = state.researchFindings; // 直接读取
const draft = await writeArticle(findings);
return {
articleDraft: draft,
messages: [new AIMessage("写作完成")],
};
}
优点:
- ✅ 数据结构化,易于处理
- ✅ 不占用消息历史
- ✅ 类型安全
缺点:
- ❌ 需要预先定义状态结构
- ❌ 灵活性不如消息历史
3、通过中间件通信
使用中间件在 Agent 之间传递元数据或执行额外逻辑。
typescript
// 日志中间件:记录 Agent 之间的交互
const loggingMiddleware = createMiddleware({
name: "AgentCommunicationLogger",
beforeModel: async (request) => {
const currentAgent = getCurrentAgent(request);
console.log(`[Communication] ${currentAgent} 开始执行`);
console.log(`[Context] 消息数量:${request.messages.length}`);
return request;
},
afterModel: async (response) => {
const currentAgent = getCurrentAgent(response);
console.log(`[Communication] ${currentAgent} 执行完成`);
console.log(`[Output] 输出长度:${response.content?.length}`);
return response;
},
});
五、多 Agent 系统的最佳实践
💡 实践 1:明确每个 Agent 的职责边界
❌ 不好的设计:
typescript
// 两个 Agent 职责重叠
const agent1 = createAgent({
systemPrompt: "你负责搜索信息和回答问题",
});
const agent2 = createAgent({
systemPrompt: "你负责查找资料和提供答案",
});
✅ 好的设计:
typescript
// 职责清晰分离
const researcher = createAgent({
systemPrompt: "你负责搜集和整理信息,输出结构化的研究报告,不直接回答用户问题。",
});
const answerer = createAgent({
systemPrompt: "你基于研究员提供的资料回答用户问题,不主动搜索信息。",
});
💡 实践 2:设计清晰的通信协议
定义标准化的输出格式:
typescript
// Researcher 的标准输出格式
interface ResearchOutput {
summary: string; // 摘要
keyFindings: string[]; // 关键发现
sources: Array<{ // 数据来源
title: string;
url: string;
reliability: number; // 可信度 0-1
}>;
openQuestions: string[]; // 待确认的问题
}
// 强制 Researcher 按此格式输出
const researcher = createAgent({
systemPrompt: `你必须按以下 JSON 格式输出:
{
"summary": "研究摘要",
"keyFindings": ["发现1", "发现2"],
"sources": [{"title": "...", "url": "...", "reliability": 0.9}],
"openQuestions": ["问题1"]
}
不要输出其他内容。`,
});
💡 实践 3:实现优雅的错误恢复
某个 Agent 失败时,不应该让整个系统崩溃:
typescript
async function robustResearchNode(state: typeof SupervisorState.State) {
try {
const result = await researchAgent.invoke({
messages: state.messages
});
return {
messages: result.messages,
nextAgent: "supervisor",
};
} catch (error) {
console.error("[Researcher] 执行失败:", error);
// 降级策略:返回友好错误消息
return {
messages: [
new AIMessage(
"抱歉,研究员暂时无法完成搜索任务。可能是网络问题或搜索服务不可用。" +
"您可以尝试简化搜索请求,或者我可以基于现有知识提供帮助。"
),
],
nextAgent: "supervisor",
};
}
}
💡 实践 4:监控和调试
为每个 Agent 添加详细的日志:
typescript
const monitoringMiddleware = createMiddleware({
name: "AgentMonitor",
beforeModel: async (request) => {
const agentId = getAgentId(request);
const startTime = Date.now();
// 记录开始时间
request.metadata = {
...request.metadata,
startTime,
agentId,
};
console.log(`[Monitor] ${agentId} 开始执行`);
return request;
},
afterModel: async (response) => {
const { startTime, agentId } = response.metadata || {};
const duration = Date.now() - startTime;
console.log(`[Monitor] ${agentId} 执行完成`);
console.log(` - 耗时: ${duration}ms`);
console.log(` - Token: ${response.usage?.total_tokens}`);
console.log(` - 输出长度: ${response.content?.length}`);
// 发送到监控系统
await sendToMonitoringSystem({
agentId,
duration,
tokens: response.usage?.total_tokens,
timestamp: new Date(),
});
return response;
},
});
⚠️ 实践 5:避免循环委托
问题: Supervisor → Agent A → Supervisor → Agent A → ... 无限循环
解决方案 1:设置最大迭代次数
typescript
const SupervisorState = Annotation.Root({
...MessagesAnnotation.spec,
nextAgent: Annotation<string | "FINISH">({...}),
// 迭代计数器
iterationCount: Annotation<number>({
reducer: (_, next) => next,
default: () => 0,
}),
});
async function supervisorNode(state: typeof SupervisorState.State) {
// 检查迭代次数
if (state.iterationCount >= 10) {
console.warn("[Supervisor] 达到最大迭代次数,强制结束");
return {
nextAgent: "FINISH",
messages: [new AIMessage("由于迭代次数过多,任务已终止。")],
};
}
// ...正常逻辑
return {
nextAgent: decision,
iterationCount: state.iterationCount + 1,
};
}
解决方案 2:检测重复模式
typescript
async function supervisorNode(state: typeof SupervisorState.State) {
// 检查最近的决策历史
const recentDecisions = state.messages
.filter(m => m.role === "system")
.slice(-5)
.map(m => m.content);
// 如果同一个 Agent 被连续选择 3 次以上,强制切换
const lastAgent = recentDecisions.at(-1);
const consecutiveCount = recentDecisions
.reverse()
.findIndex(d => d !== lastAgent);
if (consecutiveCount >= 3) {
console.warn(`[Supervisor] ${lastAgent} 已连续执行 ${consecutiveCount} 次,强制切换`);
return { nextAgent: "FINISH" };
}
// ...正常逻辑
}
六、本章小结
多 Agent 系统是构建复杂 AI 应用的关键架构模式。
📝 核心知识点回顾
| 知识点 | 关键要点 | 适用场景 |
|---|---|---|
| 为什么需要多 Agent | 专业性、可维护性、扩展性、成本控制 | 复杂任务、多领域协作 |
| 串行模式 | 按顺序执行,后一步依赖前一步 | 流水线式任务 |
| 并行模式 | 同时执行多个子任务,最后汇总 | 独立子任务、数据聚合 |
| 委托模式 | Supervisor 动态路由到专家 Agent | 复杂决策、不确定路径 |
| 资源隔离 | 工具隔离、数据隔离、执行隔离 | 安全性、权限控制 |
| 通信机制 | 消息历史、状态字段、中间件 | Agent 间信息传递 |
🎯 动手练习
尝试完成以下练习,巩固所学知识:
练习 1:实现串行工作流 创建一个"研究 → 写作 → 编辑"的串行多 Agent 系统:
- 每个 Agent 有专门的 System Prompt
- 实现完整的数据流转
- 测试不同主题的文章生成
练习 2:实现并行搜索 创建一个并行搜索 Agent:
- 同时搜索 3 个不同的数据源(Google、Wikipedia、GitHub)
- 汇总结果并去重
- 对比串行和并行的性能差异
练习 3:实现 Supervisor 模式 构建一个智能客服 Supervisor 系统:
- Supervisor 根据问题类型路由到不同专家(产品、技术、账单)
- 实现至少 3 个专家 Agent
- 添加最大迭代次数限制
练习 4:实现资源隔离 为多 Agent 系统添加权限控制:
- 不同角色的用户看到不同的 Agent
- 实现向量数据库的命名空间隔离
- 添加资源配额限制