使用LangChain+LangGraph开发AI应用(10) - 多智能体编排

学习进度
单个 agent 解决一个完整任务,任务复杂到需要查资料、写稿、审稿三种角色各干一摊时,把它们排进同一张图,就是多智能体编排。这一篇讲的就是怎么排。
底座是三样现成的东西。每个 agent 就是一个节点,或者一个 compile 过的子图;节点之间的控制流转靠 Command(goto=);共享数据靠状态和 Reducer。没有新引擎,只有几套把节点组织起来的成熟阵型。示例在 source/hello_multi.py,八个 demo,全部纯函数。完整案例是一个编辑部流水线 source/newsroom.py,主管派活,写手写稿,审稿带禁词检查,被打回就改稿重审,四个角色一张图跑完。
前言用途
什么时候需要多个 agent。单 agent 加一筐工具能干很多事,但工具超过二十个,模型选错的概率肉眼可见地涨;系统提示词塞进角色设定、业务规则、输出规范,越长越顾此失彼;同一个节点的提示词里既要它会查证又要它会修辞,两头都做不精。
拆成多个 agent,每个 agent 只背自己的工具子集和角色提示词,选工具的准确率和回答质量都会回来。代价是编排复杂度,谁先谁后、谁把结果交给谁、出了分歧听谁的,这些问题从模型脑子里搬到了图结构里。图结构是确定的、可测试的,这反而比把一切都押在单条系统提示词上更稳。
常见阵型就三种。监督者模式,一个主管节点看状态决定派谁,干完的把控制权交回来,适合流程有明确阶段的场景。群集模式,没有主管,agent 之间多对多互相移交,谁干完谁决定下一步,适合角色平权的协作。子图嵌套,把一张完整图当另一个图的节点,适合团队分层,主编只关心撰稿子图的产出,不管它内部几道工序。三种阵型底层的路由件是同一个,Command。
原理概念
多智能体在 LangGraph 里没有专用 API。一张图里放几个节点,每个节点背后是一个完整的 agent(模型加工具循环,或者干脆一个子图),节点间怎么连就是编排模式本身。条件边和 Command(goto),在这里凑成主力。
子图是第一个关键词。compile 过的图能直接 add_node 进另一张图,前提是两边共享状态 schema,字段名对得上,数据就自动流动。想隐藏内部细节,子图可以配 input_schema 和 output_schema,对外只露两个窄接口,外层图看不到子图的全量状态。这一招在团队分层时特别有用,写手 agent 内部可能有查资料、列提纲、成稿三道工序,主编只需要看到最终稿。
控制权移交是第二个关键词。节点返回 Command(goto="另一个节点", update={...}),状态更新和控制权一起交出去,这就是 agent 之间的「干完了,该你了」。默认的 goto 只在本图内生效。子图里的节点想直接指挥父图的节点,得在 Command 里加 graph=Command.PARENT,更新随控制权一起上浮。

