ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

LangGraph多智能体编排实战:构建可控的AI团队协作

LangGraph多智能体编排实战:构建可控的AI团队协作

按理说,做到第七期大家应该已经对状态图、节点、边这些基础概念很熟了,但前阵子我复盘一个实际项目时发现,真正把人卡住的往往不是某个 API 不会用,而是对整个工作流的组织方式没想清楚。尤其是当你手里捏着一个能干很多事的大模型,却越用越别扭的时候——那大概率不是模型的问题,而是你试图让一个智能体包办所有事。

这一篇我拿一个具体的多智能体协作场景来展开:模拟一个需要外部知识库支持的智能调研任务。一个协调中枢负责拆任务,几个专职智能体分别做资料查证、内容整合、质量复审,全部跑在一套 LangGraph 编排图上。你会发现,LangGraph 做 AI 工作流编排的真正价值不在于“把多个提示词串起来”,而在于它给了你一套表达分工、并行、条件转移和状态沉淀的语法,让原本不可控的智能体协作变得可以设计、可以调试、可以复盘。

1. 为什么要把单 Agent 拆成一支“多人团队”

1.1 单智能体的瓶颈:状态膨胀与角色混叠

先说说我为什么开始拆。之前有个需求很简单:输入一批文档,让模型根据外部知识库生成一份带出处的调研综述。最初我只做了一个 Agent,把“检索资料、归纳要点、撰写报告、自查引用”全塞进一套系统提示词里。坏处刚开始不明显,等文档规模上来之后全都暴露了。

第一是上下文窗口被无效信息挤占。单个 Agent 为了兼顾检索和写作,必须把检索到的候选片段全部留在上下文里,而这些片段几十条加起来往往比正稿还长,真正用于推理的空间反而变小了。第二是角色混叠。你让同一个模型既当检索员又当审查员,它会倾向于“自己的产出总是对的”,自查环节形同虚设。第三是权限没法收敛。工具一旦绑定在唯一一个 Agent 上,就等同于所有能力对所有任务开放,你很难在流程上限制“这一步绝对不能调用外部写入工具”。

这些问题的本质,不是提示词写得不好,而是职责没有被结构化。一个人同时干五件事的时候,管理精力会指数级上升;换成五个各司其职的人,每一件事的上下文和验收标准都变简单了。

1.2 分工之后的收益:可观测性、独立扩展、容错

拆成多智能体之后,我拿到的最直接收益是过程可视化。单 Agent 模式下,模型心里在想什么只能靠日志猜测;拆开之后,每个节点产出的中间结果都会落到共享状态里,我可以随时检查“检索阶段到底返回了什么”“分析节点有没有遗漏关键条目”,出问题第一时间就能定位到哪一步。

收益二是单个环节可以独立迭代。比如检索节点最终决定用一个更便宜的模型来负担,因为它的任务只是“从知识库里找条目并给出摘要”,不需要顶尖推理能力;而最终的整合节点继续使用能力更强的模型。这种“按角色分配不同模型、不同温度、不同工具列表”的做法,在单 Agent 里几乎没法干净地实现。

收益三是容错边界清晰。某一次任务中调用外部知识库超时,以前整个 Agent 的会话就废了;现在我可以单独给检索节点配置重试和降级逻辑,其他节点根本感知不到底层出了什么岔子。

1.3 什么时候不该拆:多智能体不是银弹

当然也得泼盆冷水。如果任务本身只有两步,比如“读一段文本然后润色”,强行拆成两个 Agent 只会白白增加延迟和 token 消耗。多智能体架构适合的是存在明显子任务边界、子任务之间存在依赖或并行关系、且每一步都有可验证产出的场景。

我在设计前会问自己三个问题:拆出来的每个角色,是否拥有独立且不可被其他角色替代的工具或信息面?每个角色完成后的产出,是否能在状态里被清晰地记录和验证?整个流程是否真的需要并行执行,还是线性跑一遍就够了?如果把这些问题答清楚了还觉得该拆,才值得动手。

2. 团队的原型设计:角色、共享状态与外部知识库对接

2.1 角色定义:调度中枢、检索员、整合员与质检员

这次示例项目里,我把智能体团队设计成了四个角色:

角色核心职责持有工具产出物
调度中枢拆解用户任务、判断下一步交给谁任务规划器子任务列表与执行顺序
检索员查询外部知识库、整理候选条目知识库检索工具带来源编号的资料条目
整合员综合资料撰写最终调研报告无外部工具,仅读状态报告初稿
质检员核对引用与观点一致性,决定是否退回知识库复核工具通过或退回意见

调度中枢是一个值得琢磨的设计。它本身不一定非要是一个完整的大模型调用,在很多场景里,一个轻量级的分类模型甚至一组规则就能完成路由。但既然这一期强调的是架构表达,我选择用一个大模型节点来做意图拆解,因为它还需要处理用户输入中那些含糊的表达,规则路由很容易被绕晕。

