【LangGraph】四.持久化:保存和恢复执行状态

写在前面

前面的文章里,Agent 执行完就"失忆"了------所有状态都丢了。但实际应用里,我们经常需要:

  • 聊天机器人记住之前的对话
  • 任务失败后从断点继续
  • 敏感操作前暂停,等人审批

这些都需要持久化。

这篇文章通过一个聊天机器人示例,讲解持久化的核心用法。


一、启用持久化

1.1 基本用法

第 1 步:创建 Checkpointer

python 复制代码
from langgraph.checkpoint.memory import MemorySaver

checkpointer = MemorySaver()

第 2 步:编译时传入

python 复制代码
app = graph.compile(checkpointer=checkpointer)

第 3 步:执行时传入 config

python 复制代码
config = {"configurable": {"thread_id": "conversation-1"}}
result = app.invoke(input_data, config=config)

1.2 完整示例

python 复制代码
from typing import TypedDict, Annotated
from operator import add
from langgraph.graph import StateGraph, START, END
from langgraph.checkpoint.memory import MemorySaver
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage, AIMessage

# 定义 State
class State(TypedDict):
    messages: Annotated[list, add]

# 创建模型
model = ChatOpenAI(model="gpt-4")

# 定义节点
def chat_node(state: State) -> dict:
    response = model.invoke(state["messages"])
    return {"messages": [response]}

# 构建图
graph = StateGraph(State)
graph.add_node("chat", chat_node)
graph.set_entry_point("chat")
graph.add_edge("chat", END)

# 启用持久化
checkpointer = MemorySaver()
app = graph.compile(checkpointer=checkpointer)

# 使用
config = {"configurable": {"thread_id": "conversation-1"}}
result = app.invoke(
    {"messages": [HumanMessage(content="你好")]},
    config=config
)

关键点:

  1. 创建 Checkpointer
  2. 编译时传入 checkpointer 参数
  3. 执行时传入 config 参数,包含 thread_id

二、Thread:会话隔离

2.1 为什么需要 Thread

Thread 是会话的隔离单位。每个 Thread 有独立的 State 和 Checkpoint 历史。

没有 Thread 的问题:

python 复制代码
# ❌ 错误:所有人的对话混在一起
app.invoke({"messages": ["你好"]})  # 用户 A
app.invoke({"messages": ["你好"]})  # 用户 B,会看到用户 A 的对话

使用 Thread 隔离:

python 复制代码
# ✅ 正确:每个会话独立
config_a = {"configurable": {"thread_id": "user-a"}}
config_b = {"configurable": {"thread_id": "user-b"}}

app.invoke({"messages": ["你好"]}, config=config_a)  # 用户 A
app.invoke({"messages": ["你好"]}, config=config_b)  # 用户 B,看不到用户 A 的对话

2.2 Thread ID 设计

方案 1:一个用户一个会话

python 复制代码
thread_id = f"user-{user_id}"
config = {"configurable": {"thread_id": thread_id}}

适用:每个用户只有一个活跃会话。

方案 2:一个用户多个会话

python 复制代码
thread_id = f"user-{user_id}-session-{session_id}"
config = {"configurable": {"thread_id": thread_id}}

适用:用户可以同时有多个对话。

关键原则:thread_id 必须唯一标识一个会话。


三、对话记忆

3.1 多轮对话

启用持久化后,同一个 thread_id 的对话会自动记住历史。

python 复制代码
config = {"configurable": {"thread_id": "conversation-1"}}

# 第一轮
app.invoke({"messages": [HumanMessage(content="你好")]}, config=config)

# 第二轮(AI 能记住第一轮)
app.invoke({"messages": [HumanMessage(content="我叫小明")]}, config=config)

# 第三轮(AI 还记得)
result = app.invoke(
    {"messages": [HumanMessage(content="我叫什么?")]},
    config=config
)
# AI: 你叫小明

工作原理:

  • 每次执行后,Checkpointer 自动保存 State
  • 下次执行时,自动恢复 State
  • 同一个 thread_id 共享 State

3.2 查看当前状态

python 复制代码
state = app.get_state(config)
state.values      # 当前 State
state.next        # 下一个要执行的节点
state.metadata    # 元数据

四、断点续传

4.1 从断点恢复

执行失败后,可以从最后一个 Checkpoint 继续。

python 复制代码
config = {"configurable": {"thread_id": "task-1"}}

# 第一次执行(可能失败)
try:
    app.invoke(input_data, config=config)
except Exception:
    pass  # State 已保存

# 从断点恢复
app.invoke(None, config=config)  # None 表示继续执行

关键点:

  • 恢复时传 None,不要传新数据
  • 传新数据会重新开始,而不是恢复

4.2 更新状态后恢复

某些场景需要先更新 State,再恢复执行(如审批)。

python 复制代码
# 更新 State(如审批通过)
app.update_state(config, values={"approved": True})

# 恢复执行
app.invoke(None, config=config)

五、时间旅行

5.1 查看历史状态

