前言
上篇内容《LangGraph1.0速通指南(一)------ 核心概念、点、边》中笔者介绍了LangGraph 的基本概念,以及如何通过定义节点与边来构建多种图结构。有了这些基础,大家已经能够搭建起一个可运行的智能体工作流。
在本文中笔者将进一步展开 LangGraph 更为灵活和强大的能力。你将学习到:
- 条件边(Conditional Edges) :除条件函数外,如何让图结构根据运行时的状态动态选择下一步路径,实现分支与循环逻辑;
- 记忆(Memory) :如何让 LangGraph 在多次调用中保留和更新状态,构建LangGraph持续运行的能力
- 人在回路(Human-in-the-loop) :如何在关键节点引入人工确认或干预,使LangGraph图更加可控、可靠。
LangGraph1.0 与先前版本变动不大,预计花费三节左右的时间讲解,同时也要说明笔者的专栏《深入浅出LangChain&LangGraph AI Agent 智能体开发》适合所有对 LangChain 感兴趣的学习者,无论之前是否接触过 LangChain。该专栏基于笔者在实际项目中的深度使用经验,系统讲解了使用LangChain/LangGraph如何开发智能体,目前已更新 32 讲,并持续补充实战与拓展内容。欢迎感兴趣的同学关注笔者的掘金账号与专栏,也可关注笔者的同名微信公众号 大模型真好玩 ,每期分享涉及的代码均可在公众号私信: LangChain智能体开发免费获取。
一、使用Command对象:边定义的第二种方法
上篇内容《LangGraph1.0速通指南(一)------ 核心概念、点、边》中笔者介绍了通过 add_conditional_edges 配合条件路由函数来定义边的方法。该函数根据图的状态返回下一个节点的名称,从而实现动态路由。
python
...
def conditional_edge(state: State) -> Literal['b', 'c', END]:
select = state["nList"][-1]
if select == "b":
return 'b'
elif select == 'c':
return 'c'
elif select == 'q':
return END
else:
return END
...
builder.add_conditional_edges("a", conditional_edge)
这是一种将路由逻辑 (边)与节点执行逻辑 (点)分离的清晰模式。然而,LangGraph 提供了另一种更为直接的控制流方式:允许节点函数在返回时,不仅更新状态,还能显式指定接下来要执行的节点 。这是通过返回一个特殊的 langgraph.types.Command 对象来实现的。
1.1 Command 对象:将路由逻辑内置于节点
Command 对象主要有两个关键指令:
update: 用于更新图的状态。goto: 用于指定下一个要执行的节点。
这意味着可以在节点的处理函数内部,直接决定工作流的下一步走向,无需提前在图中定义好所有条件边。对于下图中的节点A,笔者希望在执行其逻辑后,根据当前状态选择跳转节点B或节点C:

这就需要重写node_a函数
python
def node_a(state: State) -> Command[Literal['b','c', END]]:
select = state['nList'][-1]
if select == 'b':
next_node = 'b'
elif select == 'c':
next_node = 'c'
elif select == 'q':
next_node = END
else:
next_node = END
return Command(
update=State(nList=[select]),
goto=next_node
)
1.2 完整示例与执行过程分析
-
将这种思路应用到上篇文章的条件图结构中,完整的代码如下:
pythonfrom langgraph.graph import START, END, StateGraph import operator from typing import TypedDict, List, Annotated, Literal from langgraph.types import Command class State(TypedDict): nList: Annotated[List[str], operator.add] def node_a(state: State) -> Command[Literal['b','c', END]]: select = state['nList'][-1] if select == 'b': next_node = 'b' elif select == 'c': next_node = 'c' elif select == 'q': next_node = END else: next_node = END return Command( update=State(nList=[select]), goto=next_node ) def node_b(state: State): return Command( goto=END ) def node_c(state: State): return Command( goto=END ) builder = StateGraph(State) builder.add_node("a", node_a) builder.add_node("b", node_b) builder.add_node("c", node_c) builder.add_edge(START, "a") graph = builder.compile() user = input('b, c or q to quit:') input_state = State( nList=[user] ) print(graph.invoke(input_state)) -
运行测试,分析输入
'b'时的执行过程:-
初始状态 :
State(nList=['b']) -
执行节点 A:
- 读取状态
nList[-1],得到'b'。 - 逻辑判断
next_node = 'b'。 - 返回
Command(update=State(nList=['b']), goto='b')。 - 状态更新 :
update指令将['b']追加到原状态。由于reducer是operator.add,状态变为nList=['b', 'b']。
- 读取状态
-
跳转与执行 :根据
goto='b',图跳转到节点 B。 -
执行节点 B :节点 B 返回
Command(goto=END),图运行结束。 -
最终输出状态 :
{'nList': ['b', 'b']}。 -
输入
'c'或其它字符(如'q')的逻辑类似,最终状态会分别为['c', 'c']或['q']。