2.2 状态 Schema 设计:共享工作区里到底放什么

LangGraph 的核心是全局状态对象,团队里所有节点读写同一份状态,所以状态怎么设计,直接决定协作是否顺畅。我见过最常见的错误,是把状态当成“聊天消息列表”的简单包装,结果所有中间结果都堆在消息里,越往后越乱。

这次我用的状态结构大致是:

from typing import Annotated, TypedDict, List import operator class ResearchState(TypedDict): user_request: str subtasks: Annotated[List[dict], operator.add] # 调度中枢拆解出的子任务 source_items: Annotated[List[dict], operator.add] # 检索员产出的资料条目 draft_report: str # 整合员产出的报告初稿 review_verdict: str # 质检员的结论 iteration_count: int # 当前修订轮数 messages: Annotated[List[dict], operator.add] # 各节点的记录性输出

关键有两点:一是用Annotated[List[dict], operator.add]表达“追加语义”,多个节点可以并行往里加条目而互不覆盖;二是把“状态”和“消息历史”分开。子任务清单、资料条目、报告草稿这些结构化字段才是节点真正决策要用到的信息,messages只是留给人工排查时看的流水账,两者不要混为一谈。

2.3 外部知识库接入:为什么不能让每个智能体各自连一遍

知识库的接入方式也是个常见翻车点。许多人会顺手给每个 Agent 都挂上“知识库检索”工具,看起来很方便,实际上问题很大:多个节点同时检索会重复消耗额度,更麻烦的是,不同节点对“相关资料”的理解不一致,导致整合员拿到的条目来源五花八门,引用格式都对不上。

我的做法是只把检索工具下放给检索员节点。检索员统一把外部知识库返回的条目做清洗、去重、编号,存进source_items字段。后续的整合员和质检员都不直接访问原始知识库,而是基于这份清洗后的结构化资料工作。这样知识库的连接只需要维护一个节点,后续如果要切换检索策略(比如从关键词检索换成向量检索),也只影响一个角色。

2.4 工具注册的边界:哪些能力下放,哪些能力锁死

在团队协作里,工具权限不是“能给就给”,而是要遵循最小权限原则。我通常只给以下三类工具:只读型检索工具(查知识库、查文件)、纯计算型工具(解析日期、计算统计量)、以及流程必需的写工具(比如把最终报告保存到指定目录)。任何可能产生外部副作用的工具,都会被锁在单独的管理节点里,而不是让普通角色持有。

这样做的好处是安全边界很清晰:即使某个角色的提示词被误导,它能造成的破坏也被限制在只读范围内。多智能体系统的不可控性,很大程度上可以通过工具权限来收敛。

3. LangGraph 编排机制:分支、并行与条件路由的实现要点

3.1 核心节点拓扑:从线性到有向图的转变

很多教程会从“节点串成链”开始教,但那其实是退化的图。编排多智能体协作,至少要画出这样的有向图结构:

  • 入口节点接收用户请求,调用调度中枢进行意图拆解;
  • 拆解完成后,通过条件边判断:如果用户只是简单查询,直接走轻量回答路径;如果需要调研,进入检索阶段。
  • 检索阶段可以并行展开:多个检索子节点各自查询不同子主题,汇聚后再交给整合员。
  • 整合员产出初稿后,质检员复审;如果复审不通过,流程会回到整合员节点进行修订,并记录修订次数。

这个图跑起来之后,你会发现“线性”只是它的一种特殊情况。真正好用的编排,一定是带分支、有循环、能并行的。

3.2 并行执行:让不同子任务同时跑

LangGraph 支持多个节点并行执行。方式是让一个节点连接多条出边,指向多个下游节点,这些节点在同一轮执行时会被并发调度。

我在这里用了一个真实的“扇出/汇聚”模式:调度中枢拆解出三个子主题后,三个检索子节点各自负责一个子主题,带着独立的关键词去访问外部知识库。这些检索子节点互不依赖,因此可以并行。汇聚时,它们通过operator.add把各自的资料条目追加到source_items里,顺序不重要,因为整合员只关心汇聚后的完整集合。

并行看似简单,真正需要注意的是汇聚节点的等待语义。LangGraph 的机制是:上游所有并行分支都执行完毕后,下游节点才会触发。也就是说你不用自己写“等待所有任务完成”的代码,框架已经保证了下游节点只会看到完整的汇聚结果。这个特性让并行分支的代码保持干净,不需要额外的人工同步逻辑。

3.3 条件边与路由函数:如何判断下一步交给谁

条件边是编排的灵魂。前面提到调度中枢决定下一步交给谁,本质就是一条从“调度中枢”节点出发、终点不同的条件边。

