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 完成..."。

相关推荐
油炸小波2 小时前
02-AI应用开发平台Dify
人工智能·python·dify·coze
SunnyDays10113 小时前
从图片到PPT:用Python实现多图片格式(PNG/JPG/SVG)到幻灯片的批量转换
python·图片转ppt·png转ppt·jpg转ppt·svg转ppt·添加图片到ppt
CodeCraft Studio3 小时前
Excel处理控件Aspose.Cells教程:使用Python从Excel工作表中删除数据透视表
开发语言·python·excel·aspose·aspose.cells·数据透视表
普通网友3 小时前
用Python批量处理Excel和CSV文件
jvm·数据库·python
linuxxx1103 小时前
高考志愿填报辅助系统
redis·后端·python·mysql·ai·django·高考
无妄无望3 小时前
ragflow代码学习切片方式(1)docling_parser.py
人工智能·python·学习
醒过来摸鱼3 小时前
9.12 sinc插值
python·线性代数·算法·numpy
小兔崽子去哪了4 小时前
Numpy、Panads
python·numpy·pandas