LangGraph 入门实战(4)

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 实现条件循环主要分为三步:

  1. 编写路由函数,返回不同的路由标识;
  2. 使用 add_conditional_edges() 将标识映射到节点或 END;
  3. 使用普通边把末尾节点连接回路由节点。

条件边负责决定流程方向,普通边负责连接固定步骤,reducer 则负责合并循环过程中产生的状态更新。

相关推荐
Wx-bishekaifayuan5 分钟前
express皖美特色农产品网售系统53118-计算机课程设计、毕业设计
人工智能·spring boot·后端·python·课程设计·express·旅游
青少儿编程课堂34 分钟前
迪杰斯特拉(Dijkstra)算法:单源最短路与赛场路线规划
c++·python·算法·bfs·信息学竞赛
计算机编程指导师1 小时前
【计算机毕设推荐】基于Hadoop+Spark的公共交通运营数据分析与可视化系统源码 毕业设计 选题推荐 毕设选题 数据分析 机器学习
大数据·hadoop·python·计算机·数据分析·spark·交通
Wx-bishekaifayuan1 小时前
springboot汉中旅游服务平台41442-计算机课程设计、毕业设计
spring boot·后端·python·django·课程设计·express·旅游
WeiXin_DZbishe1 小时前
基于springboot城市公交实时运营与调度系统-计算机毕设 附源码【课程设计】66173
java·vue.js·spring boot·后端·python·php·课程设计
泡茶喝茶写代码1 小时前
A股量化数据工程:从 REST 接口到策略信号(第 18 篇):放量缩量异动扫描
java·python·股票数据api·股票数据api接口·股票api数据接口·股票量化数据api·股票量化数据接口
WeiXin_DZbishe1 小时前
基于Springboot+AI的kuall音乐广场的设计与实现-计算机毕设 附源码【课程设计】12715
vue.js·人工智能·spring boot·后端·python·php·课程设计
imgsq3 小时前
电子学会考级 vs CCF-GESP:两套少儿编程考级怎么选
python·青少年编程
Smoothcloud润云3 小时前
PyTorch推理服务动态批处理实战:兼顾吞吐与延迟
人工智能·pytorch·python·深度学习·ai·gpu算力