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

170 lines
9.9 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 Spec
| 项 | 内容 |
| --- | --- |
| 状态 | Implemented |
| 日期 | 2026-09-05 |
| 负责人 | fire-safety-ymd 后端 |
| 需求来源 | 用户要求“先做对话 API” |
| 关联 Change Request | 无 |
## 1. 背景
项目已经具备 SuperAgent Open API 出站 Adapter 和空间只读 MCP,但用户侧应用还没有安全的服务端对话入口。前端不能直接持有 SuperAgent Open API Key,也不能自行指定 SuperAgent Session、用户身份或授权范围。
本 checkpoint 提供一个最小可联调的对话 API。它由 Go 服务创建并复用 SuperAgent Session,通过 SSE 返回安全进度和最终回答。真实用户认证、动态区域授权、数据库会话持久化和生产审计后续单独设计。
## 2. 目标
- 新增默认关闭的 `POST /api/chat`。
- 使用独立静态 Bearer Token 保护首版测试入口,不复用 SuperAgent Key 或 MCP Token;默认要求至少 32 个可打印 ASCII 字符。
- 首轮创建随机本地 `conversation_id` 和 SuperAgent Session;后续轮次复用映射。
- 同一 `conversation_id` 同时只允许一个活动 Run,冲突时返回 `409`。
- 通过 SSE 返回对话 ID、安全进度、严格完成后的最终回答和完成事件。
- 不把消息正文、回答正文、Secret、原始工具参数或输出写入日志。
- 对输入大小、总运行时间、内存会话数量、空闲过期时间和浏览器 Origin 设上限。
## 3. 非目标
- 不实现注册、登录、JWT、SSO、角色、租户或最终用户级数据授权。
- 不接受客户端提交的 `user_id`、`external_subject_id`、SuperAgent `session_id`、角色或区域范围。
- 不持久化对话;进程重启或多实例切换后,旧 `conversation_id` 不可继续使用。
- 不返回逐字 token、模型原始思考、原始 Trace、工具输入或工具输出。
- 不实现历史消息查询、会话列表、主动取消、重试队列、限流或 WebSocket。
- 不把 Agent 回答写入消防业务事实。
## 4. HTTP 契约
### 4.1 请求
```http
POST /api/chat
Authorization: Bearer <FIRE_SAFETY_CHAT_AUTH_TOKEN>
Content-Type: application/json
Accept: text/event-stream
{
"message": "观水镇附近有哪些可用水源?",
"conversation_id": "conv_..."
}
```
- `message` 必填,去除首尾空白后不能为空,并服从 SuperAgent 单条消息字节上限。
- `conversation_id` 首轮省略;后续使用服务返回的随机值。
- 未知字段、多个 JSON 值、非法媒体类型和超大请求体均拒绝。
- 客户端不得提交身份、权限、Provider Session 或任意 metadata。
### 4.2 成功响应
响应媒体类型为 `text/event-stream`。事件顺序如下:
```text
event: conversation
data: {"conversation_id":"conv_...","reused":false}
event: progress
data: {"event":"tool.started","tool_name":"fire_safety_search_place_candidates","status":"running"}
event: message
data: {"conversation_id":"conv_...","answer":"..."}
event: done
data: {"conversation_id":"conv_...","run_id":"...","usage":{"input":0,"output":0,"total":0}}
```
- `conversation` 在 Provider Session 准备完成后首先发送。
- `progress` 只包含经过现有 Adapter 清洗的事件名、工具名和状态;不包含文本、ID、参数或工具结果。
- 没有业务事件时,服务每 15 秒发送一个 SSE comment heartbeat,保持长连接且不携带业务数据。
- `message` 只在 Adapter 同时确认最终内容、成功 `run.completed` 和顶层 `end` 后发送。
- `done` 是成功终止事件。
### 4.3 错误响应
在 SSE 开始前,错误使用 HTTP 状态码和稳定 JSON 错误,例如:
```json
{
"error": {
"code": "CHAT_AUTH_INVALID",
"message": "Chat authentication failed."
},
"request_id": "..."
}
```
SSE 开始后的 Provider 失败使用终止 `error` 事件,不返回部分回答。主要错误类别:
- `CHAT_REQUEST_INVALID`:输入或 JSON 不合法,HTTP 400。
- `CHAT_AUTH_INVALID`:Bearer 无效,HTTP 401。
- `CHAT_ORIGIN_FORBIDDEN`:浏览器 Origin 未获准,HTTP 403。
- `CHAT_CONVERSATION_NOT_FOUND`:会话不存在、已过期或服务已重启,HTTP 404。
- `CHAT_CONVERSATION_BUSY`:同一会话已有活动 Run,HTTP 409。
- `CHAT_CAPACITY_REACHED`:内存会话达到上限,HTTP 503。
- `CHAT_UPSTREAM_TIMEOUT`、`CHAT_UPSTREAM_UNAVAILABLE`、`CHAT_UPSTREAM_PROTOCOL_ERROR`、`CHAT_RUN_FAILED`:上游运行失败;SSE 尚未开始时使用 502/504,否则发送 `error` 事件。
## 5. 会话与并发规则
- 映射仅保存在当前 Go 进程内:`conversation_id -> SuperAgent session_id`。
- `conversation_id` 由加密安全随机数生成,不包含用户、镇街或业务语义。
- 新会话使用服务端固定测试主体 `FIRE_SAFETY_CHAT_SUBJECT_ID` 创建;客户端不能覆盖。
- 每次发送消息使用新的幂等键和请求关联 ID。
- 同一会话的 Run 使用互斥占用;不同会话可并发。
- 空闲会话超过 TTL 后惰性清理;活动 Run 不清理。
- 达到最大会话数后先清理过期会话,仍满则拒绝创建。
固定测试主体只是首版联调边界,不代表真实用户认证,也不能用于用户级授权或审计。
## 6. 配置契约
| 环境变量 | 默认值 | 说明 |
| --- | --- | --- |
| `FIRE_SAFETY_CHAT_ENABLED` | `false` | 对话 API 总开关;启用时要求 SuperAgent 同时启用 |
| `FIRE_SAFETY_CHAT_AUTH_TOKEN` | 空 | 独立静态 Bearer;默认至少 32 个可打印 ASCII 字符,兼容开关开启时允许已交付的旧短凭证 |
| `FIRE_SAFETY_CHAT_ALLOW_LEGACY_SHORT_TOKEN` | `false` | 仅为已交付旧客户端短凭证的受控测试/迁移临时兼容开关;新环境不得开启 |
| `FIRE_SAFETY_CHAT_SUBJECT_ID` | `fire-safety-ymd-chat-test-subject` | 服务端固定测试主体,不使用真实用户标识 |
| `FIRE_SAFETY_CHAT_ALLOWED_ORIGINS` | 空 | 逗号分隔的精确 HTTP(S) Origin;空值只允许无 Origin 的服务端/CLI 调用 |
| `FIRE_SAFETY_CHAT_MAX_BODY_BYTES` | `131072` | HTTP JSON 请求体上限,最大 1 MiB |
| `FIRE_SAFETY_CHAT_RUN_TIMEOUT` | `10m` | 创建 Session 加单轮 Run 的总时限,最大 30 分钟 |
| `FIRE_SAFETY_CHAT_SESSION_TTL` | `30m` | 空闲内存会话保留时间,最大 24 小时 |
| `FIRE_SAFETY_CHAT_MAX_SESSIONS` | `1000` | 单进程最大内存会话数,最大 10000 |
`FIRE_SAFETY_CHAT_AUTH_TOKEN` 必须分别不同于 `FIRE_SAFETY_SUPERAGENT_OPEN_API_KEY` 和 `FIRE_SAFETY_MCP_AUTH_TOKEN`。
`FIRE_SAFETY_CHAT_ALLOW_LEGACY_SHORT_TOKEN` 默认为 `false`。只有在旧客户端已经拿到、且无法立即更换的短凭证时,才可在受控测试或迁移窗口显式设置为 `true`。无论开关取值如何,`FIRE_SAFETY_CHAT_AUTH_TOKEN` 都必须非空、不超过 4096 字节,并且每个字符都在 ASCII `0x21-0x7e` 范围内(不得包含空格、控制字符或 Unicode);开关只影响 Chat 原生 Bearer 和兼容入口的 `xtoken`,不放宽 MCP Token 至少 32 个可打印 ASCII 字符的门禁。三种凭证仍必须使用不同值。凭证轮换完成后必须恢复 `false`;新环境不得以该开关绕过默认门禁。开关启用时,服务启动日志会记录不含 Secret 的安全 warning,便于后续清理。
## 7. CORS 与鉴权边界
- 无 `Origin` 的 CLI/服务端请求可以进入 Bearer 校验。
- 带 `Origin` 的浏览器请求必须精确匹配配置列表;不支持 `*`。
- 预检只允许 `POST` 以及 `Authorization`、`Content-Type` Header。
- 不启用 Cookie 身份或 CORS credentials。
- 静态 Bearer 只适合首版受控联调;legacy 短凭证开关只适合明确的迁移窗口,公网最终用户入口必须接入真实身份认证、速率限制和动态授权。
## 8. 日志与数据边界
- 只记录 request ID、结果类别、是否复用会话和耗时。
- 不记录 Bearer、Open API Key、消息、回答、Provider payload、坐标、工具参数或工具输出。
- 对外错误不包含上游响应正文、URL、Session ID、堆栈或 Secret。
- SuperAgent 回答属于辅助内容,不自动升级为权威事实,也不替代报警、撤离和现场指挥。
## 9. 验收标准
- Given 对话 API 未启用,When 请求 `/api/chat`,Then 返回 404 且不创建 SuperAgent Client 调用。
- Given 未授权或 Origin 不在白名单,When 请求 API,Then 在读取和转发消息前拒绝。
- Given 首轮合法消息,When Provider 严格成功,Then 返回新 `conversation_id`、最终回答和 `done`。
- Given 后续合法消息,When 传入同一 `conversation_id`,Then 复用原 SuperAgent Session。
- Given 同一会话已有活动 Run,When 再次发送,Then 返回 `409` 且不发第二个上游请求。
- Given Provider 流不完整或 Run 失败,When 处理结束,Then 不发送 `message` 或 `done`,只返回安全错误。
- Given 服务重启、会话过期或随机 ID 不存在,When 继续对话,Then 返回 `404` 而不把客户端值当作 Provider Session。
- Given `FIRE_SAFETY_CHAT_ALLOW_LEGACY_SHORT_TOKEN` 未开启,When Chat Token 少于 32 个字符,Then 服务启动配置校验失败。
- Given `FIRE_SAFETY_CHAT_ALLOW_LEGACY_SHORT_TOKEN=true`,When 旧短凭证用于受控测试/迁移,Then 原生 Bearer 和兼容 `xtoken` 均可校验,但非空、4096 字节上限和 ASCII `0x21-0x7e` 约束仍生效,且启动日志只记录不含 Secret 的安全 warning。
- Given 无真实网络和 Secret,When 执行自动化测试,Then 使用本地 fake/模拟 Provider 并全部通过。
## 10. Definition of Done
- 配置、Service、SuperAgent 适配、HTTP Handler 和应用装配满足本文契约。
- 覆盖鉴权、CORS、输入、SSE、复用、并发、过期、容量、上游错误和敏感信息边界测试。
- `gofmt`、`go test ./...`、`go test -race ./...` 和 `go vet ./...` 通过。
- `.env.example`、集成指南、安全边界、项目索引和 `PROJECT_STATE.md` 同步。
- 不提交真实 Secret,不自动创建 Git 提交。