11 KiB
fire-safety-ymd 测试对话页面 v1 Spec
| 项 | 内容 |
|---|---|
| 状态 | Implemented locally;测试服务器公网部署待验收 |
| 日期 | 2026-09-06 |
| 负责人 | fire-safety-ymd 后端 |
| 关联接口 | POST /api/v1/apps/{app_id}/completion |
| 关联配置 | FIRE_SAFETY_CHAT_PAGE_ENABLED |
1. 背景
项目已有 DashScope 风格的兼容对话接口。SuperAgent、数据库和公网 MCP 联调时,使用者需要 一个无需另建前端工程的最小浏览器页面,用来输入问题、观察 SSE 结果并验证同一会话的后续轮次。 这个页面只服务于测试和受控联调,不改变既有第三方请求契约,也不提供真实用户登录或授权。
2. 目标
- 由 Go 服务可选地提供
GET /chat和GET /chat/两个页面入口,并提供同源的 CSS/JavaScript 资源。 FIRE_SAFETY_CHAT_PAGE_ENABLED默认值为false;关闭时两个入口都返回 HTTP 404。- 开启时页面使用与第三方完全相同的兼容对话请求:
POST /api/v1/apps/{FIRE_SAFETY_CHAT_COMPAT_APP_ID}/completion。 - 页面让用户手动输入
xtoken,在当前页面内发起 SSE 请求,不把凭证写入页面源码、Cookie、 URL、localStorage、sessionStorage 或服务端配置。 - 页面保存上一轮成功响应的
output.session_id仅在 JavaScript 内存中,并将它用于同一页面的 后续轮次。 - 页面能区分 SSE 的中间会话事件、最终
finish_reason=stop、上游错误和 HTTP 错误。 - Nginx 公网入口固定反代
/chat、/chat/、/chat/app.css、/chat/app.js和兼容completion路径;是否实际提供页面由 Go 配置开关决定。
3. 非目标
- 不实现注册、登录、JWT、SSO、角色、租户或最终用户级动态授权。
- 不把静态测试
xtoken写入 HTML、JavaScript 常量、Nginx、镜像或 Git。 - 不持久化 Token、问题、答案、聊天历史或会话映射;刷新页面后不自动恢复会话。
- 不新增一套页面专用的聊天协议、Provider Session 或数据库访问接口。
- 不实现逐字流式动画、文件上传、图像、地图选点、后台任务或多实例共享会话。
- 不把页面公开等同于公网生产能力;页面和当前 Chat 兼容 API 都只适用于受控联调。
4. 路由与开关
| 路由 | 开关关闭 | 开关开启 | 说明 |
|---|---|---|---|
GET /chat |
404 | 308 到 /chat/ |
页面规范化入口 |
GET /chat/ |
404 | 200 text/html |
页面入口 |
GET /chat/app.css |
404 | 200 text/css |
页面样式资源 |
GET /chat/app.js |
404 | 200 text/javascript |
页面脚本资源 |
POST /api/v1/apps/{app_id}/completion |
按 Chat/Compat 配置 | 按 Chat/Compat 配置 | 页面和第三方共用 |
页面开关为 Go 进程启动时读取的配置。修改 .env 后必须重新创建 Compose 容器;仅执行
docker compose restart 不会把新的环境值加载进已有容器。Nginx 可以始终保留四个页面/资源的
精确反代 location,但 Go 关闭页面时必须返回 404。
当页面开启时,以下依赖也必须满足:
FIRE_SAFETY_CHAT_ENABLED=true;FIRE_SAFETY_CHAT_COMPAT_APP_ID非空且与 Nginx 的精确 completion location 一致;FIRE_SAFETY_CHAT_AUTH_TOKEN已配置为 Chat 方向的测试凭证;FIRE_SAFETY_CHAT_ALLOWED_ORIGINS包含精确值https://agent.nianxx.com;- SuperAgent 的服务端配置和 MCP 联调配置按对应 Spec 已完成。
页面请求不向 Go 发送身份、角色、区域、租户、Provider Session 或 metadata。页面中出现的 App ID 是公开路由标识,不是 Secret;Chat Token、MCP Bearer 和 SuperAgent Open API Key 必须分别使用不同值。
5. 页面行为
页面至少包含:Token 输入框、问题输入框、发送按钮、清空当前会话按钮、运行状态和结果区域。 Token 输入框默认为空,推荐使用密码输入类型;页面不得从 URL、Cookie 或浏览器存储预填。
用户发送问题时,页面使用浏览器 fetch(不能使用无法设置自定义 Header 的 EventSource)向
同源兼容路径发送:
POST /api/v1/apps/<public-app-id>/completion
Origin: https://agent.nianxx.com
xtoken: <用户本次手动输入的测试 Token>
Content-Type: application/json
Accept: text/event-stream
首轮请求体:
{
"input": { "prompt": "观水镇附近有哪些地点候选?" },
"parameters": {}
}
后续请求体把同一页面内最近一次成功返回的本地会话 ID 放入 input.session_id:
{
"input": {
"prompt": "选择第 2 个,再查询附近水源。",
"session_id": "conv_<previous-result>"
},
"parameters": {}
}
页面必须逐个读取 event: result 的 SSE 数据:
- 首个结果一般只包含
output.session_id和finish_reason="null";页面保存会话 ID,但不把 该事件显示为最终答案。 - 只有收到同一会话的
finish_reason="stop"且带output.text时,页面才显示为成功。 event: error、非 2xx HTTP 响应、解析失败或没有收到stop时,页面显示失败,不把部分 文本当成可信答案,也不继续复用一个结果未知的会话。- 用户点击清空、刷新页面、关闭页面或新开页面时,页面内存中的 Token 和
session_id都丢弃。
页面不应把工具名、工具参数、Provider Session、内部 Trace 或数据库敏感字段直接作为调试 信息展示。最终回答仍是 SuperAgent 辅助内容,不能替代报警、人员撤离、现场核验或现场指挥。
6. Origin、Nginx 与安全边界
- 页面与 API 均从
https://agent.nianxx.com提供,因此浏览器的Origin必须命中 Go 的精确FIRE_SAFETY_CHAT_ALLOWED_ORIGINS项;不得用*代替。 - Nginx 只负责 TLS、精确路径、限流、SSE 传输和反代,不比较、注入、记录或保存
xtoken。 /chat和/chat/页面 GET 本身不依赖 Token;真正的兼容 completion 请求仍由 Go 校验xtoken。页面公开可访问不代表 API 无鉴权。- 页面不会把 Token 放入 Referer、查询参数、片段、日志或错误消息。输入框可被浏览器用户读取, 所以 Token 只能是受控测试凭证,不能作为最终用户身份。
- 页面表单显式使用 POST,CSP 使用
form-action 'none';JavaScript 不可用时页面显示警告, 不允许浏览器把 Token 或问题退化提交到 URL。 - 当前页面和兼容 API 的会话是单进程内存映射;Docker 重建、进程重启或多实例切换会使旧
session_id失效。 - 不通过页面对外暴露
/api/chat、数据库、迁移命令或其他 Go 路由;Nginx 未列出的路径仍返回 404。
7. 验收标准
配置与路由
- Given 未设置
FIRE_SAFETY_CHAT_PAGE_ENABLED,When 请求/chat,Then 返回 404;When 请求/chat/或两个资源路径,Then 均返回 404,且不创建 SuperAgent Run。 - Given
FIRE_SAFETY_CHAT_PAGE_ENABLED=true且依赖配置完整,When 请求/chat,Then 返回 308 到/chat/;When 请求/chat/、/chat/app.css和/chat/app.js,Then 分别返回 200 HTML、 CSS 和 JavaScript。 - Given 页面开启但
FIRE_SAFETY_CHAT_ALLOWED_ORIGINS不含https://agent.nianxx.com,Then 配置校验失败或浏览器 completion 请求被精确 Origin 拒绝;不得放行通配符。 - Given 页面关闭,When Nginx 仍保留
/chat、/chat/location,Then Go 返回 404,Nginx 不应 把它们改成 DashScope 或其他 upstream。
浏览器与兼容接口
- Given 页面打开且用户手动输入合法测试 Token,When 发送首轮问题,Then 请求体和 Header 与
第三方 completion 请求相同,并收到
event: result。 - Given 首轮最终事件为
finish_reason="stop",When 用户发送第二个问题,Then 请求使用同一页面 内存中的input.session_id,服务端复用会话。 - Given 用户刷新页面,When 再次发送问题,Then 页面不提交旧的
session_id或 Token。 - Given 错误或空 Token,When 发送问题,Then 服务返回 401;页面不展示或记录 Token。
- Given SSE 只有初始
null事件、发生断流或最终不是stop,Then 页面不显示为成功。
公网部署
- Given 服务器 Compose 已重建且 Nginx 已通过
nginx -t并 reload,Whencurl -i请求公网页面, Then 开关关闭时四个页面路径均返回 404;开启时/chat返回 308,/chat/返回 200text/html, 两个资源路径返回 200。 - Given 页面开启,When 浏览器从
https://agent.nianxx.com/chat/发起 completion,Then 服务端 日志可按现有脱敏规则确认 Chat 请求,且不含 Token、问题、答案或会话内部 ID。 - Given 已知公网 MCP 地点搜索曾记录
operation=fire_safety_search_place_candidates result=success,Then 该证据只证明地点搜索 已到达并成功;tools/list之外的完整多工具链仍需单独验收。
8. 运行与回滚
页面开关变更只修改服务器未提交的 .env,不修改 Nginx 中的 Token:
# 默认关闭
FIRE_SAFETY_CHAT_PAGE_ENABLED=false
# 测试开启时
FIRE_SAFETY_CHAT_PAGE_ENABLED=true
FIRE_SAFETY_CHAT_ALLOWED_ORIGINS=https://agent.nianxx.com
重新加载运行环境:
cd /home/firee-safety-ymd
docker compose config --quiet
docker compose up -d --force-recreate --no-build
docker compose ps
curl --fail http://127.0.0.1:16587/health
页面验收:
curl -i https://agent.nianxx.com/chat/
curl -i https://agent.nianxx.com/chat
curl -i https://agent.nianxx.com/chat/app.css
curl -i https://agent.nianxx.com/chat/app.js
关闭页面时预期 /chat、/chat/ 以及两个资源均直接为 HTTP 404;开启页面时
预期 /chat 为 HTTP 308、跟随后 /chat/ 为 HTTP 200 text/html,两个资源也为 HTTP 200。
使用 curl -iL https://agent.nianxx.com/chat 可直接跟随规范化重定向。浏览器
打开 https://agent.nianxx.com/chat/,手动输入受控测试 Token,发送不含敏感信息的问题,
再发送第二轮问题确认会话复用。
回滚优先使用配置回滚:把 FIRE_SAFETY_CHAT_PAGE_ENABLED 改回 false,执行
docker compose up -d --force-recreate --no-build,确认 /chat、/chat/ 和两个资源路径都直接
返回 404,之后按需继续保留
兼容 completion 或将其一并关闭。若是版本回滚,切回维护者指定的已验证 revision 后重建;若是
Nginx 配置回滚,恢复 root-only 备份,先执行 nginx -t 再 reload。不得通过恢复全路径 /、
关闭 Go 鉴权或把 Token 写进 Nginx 解决问题。