路由函数可以直接基于状态里的结构化字段做判断,而不是去“读心”。比如我会写这样的逻辑:

def route_after_scheduling(state: ResearchState) -> str: if any("urgent" in st.get("priority", "") for st in state["subtasks"]): return "quick_answer" return "research_branch"

再比如整合员之后接质检员,质检员的 verdict 决定是否退回:

def route_after_review(state: ResearchState) -> str: if state["review_verdict"] == "approve": return "finalize" if state["iteration_count"] >= 3: return "finalize" # 不能无限修订 return "integrate_again"

注意这里我把“达到最大修订次数作为最终出口”的路由也写进去了。任何带循环的图,都必须在图上设置明确的退出条件,否则一遇到边缘情况,流程会陷入自我循环。

3.4 图内循环与上限控制:避免“对话死循环”

多智能体流程里最常见的死循环,是质检员永远不满意、整合员永远改不完。单 Agent 模式下,模型可能会因为上下文里的对话惯性一路循环下去;但 LangGraph 是显式的图执行,我可以通过两层机制锁死它。

第一层是上文的显式路由判断:iteration_count达到上限后强制进入终态。第二层是传入recursion_limit,这是整个图执行的硬边界。任何一条执行路径的节点调用次数超过这个上限,运行时会抛出异常,防止流程无限空转。

我一般把iteration_count的业务上限设成 3,而把recursion_limit留出余量,比如设为 20 甚至 50。这个余量要够一次正常复杂任务跑完,可一旦出现异常循环,又会及时爆炸让你发现。

4. 状态管理与持久化的几个深坑

4.1 Reducer 的设计:谁在累加数据,谁在覆盖数据

状态字段的行为由 reducer 决定,这是 LangGraph 和普通字典最大的不同。没有声明 reducer 的字段,每次节点写入都是覆盖式更新;声明了operator.add的列表字段,更新是追加式的。

这个设计要配合语义使用:source_items和subtasks是累积型数据,来自不同节点的产出需要合并,所以用operator.add;draft_report和review_verdict是单一型数据,只保留最近一次写入,所以不加 reducer。

我踩过的坑是:把一个本该“单个最新值”的字段设计成了追加列表,结果整合员每修订一次,报告旧版本就全堆在状态里,不仅占 token,还容易让路由函数读到过期结果。反过来,把“资料条目”设计成了覆盖字段,几个并行检索子节点跑完只剩下最后一个节点的产出,前半程的工作全白干了。Reducer 的选择不是语法细节,它代表你对数据模型的最终决策。

4.2 Checkpointer 与断点恢复:关键节点必须可回放

团队协作流程一旦跑起来,人工干预是常态而不是异常。LangGraph 的 checkpointer 机制允许在指定节点暂停执行、检查状态、甚至修改状态后再恢复。

我把断点设在三个位置:调度中枢完成之后、质检员完成之后、最终报告输出之前。这样做的目的是给人工留出“闸门”。比如调度中枢拆出的子任务列表不符合预期,我可以在执行检索之前直接修改subtasks字段,然后从暂停点继续,而不是杀掉整个进程从头再来。

实现上只需要配置好持久化后端,并在图执行时传入thread_id。同一个线程内的执行历史会被完整保存,这让“上次跑到一半这次接着跑”成为可能。在实际项目里,长耗时任务几乎离不开这个能力。

4.3 跨节点通信:不该直接在消息列表里塞系统指令

我在 2.2 里强调过消息列表只是流水账,这里再补充一个操作层面的经验:节点之间的信息传递,要走结构化字段,不要靠改动系统提示词或插入消息文本。

我们很容易写出这样的实现:整合员节点从messages里找最后一条用户消息,把它包装成新的提示语。短看没问题,但一旦并行分支存在,messages的追加顺序是不确定的,根本无法保证“最后一条消息”就是你想要的上下文。正确做法是,任何需要跨节点传递的决策信息,在写入时就放进独立字段,比如subtasks、source_items。节点只从这些字段取数,完全依赖消息列表的节点注定会在复杂流程里翻车。

4.4 并发写同一个状态字段的冲突处理

并行节点写共享字段,是另一个容易出事的角落。虽然operator.add解决了“追加合并”的问题,但如果你两个并行分支各自都对同一个普通字段做覆盖式写入,最终谁能留下完全取决于调度顺序,结果不可预测。

遇到这种需求,有两种处理思路。第一种是拆分字段:给每个并行分支分配独立的字段槽位,最后再用汇聚节点统一合并。第二种是收敛写权限:并行分支只读公共字段,把写操作集中在汇聚节点里执行。我倾向于第二种,因为它的状态流转更清晰:并行阶段只负责产出中间结果,合并逻辑集中在一处,也好做单元测试。