-
1.3 方法对比选型
虽然 Command 对象提供了强大的、由节点驱动流程的能力,但在大多数智能体工作流的设计中,笔者更推荐使用 add_conditional_edges 配合条件路由函数的方式来定义边。
主要原因在于"关注点分离 "和"可维护性 "。将"做什么"(节点逻辑)和"接下来去哪"(路由逻辑)分开定义,使得图的结构更加清晰、易于理解和调试。图的builder本身就能完整反映业务逻辑的流转,而不是将控制流隐藏在节点的实现细节里。
因此,Command 方案更适合作为一种高级或特定场景下的补充手段,而在构建清晰、可维护的 LangGraph 应用时,应优先考虑使用条件边函数来定义路由。
二、Memory:实现智能体的长期记忆
在 LangGraph 的工作流中,节点按顺序执行,状态随之更新。然而,这种状态默认只存在于单次调用(invoke)的生命周期内。为了让智能体像人类一样拥有"记忆",能够在多次交互中记住上下文(例如,像在 DeepSeek 网页中进行的多轮对话),LangGraph 提供了检查点(Checkpoint) 机制。

2.1 理解检查点:状态快照与线程
检查点 是 LangGraph 实现记忆功能的核心。它本质上是在图运行的关键步骤(如每个节点执行后) 对当前完整状态进行的一次快照(Snapshot),并将其持久化存储。这个过程类似于为虚拟机保存恢复点。

这些按时间顺序排列的检查点集合,构成了一个线程(Thread) 。在线程中,每次graph.invoke调用的历史记录都被完整保存。这解释了为何在 DeepSeek 中,"新对话"会开启一个没有历史的纯净会话------因为它开启了一个新的线程。
检查点的核心优势:
引入检查点机制为智能体开发带来了如下关键能力:
- 状态持久化:即使图停止运行,状态也能被保留,下次可从断点恢复。
- 错误恢复与重试:当某个节点执行失败时,可以从上一个检查点恢复状态并重试,避免丢失全部进度。
- 时间旅行与回滚:如果发现智能体在长期运行后"跑偏",可以回滚到任意历史检查点,并从该健康状态重新开始。
- 暂停与继续:可以暂停一个长时间运行的任务,并在之后准确地从中断处继续执行。

