ARTICLE · 1266823
【LangGraph源码精读】第08期|条件边的执行时机,90%的人搞错了
【LangGraph源码精读】第08期|条件边的执行时机,90%的人搞错了
30篇源码精读系列·第08篇(用户API/编译层第二篇)。你以为条件边在节点执行前路由?不是。你以为路由函数读的是节点执行前的状态?不是。你以为一个节点配了条件边就不能再配普通边?也不是。这篇把条件边的执行时机、状态读取、路径映射、多目标返回、和Command的区别全部讲透,最后附上条件边不生效的5个原因。
一、开头:你的条件边为什么"不生效"?
你写了一个Agent,"agent"节点配了条件边:如果模型输出tool_calls,路由到"tools";否则路由到END。测试时,模型明明输出了tool_calls,却直接走到END了。
排查了半天,可能是这些原因:
- ● 路由函数检查的字段名写错了,返回了END
- ● 路由函数的参数是节点执行前的状态,看不到节点刚写入的tool_calls
- ● 你同时配了普通边和条件边,普通边把执行流带到了别的地方
- ● 路由函数返回的字符串在path_map里没有对应项
- ● 条件边是在节点执行后才调用的,但你在节点函数内部用Command(goto=...)做了路由,两者冲突
条件边是LangGraph中最容易"看起来对但实际不生效"的机制。 核心原因是90%的人搞错了它的执行时机——它到底在节点执行前还是执行后路由?路由函数读到的是节点执行前还是执行后的状态?
这篇把条件边的执行时机彻底讲清。
二、条件边的执行时机:节点执行后
这是最关键的一点,先给结论。
2.1 条件边在source节点执行完成后才被调用
| 此时被调用 | ||
条件边是在source节点完整执行完、writes已经应用到Channel之后、下一轮Plan阶段才被调用的。 不是在节点执行前,也不是在节点执行中。
2.2 这意味着什么?
条件边是"出口路由",不是"入口守卫"。 它决定的是"从source节点出来后往哪走",不是"要不要进入source节点"。source节点一定会执行,条件边只影响它的后续。
三、路由函数读到的状态:包含source节点的writes
这是第二个关键结论。
3.1 路由函数的状态快照
因为条件边在source节点执行后才被调用,所以路由函数读到的状态已经包含了source节点的输出:
这就是为什么经典的ReAct路由函数能检查last_message的tool_calls。 因为它在agent节点执行后才被调用,读到的状态已经包含了agent节点刚输出的AI消息。
3.2 经典ReAct路由的完整流程
路由函数在每一轮source节点执行后都会被调用,不是只调用一次。 这就是循环的实现——agent→tools→agent→tools→...直到agent不再输出tool_calls。
四、条件边的完整执行流程
从编译到运行时,条件边经历了5个步骤。
4.1 编译时:attach_branch
4.2 运行时:路由函数调用
五、path_map的作用和3种写法
path_map是路由函数返回值和真实节点名之间的映射。
5.1 path_map的3种写法
5.2 为什么推荐提供path_map?
5.3 path_map的特殊目标
六、普通边+条件边共存:所有目标都会触发
这是第三个容易搞错的点。
6.1 一个节点可以同时有普通边和条件边
6.2 一个具体例子
假设节点A有:
- ● 一条普通边:A→B
- ● 一条条件边,路由函数返回"C"
普通边和条件边不是"二选一",而是"合并"。 普通边的目标固定触发,条件边的目标根据路由函数动态触发,两者合并后在下一轮并行执行。
6.3 这个设计的应用场景
但如果条件边也可能返回B,就会出现重复触发。 这时B只会执行一次(版本号去重),不会执行两次,但要注意这个设计可能不是你想要的。
七、条件边vs Command:两种路由方式
LangGraph有两种动态路由方式,容易混淆。
7.1 怎么选?
不要同时用条件边和Command做路由。 如果节点返回了Command(goto=...),同时又配了条件边,两者会冲突或产生意外的合并结果。选一种。
八、条件边返回多个目标
条件边不仅可以路由到一个节点,还可以路由到多个节点。
8.1 多目标返回
8.2 多目标的典型场景
多目标返回时,所有目标节点在下一轮并行执行。 如果多个目标写入同一个Channel,需要有reducer,否则会冲突。
九、条件边不生效的5个原因
这是最实用的排查指南。
9.1 调试路由函数的3个方法
最佳实践:路由函数永远写成纯函数,不调用LLM、不产生副作用、不依赖外部状态。 这样可以单独单元测试,也可以在出问题时直接调用排查。
十、路由函数的设计原则
10.1 把路由信号存在State里
最佳实践是:节点负责产生路由信号(写入State字段),路由函数只负责读取信号并路由。
推荐第二种。 节点负责"业务决策"(写入明确的route字段),路由函数只负责"读字段返回key"。这样路由函数极其简单,不会出错,也方便单元测试。
十一、常见坑:条件边的5个隐蔽问题
十二、面试关联:条件边的5道高频题
十三、本期小结
条件边执行时机,核心就6句话:
- 1. 条件边在source节点执行完成后才被调用。 不是执行前,不是执行中,是writes应用到Channel之后、下一轮Plan阶段。它是"出口路由",不是"入口守卫"——source节点一定会执行,条件边只影响后续。
- 2. 路由函数读到的是节点执行后的状态。 包含source节点刚写入的writes,所以经典ReAct路由能检查last_message的tool_calls。路由函数在每一轮source节点执行后都会被调用,这就是循环的实现。
- 3. path_map是路由返回值和真实节点名的映射。 3种写法:不提供(不推荐,编译时无法校验)、dict映射(推荐)、list(推荐)。提供path_map让编译时能校验所有目标、图可视化完整。
- 4. 普通边和条件边可以共存,目标会合并。 普通边目标固定触发,条件边目标动态触发,合并后下一轮并行执行。不是二选一,不会覆盖。应用场景包括固定日志+动态路由。
- 5. 条件边和Command是两种路由方式,不要同时用。 条件边是外部纯函数,可单独测试、可复用;Command是节点内部返回,可同时更新状态。选哪种取决于路由逻辑是否独立、是否需要单独测试。
- 6. 条件边可以返回多个目标。 字符串列表或Send列表,所有目标并行执行。条件边不生效的5个原因:path_map缺项、字段名错误、用了Command、普通边冲突、路由函数异常。最佳实践:路由函数写成纯函数,路由信号存在State里,总是提供path_map。
十四、下一期预告
下一期(源码09),用户API/编译层第三篇:
【LangGraph源码精读】第09期|从StateGraph到CompiledStateGraph,compile时发生了什么
compile的完整流程:schema解析、Channel创建、节点编译、边和分支处理、校验、Pregel实例构建。搞懂你调用compile()的那一刻,底层到底做了多少工作。
你在使用条件边时遇到过不生效的问题吗?评论区聊聊,我优先写你最关心的场景。
本文为《LangGraph源码精读30讲》系列第08篇。内容基于LangGraph v1.2.11源码结构梳理,核心文件位于libs/langgraph/langgraph/graph/目录下的graph.py(add_conditional_edges、Branch)、state.py(StateGraph.compile、attach_branch),以及pregel/_algo.py(prepare_next_tasks中条件边路由)。版本更新后部分细节可能变化。如有错漏欢迎指正。















































































