LangGraph 实战:用条件分支控制循环与结束
LangGraph 可以通过条件边动态决定下一步执行哪个节点。本文用一个简单示例实现以下流程:
text
START -> step1 -> step2 -> step3 -> step4 -> step1
^ |
|------ 条件满足时 -------|
step1 -> END(条件不满足时)
程序还会使用 reducer 累积每个节点的执行记录,并将图导出为 PNG。

1. 环境准备
本文实际运行环境:
text
Python 3.14.7
LangGraph 1.2.10
安装依赖:
bash
python -m pip install -U langgraph typing-extensions
2. 定义可累积状态
python
import operator
from typing import Annotated, Any
from typing_extensions import TypedDict
class State(TypedDict):
aggregate: Annotated[list[Any], operator.add]
Annotated[list[Any], operator.add] 表示 aggregate 是列表,并使用列表加法合并节点返回的新值。
例如:
python
["step1"] + ["step2"]
# 结果
["step1", "step2"]
因此每个节点只需要返回自己的执行记录,LangGraph 会自动累积结果。
3. 编写条件路由
核心路由函数如下:
python
def routerDef(state: State) -> Literal["step2", "end"]:
if len(state["aggregate"]) < 6:
return "step2"
return "end"
它会检查 aggregate 的长度:
- 小于
6:进入step2,继续执行循环; - 大于或等于
6:返回end,结束流程。
然后使用 add_conditional_edges() 把路由结果映射到真正的节点:
python
graph_builder.add_conditional_edges(
"step1",
routerDef,
{
"step2": "step2",
"end": END,
},
)
这里的 "end" 只是路由函数返回的业务标识,必须映射到 LangGraph 的特殊终点 END。如果直接把 "end" 当成目标,LangGraph 会寻找一个名为 end 的普通节点。
4. 构建循环
python
graph_builder.add_edge(START, "step1")
graph_builder.add_edge("step2", "step3")
graph_builder.add_edge("step3", "step4")
graph_builder.add_edge("step4", "step1")
step4 -> step1 让流程回到路由节点,从而形成循环。每次执行 step1 后,routerDef 都会重新判断是继续还是结束。
生成的流程图如下:

虚线表示条件边,实线表示普通边。
5. 完整代码
以下代码已经实际运行验证:
python
import operator
from typing import Annotated, Any, Literal
from langgraph.graph import END, START, StateGraph
from typing_extensions import TypedDict
# Annotated 可以在类型中提供额外的元数据,而不影响类型检查对类型的理解
class State(TypedDict):
aggregate: Annotated[list[Any], operator.add]
def step1(state: State):
print("这是step1")
return {"aggregate": ["step1"]}
def step2(state: State):
print("这是step2")
return {"aggregate": ["step2"]}
def step3(state: State):
print("step3")
return {"aggregate": ["step3"]}
def step4(state: State):
print("step4")
return {"aggregate": ["step4"]}
graph_builder = StateGraph(State)
graph_builder.add_node(step1).add_node(step2).add_node(step3).add_node(step4)
def routerDef(state: State) -> Literal["step2", "end"]:
if len(state["aggregate"]) < 6:
return "step2"
return "end"
graph_builder.add_edge(START, "step1")
graph_builder.add_conditional_edges(
"step1",
routerDef,
{
"step2": "step2",
"end": END,
},
)
graph_builder.add_edge("step2", "step3")
graph_builder.add_edge("step3", "step4")
graph_builder.add_edge("step4", "step1")
# 编译图
graph = graph_builder.compile()
# 生成流程图
png_bytes = graph.get_graph().draw_mermaid_png()
with open("langgraph_chain.png", "wb") as f:
f.write(png_bytes)
# 执行图
res = graph.invoke({"aggregate": []}, {"configurable": {"thread_id": "1"}})
print("执行结果:", res)
6. 真实运行输出
执行:
bash
python index.py
控制台输出如下:
text
这是step1
这是step2
step3
step4
这是step1
这是step2
step3
step4
这是step1
执行结果: {'aggregate': ['step1', 'step2', 'step3', 'step4', 'step1', 'step2', 'step3', 'step4', 'step1']}
7. 为什么最终长度是 9,而不是 6
路由函数只在 step1 执行后检查列表长度。检查时的长度依次是:
text
1 -> 5 -> 9
第二次检查时长度为 5,仍然满足 < 6,所以程序又执行了一轮 step2 -> step3 -> step4 -> step1。第三次检查时长度变成 9,流程才进入 END。
如果业务要求在精确次数后结束,应该使用单独的计数字段,而不是依赖聚合列表的长度。
8. thread_id 是否会保存状态
示例传入了:
python
{"configurable": {"thread_id": "1"}}
但当前图编译时没有配置 checkpointer:
python
graph = graph_builder.compile()
因此 thread_id 在本例中不会自动保存或恢复状态。只有接入 checkpointer 后,它才会用于区分不同会话。
总结
使用 LangGraph 实现条件循环主要分为三步:
- 编写路由函数,返回不同的路由标识;
- 使用
add_conditional_edges()将标识映射到节点或END; - 使用普通边把末尾节点连接回路由节点。
条件边负责决定流程方向,普通边负责连接固定步骤,reducer 则负责合并循环过程中产生的状态更新。