Files
fire-safety-ymd/docs/workflows/user-chat.md
2026-09-06 01:40:15 +08:00

128 lines
6.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 工作流
## 1. 首轮对话
客户端向 `POST /api/chat` 发送 `message`,不传 `conversation_id`。服务端创建 SuperAgent Session,随后通过 SSE 返回:
1. `conversation`:包含新生成的 `conversation_id` 和 `reused=false`。
2. 零个或多个 `progress`:只用于展示运行/工具进度。
3. `message`:严格完成后的最终回答。
4. `done`:包含同一 `conversation_id`、安全 Run ID 和 token usage。
客户端只有在收到 `done` 后才把本轮标记为成功,并保存 `conversation_id`。
## 2. 受控测试页面
当 `FIRE_SAFETY_CHAT_PAGE_ENABLED=true` 时,Go 服务提供 `GET /chat` 和 `GET /chat/`。页面只
用于测试,不是新的对话协议或生产用户入口;关闭开关时两个路径都返回 404。Nginx 可以保留这
两个精确反代路径,最终是否可用由 Go 开关决定。
页面与既有第三方客户端使用完全相同的兼容接口:
```text
页面 GET /chat/
-> 同源 POST /api/v1/apps/<FIRE_SAFETY_CHAT_COMPAT_APP_ID>/completion
-> xtoken + input.prompt + parameters
-> event: result SSE
```
页面打开后由用户手动输入受控测试 `xtoken`。页面不在 HTML、JavaScript 常量、Cookie、URL、
localStorage 或 sessionStorage 中嵌入/保存 Token,也不把 Token 发送到除同源 completion 之外
的地址;页面只在当前 JavaScript 内存中使用它。首轮返回的 `output.session_id` 也只保存在页面
内存中,并在同一页面的后续请求中作为 `input.session_id` 发送。刷新、关闭或新开页面都会丢弃
Token 和 Session。
浏览器页面的 Origin 是 `https://agent.nianxx.com`,所以服务器配置
`FIRE_SAFETY_CHAT_ALLOWED_ORIGINS` 必须包含该精确值,不得用 `*`。页面 GET 本身不要求 Token,
但 completion 仍由 Go 校验 `xtoken`;静态测试 Token 不是最终用户认证。
页面验收时打开 `https://agent.nianxx.com/chat/`,输入测试 Token 和不含敏感信息的问题。浏览器
Network 应显示同源 POST 到精确 app ID 的 completion 路径,Request Headers 含 `xtoken`,请求
体为兼容 JSON,响应为 `event: result` SSE。只有 `output.finish_reason=stop` 才算本轮成功;再
发送第二个问题时,应在第二次请求中看到上一轮返回的 `session_id` 被放入 `input.session_id`。不要把初始 `finish_reason=null` 或
断流内容当作最终答案。
页面客户端看到的成功契约不随上游 Trace 开关变化,始终以兼容流最后的
`output.finish_reason=stop` 为准。服务端到 SuperAgent 的严格判定由
`FIRE_SAFETY_SUPERAGENT_INCLUDE_TRACE` 控制:默认 `false` 时要求上游最终 AI 消息
`finish_reason=stop`、非空顶层 `message.final` 和顶层 `event: end`;设置为 `true` 时还要求
`run.completed(status=success)`。无 Trace 只是不返回工具/步骤轨迹,不代表 SuperAgent 不会
调用 MCP;要确认 MCP 是否实际执行,必须同时检查服务日志和工具结果。
## 3. 后续对话
后续请求同时发送 `message` 和前一轮保存的 `conversation_id`。成功流中的 `conversation` 事件返回 `reused=true`,说明复用了原 SuperAgent Session 上下文。
同一对话在收到 `done` 或终止 `error` 前不得再次提交。若服务端返回 `CHAT_CONVERSATION_BUSY`,客户端应保留当前流并稍后重试,不能自动改用同一个问题创建多轮并发 Run。
## 4. 失败处理
- HTTP JSON 错误:SSE 尚未开始;按 HTTP 状态和 `error.code` 处理。
- SSE `error`:流已经开始,但本轮没有可信最终回答;不得把此前进度当作答案。
- `CHAT_CONVERSATION_NOT_FOUND`:会话已过期、服务重启或请求落到其他实例;提示用户上下文已失效,并在用户确认后省略 `conversation_id` 创建新对话。
- 上游超时、协议错误、Run 失败或客户端中途断开:当前实现会使会话映射失效,以免继续复用可能仍有活动 Run 的 Provider Session。
- 网络断开且没有看到 `done`:结果未知;首版不自动重放原消息,避免重复 Run。
如果上游返回 `open_agent_trace_disabled` 或 HTTP 403,先检查是否将
`FIRE_SAFETY_SUPERAGENT_INCLUDE_TRACE` 设置为 `true`。当前应用策略关闭 Trace 时,使用
`include_trace=true` 会被拒绝;同一 Key 使用默认的 `include_trace=false` 可以返回 HTTP 200。
只有在外部应用策略明确启用 `trace_policy.enabled=true` 后,才应切换为 `true`。修改环境变量后
必须重建或重新创建运行容器,不能只依赖 `docker compose restart` 重新读取配置。
## 5. curl 联调
先把 `.env` 显式加载到当前 shell,再启动服务;Go 程序不会自动读取 `.env`:
```bash
set -a
source .env
set +a
go run ./cmd/server
```
另开终端发起首轮请求:
```bash
curl -N \
-H "Authorization: Bearer ${FIRE_SAFETY_CHAT_AUTH_TOKEN}" \
-H "Content-Type: application/json" \
-H "Accept: text/event-stream" \
--data '{"message":"观水镇附近有哪些水源候选?"}' \
http://127.0.0.1:8080/api/chat
```
从 `conversation` 或 `done` 事件复制 ID 后测试下一轮:
```bash
curl -N \
-H "Authorization: Bearer ${FIRE_SAFETY_CHAT_AUTH_TOKEN}" \
-H "Content-Type: application/json" \
-H "Accept: text/event-stream" \
--data '{"message":"再说明这些候选的限制","conversation_id":"conv_替换为上一轮返回值"}' \
http://127.0.0.1:8080/api/chat
```
浏览器前端还必须把其精确 Origin 加入 `FIRE_SAFETY_CHAT_ALLOWED_ORIGINS`,例如 `http://localhost:5173`。静态 Chat Bearer 会被浏览器用户看到,因此只适用于受控联调,不能直接作为公网最终用户鉴权。
## 6. 既有 DashScope 风格客户端
设置 `FIRE_SAFETY_CHAT_COMPAT_APP_ID` 后,同一个 Chat Service 还会注册:
```text
POST /api/v1/apps/{FIRE_SAFETY_CHAT_COMPAT_APP_ID}/completion
```
首轮请求使用 `xtoken: ${FIRE_SAFETY_CHAT_AUTH_TOKEN}`:
```bash
curl -N \
-H "xtoken: ${FIRE_SAFETY_CHAT_AUTH_TOKEN}" \
-H "Content-Type: application/json" \
--data '{"input":{"prompt":"杨家盘瞭望哨 3 公里内的水源?"},"parameters":{}}' \
http://127.0.0.1:8080/api/v1/apps/fire-safety-public-app/completion
```
兼容流先返回 `finish_reason: "null"` 和本地 `session_id`,严格成功后返回 `finish_reason: "stop"` 与最终 `text`。后续轮次把该 `session_id` 放入 `input.session_id`。客户端不得使用 URL 中的 App ID、静态 token 或 session ID推断身份与权限,也不得把未出现 `stop` 的断流结果当作成功答案。
公网 Nginx 示例和完整 curl 见 [`../project/operations/nginx-public-entry.md`](../project/operations/nginx-public-entry.md)。兼容字段的唯一项目契约见 [`../specs/fire-safety-ymd-dashscope-compatible-chat-v1.md`](../specs/fire-safety-ymd-dashscope-compatible-chat-v1.md)。页面开关、Docker 重建、浏览器验收和回滚见 [`../specs/fire-safety-ymd-chat-page-v1.md`](../specs/fire-safety-ymd-chat-page-v1.md)。