# LangGraph 会话存储:memory / SQLite / PostgreSQL 每个应用实例只选择一个存储后端。两个数据库地址可以同时写在 `.env`, 启动时只连接 `AGENT_STORAGE` 指定的一套;切换后需要重启,配置切换不搬迁历史数据。 现有 `DATABASE_URL` 不控制本模块。 | 模式 | 用途 | checkpoint | 会话互斥 | |---|---|---|---| | memory(默认) | 离线测试与临时开发,重启丢失 | InMemorySaver | 进程内会话锁 | | sqlite | 单实例、单 worker,本地持久化 | AsyncSqliteSaver | 进程内会话锁 + 数据库文件的实例锁 | | postgresql | 多 worker、多实例 | AsyncPostgresSaver | 进程内锁 + PostgreSQL session advisory lock | SQLite 实例锁由操作系统管理,第二个实例打开同一数据库会失败,进程退出自动释放。 SQLite 文件必须放在本地持久化磁盘,不作为多主机共享文件使用。PostgreSQL 的 锁连接与业务事务连接使用独立连接池;LLM 运行期间不持有业务行事务。 ## SQLite 启用步骤 先安装 `requirements.txt`,Python 使用 Conda `py3.11` 环境。在 `.env` 中设置: ```dotenv AGENT_BACKEND=langgraph AGENT_STORAGE=sqlite AGENT_SQLITE_PATH=./data/agent.sqlite3 AGENT_DB_AUTO_MIGRATE=false ``` 首次启动前初始化两类表: ```bash conda run -n py3.11 python -m src.storage.migrate --backend sqlite conda run -n py3.11 python -m src.serve --port 8000 ``` 可用 `--sqlite-path` 指定迁移目标,但必须与服务配置相同。 本地开发可以显式设 `AGENT_DB_AUTO_MIGRATE=true`,在启动阶段建表;请求处理不建表。 默认关闭自动 migration,未初始化、版本不匹配或数据库不可用时启动失败,不回退内存。 ## PostgreSQL 启用步骤 使用已有 PostgreSQL 数据库,或用 `compose.agent-db.yml` 启动本地开发库。 建议为本服务使用独立数据库,业务表使用 `agent_` 前缀,checkpointer 管理自己的表。 ```dotenv AGENT_BACKEND=langgraph AGENT_STORAGE=postgresql AGENT_POSTGRES_DSN=postgresql://agent:replace-me@127.0.0.1:5432/agent AGENT_DB_POOL_SIZE=10 AGENT_DB_LOCK_TIMEOUT_SECONDS=30 AGENT_DB_AUTO_MIGRATE=false # 以下四项供本地 compose 使用;密码必须与 DSN 一致。 AGENT_POSTGRES_USER=agent AGENT_POSTGRES_DB=agent AGENT_POSTGRES_PASSWORD=replace-me AGENT_POSTGRES_PORT=5432 ``` `replace-me` 是占位值,需要改为自己的密码;含特殊字符的 DSN 密码需 URL 编码。 ```bash docker compose -f compose.agent-db.yml up -d conda run -n py3.11 python -m src.storage.migrate --backend postgresql conda run -n py3.11 python -m src.serve --port 8000 ``` Linux 部署也可使用 `uvicorn src.main:app --host 0.0.0.0 --port 8000 --workers 4`。 Windows 本地使用 `python -m src.serve`,该入口选择 psycopg 支持的 SelectorEventLoop。 连接池在 lifespan 内创建、关闭;每个 worker 使用独立池。业务、checkpoint、 会话锁各有一个池,每池最大连接数由 `AGENT_DB_POOL_SIZE` 控制,需计入数据库容量。 同一组 worker 必须使用相同数据库,不能混用 SQLite 和 PostgreSQL。 ## 数据结构与恢复规则 - `agent_sessions`:强类型业务字段、单调递增版本、会话 epoch、最后提交的完整图快照。 - `agent_turns`:稳定 turn ID、pending/committed/aborted 状态、可选幂等键、请求摘要、已提交结果。 - `agent_schema`:本模块 migration 版本。LangGraph saver 独立管理自己的 migration 和 checkpoint 表。 - `/get_info` 以业务字段为权威读源。图快照保存历史、阶段和簿记;未确认的 checkpoint 不作为成功业务结果。 ```mermaid sequenceDiagram participant A as 应用服务 participant B as 业务数据库 participant G as LangGraph/checkpointer A->>B: 同会话锁内记录 pending turn B-->>A: 已提交字段、版本、完整图快照 A->>G: 清除旧执行 checkpoint,从提交快照执行本轮 G-->>A: 合法前缀及暂定正文增量 G->>G: 完成节点与 checkpoint 写入 G->>A: await 提交回调(完整结果) A->>B: 同一事务提交字段、快照、版本和 committed 结果 B-->>A: 提交成功 A-->>A: 允许成功响应 / SSE done ``` 第一版每轮从业务提交快照重建图执行,丢弃旧 checkpoint 执行进度;不会从中断节点中途续跑。 这会减少 saver 中的历史 checkpoint,但已提交的对话历史保存在业务图快照中, 轮次结果保存在业务日志中。LLM 决策与流程提示词保持不变。 | 故障窗口 | 下次请求的确定行为 | |---|---| | checkpoint 先写入,业务提交前退出 | pending 轮次转为 aborted,丢弃未提交执行进度,从上次业务快照开始 | | 业务提交成功,checkpoint 缺失或滞后 | 业务快照是权威,下次重新构建 checkpoint,不重复应用字段差量 | | 业务提交成功,成功响应发送前退出 | 同一 Idempotency-Key 重放原结果,不再调用 LLM 或推进版本 | set/get/delete 与 chat 使用同一个会话锁。字段写入带版本条件,过时 turn 或旧 epoch 不能覆盖新值;即使 advisory-lock 连接丢失,版本与 epoch 校验仍拒绝迟到提交。 数据库不可用时返回失败,未提交的流不会发 done。客户端已收到的流式正文不可撤回, 必须把 done 当作本轮成功结束标记。 ## 可选 Idempotency-Key SQLite/PostgreSQL 模式的 `/chat` 接受请求头 `Idempotency-Key`(1—128 字符)。 同一会话 epoch 下,相同 key 和相同 text/clientMode/needFormUpdate 重放已提交结果; 相同 key 对应不同请求返回业务码 409,SSE 使用 error 事件。 未提交请求可使用相同 key 重试;流式结果重放来自已提交结果,不会再次流式调用模型。 未提供 key 时,每次 POST 都是新轮次,不承诺网络重试自动去重。 memory 模式收到此头返回 409,不假装提供持久化幂等能力。FastGPT 路径不提供本模块的幂等保证。 ## 删除与数据库切换 删除先清理图 checkpoint,再删除业务数据与轮次日志。真实故障返回失败、允许重试。 数据库保留不含业务字段/历史的 epoch 与版本墓碑,用于拒绝旧任务提交;重复删除成功。 删除后新聊天进入新的 epoch,不会重放旧的幂等结果。 把 `AGENT_STORAGE=sqlite` 改为 `postgresql` 并重启,仅改变数据源。 原 SQLite 文件仍保留,PostgreSQL 不会自动出现其中的会话。本版不提供自动跨数据库迁移。 切换生产数据源需要单独的数据迁移与验证步骤。备份 SQLite 时先停止服务或使用 SQLite 备份机制;PostgreSQL 使用数据库备份工具。本地 compose 的 `down` 保留数据卷。 ## 验证与测试资源 ```bash conda run -n py3.11 python -m pytest test/storage/ -q ``` 默认跑真实 SQLite;PostgreSQL 测试需要 `AGENT_TEST_POSTGRES_DSN` 指向隔离测试库, 没有该变量时跳过 PostgreSQL 用例,不把跳过当作 PostgreSQL 已验证。 测试使用 fake LLM,不消耗模型额度,覆盖:重启续聊、四接口、两种数据类型、 幂等重放、版本冲突、checkpoint 缺失/超前、提交前后进程直接退出、数据库锁和删除恢复。