Files
fire-safety-ymd/docs/architecture/chat-api-v1.md
2026-09-05 17:36:23 +08:00

85 lines
4.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 用户对话 API v1 架构
## 1. 组件边界
```mermaid
flowchart LR
UI[用户侧应用或 curl] -->|POST /api/chat\nChat Bearer + JSON| H[Chat Handler]
H -->|Prepare / Stream| S[Chat Service]
S -->|CreateSession / StreamMessage| A[SuperAgent Chat Adapter]
A -->|Open API Key + HTTPS/SSE| SA[SuperAgent]
SA -->|独立 MCP Bearer| M[POST /mcp]
M --> P[(PostgreSQL/PostGIS)]
```
三种凭证属于不同信任方向:
- Chat Bearer:用户侧应用到本 Go 服务,仅用于首版受控联调。
- SuperAgent Open API Key:本 Go 服务到 SuperAgent,只保存在服务端。
- MCP Bearer:SuperAgent 到本 Go 服务,只保护 `/mcp`。
三者必须使用不同值。
## 2. 请求生命周期
1. Handler 校验请求方法、精确 Origin、Chat Bearer、媒体类型、Accept、请求体大小和严格 JSON。
2. Service 校验消息和本地 `conversation_id`。
3. 首轮请求由 Service 生成随机 `conversation_id`,并使用服务端固定测试主体创建 SuperAgent Session。
4. Service 在内存中保存 `conversation_id -> provider session_id`;Provider Session ID 不返回客户端。
5. Handler 开始 SSE,先返回 `conversation` 事件。
6. Adapter 把消息发送到已准备的 Session,只将经过清洗的 `run.*` / `tool.*` 进度投影给 Handler。
7. Adapter 严格确认最终内容、成功 `run.completed` 和顶层 `end` 后,Handler 才发送 `message` 与 `done`。
8. 发生断流、超时、协议错误或失败 Run 时,不返回部分回答;当前映射失效,客户端下一次应创建新对话。
Handler 在没有可公开进度时每 15 秒发送不含数据的 SSE comment heartbeat,并把单请求写期限扩展到配置的总运行 deadline 之后 5 秒,以便返回终止错误。反向代理仍需显式关闭该路径的响应缓冲并设置相容的空闲超时。
## 3. 会话状态机
```text
不存在
-> 创建 Provider Session
-> busy
-> 成功:idle
-> 上游结果不确定或失败:删除映射
idle
-> 新一轮 Prepare:busy
-> TTL 到期:惰性删除
busy
-> 同 conversation_id 的并发请求:409
```
会话存储是单进程内存映射:
- 进程重启后全部丢失。
- 多实例之间不共享,未配置粘性路由时后续轮次可能返回 404。
- 只保存随机本地 ID、Provider Session ID、占用状态和最后使用时间,不保存消息历史。
- SuperAgent 负责其 Session 内的上下文;本服务不会把完整历史在每轮重新发送。
## 4. 安全设计
- 客户端 DTO 没有 `user_id`、`external_subject_id`、角色、镇街范围、Provider Session 或 metadata 字段;未知字段直接拒绝。
- 固定测试主体由服务端配置,不能作为最终用户身份或权限依据。
- CORS 使用精确 Origin 列表,不支持 `*` 或 credentials。
- Handler 日志只记录 request ID、结果、是否复用和耗时,不记录问题、答案或对话 ID。
- 进度只允许安全字符组成的 `run.*` / `tool.*` 事件,以及工具名和状态;消息 delta、Provider ID、工具参数和结果不向客户端透传。
- 错误映射为稳定代码,不回显 Provider 响应正文、URL、Session、堆栈或 Secret。
- API 默认关闭;启用必须同时具备有效 SuperAgent 配置和独立 Chat Bearer。
- Chat 凭证默认至少 32 个可打印 ASCII 字符。为兼容已经交付且无法立即更换的旧客户端短凭证,只有在受控测试/迁移窗口显式设置 `FIRE_SAFETY_CHAT_ALLOW_LEGACY_SHORT_TOKEN=true` 才允许短值;无论开关如何,凭证都必须非空、不超过 4096 字节且仅含 ASCII `0x21-0x7e`(无空格、控制字符或 Unicode)。该开关同时适用于原生 Bearer 和兼容 `xtoken`,不影响 MCP 的至少 32 字符门禁;三种凭证必须不同。轮换后恢复 `false`,新环境不得开启。
- 开关启用时只记录不含 Secret 的安全 warning,提醒运维在迁移完成后关闭兼容。
## 5. 伸缩与后续替换点
当前内存实现适合单实例受控联调。进入多实例或真实用户阶段前,需要把 `ChatService` 的本地状态替换或扩展为:
- 经验证的最终用户身份与动态数据授权上下文。
- 持久化或共享的会话映射,并明确并发租约、过期和恢复语义。
- 网关限流、滥用防护、请求配额和指标。
- 对话、工具调用和安全事件的脱敏持久审计。
- 主动取消 Provider Run,以及客户端断开后的终止策略。
这些扩展不能通过简单放宽当前静态 Token 或把客户端用户字段原样传给 Agent 来实现。
既有客户端所需的 DashScope 风格协议通过独立 Handler 适配并复用本文 Chat Service,不改变原生契约。公网入口和两种协议映射见 [`public-chat-entry-v1.md`](public-chat-entry-v1.md)。