Agent 为什么需要 Checkpoint?

Agent 为什么需要 Checkpoint?

文章目录

前言:为什么我会学这个问题

一、问题背景

二、核心概念

Checkpoint 是什么?

LangGraph Checkpoint 机制

Checkpoint 的存储方式

三、错误理解 / 常见误区

四、正确实现思路

步骤1:设计可恢复的 State

步骤2:每个 Node 检查进度再执行

步骤3:配置 Checkpoint 并执行

五、代码示例

六、运行结果 / 流程图

七、项目中怎么用

八、总结 + 下篇预告

前言:为什么我会学这个问题

上篇我们讲了 LangGraph 里 State、Node、Edge 怎么配合完成多步骤任务。主包在学习Langgraph的时候准备构建一个客服Agent,跑完一个客服流程需要 5-6 步,但如果执行到一半服务器崩了呢?或者用户突然关闭页面?所有 State 都没了,流程只能从头再来。

这就很蛋疼了——用户问了个复杂问题,Agent 查了半天文档,刚要返回结果时网络断了。用户重新打开页面,发现一切要从头开始,肯定骂街。

Checkpoint 就是来解决这个问题的。它是 Agent 执行过程的"存档点"。

前文关联:本文接续《LangGraph 中 State、Node、Edge 是怎么协作的?》,在前文理解 State 流转的基础上,探讨如何让 State 持久化、不怕中断。

一、问题背景

假设一个复杂的数据分析 Agent,执行流程是这样的:

跑了 4 步,刚到"生成图表"这一步,突然:

服务器重启了

用户关掉了浏览器

网络断了

进程被 OOM Kill 了

没有 Checkpoint = 从头来过。用户得重新等 4 步重新跑一遍,浪费时间和资源。

二、核心概念

Checkpoint 是什么?

Checkpoint = State 的快照。在某个 Node 执行完成后,把当前的 State 完整保存下来。

就像游戏存档:打到 BOSS 血剩一半,保存一下。哪天游戏崩了,从存档点恢复,不用从头打。

# 伪代码:Checkpoint 本质就是保存 State

checkpoint = {

"step": 4,

"state": {

"user_question": "分析Q2销售额",

"retrieved_data": {...},

"analysis_result": {...},

"chart_data": {...}, # 已生成,还没用来生成报告

"response": ""

},

"node_history": ["understand", "query", "analyze", "generate_chart"]

}

LangGraph Checkpoint 机制

LangGraph 的 Checkpoint 在每个 Node 执行完成后自动保存。恢复时从最近的 Checkpoint 开始,不用重新跑。

from langgraph.checkpoint import MemorySaver

from langgraph.graph import StateGraph

# 创建带 Checkpoint 的 Graph

checkpointer = MemorySaver()

graph = StateGraph(DataAnalysisState)

# ... 添加 Node 和 Edge ...

app = graph.compile(checkpointer=checkpointer)

# 正常执行(每个 Node 完成后自动保存 Checkpoint)

config = {"configurable": {"thread_id": "user-123-session-1"}}

result = app.invoke(initial_state, config)

# 中断后恢复(从最近 Checkpoint 继续)

restored_result = app.invoke(None, config) # 传 None 表示从存档点恢复

Checkpoint 的存储方式

存储方式

适用场景

特点

MemorySaver

开发测试

内存存储,重启丢失

SqliteSaver

单机部署

SQLite 文件持久化

PostgresSaver

生产环境

PostgreSQL,支持多实例

生产环境推荐 PostgresSaver,多个 Agent 实例可以共享同一个 Checkpoint 存储。

三、错误理解 / 常见误区

误区1:Checkpoint 会拖慢执行速度

错。Checkpoint 保存是异步的,不阻塞 Node 执行。而且只有 State 变化时才保存,不是每次都全量写入。

误区2:有了 Checkpoint 就不用担心数据丢失

不完全对。Checkpoint 保存的是 Node 执行完成后的 State,如果 Node 执行到一半崩溃,那一步的修改会丢失。所以要保证 Node 的原子性——每个 Node 要么完全成功,要么完全失败,不要有中间状态。

误区3:Checkpoint 可以无限恢复

不对。Checkpoint 存储会消耗资源,生产环境通常只保留最近 N 个版本。或者设置过期时间,比如只保留 24 小时内的 Checkpoint。

误区4:所有 State 都需要 Checkpoint

错。只保存关键数据就行,比如中间结果、用户输入、配置参数。不需要保存的是:临时变量、缓存数据、日志等。

四、正确实现思路

步骤1:设计可恢复的 State

