# 用户对话 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)。