85 lines
4.8 KiB
Markdown
85 lines
4.8 KiB
Markdown
# 用户对话 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)。
|