State 要设计成可以"从哪里继续"的样子:

class DataAnalysisState(TypedDict):

# 用户原始问题(不变)

user_question: str

# 流程进度(关键!用于判断从哪继续)

current_step: str # understand / query / analyze / chart / report

completed_steps: list[str]

# 各步骤的结果(保存中间产物)

understanding: str

query_result: str

analysis_result: str

chart_data: dict

response: str

步骤2:每个 Node 检查进度再执行

def analyze_data(state: DataAnalysisState) -> DataAnalysisState:

# 如果已经分析过了,直接返回(防止重跑)

if "analyze" in state["completed_steps"]:

print(" [analyze] 已完成,跳过")

return state

# 正常执行分析逻辑

print(" [analyze] 执行数据分析...")

result = do_analysis(state["query_result"])

return {

"analysis_result": result,

"current_step": "analyze",

"completed_steps": state["completed_steps"] + ["analyze"]

}

步骤3:配置 Checkpoint 并执行

from langgraph.checkpoint import PostgresSaver

import psycopg2

# 生产环境:PostgreSQL 存储

conn = psycopg2.connect(os.getenv("DATABASE_URL"))

checkpointer = PostgresSaver(conn)

# 开发环境:内存存储

# checkpointer = MemorySaver()

app = graph.compile(checkpointer=checkpointer)

# 第一次执行

config = {"configurable": {"thread_id": "user-123"}}

result = app.invoke(initial_state, config)

# 模拟中断后恢复

# (同一个 thread_id 会自动找到最近的 Checkpoint)

result = app.invoke(None, config) # None 表示从存档点继续

五、代码示例

"""

LangGraph Checkpoint 完整示例

演示:执行中断 → 自动从 Checkpoint 恢复

"""

from typing import TypedDict, Literal

from langgraph.graph import StateGraph

from langgraph.checkpoint import MemorySaver

class ShoppingAgentState(TypedDict):

"""购物 Agent 状态"""

user_request: str

current_step: Literal["understand", "search", "compare", "recommend", "done"]

completed_steps: list[str]

products: list[str]

recommendation: str

def understand(state: ShoppingAgentState) -> ShoppingAgentState:

"""理解用户需求"""

print(f" [understand] 理解需求: {state['user_request']}")

return {

"current_step": "understand",

"completed_steps": ["understand"]

}

def search(state: ShoppingAgentState) -> ShoppingAgentState:

"""搜索商品"""

if "search" in state["completed_steps"]:

print(" [search] 已完成,跳过")

return state

print(" [search] 搜索商品中...")

return {

"products": ["商品A", "商品B", "商品C"],

"current_step": "search",

"completed_steps": state["completed_steps"] + ["search"]

}

def compare(state: ShoppingAgentState) -> ShoppingAgentState:

"""对比商品"""

if "compare" in state["completed_steps"]:

print(" [compare] 已完成,跳过")

return state

print(" [compare] 对比商品...")

return {

"current_step": "compare",

"completed_steps": state["completed_steps"] + ["compare"]

}

def recommend(state: ShoppingAgentState) -> ShoppingAgentState:

"""推荐商品"""

if "recommend" in state["completed_steps"]:

print(" [recommend] 已完成,跳过")

return state

best = state["products"][0] if state["products"] else "无"

print(f" [recommend] 推荐: {best}")

return {

"recommendation": f"强烈推荐 {best}!",

"current_step": "recommend",

"completed_steps": state["completed_steps"] + ["recommend"]

}

# 构建图

graph = StateGraph(ShoppingAgentState)

graph.add_node("understand", understand)

graph.add_node("search", search)

graph.add_node("compare", compare)

graph.add node("recommend", recommend) # 注意:不是 recommend()

graph.set_entry_point("understand")

graph.add_edge("understand", "search")

graph.add_edge("search", "compare")

graph.add_edge("compare", "recommend")

graph.set_finish_point("recommend")

# 模拟中断:在 compare 步骤手动抛出异常

class InterruptException(Exception):

pass

def compare_with_interrupt(state: ShoppingAgentState) -> ShoppingAgentState:

if "compare" in state["completed_steps"]:

print(" [compare] 已完成,跳过")

return state

print(" [compare] 对比商品... (模拟中断点)")

# 模拟执行到一半崩了

if len(state["completed_steps"]) < 3: # 只有前3步完成时抛出

raise InterruptException("模拟中断:服务器崩了")

return {

"current_step": "compare",

"completed_steps": state["completed_steps"] + ["compare"]

}

# 用带中断的版本替换

graph.add_node("compare", compare_with_interrupt)

