深入浅出 LangChain —— 第九章:多 Agent 系统

📖 本章学习目标

完成本章后,你将能够:

  • ✅ 理解为什么需要多 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:任务分解

示例:内容创作工作流

flowchart LR A["研究员 Agent
搜集资料"] --> 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)

适用场景: 任务有明确的先后顺序,后一步依赖前一步的结果。

流程图:

flowchart LR A["Agent A
第一步"] -->|传递结果| 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 发展趋势的文章" }],
});

执行流程:

  1. Researcher 搜集 AI 发展趋势的最新资料
  2. Writer 基于资料撰写初稿
  3. Editor 润色和优化文章
  4. 返回最终版本

优点:

  • ✅ 流程清晰,易于理解
  • ✅ 每步结果可单独检查
  • ✅ 便于调试和优化

缺点:

  • ❌ 总耗时 = 各步骤耗时之和
  • ❌ 某一步失败会影响整个流程

2、并行模式(Parallel)

适用场景: 多个子任务可以独立执行,最后汇总结果。

流程图:

flowchart TB Coordinator["协调器 Agent"] --> B1["Agent 1
子任务 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();

执行流程:

  1. 并行启动三个 Agent(天气、交通、活动)
  2. 等待所有 Agent 完成
  3. 汇总结果并生成综合建议

优点:

  • ✅ 大幅缩短总耗时(取决于最慢的子任务)
  • ✅ 提高系统吞吐量
  • ✅ 子任务之间互不影响

缺点:

  • ❌ 需要处理结果合并逻辑
  • ❌ 某个子任务失败需要容错机制

3、委托模式(Delegation / Supervisor)

适用场景: 需要一个"主管"来协调多个"专家",动态决定下一步交给谁处理。

流程图:

flowchart TB Supervisor["主管 Agent
决策路由"] -->|"委托"| 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;
}

执行流程:

flowchart LR A["用户请求"] --> B{"检查角色"} B -->|"Admin"| C["加载管理员工具集"] B -->|"Editor"| D["加载编辑工具集"] B -->|"Viewer"| E["加载查看工具集"] C --> F["执行 Agent"] D --> F E --> F F --> G["返回结果"]

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
  • 实现向量数据库的命名空间隔离
  • 添加资源配额限制

📚 延伸阅读


*下一章:《第十章 ------ 上下文工程与安全护栏*》

相关推荐
Main. 241 小时前
LangChain - AI应用开发利器(三)
langchain
用户068866817511 小时前
Windows端Codex接入第三方模型(DeekSeek,BaiLian)
人工智能
陈天伟教授1 小时前
AI 未来趋势:产业应用范式之变
大数据·开发语言·人工智能·gpt
技术达芬奇1 小时前
OpenClaw 模型配置问题调试实战 - DeepSeek 404 错误解决
agent
Luhui Dev1 小时前
AHE 深度解析:Coding Agent 的 Harness 如何自动演化
人工智能·agent·luhuidev
码农的神经元2 小时前
从论文复现到模型升级:Transformer-Attention-WOA-XGBoost 在含新能源配电网故障诊断中的实现
人工智能·深度学习·transformer
EnCi Zheng2 小时前
04-缩放点积注意力代码实现 [特殊字符]
人工智能·pytorch·python
一江寒逸2 小时前
5个免费开源大模型API,完美平替OpenAI,个人开发完全够用了(2026最新保姆级指南)
人工智能·个人开发
不愿透露姓名的大鹏2 小时前
2026全网最全AI Skill开源合集|从爆火角色蒸馏到全场景生产级技能开箱即用
人工智能·开源