2.2 实战:使用内存检查点器
LangGraph 提供了多种检查点存储后端,包括内存 (InMemorySaver)、Postgres数据库等。本篇分享笔者以最简单的内存检查点器为例进行演示。其他存储器的用法类似,大家可以在笔者的文章《深入浅出LangGraph AI Agent智能体开发教程(九)---LangGraph长短期记忆管理》中查看详细示例。
-
构建基础图: 复用上一节中基于
Command对象的路由图结构。pythonfrom langgraph.graph import START, END, StateGraph import operator from typing import TypedDict, List, Annotated, Literal from langgraph.types import Command class State(TypedDict): nList: Annotated[List[str], operator.add] def node_a(state: State) -> Command[Literal['b','c', END]]: select = state['nList'][-1] if select == 'b': next_node = 'b' elif select == 'c': next_node = 'c' elif select == 'q': next_node = END else: next_node = END return Command( update=State(nList=[select]), goto=next_node ) def node_b(state: State): return Command( goto=END ) def node_c(state: State): return Command( goto=END ) builder = StateGraph(State) builder.add_node("a", node_a) builder.add_node("b", node_b) builder.add_node("c", node_c) builder.add_edge(START, "a")
-
引入内存检查点器: 从
langgraph.checkpoint.memory导入InMemorySaver,并实例化。config中的thread_id是关键,它标识了会话线程。相同thread_id下的所有调用将共享状态历史。pythonfrom langgraph.checkpoint.memory import InMemorySaver memory = InMemorySaver() config = { "configurable":{ "thread_id", "1" } } -
编译时启用检查点: 在编译图时,通过
checkpointer参数传入检查点器实例。pythongraph = builder.compile(checkpointer=memory) -
在循环调用中体验记忆: 现在笔者通过一个循环来模拟多轮交互。关键在于,每次调用
graph.invoke时都传入相同的config,这样 LangGraph 就会自动加载该线程 (thread_id='1') 的上一次状态,并在执行后保存新状态。pythonwhile True: user = input('b, c or q to quit:') input_state = State( nList=[user] ) result = graph.invoke(input_state, config) print(result) if result['nList'][-1] == 'q': print('quit') break -
运行测试: 输入序列:
b → c → b → q,执行过程解析:- 第一轮 (
输入b) :初始状态['b']。节点A执行后更新状态为['b', 'b']并跳转到节点B,最终状态为['b', 'b']。 - 第二轮 (
输入c) :由于检查点存在,初始状态并非空列表,而是上一轮的最终状态['b', 'b']。输入c后,状态会累积到['b','b','c','c'] - 第三轮 (
输入b) :状态继续累积。 - 第四轮 (
输入q) :触发退出。

