feat(storage): add selectable SQLite and PostgreSQL persistence

- 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.
This commit is contained in:
Eric Wang
2026-10-24 13:41:54 +08:00
parent 3caf17494f
commit 97de142e6d
30 changed files with 1368 additions and 333 deletions
+64 -54
View File
@@ -1,53 +1,64 @@
# AI 事故信息采集 API(LangGraph 迁移分支)
本分支是“将对话后端从 FastGPT 工作流迁移到 LangGraph”课程的迁移准备分支。
本分支把事故采集、拍照和信息核实从 FastGPT workflow 迁移到嵌入式 LangGraph。
文本四接口通过 `AGENT_BACKEND` 选择 FastGPT 或 LangGraph,HTTP schema 与 SSE 事件保持兼容。
迁移作业计划见 [docs/langgraph-backend-migration-plan.md](docs/langgraph-backend-migration-plan.md),
字段兼容约定见 [docs/assignment6-compat.md](docs/assignment6-compat.md)。
当前状态:对外 API 契约保持不变,`/chat` 的供应商细节已封装到后端适配层(FastGPT 仍
为唯一实现),迁移计划见 [docs/langgraph-backend-migration-plan.md](docs/langgraph-backend-migration-plan.md)。
本阶段交付说明与验证证据见 [docs/assignment-baseline.md](docs/assignment-baseline.md)。
## 环境与安装
## 环境要求
- Python 3.11(使用 Conda 环境 `py3.11`,见 [AGENTS.md](AGENTS.md))
- `fastgpt-client` SDK(当前以本地可编辑方式安装,见下文)
## 安装
Python 3.11 使用 Conda 环境 `py3.11`(见 [AGENTS.md](AGENTS.md))。
```bash
conda activate py3.11
# 项目依赖
pip install -r requirements.txt
# FastGPT SDK(本地可编辑安装;SDK 源码在独立仓库维护)
# 项目仍使用该 SDK 支持 FastGPT 路径;源码在独立仓库维护。
pip install -e /path/to/fastgpt-python-sdk
```
## 配置
## 配置与后端选择
服务端通过环境变量读取 FastGPT 连接信息。复制示例配置并按环境填写:
复制 `.env.example` 为 `.env` 并填写配置;`.env` 不进入 Git。
```bash
cp .env.example .env
- FastGPT:`AGENT_BACKEND=fastgpt`,填写 `ANALYSIS_SERVICE_URL`、`ANALYSIS_AUTH_TOKEN`、`APP_ID`。
- LangGraph:`AGENT_BACKEND=langgraph`,按 `.env.example` 填写命名 LLM 配置,
由 `LLM_PROVIDER` 选择模型;文本后端不要求 FastGPT 凭证。
- LangGraph 存储:`AGENT_STORAGE=memory|sqlite|postgresql`。每个实例只选一个,修改后重启。
`DATABASE_URL` 不控制 LangGraph 会话存储,切换数据库不自动搬迁旧数据。
本地 SQLite 示例:
```dotenv
AGENT_BACKEND=langgraph
AGENT_STORAGE=sqlite
AGENT_SQLITE_PATH=./data/agent.sqlite3
AGENT_DB_AUTO_MIGRATE=false
```
需要设置的变量:
| 变量 | 说明 |
|---|---|
| `ANALYSIS_SERVICE_URL` | FastGPT 服务地址 |
| `ANALYSIS_AUTH_TOKEN` | FastGPT API Key |
| `APP_ID` | FastGPT 应用 ID |
## 运行
首次初始化和运行:
```bash
python -m uvicorn src.main:app --host 127.0.0.1 --port 8000
conda run -n py3.11 python -m src.storage.migrate --backend sqlite
conda run -n py3.11 python -m src.serve --port 8000
```
- `GET /` 存活检查
- `POST /chat` 对话(`?stream=true` 为 SSE 流式)
- `POST /set_info`、`POST /get_info`、`DELETE /delete_session` 业务数据接口
PostgreSQL 的连接池、迁移、本地 compose 与多 worker 部署见
[docs/agent-storage.md](docs/agent-storage.md)。Windows 使用 `python -m src.serve`,
它为 psycopg 选择兼容的事件循环;Linux 也可用常规 uvicorn 命令启动。
## HTTP 接口
- `GET /`:存活检查。
- `POST /chat`:对话;`?stream=true` 使用真实增量 SSE。
- `POST /set_info`、`POST /get_info`:业务字段读写,不调用模型。
- `DELETE /delete_session`:清理 checkpoint 和业务状态,真实存储错误返回失败。
- SQLite/PostgreSQL 模式可给 `/chat` 添加 `Idempotency-Key` 请求头,
重放已提交结果;复用 key 但请求内容不同返回业务码 409。
SSE 的 `error` 是失败终止,`done` 是成功结束。正文可以先流出,
调用方必须在收到 `done` 后才将本轮视为成功。`needFormUpdate=false`
只隐藏对外差量,不影响内部字段提交。
## 测试
@@ -55,36 +66,35 @@ python -m uvicorn src.main:app --host 127.0.0.1 --port 8000
conda run -n py3.11 python -m pytest -q
```
测试分三层:
测试覆盖公共 schema、两后端适配、流式校验/取消、会话串行、字段别名、错误码与持久化恢复。
SQLite 集成测试使用临时真实文件;PostgreSQL 集成测试需要
`AGENT_TEST_POSTGRES_DSN` 指向隔离测试库。未配置时 PostgreSQL 用例会明确跳过。
两端都有直接退出子进程的恢复测试,不调用真实 LLM。
- `test/api/test_public_schema_contract.py`:公共请求/响应 schema 的契约冻结测试
- `test/api/test_chat_backend_boundary.py`:API 层与后端适配层的边界测试(fake 后端)
- `test/backends/test_fastgpt_backend.py`:FastGPT 适配器的翻译测试(fake SDK)
真实模型评测使用 `evals/run.py`,结果按每轮模型与代码版本单独保存,
历史轮次通过率不代替当前提交的业务验收。
## 目录结构
```text
src/
api/endpoints.py # HTTP 协议层:schema、state 前缀解析、SSE 事件
backends/chat.py # 后端中立契约(ChatBackend Protocol、ChatInput/ChatResult/事件)
backends/fastgpt.py # FastGPT 适配器:SDK 调用与响应翻译
core/fastgpt_client.py # 客户端生命周期与依赖注入(lifespan)
schemas/models.py # 公共 Pydantic 模型
docs/langgraph-backend-migration-plan.md # 十阶段迁移作业计划
evals/ # 调用真实服务的评测用例与 runner
api/endpoints.py # HTTP schema、阶段标签解析、SSE 协议
agent/ # 流程图、节点、版本化提示词与模型服务
backends/ # FastGPT / LangGraph 中立适配
services/ # 四接口应用服务、字段注册表、会话锁
storage/ # SQLite / PostgreSQL、轮次日志、migration
core/fastgpt_client.py # 后端与存储生命周期、依赖注入
serve.py # 跨平台单 worker 启动入口
schemas/models.py # 公共请求/响应模型
test/storage/ # 两种数据库的真实集成与故障恢复测试
evals/ # 真实模型评测用例与 runner
```
## 当前状态与已知限制
## 部署边界
作业 1—5 已完成(LLM 提议 + 代码校验架构,提示词与 `workflow/20261020`
逐字对齐,全量测试 143 通过)。逐作业进度、真实模型评测结果与开放问题
见 [docs/langgraph-backend-migration-plan.md](docs/langgraph-backend-migration-plan.md)
文末“当前进度与已知差异”。
- 真实模型黄金评测:`collection_skip_known_info` 12/12 通过;
`photo_skip_plate_confirm` 0/4、`no_false` 2/8,统一卡在短促回答“对”
的分流语义上,待与 FastGPT 应用层配置对账(差异已留档,未叠代码守卫)。
- `set_info` / `get_info` 仍直接依赖 FastGPT 状态与历史,迁移到共享
业务状态属于作业 6;`delete_session` 已按后端分流(langgraph 清 checkpoint)。
- 服务启动(langgraph 模式)不需要 FastGPT 配置;需要 LLM provider 配置。
- 流式为“完整答案切片”,真实增量输出属作业 8;`formUpdate` 差量属作业 6。
- memory 是临时存储,重启丢失;SQLite 限定单实例、单 worker。
- PostgreSQL 使用数据库级会话锁、版本和 epoch 校验;仍需按目标负载验证池容量与吞吐。
- 业务快照是已提交状态的权威源,旧 checkpoint 执行进度在下一轮重建,
不承诺从未提交的 LLM 调用中途续跑。
- 语音 WebSocket 管线仍有独立配置,不由本次数据库选择自动迁移。
- 未知字段写入、敏感字段访问边界等兼容事项继续按兼容清单处理。