Start LangGraph backend migration foundation

This commit is contained in:
Xin Wang
2026-07-27 17:21:29 +08:00
parent 5c719ed2ea
commit 1c8e9da486
33 changed files with 1859 additions and 86 deletions

115
docs/domain/api-contract.md Normal file
View File

@@ -0,0 +1,115 @@
# 兼容 API 契约基线
## 通用约定
- 路径保持 `/chat``/set_info``/get_info`
- 请求和响应字段保持 camelCase。
- 业务成功/失败主要通过响应体字符串 `code` 表达;现有 endpoint 通常仍返回 HTTP 200。
- `sessionId` 最大 64 字符,`timeStamp` 最大 32 字符。
- Pydantic 校验失败由 FastAPI 返回 HTTP 422。
- 本文冻结的是当前可观察行为;“目标行为”标记为后续迁移必须修复的已批准偏差。
## `POST /chat`
### 请求
| 字段 | 类型 | 必填 | 默认值 |
|---|---|---:|---|
| `sessionId` | string | 是 | - |
| `timeStamp` | string | 是 | - |
| `text` | string | 是 | - |
| `needFormUpdate` | boolean | 否 | `false` |
| `useTextChunk` | boolean | 否 | `false` |
### 非流式响应
字段为 `sessionId``timeStamp``outputText``formUpdate``nextStage``nextStageCode``code``msg`
- 成功时 `code="200"`
- `<state>XXXX</state>` 从正文中移除。
- `3001/3002/1002 → 1002``2006 → 2004``2017 → 2016``2020 → 0002`
- Prefix 缺失或正文不可解析时当前返回 `code="500"` 和“消息不完整”。
- FastGPT 认证、限流和 API 异常分别映射为响应体 `401``429``500`
- `formUpdate` 保持无固定 schema 的 JSON 值,以兼容现有调用方。
### SSE 响应
事件名和数据:
| 事件 | 数据 | 基数 |
|---|---|---|
| `stage_code` | `{"nextStageCode":"1002","nextStage":"通话中"}` | 成功轮最多一次 |
| `formUpdate` | 表单 patch 对象 | 有更新时最多一次 |
| `text_delta` | `{"text":"..."}` | 零到多次 |
| `done` | `{"status":"completed"}` | 成功恰好一次 |
| `error` | `{"msg":"...","code":"500"}` | 失败恰好一次且终止 |
V1.0.9 文档要求 `stage_code` 先于 `text_delta``formUpdate` 的位置由 FastGPT `flowResponses` 到达时间决定,文档示例允许它出现在两个 `text_delta` 之间。LangGraph 迁移目标固定为:
```text
stage_code -> formUpdate(可选) -> text_delta* -> done
```
迁移后的错误路径不得同时产生 `done``error``useTextChunk=true` 只改变 `text_delta` 切分,不改变拼接后的文本。
### 已知偏差
- 当前流式 parser 会在整段文本中搜索标签,而不是强制标签位于开头。
- 当前流式未知状态码仍可能发送空 `nextStage`
- 当前流式缺少 prefix 时仍可能输出正文和 `done`
- 当前流式内部事件处理异常会记录后继续,可能掩盖部分失败。
- 当前实现记录完整输入、输出和 `formUpdate`,不符合数据保护目标。
以上偏差被字符化测试记录,但不作为 LangGraph 新实现的目标行为Phase 58 必须按迁移计划修正。
## `POST /set_info`
### 请求
字段为 `sessionId``timeStamp``key``value``includeInputInfo`;其中 `includeInputInfo` 默认 `false`
当前实现:
1. 通过一次 FastGPT 对话读取 `newVariables.state`
2. 删除辅助对话记录;
3. 直接执行 `state[key] = value`
4. 再通过 FastGPT 对话写回并删除辅助记录。
成功返回 `code="200"`;任一步失败返回响应体 `code="500"`
目标行为:
- 仅接受 `field-registry.json``external_write=true` 的 key。
- 进行类型转换和领域校验。
- 禁止写内部状态字段。
- 直接事务化写业务状态,不调用 LLM不创建/删除辅助聊天记录。
## `POST /get_info`
请求字段为 `sessionId``timeStamp``key``includeInputInfo``includeInputInfo` 默认 `false`
支持:
- `all`
- `acdinfo`
- `acdhuman1`
- `acdhuman2`
- 单个字段 key
兼容序列化:
- `value` 始终是 JSON 编码后的字符串。
- boolean 转为字符串 `"1"``"0"`
- 缺失字段转为空字符串。
- 未知单字段 key 当前返回 JSON 字符串 `""`,而不是报错。
目标实现仍保留上述响应编码,但直接读取业务投影,不调用 FastGPT。
## 契约来源
- `src/schemas/models.py`
- `src/api/endpoints.py`
- `docs/视频快处智能信息采集机器人交互接口文档V1.0.9.docx`
- `docs/chat-stream-mode.md`
- `test/api/test_public_schema_contract.py`
- `test/api/test_chat_backend_boundary.py`