跳转至

sqlite_persistence · SQLite 断点持久化

本页收录可运行示例,配套本文档「指南 06」(指南 06)。 代码为随框架交付的示例,已一并收录到本站供直接对照使用。

说明与运行

示例: Checkpoint 持久化(SQLite 官方 saver 直连). 演示 S1 断点持久化的两条核心能力: 1. interactive 暂停 → 模拟进程重启(新连接 + 新 saver + 同一 db 文件) → 挂起交互不丢失, 恢复值续跑成功 2. 会话续接: 同一 chatId 的新请求从上次断点状态继续 关键点: - 网关是异步的, 必须用 AsyncSqliteSaver(同步 SqliteSaver 不支持 async 方法) - 必须注入 PersistentSerializer: 框架 _ctx 含 stream_callback 闭包, 默认 msgpack 序列化器无法落库, 该序列化器落库前剥离 _ctx、恢复时网关注入新 ctx - 官方 saver 经 await saver.setup() 自动建表, 无需手写建表语句 运行: cd agentframework PYTHONPATH=src python examples/sqlite_persistence.py

运行前需在已安装 agentframeworkPython ≥ 3.11 环境;示例多为自带本地模型,无需真实 API Key;需要外部依赖(如 chromadb / 数据库驱动)会在说明中注明。

完整源码

"""示例: Checkpoint 持久化(SQLite 官方 saver 直连).

演示 S1 断点持久化的两条核心能力:
  1. interactive 暂停 → 模拟进程重启(新连接 + 新 saver + 同一 db 文件)
     → 挂起交互不丢失, 恢复值续跑成功
  2. 会话续接: 同一 chatId 的新请求从上次断点状态继续

关键点:
  - 网关是异步的, 必须用 `AsyncSqliteSaver`(同步 SqliteSaver 不支持 async 方法)
  - 必须注入 `PersistentSerializer`: 框架 `_ctx` 含 stream_callback 闭包,
    默认 msgpack 序列化器无法落库, 该序列化器落库前剥离 _ctx、恢复时网关注入新 ctx
  - 官方 saver 经 `await saver.setup()` 自动建表, 无需手写建表语句

运行:
    cd agentframework
    PYTHONPATH=src python examples/sqlite_persistence.py
"""

import asyncio
import tempfile

import aiosqlite
import httpx
from langgraph.checkpoint.sqlite.aio import AsyncSqliteSaver

from agentframework import Workflow
from agentframework.gateway.app import create_app
from agentframework.gateway.registry import AppRegistry
from agentframework.nodes import Answer, UserSelect
from agentframework.runtime.checkpointer import PersistentSerializer


async def build_app(db_path: str):
    """构建带 SQLite 持久化断点的网关应用.

    saver 持有到 db 文件的连接; setup() 自动建表(checkpoints + checkpoint_writes).
    返回 (FastAPI 应用, aiosqlite 连接) —— 连接由调用方在使用后关闭(否则进程不退出).
    """
    conn = await aiosqlite.connect(db_path)
    saver = AsyncSqliteSaver(conn, serde=PersistentSerializer())
    await saver.setup()

    wf = Workflow(name="sqlite_persistence")
    wf.add_node(
        UserSelect(
            name="sel",
            description="请选择操作",
            options=[
                {"key": "query", "value": "查询"},
                {"key": "exit", "value": "退出"},
            ],
        )
    )
    wf.add_node(Answer(name="ans", text=wf.ref("sel.selected")))
    wf.add_edge("sel", "ans")

    compiled = wf.compile(store=saver)  # 官方 saver 直传: 框架注入 PersistentSerializer
    registry = AppRegistry()
    registry.register("app-sqlite", compiled, name="SQLite 持久化演示")
    return create_app(registry), conn


async def main() -> None:
    db_path = tempfile.mktemp(suffix=".db")

    # ---------- 第一次请求(进程 A): 执行到 UserSelect 暂停 ----------
    app_a, conn_a = await build_app(db_path)
    async with httpx.AsyncClient(
        transport=httpx.ASGITransport(app=app_a), base_url="http://test"
    ) as client:
        r1 = await client.post(
            "/api/v2/chat/completions",
            json={
                "appId": "app-sqlite",
                "chatId": "session-1",
                "messages": [{"role": "user", "content": "你好"}],
                "stream": True,
            },
        )
    await conn_a.close()
    assert "event: interactive" in r1.text, "应输出 interactive 暂停事件"
    print("① 首次请求 → 暂停, 输出 interactive 事件")

    # ---------- 模拟进程重启: 新连接 + 新 saver + 同一 db 文件 ----------
    app_b, conn_b = await build_app(db_path)
    async with httpx.AsyncClient(
        transport=httpx.ASGITransport(app=app_b), base_url="http://test"
    ) as client:
        r2 = await client.post(
            "/api/v2/chat/completions",
            json={
                "appId": "app-sqlite",
                "chatId": "session-1",
                "messages": [{"role": "user", "content": "查询"}],
                "stream": True,
            },
        )
    await conn_b.close()
    assert "event: answer" in r2.text and "查询" in r2.text, "重启后应恢复并输出 answer"
    print("② 模拟进程重启后恢复 → 挂起交互不丢失, 恢复值续跑成功")

    # ---------- 会话隔离: 新 chatId = 独立断点, 互不干扰 ----------
    app_c, conn_c = await build_app(db_path)
    async with httpx.AsyncClient(
        transport=httpx.ASGITransport(app=app_c), base_url="http://test"
    ) as client:
        r3 = await client.post(
            "/api/v2/chat/completions",
            json={
                "appId": "app-sqlite",
                "chatId": "session-2",
                "messages": [{"role": "user", "content": "另一会话"}],
                "stream": True,
            },
        )
    await conn_c.close()
    assert "event: interactive" in r3.text
    print("③ 新会话(独立 chatId) → 独立断点, 互不干扰")


if __name__ == "__main__":
    asyncio.run(main())
    print("\nSQLite 断点持久化演示完成 ✅(断点已落库, 重启可恢复)")

回到示例索引