- 第一轮 (
-
通过简单的几步配置,LangGraph图即可具备"记忆"能力。
thread_id是管理不同记忆会话的钥匙。只要使用相同的thread_id,智能体就能在多次调用间保持并累积状态。对于生产环境,只需将InMemorySaver替换为PostgresSaver或自定义的持久化检查点器,即可实现稳定可靠的长期记忆。更详细的关于LangGraph记忆的内容大家可参考笔者的文章: 深入浅出LangGraph AI Agent智能体开发教程(九)---LangGraph长短期记忆管理
三、中断与人在回路
在许多实际场景中需要在智能体工作流的关键节点引入人工判断。例如,在大模型调用工具执行数据库写操作、发送邮件或进行其他高风险动作前,必须由人工确认。LangGraph 通过中断(Interrupt) 机制优雅地支持了这种"人在回路(Human-in-the-loop)"模式。
中断允许一个正在运行的图在特定节点暂停,将控制权交还给外部程序(通常是等待用户输入),然后根据外部输入的结果决定如何恢复执行。这个强大的功能同样建立在之前介绍的检查点(Checkpoint) 机制之上。

3.1 核心概念:interrupt 与 Command(resume=...)
继续改造之前使用的条件路由图。核心改动在于,当节点 A 遇到无法处理的输入时,不再直接结束,而是主动引发一个中断,等待外部干预。
实现中断需要两个关键操作:
- 在节点内引发中断 :使用
interrupt()函数。这会抛出一个特殊信号,使图立即暂停运行,并将包含中断信息的快照保存到检查点中。 - 从外部恢复执行 :向图中发送一个带有
resume指令的Command对象。这个指令的值会传递回引发中断的节点,作为其interrupt()调用的返回值,从而使节点得以继续执行。
3.2 示例:中断代码实战
-
笔者对之前的
node_a进行修改。当用户输入不是预期的'b','c'或'q'时,触发中断。pythondef node_a(state: State) -> Command[Literal['b', 'c', END]]: print('进入 A 节点') select = state['nList'][-1] if select == 'b': next_node = 'b' elif select == 'c': next_node = 'c' elif select == 'q': next_node = END else: admin = interrupt(f"未期望的输出 {select}") print('用户重新输入是:',admin) if admin == 'continue': next_node = 'b' select = 'b' else: next_node = END select = 'q' return Command( update=State(nList=[select]), goto=next_node ) -
构建图并启用检查点,中断依赖检查点来保存和恢复状态,因此必须配置检查点器。
pythonfrom langgraph.graph import START, END, StateGraph from langgraph.checkpoint.memory import InMemorySaver # 构建图 builder = StateGraph(State) builder.add_node("a", node_a) builder.add_node("b", node_b) builder.add_node("c", node_c) builder.add_edge(START, "a") # 配置内存检查点器和线程 memory = InMemorySaver() config = {"configurable": {"thread_id": "1"}} # 编译时传入检查点器 graph = builder.compile(checkpointer=memory) -
实现外部中断处理循环:
pythonwhile True: user = input('b, c or q to quit:') input_state = State( nList=[user] ) result = graph.invoke(input_state, config) print(result) if '__interrupt__' in result: print(f'Interrupt:{result}') msg = result['__interrupt__'][-1].value print(msg) human = input(f"\n{msg}, 重新输入: ") human_response = Command( resume=human ) result = graph.invoke(human_response, config) if result['nList'][-1] == 'q': print('quit') break -
运行结果分析:假设输入
p(一个非法值),流程如下:- 触发中断 :节点A识别到
p,调用interrupt("未预期的输入..."。图在此刻立即暂停 ,并将当前状态(包括中断信息)保存为检查点。graph.invoke返回的结果中会包含一个特殊的__interrupt__字段。 - 人工干预 :外部循环检测到中断,打印信息并等待用户输入。用户输入
continue。 - 恢复执行 :外部程序构建一个
Command(resume='continue')并再次调用graph.invoke。关键点 :因为使用了相同的thread_id,图会从上一个检查点(即中断处)恢复。 - 节点继续运行 :恢复后,节点A中
interrupt()调用处将返回admin_decision = 'continue'。程序流程进入if admin_decision == 'continue'分支,最终前往节点B并结束。

重要说明 :当图从中断点恢复时,整个图会重新执行。但由于检查点保存了中断前的状态(包括
nList),并且interrupt()会返回新的resume值,因此节点能基于新信息做出不同的决策。__interrupt__是一个列表,因为一个图可以在不同节点触发多个中断,LangGraph 会跟踪所有中断的上下文。 - 触发中断 :节点A识别到
以上完整代码均可关注笔者同名微信公众号:大模型真好玩 ,并私信 LangChain智能体开发 获得
四、总结
本期文章深入讲解了 LangGraph 1.0 的三大进阶功能:条件边 (除条件函数外,还能通过 Command 对象实现节点内路由)、记忆 (利用检查点机制实现状态持久化与多轮对话能力)以及人在回路(通过中断机制在关键节点引入人工干预)。现在大家就学习到了LangGraph的基本技能,但目前这些知识点还比较零碎,下篇分享笔者将结合大模型使用LangGraph编写一个智能体,帮助大家全局掌握这些知识点的用法,掌握LangGraph智能体开发的基本技能!
《深入浅出LangChain&LangGraph AI Agent 智能体开发》专栏内容源自笔者在实际学习和工作中对 LangChain 与 LangGraph 的深度使用经验,旨在帮助大家系统性地、高效地掌握 AI Agent 的开发方法,在各大技术平台获得了不少关注与支持。目前已更新32讲,正在更新LangGraph1.0速通指南,并随时补充笔者在实际工作中总结的拓展知识点。如果大家感兴趣,欢迎关注笔者的掘金账号与专栏,也可关注笔者的同名微信公众号 大模型真好玩 ,每期分享涉及的代码均可在公众号私信: LangChain智能体开发免费获取。