# 编译(启用 Checkpoint)

checkpointer = MemorySaver()

app = graph.compile(checkpointer=checkpointer)

# ========== 第一次执行:跑到 compare 时中断 ==========

print("=" * 50)

print("第一次执行:模拟中断")

print("=" * 50)

config = {"configurable": {"thread_id": "user-456"}}

initial_state: ShoppingAgentState = {

"user_request": "想买一个打游戏快的笔记本电脑",

"current_step": "",

"completed_steps": [],

"products": [],

"recommendation": ""

}

try:

result = app.invoke(initial_state, config)

print(f"最终结果: {result['recommendation']}")

except InterruptException as e:

print(f"中断了: {e}")

print(f"当前完成步骤: {result.get('completed_steps', []) if 'result' in dir() else []}")

print()

# ========== 第二次执行:从 Checkpoint 恢复 ==========

print("=" * 50)

print("第二次执行:从 Checkpoint 恢复")

print("=" * 50)

# 同一个 thread_id,自动从最近的 Checkpoint 恢复

result = app.invoke(None, config) # 传入 None,表示从存档点继续

print(f"最终结果: {result['recommendation']}")

六、运行结果 / 流程图

运行输出:

==================================================

第一次执行:模拟中断

==================================================

[understand] 理解需求: 想买一个打游戏快的笔记本电脑

[search] 搜索商品中...

[compare] 对比商品... (模拟中断点)

中断了: 模拟中断:服务器崩了

==================================================

第二次执行:从 Checkpoint 恢复

==================================================

[compare] 已完成,跳过

[recommend] 推荐: 商品A

最终结果: 强烈推荐 商品A!

可以看到恢复时,compare 检测到已完成就直接跳过了,recommend 从之前保存的 products 继续执行。

七、项目中怎么用

实际场景:

长任务恢复:用户提交一个复杂的数据分析任务,跑了 2 小时后服务器重启。从 Checkpoint 恢复,不用重新跑。

页面刷新保留状态:用户在 AI 助手页面填写了一个长表单,刷新后继续填,不用重新填。

多轮对话恢复:用户和 Agent 对话了 10 轮,中断了。重新打开时从第 10 轮的 Checkpoint 继续。

注意点:

设计幂等的 Node:每个 Node 要么完全成功,要么完全失败,避免中间状态丢失。

分离关键数据和临时数据:Checkpoint 只保存关键数据,减少存储开销。

设置合理的 Checkpoint 过期时间:避免存储无限增长。

八、总结 + 下篇预告

核心要点:

Checkpoint = State 快照,在 Node 执行完成后保存

恢复时传入同一个 thread_id,Agent 自动找到最近的 Checkpoint 继续执行

Checkpoint 让 Agent 不怕中断,用户不用重新等待

存储方式:开发用 MemorySaver,生产用 PostgresSaver

下篇预告:下一篇我们将探讨《Agent 的 Time Travel 是什么?》,理解如何利用 Checkpoint 实现"回到过去"——不是简单恢复,而是像时间旅行一样回到任意之前的状态。

延伸阅读:

LangGraph Checkpoint 文档:

相关推荐

怎么可以消除皱纹?全面抗衰去皱指南首次分享
365bet世界杯欢迎您

怎么可以消除皱纹?全面抗衰去皱指南首次分享

📅 10-19 👁️ 6582
轻松上手,问道账号注册指南,全方位解析注册流程及注意事项,问道新手攻略,轻松注册账号,掌握注册流程与关键要点
【乘客】误机了!怎么办?【速速收藏】误机的解决方案~
英超365bet体育投注

【乘客】误机了!怎么办?【速速收藏】误机的解决方案~

📅 10-18 👁️ 2338
A4纸的一半大小是什么尺寸?A4纸一半是A5吗?
英超365bet体育投注

A4纸的一半大小是什么尺寸?A4纸一半是A5吗?

📅 01-16 👁️ 4321
[求助]电脑护眼软件的选择
英超365bet体育投注

[求助]电脑护眼软件的选择

📅 02-04 👁️ 2216
冰岛世界杯足球队名单,冰岛足球世界杯最好成绩进入几强
3、天空探索(1)
英超365bet体育投注

3、天空探索(1)

📅 10-22 👁️ 8144
抖音限流恢复时间及方法
365bet世界杯欢迎您

抖音限流恢复时间及方法

📅 11-02 👁️ 7415
炉石传说新皮肤怎么获得?限时活动与合成路径详解
365bet世界杯欢迎您

炉石传说新皮肤怎么获得?限时活动与合成路径详解

📅 09-04 👁️ 8824