架构模式

多代理系统

构建协作式的多 Agent 系统,实现任务分发与结果整合

📚 学习目标

学完这篇文章后,你将能够:

  • 理解多代理协作的优势与适用场景
  • 掌握“主管模式(Supervisor)”架构
  • 实现“层级代理(Hierarchical)”系统
  • 设计代理间的切换与消息传递机制

前置知识

在开始学习之前,建议先阅读:

你需要了解:

  • 单一 Agent 的局限性(Context 窗口、职责混淆)
  • 模块化设计思想

1 为什么需要多代理?

就像软件开发中的“微服务”或“单一职责原则”一样,将复杂的 AI 任务拆解给多个专用的 Agent 可以:

  1. 提高准确性:专精的 Prompt 和工具集让 Agent 在特定领域表现更好。
  2. 易于维护:可以独立调试和优化每个 Agent。
  3. 突破限制:避免单一 System Prompt 过于复杂导致的混乱。

2 主管模式 (Supervisor)

这是最常见的多代理模式。由一个主管 Agent 负责接收用户需求,并路由给具体的工兵 Agent

架构图

关键代码实现

需要一个特殊的路由节点(Supervisor)来决定下一个是谁。

下面给出两种常见实现方式:

  1. 简单版(路由 + conditional edges):主管只输出“下一站是谁”。
  2. 工程版(预构建 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();

代码解析

  1. supervisorNode 的职责很窄:只做决策,不做产出。
  2. coder/writer 产出写回 draft,然后回到 supervisor 再决策。
  3. 通过 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 件事

  1. 共享 messages:所有 agent 读写同一份 messages,导致上下文污染。
  2. 职责不清:工兵 agent 既要“做事”又要“决定下一步”,会与主管冲突。
  3. 循环失控:主管不断分派任务(或者 agent 不断调用工具)。

对应解决思路:

  • 用子图隔离每个 agent 的 messages
  • 主管只做“选择”,工兵只做“产出”
  • 设置 recursionLimit / retry_count,必要时引入人工审批

💡 练习题

  1. 设计题:设计一个“软件开发团队”的多代理系统,包含产品经理、架构师、程序员和测试员。请画出他们的协作流程图。
点击查看参考思路

可以采用“主管模式”:由 PM 代理拆分需求,架构师代理给出技术方案,程序员代理产出代码,测试代理验证结果,最后由主管汇总输出。

  1. 思考题:在主管模式中,如果“工兵 Agent”完成了任务,它应该把结果直接给用户,还是还给主管?由什么因素决定?
点击查看答案

取决于是否需要统一口径与一致性:若需要统一风格、去重和最终审核,应回传给主管;如果任务完全独立且无需合并,可直接响应用户。

  1. 操作题:为你的多代理系统添加 recursionLimit,避免主管反复分派。

    点击查看答案

    在调用 app.invokeapp.stream 时传 recursionLimit

    const result = await multiAgentApp.invoke(
      { messages },
      { recursionLimit: 5 },
    );
  2. 设计题:选择一个“接力模式”场景,并说明为什么比主管模式更合适。

    点击查看答案

    例如“售前 -> 售后 -> 运维”的固定流程,步骤清晰且顺序固定,接力模式能直观表达转交关系。

  3. 操作题:设计一个并行模式,让 3 个 agent 同时收集信息并合并结果。

    点击查看答案

    让多个 agent 输出结构化内容,再由 merge 节点统一拼接或做去重合并。


✅ 总结

本章要点

  • 多代理系统通过分工协作解决复杂问题。
  • 主管模式适合中心化调度,接力模式适合流程化协作。
  • 合理的 Prompt 设计让代理明确自己的职责边界。

下一步:如何优雅地隔离代理状态?让我们学习子图

登录以继续阅读

解锁完整文档、代码示例及更多高级功能。

立即登录

On this page