- Select one storage backend per instance via AGENT_STORAGE, retaining memory as the backward-compatible default. Manage the selected async repository, official checkpointer and connection pools in lifespan; fail startup on unavailable or uninitialized storage without fallback. - Add SQLite and PostgreSQL adapters, schema initialization CLI, sample PostgreSQL compose configuration and a Windows-compatible serve entry. SQLite enforces single-instance file ownership; PostgreSQL uses a dedicated pool for session advisory locks plus version/epoch fencing. - Make repository IO and commit callbacks awaitable. Persist business fields, a complete committed graph snapshot and turn result/version in one business transaction before success. Rebuild execution checkpoints from that snapshot, discarding uncommitted progress after interruption. - Support optional Idempotency-Key on durable chat requests. Replay committed results and return 409 on changed request content; delete removes business/history data and replay records while retaining only an epoch/version tombstone to reject stale commits. - Document migration, database switching boundaries, backup/deployment expectations and crash recovery. Ignore generated database files. Validation: 315 tests passed with real SQLite and isolated PostgreSQL, including abrupt child-process exits before/after business commit; both backends passed CLI migration and actual HTTP server restart smoke. Existing API tests were rechecked after isolating them from user storage. An initial Windows CreateProcess permission failure did not recur in the full diagnostic run; failure assertions were not skipped or relaxed.
142 lines
7.2 KiB
Markdown
142 lines
7.2 KiB
Markdown
# 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 缺失/超前、提交前后进程直接退出、数据库锁和删除恢复。
|