# Agent 流式协议前后端对齐 决策记录

## 1. 基本信息
- 记录编号：FDR-009
- 功能名称：Agent Chat SSE 协议前后端对齐（去伪思考化）
- 记录日期：2026-04-18
- 决策状态：提议（评审后执行）
- 负责人：SmartFlow 团队
- 关联需求 / Issue：
  - 工具调用信息前端可视化（折叠式、通俗文案）
  - 降低“深度思考”误导（当前为伪装块）

## 2. 背景与问题
- 业务背景：
  - 目前聊天页已经在做流式展示、确认卡片、会话管理；
  - 计划将“工具调用过程”以专属样式内联展示。
- 现状问题：
  1. 后端阶段状态/工具状态，会通过 `reasoning_content` 回传给前端，形成“看起来像深度思考”的内容；
  2. 前端目前只消费 `extra.kind=confirm_request`，其他 `extra` 结构化事件并未真正用于渲染；
  3. 用户感知层面会误以为模型正在“深度思考”，但其中大量内容其实是流程状态（非真实思考）。
- 不做此决策的后果：
  1. 前端即使先移除“深度思考框”，也会连同状态可见性一起丢失；
  2. 前后端协议继续漂移，工具可视化落地会重复返工；
  3. 后续“真流式 speak”与“工具事件流”会相互干扰，排查困难。

## 3. 决策目标
- 目标 1：统一 SSE 协议语义边界：`reasoning_content` 仅承载真实思考文本，不再承载阶段/工具状态文案。
- 目标 2：以 `extra.kind` 作为结构化事件主通道，前端据此渲染工具/状态专属 UI。
- 目标 3：明确迁移顺序：先后端协议就位，再前端切换展示，最后清理兼容层。
- 非目标（本次不解决）：
  - 不在本轮实现“每一次 speak 都改成 chat 节点流式消息头”；
  - 不在本轮重构全部历史会话数据存储格式。

## 4. 备选方案
### 方案 A：先改前端，先隐藏深度思考区
- 描述：前端先把思考区关闭或默认不展示，后端暂不改。
- 优点：
  - 见效快，UI 即刻变“干净”。
- 缺点：
  - 只是遮罩，不是协议治理；
  - 状态可见性与思考内容耦合，后续仍要返工。
- 复杂度 / 成本：低（短期）/ 高（长期返工）

### 方案 B：先改后端协议，再改前端渲染（采纳）
- 描述：后端先把状态/工具事件改为结构化主通道，前端再切换消费逻辑与样式。
- 优点：
  - 语义边界清晰，长期维护成本低；
  - 前端可直接做“折叠式工具行”，不再依赖伪思考文本；
  - 可通过双写/开关平滑迁移，风险可控。
- 缺点：
  - 首轮需要前后端并行协作与联调。
- 复杂度 / 成本：中

### 方案 C：前后端同一轮同时硬切
- 描述：单次发布同时切后端协议和前端展示，不保留兼容层。
- 优点：
  - 路径最短，代码最“干净”。
- 缺点：
  - 回归风险高，灰度与回滚空间小；
  - 一旦线上混部或缓存命中旧逻辑，容易出现空白块/重复块。
- 复杂度 / 成本：中（开发）/ 高（上线风险）

## 5. 最终决策
- 采纳方案：方案 B（先后端协议，再前端渲染，最后清理兼容层）。
- 关键理由：
  1. 先解决协议语义，再做视觉层改造，避免 UI 层“治标不治本”；
  2. 与“工具调用可视化”目标一致，能直接对接折叠式工具行；
  3. 具备可灰度、可回滚的工程路径。

## 6. 影响范围
- 涉及模块：
  - 后端：`backend/newAgent/stream`、`backend/newAgent/node`
  - 前端：`frontend/src/components/dashboard/AssistantPanel.vue`
- 数据与存储影响：
  - 无数据库结构变更；
  - 仅影响实时 SSE 事件解释方式。
- 接口 / 协议影响：
  - `POST /api/v1/agent/chat` 的 SSE chunk 语义调整（见第 7/8 节）。
- 监控与日志影响：
  - 需新增“事件类型计数”与“前端解析命中率”观测。

## 7. 前后端接口现状（AS-IS）
### 7.1 SSE 外层协议（当前）
- 后端已定义 OpenAI 兼容壳 + `extra` 扩展，代码位置：
  - `backend/newAgent/stream/openai.go`
  - `backend/newAgent/stream/emitter.go`
- `extra.kind` 已有枚举：`status`、`tool_call`、`tool_result`、`confirm_request` 等。

### 7.2 当前关键现象
1. 后端 `EmitStatus` 会把阶段文案同时写入：
   - `extra.kind=status`
   - `choices[0].delta.reasoning_content`（降级文本）
2. 执行节点多数工具过程仍通过 `EmitStatus(code=tool_call/tool_blocked)` 推送；
3. 前端 `processSseBlock` 当前只显式处理：
   - `parsed.extra?.kind === 'confirm_request'`