python 复制代码
states = app.get_state_history(config)
for state in states:
    print(f"Step: {state.metadata['step']}")
    print(f"State: {state.values}")
    print(f"Next: {state.next}")

5.2 回到过去的状态

python 复制代码
# 获取历史状态
states = app.get_state_history(config)
first_state = list(states)[-1]  # 第一个状态

# 恢复到那个状态
app.update_state(config, values=first_state.values)

# 从那个状态重新开始
app.invoke({"messages": [HumanMessage(content="新问题")]}, config=config)

适用场景:

  • 调试:回到之前的状态重新执行
  • 用户想重新开始对话
  • 分支对话:从某个历史状态开始新分支

六、Checkpointer 类型

LangGraph 提供多种 Checkpointer 实现。

6.1 MemorySaver(内存)

python 复制代码
from langgraph.checkpoint.memory import MemorySaver
checkpointer = MemorySaver()
  • ✅ 简单,无需配置
  • ❌ 重启后数据丢失
  • 适用:开发、测试

6.2 SqliteSaver(SQLite)

python 复制代码
from langgraph.checkpoint.sqlite import SqliteSaver
checkpointer = SqliteSaver.from_conn_string("checkpoints.db")
  • ✅ 持久化,重启不丢失
  • ❌ 单机
  • 适用:单机生产

6.3 RedisSaver(Redis)

python 复制代码
from langgraph.checkpoint.redis import RedisSaver
checkpointer = RedisSaver.from_conn_string("redis://localhost:6379")
  • ✅ 持久化,支持分布式
  • ❌ 需要 Redis 服务
  • 适用:分布式生产

6.4 PostgresSaver(PostgreSQL)

python 复制代码
from langgraph.checkpoint.postgres import PostgresSaver
checkpointer = PostgresSaver.from_conn_string("postgresql://user:pass@localhost/db")
  • ✅ 持久化,支持分布式,企业级
  • ❌ 需要 PostgreSQL 服务
  • 适用:企业级生产

6.5 选择建议

|---------------|-----|-----|-------|
| Checkpointer | 持久化 | 分布式 | 适用场景 |
| MemorySaver | ❌ | ❌ | 开发测试 |
| SqliteSaver | ✅ | ❌ | 单机生产 |
| RedisSaver | ✅ | ✅ | 分布式生产 |
| PostgresSaver | ✅ | ✅ | 企业级生产 |


七、常见错误

错误 1:忘记传 config

python 复制代码
# ❌ 错误
app.invoke({"messages": [...]})  # 没有持久化

# ✅ 正确
config = {"configurable": {"thread_id": "user-1"}}
app.invoke({"messages": [...]}, config=config)

错误 2:thread_id 不唯一

python 复制代码
# ❌ 错误
config = {"configurable": {"thread_id": "default"}}  # 所有人混在一起

# ✅ 正确
config = {"configurable": {"thread_id": f"user-{user_id}"}}

错误 3:恢复时传了 input

python 复制代码
# ❌ 错误
app.invoke({"new_input": "..."}, config=config)  # 这会重新开始

# ✅ 正确
app.invoke(None, config=config)  # None 表示继续执行

错误 4:不更新 State 就恢复

python 复制代码
# ❌ 错误
app.invoke(None, config=config)  # approved 还是 None

# ✅ 正确
app.update_state(config, values={"approved": True})
app.invoke(None, config=config)

总结

核心语法:

  1. 创建 Checkpointer
  2. 编译:graph.compile(checkpointer=...)
  3. 执行:app.invoke(..., config={"configurable": {"thread_id": "..."}})

关键要点:

  • thread_id 唯一标识会话
  • 恢复执行传 None
  • 恢复前可能需要更新 State

常用 API:

  • app.get_state(config):获取当前状态
  • app.get_state_history(config):获取历史状态
  • app.update_state(config, values=...):更新状态
  • app.invoke(None, config=config):恢复执行
相关推荐
xxyy8883 小时前
关于labelimg安装后在标注过程中闪退和死机的问题处理
开发语言·python
卷Java3 小时前
上下文压缩
开发语言·windows·python
AI技术增长3 小时前
Pytorch图像去噪实战(十二):DDPM图像去噪完整训练流程,构建可复现扩散模型工程
pytorch·python·深度学习
本地化文档3 小时前
setuptools-docs-l10n
python·github·gitcode
梦想不只是梦与想3 小时前
Python 属性访问的 MRO 规则
python·mro规则
Ulyanov3 小时前
基于 Python 的三维动态导弹攻防演示系统设计与实现:从架构到实战的深度剖析
开发语言·python·qt·架构·雷达电子对抗
Leinwin3 小时前
Claude 四月宕机七次:从一次事故看企业级 AI 部署的容灾设计
后端·python·flask
棉猴3 小时前
Python海龟绘图之绘制文本
javascript·python·html·write·turtle·海龟绘图·输出文本
渣渣盟3 小时前
大数据技术栈全景图:从零到一的入门路线(深度实战版)
大数据·hadoop·python·flink·spark