5. 真实跑起来的调试方法论与性能观察

5.1 用状态快照还原每一步决策

多智能体流程出问题时,单看日志往往不够,因为日志是一连串字符串,很难定位到具体某个节点写入的中间数据。我的调试习惯是:在关键节点开启状态快照,把每次执行完之后的完整状态导出来看。

具体做法很简单:在图的回调或节点返回时,把state序列化成 JSON 存档,文件名带thread_id和节点名。出问题时,我可以直接从“调度中枢刚拆完任务”这个时间点开始回放,看它是怎么判断的、检索结果是什么、整合员读了什么、质检员基于什么结论打回的。这种“状态时间线”比任何自然语言日志都更接近问题本质。

5.2 常见失败模式及应对

跑了几十次之后,我总结出四个高频失败模式:

失败模式表现应对手段
检索结果噪声大资料条目太多、整合员抓不住重点限制单节点返回条目上限,并让检索员在返回前做一轮轻量过滤
质检标准漂移同一批内容,有时通过有时退回给质检员提供显式的核对清单,要求逐项输出判断理由
修订不收敛每轮都改,但改不到点上把修订差分限制在“仅修改被质疑部分”,并设置最多三轮
外部工具超时检索节点频繁失败在节点内部封装重试与降级,先重试两次,再降级为更宽泛的关键词查询

每一项的根因都不完全一样,但共同点是不要把智能体当黑盒,要给每个节点定义明确的失败出口。比如检索节点连续失败三次后,不是直接报错,而是返回一个标记“资料不全”的占位结果,让调度中枢决定是换一种检索策略还是直接生成带有局限说明的报告。

5.3 延迟、Token 消耗与预算控制

多智能体编排不是免费的。一个带两个并行分支的调研任务,总 token 消耗可能是单 Agent 方案的两倍以上。这也正常,毕竟上下文更集中、步骤可追溯。但如果不加控制,成本会很快失控。

我的控制手段有三项:按角色选模型,检索员用便宜快速的模型,只有调度中枢和整合员用高能力模型;限制上下文里传递的资料规模,检索员返回前先做摘要压缩,只保留与任务最相关的字段;在状态里记录累计消耗,每跑完一个阶段比对预算,超了就提前终止进入人工处理。

实测下来,并行执行虽然增加了单时刻的资源开销,但因为整体耗时显著下降(比如三个子主题并行检索,耗时接近单个子主题),对实时交互场景的价值很大。关键是要把 token 消耗纳入状态设计,而不是事后看账单。

6. 系列走向:从“团队协作”到“可插拔角色平台”

6.1 动态注册新角色:把角色变成配置而不是代码

这一期做完之后,我还原了一个重要问题:角色不能写死在代码里。每加一个新角色,就要改路由函数、改节点类、改状态字段,这违背了使用图编排的初衷。

更合理的思路是把角色定义成配置:角色名称、系统提示词模板、可调用工具范围、默认模型、输入输出字段映射,全都放进一个配置结构里。图的结构仍然由代码控制,但“这个角色是不是存在”“它被路由到哪个环节”由配置驱动。比如想增加纠错角色,我只需要在配置里定义它,并在路由表里加一条分支规则,图的整体骨架不用变。

这种做法的价值在于,它让多智能体系统的“演进成本”大幅下降。运营同学可以调整角色分工策略,而不需要会写代码;开发者只需要维护那几张配置表。

6.2 与评估体系结合:多智能体团队的回归测试

多智能体系统的质量评估比单 Agent 更难,因为整体输出质量取决于多个节点的协作效果。我目前采用的是分层评估:先对单个节点做单元指标(检索员看召回率、整合员看文本质量评分、质检员看重合率),再对端到端流程做综合指标(答案完整度、引用准确率、修订收敛率)。

分层评估的意义在于,它能帮助定位“到底是哪个环节坏了”。如果单节点指标都正常但端到端指标崩了,问题大概率出在节点之间的状态衔接上,这时候再回去检查字段设计和路由逻辑。

6.3 我的下一步实验方向

沿着这个成果,我下一步想尝试把一个“工具型智能体”和一个“决策型智能体”彻底分开:决策型只负责判断任务意图、拆解步骤、校验结果;工具型只负责执行具体操作并返回结构化结果。这样一套基于事件驱动的协作框架,也许可以支持更复杂的异步工作流。

另一个方向是给角色加上“记忆分区”。目前共享状态是全局的,但某些角色的中间结论其实不需要被其他角色看到。给状态增加分层可见性,会让我们离开“全局共享一切”的简单模式,更接近真实组织里的信息流边界。

说到底,这套团队协作架构能不能跑好,一半靠 LangGraph 的编排能力,另一半靠你对自己任务结构的理解。先把角色的职责边界画清楚,再去写图,这一期内容才算真正落地了。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进