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 则负责合并循环过程中产生的状态更新。

相关推荐
闲猫1 小时前
LangChain / Advanced usage / Long-term memory
langchain
liwulin05061 小时前
【PYTHON】使用Selenium + ChromeDriver以及XPATH语法
开发语言·python·selenium
copyer_xyf1 小时前
Agentic RAG 实战:PostgreSQL + LangGraph 一条链路
python·postgresql·agent
动力 continue1 小时前
Python 元类与异常类:类的“制造工厂”与“报错定制师”
开发语言·python·制造
147API1 小时前
Python批量生成蒸馏数据,并发、重试、幂等和断点续跑
开发语言·jvm·python
青 春 记 忆1 小时前
零基础入门python04:用注册校验理解字典、集合和条件判断
开发语言·vscode·python·python3.11
JaydenAI1 小时前
[基于OpenEvals的自动化评估-12]Agent执行轨迹评估[无LLM参与]
ai·langchain·agent·evaluation·openevals
kels88991 小时前
实战排坑:黄金实时API开发,XAUUSD Tick报文异常处理实践
开发语言·python·websocket·网络协议·信息可视化
FBI HackerHarry浩1 小时前
Pandas 库中用于分类变量独热编码(One-Hot Encoding)的函数 pd.get_dummies() 函数
开发语言·人工智能·python·pandas