LangGraph 的持久化

LangGraph 的持久化管理,核心目的是:让图(Graph)在多次调用、异常中断甚至进程重启后,仍能"记住"上次的执行状态,实现真正的有状态智能体。整套机制分成两层:

  1. 短期记忆(线程级)------ Checkpointer

  2. 长期记忆(跨线程/跨会话)------ Store 抽象

一、短期记忆:Checkpointer 机制

1. 原理
  • 图编译时传入一个 checkpointer 实例

  • 每次 invoke/stream 结束,LangGraph 自动把 整个 State 快照到一个"检查点"

  • 下次用相同的 thread_id 调用,自动从最新快照恢复,实现"接着聊"

  • 支持人工中断(Human-in-the-loop):中断时也会落盘,重启后 app.invoke(..., config=config) 继续跑

2. 官方已实现的后端
后端类 持久性 适用场景 安装命令
InMemorySaver ❌ 进程结束即失 单元测试、快速体验 内置
SqliteSaver ✅ 本地文件 单机小应用 pip install langgraph-checkpoint-sqlite
PostgresSaver ✅ 分布式库 生产、高并发 pip install langgraph-checkpoint-postgres
RedisSaver ✅ 内存级KV 高速读写、重启不丢 pip install langgraph-checkpoint-redis
3. 最小可运行示例(SQLite)
python 复制代码
# 1. 安装依赖
# pip install langgraph langgraph-checkpoint-sqlite

from typing_extensions import TypedDict
from langgraph.graph import StateGraph, START, END
from langgraph.checkpoint.sqlite import SqliteSaver
import sqlite3

# 1) 定义状态
class State(TypedDict):
    count: int

# 2) 节点函数
def increment(state: State) -> State:
    return {"count": state.get("count", 0) + 1}

# 3) 建图
builder = StateGraph(State)
builder.add_node("inc", increment)
builder.add_edge(START, "inc")
builder.add_edge("inc", END)

# 4) 编译 + 持久化
conn = sqlite3.connect("checkpoints.db", check_same_thread=False)
checkpointer = SqliteSaver(conn)
graph = builder.compile(checkpointer=checkpointer)

# 5) 运行两轮
config = {"configurable": {"thread_id": "session-1"}}

print(graph.invoke({"count": 0}, config))        # {'count': 1}
print(graph.invoke({}, config))                  # {'count': 2}
python 复制代码
{'count': 1}
{'count': 2}
  • 第二次调用没给初始值,依旧拿到 2,因为状态被自动恢复

  • 把文件 checkpoints.db 拷到另一台机器,状态仍在

人工中断恢复示例

python 复制代码
# pip install langgraph langgraph-checkpoint-sqlite
import sqlite3, asyncio
from contextlib import asynccontextmanager

from langgraph.checkpoint.sqlite.aio import AsyncSqliteSaver
from langgraph.graph import StateGraph, START, END
from langgraph.checkpoint.sqlite import SqliteSaver
from langgraph.types import interrupt, Command
from typing_extensions import TypedDict

class State(TypedDict):
    count: int

# 1) 节点:累加后人工确认
def add_and_confirm(state: State) -> State:
    new_count = state["count"] + 1
    # ===== 人工中断点 =====
    state["count"] = 5
    human = interrupt({"question": f"新 count={new_count},确认继续吗?(yes/no)"})
    if human.strip().lower() != "yes":
        raise ValueError("用户拒绝,流程终止")
    return {"count": new_count}

# 2) 建图
builder = StateGraph(State)
builder.add_node("add", add_and_confirm)
builder.add_edge(START, "add")
builder.add_edge("add", END)



