一、LangGraph Checkpoint 的核心设计目标
LangGraph Checkpoint 解决的并不是简单的"存储状态"问题,而是 复杂工作流系统中的可恢复执行问题。
从架构角度看,它承担了四个关键职责:
1️⃣ 持久化状态管理
保存 Graph 的完整状态,使系统可以跨请求恢复执行。
2️⃣ 工作流恢复能力(Fault Tolerance)
当任务执行失败或系统重启时,可以从最近状态继续执行。
3️⃣ 时间旅行调试(Time Travel Debugging)
开发者可以回滚到历史状态重新执行。
4️⃣ Human-in-the-loop 支持
允许工作流暂停等待人工输入,然后恢复执行。
这四个能力构成了 LangGraph Agent 能够进入生产环境的重要基础。
二、Checkpoint 在 LangGraph 架构中的位置
LangGraph 的运行时架构可以抽象为四层:
Application Layer
│
▼
Graph Runtime
│
▼
Checkpoint System
│
▼
Storage Backend
其中:
Graph Runtime
负责节点执行、调度、并行控制。
Checkpoint System
负责保存和恢复 Graph State。
Storage Backend
负责物理存储(数据库 / Redis / SQLite 等)。
Checkpoint 系统是连接 运行时与存储层 的关键组件。
三、Pregel 执行模型与 Super-step
理解 Checkpoint 的关键是理解 LangGraph 的执行模型。
LangGraph 的执行模型借鉴了 Pregel 分布式图计算模型。
Pregel 模型核心思想
图计算被分为一系列 Super-step(超级步骤):
superstep_1
superstep_2
superstep_3
...
每个 super-step 包含:
- 节点执行
- 消息传递
- 状态更新
在 LangGraph 中:
Graph State
│
▼
Node Execution
│
▼
Channel Updates
│
▼
Next Node Selection
Checkpoint 就是在 super-step 边界生成的。
Super-step 与 Checkpoint 的关系
假设一个简单 Graph:
START → A → B → C → END
执行过程:
superstep0 initial state
superstep1 node A
superstep2 node B
superstep3 node C
Checkpoint 会自动生成:
checkpoint0
checkpoint1
checkpoint2
checkpoint3
每个 checkpoint 都保存:
Graph state snapshot
因此:
Checkpoint = Super-step 边界的状态快照
四、Checkpoint 的核心数据结构
LangGraph 使用两个核心对象描述状态快照:
Checkpoint
StateSnapshot
4.1 StateSnapshot
StateSnapshot 是真正的状态载体。
其核心字段包括:
StateSnapshot
├─ values
├─ next
├─ tasks
├─ metadata
├─ config
├─ created_at
└─ parent_config
下面逐一解释。
values
values: Dict[str, Any]
保存 Graph channel 的当前值。
例如:
{
"messages": [...],
"tools_result": "...",
"analysis": "..."
}
这是 Graph 的 核心业务数据。
next
next: tuple[str]
表示:
下一步要执行的节点
例如:
("tool_executor",)
如果为空:
()
说明:
Graph execution finished
tasks
tasks: List[Task]
保存待执行任务信息。
可能包含:
- 未完成任务
- 中断信息
- 错误信息
例如:
interrupt waiting for human input
metadata
metadata 包含执行信息:
step
source
parents
例如:
{
"step": 4,
"source": "loop"
}
用于:
- 调试
- tracing
- observability
parent_config
Checkpoint 形成一个 链式结构:
checkpoint1
│
▼
checkpoint2
│
▼
checkpoint3
通过:
parent_config
建立父子关系。
这使得系统可以:
time travel
五、Thread:LangGraph 的会话隔离机制
Checkpoint 体系的核心概念之一是 Thread。
Thread 是:
checkpoint sequence
结构如下:
thread_id
├─ checkpoint1
├─ checkpoint2
├─ checkpoint3
也就是说:
Thread = 一个工作流会话
典型映射:
| 场景 | thread_id |
|---|---|
| 聊天 | user_session_id |
| Agent | task_id |
| Workflow | run_id |
因此 thread_id 是:
LangGraph 中最重要的配置参数之一。
示例:
python
config = {
"configurable": {
"thread_id": "user_123"
}
}
六、Checkpointer:持久化接口
Checkpoint 的存储由 Checkpointer 完成。
核心抽象类:
BaseCheckpointSaver
定义了统一接口。
主要方法包括:
put()
putWrites()
getTuple()
list()
put()
put(checkpoint)
保存 checkpoint。
getTuple()
getTuple(thread_id)
读取 checkpoint。
list()
list(thread_id)
列出历史 checkpoint。
putWrites()
LangGraph 的一个高级特性。
用于保存:
pending writes
七、Pending Writes 与部分恢复
LangGraph 的 checkpoint 系统支持 部分恢复(Partial Recovery)。
假设:
superstep
├─ NodeA success
└─ NodeB fail
系统会保存:
NodeA result
恢复时:
NodeA 不重新执行
NodeB 重新执行
这样可以避免:
重复计算
这是 LangGraph 相比传统 workflow 引擎的重要优势。
八、序列化机制(Serde)
Checkpoint 数据需要在内存和数据库之间传输。
LangGraph 使用 SerializerProtocol。
默认实现:
JsonPlusSerializer
特点:
- 支持 datetime
- 支持 tuple
- 支持 Python object
因此 Graph state 不需要强制 JSON 化。
九、存储后端架构
LangGraph 的 checkpoint 系统支持多个存储后端。
常见实现:
生产环境推荐:
PostgreSQL
原因:
- 事务支持
- 稳定性
- 并发能力
十、Checkpoint 支持的高级能力
Checkpoint 不只是持久化。
它直接支持四个高级能力。
1 会话记忆
Graph state 会跨请求保存。
例如聊天 Agent:
User → Agent → Tool → Agent
所有消息会自动持久化。
2 Human-in-the-loop
LangGraph 支持:
interrupt()
resume()
流程:
Graph pause
human input
Graph resume
Checkpoint 保存暂停状态。
3 Fault Tolerance
系统崩溃时:
load latest checkpoint
resume execution
避免任务从头开始。
4 Time Travel
开发者可以:
rollback checkpoint
重新执行 Graph。
适用于:
- debugging
- experiment
十一、Checkpoint 生命周期
完整生命周期:
Graph invoke
│
▼
create superstep
│
▼
generate checkpoint
│
▼
serialize snapshot
│
▼
persist storage
│
▼
load on next invocation
十二、工程最佳实践
1 生产环境使用数据库
推荐:
PostgresSaver
2 thread_id 必须稳定
不要使用:
random uuid
否则无法恢复会话。
3 控制状态大小
不要在 state 中存:
- 大文件
- embedding 向量
建议:
store reference
4 定期清理 checkpoint
长期系统需要:
TTL
archiving
十三、总结
LangGraph 的 Checkpoint 系统本质上是一套 基于 Pregel 执行模型的状态版本化系统。
其核心架构:
Pregel Runtime
│
▼
Super-step
│
▼
StateSnapshot
│
▼
Checkpoint
│
▼
Thread
│
▼
Checkpointer
│
▼
Storage Backend
通过这一体系,LangGraph 实现了:
- 持久化状态管理
- 可恢复工作流执行
- Human-in-the-loop
- Time travel debugging
这也是为什么 LangGraph 能够构建 真正生产级 Agent 系统 的关键原因。
from dotenv import load_dotenv
from typing_extensions import TypedDict
from langgraph.graph import StateGraph, START, END
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.types import interrupt, Command
load_dotenv()
############################################################
# 1 定义 Graph State
############################################################
class AgentState(TypedDict):
user_input: str
analysis: str
decision: str
result: str
############################################################
# 2 定义节点
############################################################
def analyze_node(state: AgentState):
print("\n--- analyze_node ---")
analysis = f"analysis of '{state['user_input']}'"
return {
"analysis": analysis
}
def human_review_node(state: AgentState):
print("\n--- human_review_node ---")
# interrupt 会创建 checkpoint 并暂停
feedback = interrupt(
{
"question": "Human approval required",
"analysis": state["analysis"]
}
)
return {
"decision": feedback["decision"]
}
def execute_node(state: AgentState):
print("\n--- execute_node ---")
if state["decision"] == "approve":
result = f"EXECUTED: {state['analysis']}"
else:
result = "REJECTED BY HUMAN"
return {
"result": result
}
############################################################
# 3 构建 Graph
############################################################
builder = StateGraph(AgentState)
builder.add_node("analyze", analyze_node)
builder.add_node("review", human_review_node)
builder.add_node("execute", execute_node)
builder.add_edge(START, "analyze")
builder.add_edge("analyze", "review")
builder.add_edge("review", "execute")
builder.add_edge("execute", END)
############################################################
# 4 创建 Checkpointer
############################################################
checkpointer = InMemorySaver()
app = builder.compile(checkpointer=checkpointer)
############################################################
# 5 配置 thread_id
############################################################
config = {
"configurable": {
"thread_id": "demo-session"
}
}
############################################################
# 6 第一次执行(会在 review 节点 interrupt)
############################################################
print("\n================ RUN GRAPH =================")
result = app.invoke(
{
"user_input": "analyze this data"
},
config=config
)
print("\nExecution paused.")
print("Current state:")
state = app.get_state(config)
print(state.values)
############################################################
# 7 查看 checkpoint 历史
############################################################
print("\n================ CHECKPOINT HISTORY =================")
history = list(app.get_state_history(config))
for i, snapshot in enumerate(history):
print(f"\nCheckpoint {i}")
print("step:", snapshot.metadata.get("step"))
print("next:", snapshot.next)
print("values:", snapshot.values)
############################################################
# 8 Human Resume(继续执行)
############################################################
print("\n================ RESUME GRAPH =================")
result = app.invoke(
Command(resume={"decision": "approve"}),
config=config
)
print("\nFinal result:")
print(result)
############################################################
# 9 Time Travel 示例(回滚到旧 checkpoint)
############################################################
print("\n================ TIME TRAVEL =================")
# 获取历史 checkpoint
history = list(app.get_state_history(config))
# 选择 checkpoint 2(初始状态),完全重新执行
checkpoint_id = history[2].config["configurable"]["checkpoint_id"]
rollback_config = {
"configurable": {
"thread_id": "demo-session",
"checkpoint_id": checkpoint_id
}
}
state = app.get_state(rollback_config)
print("\nRollback state:")
print(state.values)
print("\nRe-run from this checkpoint (without resume - will hit interrupt again)")
# Time travel: 回滚后不带 resume 重新执行,会重新触发 interrupt
result = app.invoke(
None,
config=rollback_config
)
print("\nNew result after time travel:")
print(result)