4. 结果：大量状态文案作为“reasoning”显示，形成伪“深度思考”体验。

### 7.3 当前事件样例（简化）
```json
{
  "choices": [
    {
      "delta": {
        "reasoning_content": "阶段：execute\n正在调用工具：queue_status"
      }
    }
  ],
  "extra": {
    "kind": "status",
    "status": {
      "code": "tool_call",
      "summary": "正在调用工具：queue_status"
    }
  }
}
```

## 8. 目标协议（TO-BE）
### 8.1 总体原则
1. `reasoning_content`：只承载真实模型思考文本；
2. `extra.kind`：承载流程状态和工具事件（前端主消费通道）；
3. 前端渲染：默认不把状态事件塞入“深度思考区”，而是进入工具/状态专属样式。

### 8.2 目标事件约定
| 事件类型 | `extra.kind` | 前端默认表现 | 是否进入 reasoning 区 |
|---|---|---|---|
| 阶段状态 | `status` | 轻量状态行 / 提示条 | 否 |
| 工具调用开始 | `tool_call` | 折叠工具行（默认摘要） | 否 |
| 工具调用结果 | `tool_result` | 更新同一工具行状态与详情 | 否 |
| 待确认 | `confirm_request` | 确认卡片 | 否 |
| 真思考 | `reasoning_text` | 思考区（可折叠） | 是 |
| 正文输出 | `assistant_text` | 正文区 | 否 |

### 8.3 目标工具事件样例（简化）
```json
{
  "choices": [],
  "extra": {
    "kind": "tool_call",
    "stage": "execute",
    "tool": {
      "name": "queue_status",
      "status": "start",
      "summary": "已调用工具：查看任务队列",
      "arguments_preview": "默认参数"
    }
  }
}
```

```json
{
  "choices": [],
  "extra": {
    "kind": "tool_result",
    "stage": "execute",
    "tool": {
      "name": "queue_status",
      "status": "done",
      "summary": "待处理 3 项，已完成 1 项"
    }
  }
}
```

## 9. 实施计划（先后端、再前端）
### 里程碑 1：后端协议对齐（先做）
- 目标：状态/工具事件走结构化主通道，不再依赖伪 reasoning 文本。
- 开工清单（后端）：
  1. `execute` 工具链路改用 `EmitToolCallStart/EmitToolCallResult`；
  2. `EmitStatus` 增加策略开关：支持“仅 extra，不回写 reasoning_content”模式；
  3. `tool_blocked` 统一归类到工具事件（或结构化 status），避免文本拼接歧义；
  4. 输出契约补充到 `backend/newAgent/ARCHITECTURE.md`。

### 里程碑 2：前端事件消费切换
- 目标：前端按 `extra.kind` 渲染，不再把流程状态当“深度思考”。
- 开工清单（前端）：
  1. `processSseBlock` 增加 `status/tool_call/tool_result` 解析；
  2. 新增“折叠式工具行”状态机（默认摘要、展开详情）；
  3. 深度思考区默认只接收真 `reasoning_text`；
  4. 确认卡片逻辑保持兼容。

### 里程碑 3：兼容层收敛
- 目标：确认稳定后去除伪思考降级写法。
- 开工清单：
  1. 关闭后端状态写入 `reasoning_content`；
  2. 清理前端旧降级路径；
  3. 补齐回归用例与文档。

## 10. 风险与应对
- 风险 1：前端未及时消费 `extra.kind`，导致状态缺失。
  - 应对策略：后端先双写一段窗口期（可配置开关），灰度切换。
- 风险 2：工具事件顺序乱序导致 UI 折叠状态错位。
  - 应对策略：使用 `block_id + tool.name + stage` 做关联键，按到达顺序幂等更新。
- 风险 3：历史会话与新会话混合展示不一致。
  - 应对策略：仅对新流式消息生效，历史消息维持只读展示。

## 11. 验证与回滚
- 验证方式：
  1. 后端单测：事件序列与 payload 结构校验；
  2. 前端联调：`tool_call -> tool_result -> summary` 顺序回放；
  3. 手工场景：普通问答 / 工具调用 / confirm / tool_blocked。
- 成功判定标准：
  1. 状态类文本不再出现在“深度思考区”；
  2. 工具事件能稳定渲染为折叠行；
  3. confirm 卡片行为不回归。
- 回滚方案：
  - 后端切回“状态写 reasoning_content + 旧前端渲染”兼容模式；
  - 前端保留旧路径开关，必要时回退版本。

## 12. 后续计划
- 后续优化项 1：把 speak 也统一到更细粒度真流式事件头（下一轮决策）。
- 后续优化项 2：工具详情文案模板化（通俗中文 + 可本地化）。
- 后续优化项 3：工具事件接入埋点（点击展开率、阅读停留、错误率）。

## 13. 复盘结论（上线后补充）
- 实际效果：待补充
- 与预期偏差：待补充
- 后续是否需要二次决策：待补充