async def main():
    # 3) 绑定持久化
    # conn = sqlite3.connect("demo.db", check_same_thread=False)
    # checkpointer = AsyncSqliteSaver(conn)
    # AsyncSqliteSaver.from_conn_string("demo.db")

    async with AsyncSqliteSaver.from_conn_string("demo.db") as checkpointer:
        graph = builder.compile(checkpointer=checkpointer)

        # 4) 第一次运行:触发中断
        config = {"configurable": {"thread_id": "demo"}}

        print("--- 第一次调用,会中断 ---")
        try:
            async for chunk in graph.astream({"count": 0}, config):
                print("显示",chunk)
        except Exception as e:
            # 中断异常被外部捕获,流程暂停
            print(">>> 捕获中断,模拟前端弹窗 <<<")8

        # 5) 人工输入后恢复
        print("--- 人工输入 'yes' 后恢复 ---")
        async for chunk in graph.astream(Command(resume="yes"), config):
            print(chunk)

if __name__ == "__main__":
    asyncio.run(main())
python 复制代码
--- 第一次调用,会中断 ---
显示 {'__interrupt__': (Interrupt(value={'question': '新 count=1,确认继续吗?(yes/no)'}, id='7a25377a8d5078cd498f0d826f3f73b4'),)}
--- 人工输入 'yes' 后恢复 ---
{'add': {'count': 1}}
复制代码
human = interrupt({"question": f"新 count={new_count},确认继续吗?(yes/no)"}) 会中断返回,把当前 State 落盘(Checkpointer),图冻结在这一行,后面的代码暂时不会执行。
复制代码
再次调用,graph.astream(Command(resume="yes"), config) 此时会从之前中断的函数再次执行(函数重新执行),human = interrupt({"question": f"新 count={new_count},确认继续吗?(yes/no)"}),这个时候human接受到值为yes。

invoke 和 astream

  • invoke:同步调用,图跑完后一次性返回最终 State(适合脚本、单元测试)。

  • astream:异步生成器,每过一个节点就 yield 一次中间快照(适合聊天 UI、实时日志、进度条)。

方法 类型 输入 返回 典型耗时
graph.invoke(input, config?, **kwargs) 同步 单 dict 终态 dict 整图跑完
graph.astream(input, config?, **kwargs) 异步 单 dict AsyncIterator[dict] 逐节点实时
需求 用 invoke 用 astream
快速验证业务逻辑 ✅ 一行代码 ❌ 需 async for
聊天机器人逐句回复 ❌ 会阻塞整图 ✅ 每节点 yield 可立即推给前端
进度条/实时日志 ❌ 拿不到中间态 ✅ 每步快照可刷新 UI
中断/继续 (Human-in-the-Loop) ✅ 支持 ✅ 支持(更常用)
批跑离线任务 ✅ 简单高效 ❌ 无必要
python 复制代码
# 图定义略
graph = builder.compile()

# ① invoke:阻塞直到终点
final_state = graph.invoke({"count": 0})
print(final_state)          # {'count': 3}

# ② astream:异步逐节点产出
async for snapshot in graph.astream({"count": 0}):
    print(snapshot)
# 输出顺序类似
# {'node_a': {'count': 1}}
# {'node_b': {'count': 2}}
# {'count': 3}            # 终态

前端体验差异:

  • invoke 要等 3 秒才一次性把结果吐给前端;

  • astream 每 1 秒就能收到一次快照,页面可实时展示"节点 A 完成→节点 B 完成..."。

相关推荐
Michelle80233 小时前
24大数据 16-1 函数复习
python
dagouaofei3 小时前
AI自动生成PPT工具对比分析,效率差距明显
人工智能·python·powerpoint
ku_code_ku3 小时前
python bert_score使用本地模型的方法
开发语言·python·bert
祁思妙想4 小时前
linux常用命令
开发语言·python
流水落花春去也4 小时前
用yolov8 训练,最后形成训练好的文件。 并且能在后续项目使用
python
Serendipity_Carl4 小时前
数据可视化实战之链家
python·数据可视化·数据清洗
小裴(碎碎念版)4 小时前
文件读写常用操作
开发语言·爬虫·python
TextIn智能文档云平台4 小时前
图片转文字后怎么输入大模型处理
前端·人工智能·python
ujainu5 小时前
Python学习第一天:保留字和标识符
python·学习·标识符·保留字
studytosky5 小时前
深度学习理论与实战:反向传播、参数初始化与优化算法全解析
人工智能·python·深度学习·算法·分类·matplotlib