有两处行为和直觉对不上。一是注解,写 Command[Literal["x"]] 能让静态检查器校验 goto 目标,但校验范围是本图节点,跨图目标写了注解,编译都过不去。二是更新合并,PARENT 上浮的更新要并进父图状态,多个并行子图同时写一个共享 key 时必须有 Reducer 兜着,否则撞车报错;串行场景下单个子图上浮一个无 Reducer 的普通字段,实测是可以的。写并行分支时,共享字段一律配 Reducer,这条别赌。
核心用法
八个用法,示例在 source/hello_multi.py,全部纯函数不依赖模型。
1. 子图当节点
compile 过的图直接当节点,这是最朴素的组合方式。
| |
| |
文本从外层进子图变大写,出来再被 wrap 加上括号。两边共用同一个 Base schema,字段名 text 全程对得上,数据自动流。子图内部可以有自己的节点、边、条件路由,对外只是图里的一个点。
2. 输入输出收窄
子图配 input_schema 与 output_schema,对外只露两个窄接口。
| |
| |
子图内部看得到 Overall 全部字段,外人只能送进 In、拿回 Out。invoke 完返回值里只剩 clean 一个键,内部字段 raw 不会漏出去。给 agent 子图套上这一层,上层团队图的提示词里就只会出现该看的字段。
3. 同图移交
节点返回 Command,把控制权连同更新一起交出去。
| |
| |
a 返回的 Command 里 goto 指向 b,log 的更新随行。类型注解 Command[Literal["b"]] 声明本节点可能的去向,图编译时会校验 b 确实是本图节点,写错名字编译就报错。这个注解是可选的,但多智能体图里我建议都写,移交关系即文档。
4. 跨图导航
子图节点想直接指挥父图的节点,Command 里加 graph=Command.PARENT。
| |
| |
子图 cmd 的更新 child 上浮进父图,父图接着跑 outer_end 再补一笔 outer,加上初始的 init,三笔更新按顺序合并。注意这个节点函数没写 Command[Literal[…]] 注解,编译期校验的范围是本图节点表,父图里的目标过不了编译。跨图导航是双刃剑,它让子图能穿透封装直接插手外层流程,用多了子图就失去了独立可测试性,我的原则是子图只上浮数据,控制权尽量留在子图内部收尾。
5. PARENT 误用
graph=Command.PARENT 写在顶层图节点里,直接炸。
| |
| |
LangGraph 抛的是专门的 ParentCommand 异常,语义很清楚,你要求上浮,可你在顶层。demo 里把异常接住打了一行说明,实际代码里这个错发生在运行期,图跑起来才炸。规避靠纪律,Command.PARENT 只出现在明确知道自己是子图的节点函数里。
6. Supervisor
主管节点决定下一个干活的,干完的把控制权交回主管。
| |
| |
结构上有三个特征。主管有到每个 agent 的出边(demo 里走 Command),每个 agent 有一条回主管的边,主管到 END 的边让流程有出口。demo 里主管用固定序列占位,真实实现换成模型的结构化输出,输入是当前状态摘要,输出是下一个 agent 的名字,路由机制一行不用改。主管决策读状态,agent 产出写状态,状态是唯一的沟通媒介,这是监督者模式最值得维持的纪律。
7. Swarm 网络
没有主管,agent 之间多对多,谁干完谁决定交给谁。
| |
| |
Swarm 的移交决策写在各 agent 自己的函数里,每个 agent 知道自己擅长什么、干完后该轮到谁。demo 里移交关系恰好能画成一条直线,真实场景里可以是网状的,x 干完可能去 y 也可能去 z。移交函数多了以后,公共的移交逻辑(比如把对话历史带上)可以抽成工厂函数,官方文档里叫 handoff tools 的做法就是把移交包装成工具给模型选。
8. 可视化
compile 过的图自带一张结构图,get_graph().draw_mermaid() 生成文本图。
| |
| |
静态边画实线,Command 这类动态路由画虚线,一眼看出控制权怎么流。零依赖零联网,把输出粘进任何支持 mermaid 的渲染器(GitHub 的 markdown 原生支持)就能看图。draw_png 要装 pygraphviz,draw_mermaid_png 要联网调用渲染服务,本地开发我固定用文本版。多智能体图的移交关系比单图复杂得多,写完先出图看一眼,能省掉大半接线错误。
完整案例
八个用法都过了一遍。收尾搭一个真的能协作的编辑部,newsroom.py,四个角色一张图。researcher 查资料,writer 写稿,reviewer 审稿带禁词检查,supervisor 看状态派活。
| |
| |
supervisor 是纯状态驱动的决策器,代码短得像伪码。
| |
没有队列没有计数,每个判断对应一个阶段,notes 空就查资料,draft 空就写稿,有批注先改稿,没定稿先送审,全齐了收工。reviewer 是质检关口,草稿里出现禁词就地打回。
| |
打回不直接跳 writer,控制权先回 supervisor,由它决定下一步。这是我在多 agent 图里守的一条规矩,agent 之间不做横向移交,全部经主管中转,移交关系只存在于 supervisor 一个函数里,改流程只改一处。试试喂一个故意带禁词的选题。
| |
| |
reviewer 抓到禁词打回,writer 真的把那个词换成了助力,二审通过。批注、改稿、复审,整条返工链在 trace 里看得清清楚楚。writer 的改稿必须是真改(把禁词从稿子里换掉),如果只是把批注文字拼在稿子后面,禁词还在,reviewer 每轮都会打回,图就死循环了,我在开发这个案例时真踩过。 supervisor 里还有一个兜底,revisions 达到上限就 goto 结束并写转人工,多智能体系统里每个循环都必须有硬上限,这是从那次死循环里换来的纪律。
结构图用 demo 8 的方法打出来看一眼。
| |
星型拓扑,四个 agent 围着 supervisor,虚线全是动态路由。测试在 tests/test_hello_multi.py 七条加 tests/test_newsroom.py 八条,子图合并、收窄接口、移交路由、ParentCommand、注解编译校验、打回改稿再审、返工上限都钉住了。加上前九篇的,全仓一百六十七条,全绿。
源码
https://github.com/yidao620c/xneng-langchain-langgraph