想象你是一家大型公司的运营总监。公司有一套标准的项目流转制度:每个项目都有一本共享的《项目档案》,档案里记录着所有关键进展;每个部门按固定流程依次处理项目,市场部做完给设计部,设计部做完给法务部……直到有一天,你发现手头有 10 家分公司的合同要审,如果按顺序排队,项目要拖延一个月。于是你引入两台新设备:一台是“智能复印机”(Send),能把档案复制成多份,同时传真到多个部门并行处理;另一台是“智能批注笔”(Command),部门员工处理完自己的部分后,可以在档案上写下最新进展,并顺手写上“下一步送去财务部”。这就是 LangGraph 中 Send 与 Command 的核心区别:Send 负责“一对多并行派发”,Command 负责“状态更新 + 指定下一步跳转”。掌握了这两件工具,你就能像调度总监一样,把复杂的多智能体 Agent 工作流编排得井井有条。
对 AI 从业者而言,理解 Send 与 Command 是掌握 LangGraph 状态机的关键一跃:它决定了你的 Agent 工作流能否真正动态、可扩展,是把原型 Demo 升级为生产级多智能体系统的基础能力。
目录
可视化演示
动态演示LangGraph中Send的核心机制,通过输入/处理/存储/输出四种模式展示关键概念与交互流程。
本节配有交互动效(进入视口后自动加载,约 3KB)。
LangGraph中Send:Send Send API与Comman Send API的并行任务派发 核心机制可视化 LangGraph中Send整体架构与关键组件
状态机基础:一张不断流转的项目档案
还记得那本《项目档案》吗?在 LangGraph 中,整个流程不是写死的一步步代码,而是一张有向图:每个“部门”是一个节点,每条“走廊”是一条边,而那本被传来传去的档案就是 State(状态)。部门员工读档案、做自己负责的部分、再把新进展写回档案——这个过程就叫一次状态更新。只要档案上还有“下一步去哪”的批示,流程就会继续走下去。
在技术世界里,LangGraph 把 AI 工作流建模为“状态机”:图中每个节点是一个 Python 函数,输入是当前 State,输出是对 State 的更新;边决定哪些节点可以接续执行。状态被设计成可共享的容器,比如一个字典或 TypedDict,所有节点都能读取和修改。为了防止多节点同时写档案时互相覆盖,LangGraph 还允许你为每个字段配置 Reducer——就像档案上贴了一张“合并规则”:新增内容用追加,计数用求和,覆盖字段用替换。
技术展开来看,状态机每一步都可以抽象成一个“应用更新”的动作:
回到公司比喻:State 就是档案,Reducer 就是档案室的归档规则。只要规则清晰,再多部门同时批注也不会乱。
- 当前状态 S_t执行节点前,项目档案里的全部内容。
- 更新量 u_t节点函数返回的字典或消息,比如
{"results": ["分析完成"]}。 - 应用函数 applyLangGraph 运行时把 u_t 按字段的 Reducer 规则合并进 S_t。
- 新状态 S_{t+1}合并完成后,档案被改写,流程进入下一步。
Send 并行派发:智能复印机
当十份合同要同一天审完时,你不会让合同在各部门门口排队,而是按一下复印机,把档案复制成十份,同时发到十个审阅员手里。LangGraph 的 Send API 就是这个“复印机”。它允许你在一个节点内,一次返回多个 Send 对象,每个 Send 指定一个目标节点名和一份“子档案”(传参)。运行时拿到这些 Send,就会并行激活多个目标节点。
概念上,Send 解决的是一对多动态分发。它不像静态边那样由开发者预先画好,而是在运行时根据当前状态决定“这次要分几份、发给谁”。比如,从数据库里查到 10 个待处理城市,就 Send 10 个任务;查到 3 个,就 Send 3 个。这种动态性让工作流能适应任意数量的输入。
当所有并行子任务结束后,LangGraph 会通过 State 里的 Reducer 把各分支的更新合并回主档案,这个过程叫 fan-in(汇聚)。我们可以把 Send 的分发看作状态的一次拆分:
复印机最擅长的就是“把一份变多份、分头执行、再汇总回来”。它不修改当前节点的状态,只负责送件,所以当你想同时跑多个独立子任务时,Send 是第一选择。
- 拆分状态 S_split复印机把主档案复制成 n 份,每份携带不同的关键词或参数。
- 第 i 份子状态 S^(i)每个 Send 对象携带的子档案,只包含该分支需要的数据。
- 合并符号 ⊕Reducer 的抽象表示,如
operator.add把多个结果追加成列表。 - 重组后的 S所有并行分支完成后,全局状态被完整还原,流程继续。
Command 状态路由:智能批注笔
有了复印机,公司效率大增;但很快你发现另一个需求:员工处理完档案后,不但要写入新进展,还经常要临时决定“下一步去找谁”。比如法务发现合同有风险,要退回业务部修改;没有风险,就送去盖章。这个“写进展 + 指定下一站”的组合动作,就是 Send 做不到的了——因为它只送件,不更新档案。于是你需要第二台设备:智能批注笔 Command。
Command 的本质是一条“打包指令”:它把两件事绑在一起——更新状态(update)和跳转(goto)。当一个节点返回 Command(goto="review", update={"risk": "high"}) 时,LangGraph 会先把 update 里的内容按 Reducer 合并进 State,再把执行权交给 review 节点。这个过程不需要预先定义静态边,是真正的动态单步流转。
横向对比一下:Send 是“向外派件”,Command 是“自己写完后指路”。Command 的 update 字段直接修改共享档案,这使它在决策节点、循环控制和多智能体交接时格外有用。它甚至可以不带 update 只做跳转,或者不带 goto 只做状态更新,像一支可以只写字、也可以只画箭头的笔。数学上,Command 的表达很简洁:
批注笔让每个节点都拥有了“临场决策”的能力:它一边更新档案内容,一边画出下一步的箭头。这正是动态路由最优雅的打开方式。
- 状态增量 Delta SCommand 的 update 参数,表示要在档案上追加或修改的内容。
- 目标节点 v_nextCommand 的 goto 参数,指定下一步由哪个节点处理。
- 更新新状态 S'Delta S 按 Reducer 合并进 S 后得到的新档案。
- 下一节点 v'执行权切换到 goto 指定的节点,整个图的状态指针移动。
双 API 对比:何时用复印机,何时用批注笔
现在你办公室里同时有了复印机和批注笔,问题来了:什么时候用哪台?最朴素的答案是——看任务形状。如果任务是一份档案要拆给多个部门同时处理,就用 Send 复印;如果任务是一个部门处理完后,需要更新档案并决定下一站,就用 Command 批注。Send 是“扇出/扇入”结构,Command 是“单线跳转”结构。
用途差异还体现在状态上:Send 不直接更新当前节点的状态,它把状态碎片寄出去,最后靠 Reducer 收回来;Command 则直接在当前执行点修改状态,并且马上改变执行路径。所以如果你的目标是“并行提效”,选 Send;目标是“决策分流、循环控制或顺序交接”,选 Command。两者并不互斥,同一个节点里可以既返回多个 Send,也可以返回一个 Command,甚至根据条件决定返回哪种。
选择逻辑可以用一个简单的决策式表示:
回到比喻:复印机适合“发传单”,批注笔适合“写批示”。理解任务的形状,比死记 API 更重要。
- 决策函数 Choice根据当前需求和状态选择 API。
- 条件一Send:任务可并行拆分,如多文档分析、多源检索。
- 条件二Command:需要先写入新状态,再跳转单个目标节点。
- 分支环境 casesLaTeX 的分支式结构,清晰地表达“什么时候走哪条路”。
动态路由模式:让档案自己找路
有了这两种设备,公司的流程终于可以“自己找路”了。动态路由的本质,是让当前状态决定下一站在哪里。在 LangGraph 中,这一切通过节点返回值实现:返回 Send 列表就并行分叉,返回 Command 就单线跳转;返回普通字典就沿静态边继续走。
条件分支是最常见的模式:一个“风险判断”节点读取档案里的 risk_level 字段,然后用 Command 把高风险档案送到人工复核节点,把低风险档案送到自动归档节点。循环控制同样如此:让一个节点在档案里累积迭代次数,若次数没到阈值,就用 Command 跳转回自己;次数到了,就跳转到出口。这就像一个快递柜,根据包裹重量自动把包裹分到不同的柜口。
这些模式背后都依赖同一个抽象——用状态函数计算下一节点:
档案自己会找路,正是状态机最迷人的地方:你不需要写下无穷无尽的 if-else,只需要定义“档案长什么样、节点怎么判断”,流程就会自动蜿蜒向前。
- 决策函数 f以当前档案内容为输入,输出目标节点名。
- 当前状态 S_t读取档案中用于判断的字段,比如风险等级、完成度。
- 下一节点 v_next动态计算出的路由结果,可能是另一个节点,也可能是自己(循环)。
- 状态更新 update节点把自己的工作成果写回档案。
多 Agent 协作:一群部门同时开工
最后,我们把视野拉回多智能体协作。一个复杂的 Agent 系统,往往不是单个模型的连环调用,而是一群各有专长的“部门”协同工作:检索 Agent、写作 Agent、质检 Agent、发布 Agent。LangGraph 的状态机骨架,让每个 Agent 成为图中的一个节点,共享同一份全局 State,通过 Send 和 Command 互相传递任务与结果。
典型协作流程是“先并行、后汇聚、再决策”:主控节点收到用户请求后,用 Send 把任务并行派发给检索 Agent、记忆 Agent、工具调用 Agent;各 Agent 把结果追加到 State 的 results 列表里(Reducer 自动合并);随后一个“规划 Agent”读取合并后的 State,用 Command 决定是直接生成最终答案,还是跳回某个 Agent 补充信息。整个过程就像总监派活:复印机发任务单,部门干完活用批注笔写进度并指路。
用数学语言描述多 Agent 的状态汇聚,就是多个更新量的合并:
当一群部门同时开工,却还能步调统一、信息不丢失时,靠的不是运气,而是 Send 的分发能力、Command 的路由能力,以及 State 的汇聚规则——这套组合拳正是 LangGraph 构建复杂 Agent 工作流的底气。
- Agent 集合 A参与协作的全部智能体节点。
- 单个 Agent 的更新量 Delta S_a每个 Agent 返回的状态片段,比如检索结果、生成文本、校验结论。
- 全局合并所有增量按字段 Reducer 追加或覆盖,形成统一档案。
- 全局状态 S_global下一阶段所有 Agent 都能读到的完整上下文。
AI 应用场景
多源信息并行采集
阶段:数据准备 / Agent 编排
解决的问题:传统顺序抓取网页、数据库和文档时,耗时随数据源数量线性增长。用 LangGraph 的 Send 可以把每个数据源变成一个并行子任务,几倍提升采集效率。
具体案例:一个法律咨询 Agent 需要同时检索裁判文书、法规库和新闻资讯。主控节点读取查询词后,返回三个 Send("search", {...}),三个检索节点并行执行,结果通过 results 字段的 Reducer 合并,最终汇总成长文答复。整个过程从 9 秒压缩到 3 秒。
客服工单智能分派
阶段:决策与路由
解决的问题:传统工单系统用大量 if-else 匹配规则,难以处理模糊意图。LangGraph 的 Command 让 AI 节点先理解语义,再动态更新工单状态并跳转到对应处理 Agent。
具体案例:智能客服收到“我的订单还没到,想退款”,意图识别 Agent 判断为“退款 + 物流查询”复合意图,用 Command 把工单状态更新为 priority=high,并 goto 到退款处理 Agent;若判断为简单咨询,则 goto 到自助回答 Agent。路由决策清晰可控。
多智能体内容生产线
阶段:并行编排与审核
解决的问题:营销内容生产需要文案、配图和合规检查等多个角色协作。直接用大模型串联会丢失中间状态,用 Send 和 Command 可以编排出“可观测、可干预”的生产流水线。
具体案例:产品发布场景中,主节点用 Send 并行派发“文案撰写 Agent、配图生成 Agent、合规检查 Agent”;三个结果合并后,审校节点用 Command 根据合规分数决定 goto 发布,还是带着修改意见 goto 文案 Agent 重写。每个环节的状态都能在 State 里追溯。
Python 演示
import operatorfrom typing import Annotated, TypedDict from langgraph.graph import StateGraph, START, ENDfrom langgraph.types import Command, Send class WorkState(TypedDict): keywords: list[str] results: Annotated[list, operator.add] summary: str keyword: str def dispatch(state: WorkState): keywords = state.get("keywords", []) return [Send("process", {"keyword": kw}) for kw in keywords] def process(state: WorkState): return {"results": [f"已分析: {state['keyword']}"]} def aggregate(state: WorkState): summary = " + ".join(state.get("results", [])) if len(state.get("results", [])) > 3: return Command(goto="review", update={"summary": summary}) return Command(goto="final", update={"summary": summary}) def review(state: WorkState): return Command( goto="final", update={"summary": state["summary"] + " [已复核]"} ) def final(state: WorkState): return {"summary": state["summary"] + " [终稿]"} graph = StateGraph(WorkState)graph.add_node("dispatch", dispatch)graph.add_node("process", process)graph.add_node("aggregate", aggregate)graph.add_node("review", review)graph.add_node("final", final)graph.add_edge(START, "dispatch")graph.add_edge("process", "aggregate")graph.add_edge("final", END) app = graph.compile() if __name__ == "__main__": result = app.invoke({"keywords": ["AI", "Agent", "LangGraph", "RAG"]}) print(result["summary"])
from langgraph.types import Command, Send引入 LangGraph 的两大动态控制 API。results: Annotated[list, operator.add]用 Reducer 声明 results 字段的合并规则,并行分支的返回值会被追加成列表。return [Send("process", {"keyword": kw}) for kw in keywords]dispatch 节点把每个关键词打包成一个 Send 对象,触发 process 并行执行。def process(state: WorkState):目标节点通过 state 接收 Send 携带的keyword参数。return {"results": [f"已分析: {state['keyword']}"]}每次执行返回一个单元素列表,Reducer 自动合并所有并行结果。if len(state.get("results", [])) > 3:根据合并后的状态做动态决策。return Command(goto="review", update={"summary": summary})用 Command 同时更新 summary 并跳转到 review。graph.add_edge("process", "aggregate")静态边表示所有 process 分支完成后汇聚到 aggregate。graph.add_edge("final", END)终稿节点执行完毕后结束整个图。
常见误区
误区以为 Send 像普通 return 一样能直接修改当前节点的状态,于是在 dispatch 节点里同时返回 Send 和更新字段,结果发现当前节点的状态没被更新。
正解Send 的职责是“派件”,它把子任务寄给目标节点,自身不产生状态合并。想修改当前状态,要么返回普通字典,要么用 Command 的 update。复印机只负责复印和分发,不负责在原件上写字。
为何常见:因为 Send 和 Command 都出现在节点返回值中,初学者容易把它们都当成“返回值更新”,忽略了 Send 的纯派发属性。
误区以为 `Command(goto="review")` 之前必须像静态边一样 `add_edge("node_a", "review")`,否则会报错。
正解Command 的 goto 是动态路由指令,只要目标节点已通过 add_node 注册,就可以直接跳转,不需要静态边。它是“智能批注笔”自己画箭头,而不是走预设走廊。
为何常见:因为 LangGraph 的传统流程用边定义连接,大家下意识认为所有跳转都要先声明边。
误区以为 Send 把同一个 State 引用传给多个目标节点,某个分支修改状态会立刻影响其他分支。
正解每个 Send 携带的是独立的子状态片段,分支之间互相隔离;只有通过目标节点的返回值配合 Reducer,更新才会合并回全局状态。各分店独立记账,月底统一汇总。
为何常见:面向对象编程里的“共享对象引用”思维被迁移到了图状态机,忽略了 Reducer 合并机制。
误区以为 Command 可以像 Send 一样一次跳转到多个节点,于是返回 `Command(goto=["a", "b"])`。
正解Command 的 goto 是单个节点名,它天然用于“更新状态 + 单线跳转”。要实现并行,应在节点内返回多个 Send 对象。批注笔一次只能画一个箭头,要分发多份得用复印机。
为何常见:两个 API 名字里都有“动态控制”的影子,功能边界容易混淆。
误区以为动态路由只能用 `add_conditional_edges` 写映射函数,Command 只能在普通节点中出现。
正解Command 正是 LangGraph 推荐的“节点内动态路由”方式,它把状态更新和路径选择打包,比单纯的条件映射表达力更强,尤其适合需要同时写状态的场景。条件边和 Command 可以并存。
为何常见:很多教程先讲 add_conditional_edges,导致学习者把动态路由等同于条件边,忽略了 Command 的封装能力。
面试题
Q1. LangGraph 中,Send API 的核心作用是什么?
- A.并行派发任务到多个节点← 正确答案
- B.串行更新全局状态数据
- C.删除图内已定义的节点
- D.定义新节点的名称
答案:A。Send 负责从当前节点把子任务动态派发给多个目标节点,实现并行扇出。它不直接更新状态,也不负责定义或删除节点。
Q2. Command 对象中的 goto 字段用于什么?
- A.指定下一步执行的节点← 正确答案
- B.修改并行分支的数量
- C.暂停整个图的执行
- D.设置节点的超时时间
答案:A。goto 是 Command 的路由字段,表示执行完当前节点后跳转到哪个节点。它不控制并行数、暂停或超时。
Q3. 下面哪个说法正确描述了 Send 与 Command 的区别?
- A.Send 并行派发,Command 更新状态并跳转← 正确答案
- B.两者功能完全相同可以互换
- C.Send 只用于串行流程控制
- D.Command 不能更新任何状态
答案:A。Send 是一对多并行分发,Command 是“状态更新 + 显式跳转”的打包指令。二者用途不同但可组合,不能完全互换。
Q4. LangGraph 中,多个并行分支的状态更新靠什么合并?
- A.字段上声明的 Reducer 函数← 正确答案
- B.系统随机选择一条分支覆盖
- C.所有更新被静默丢弃
- D.只保留最后完成的更新
答案:A。Reducer(如 operator.add)定义了字段如何合并多次更新,例如列表追加、数值求和或覆盖。它让并行分支的结果能安全汇合。
Q5. 在节点中返回 [Send("worker", {"x": 1}), Send("worker", {"x": 2})] 会发生什么?
- A.worker 节点被并行调用两次← 正确答案
- B.程序立即抛出语法错误
- C.worker 节点串行执行两次
- D.全局状态被直接覆盖
答案:A。返回 Send 列表会触发动态扇出,worker 以不同参数并行执行两次。LangGraph 使用 Pregel 超步机制让它们并发运行。
Q6. Command(goto="check", update={"score": 0.9}) 执行后的效果是?
- A.状态更新 score 为 0.9 并跳转 check 节点← 正确答案
- B.状态保持不变只做跳转
- C.修改状态但停留在当前节点
- D.清空状态后跳转 check 节点
答案:A。Command 的 update 字段先按 Reducer 合并进 State,goto 字段再把执行权交给 check 节点。更新和跳转在同一指令内完成。
Q7. 使用 Command 实现循环工作流时,必须注意什么?
- A.设置终止条件避免无限循环← 正确答案
- B.每次循环必须新增一个节点
- C.循环时不能更新任何状态
- D.只能使用 Send 实现循环
答案:A。Command 可以不断 goto 回自己或前置节点,因此必须在状态中维护计数器或完成标志作为终止条件,否则流程会无限循环。
Q8. 关于 LangGraph 的 Send 和 Command,下列说法正确的有?
- A.Send 返回列表可触发多个节点并行执行← 正确答案
- B.Command 可同时更新状态和指定 goto← 正确答案
- C.Send 可以替代 Command 完成状态更新
- D.两者可在同一工作流中组合使用← 正确答案
答案:ABD。Send 专职并行派发,Command 专职状态更新与跳转,二者职责不同,不能相互替代,但在一个工作流里可以组合搭配、各司其职。
Q9. 使用 Send 并行派发时,子任务结果合并的正确条件包括?
- A.目标节点返回带有 Reducer 的字段← 正确答案
- B.每次返回值直接覆盖旧状态
- C.使用 operator.add 等合并函数声明← 正确答案
- D.必须手动调用 merge 函数
答案:AC。合并依赖字段上的 Reducer 注解,比如 Annotated[list, operator.add]。没有 Reducer 的字段会被多次更新覆盖,而不是自动合并。
Q10. 一个多智能体工作流需要并行分析三份文档再汇总,合理设计包括?
- A.用 Send 并行派发三个分析子节点← 正确答案
- B.用 Reducer 合并各分析结果← 正确答案
- C.用 Command 在汇总节点根据结果跳转← 正确答案
- D.所有节点共享一个可变全局变量
答案:ABC。Send 负责扇出,Reducer 负责合并,Command 负责汇总后的动态路由,这是标准的多智能体编排模式。全局可变变量会破坏状态可追溯性,不应使用。