SSE 事件协议

对话页与编码会话都靠同一条 SSE 流把「模型在做什么」实时推给前端。协议本身不含业务语义:后端只负责把结构化事件按帧写出,前端按 type 分派。

帧的三种形态

形态识别方式前端处理
结构化事件合法 JSON,且含 type映射成强类型对象后分派
纯文本片段不是 JSON作为 plainText 直接追加到正文(不丢内容)
错误帧以 [ERROR] 前缀开头渲染为错误状态
未知 type 不会被丢弃,而是归到 unknown 并保留原始 JSON——这样后端先上新事件、前端后适配也不会丢帧。

事件清单

事件关键字段用途
contenttext正文增量
thinkingtext可折叠的思考轨迹
tool_callid / name / arguments / hidden开始调用工具
tool_resultid / name / content / isError / collapsed / hidden工具输出(可折叠、可隐藏)
approval_requestid / name / arguments工具待批准(交互卡片)
questiontoolCallId / data向用户追问信息
todo / plandata / toolCallId / data计划与待办清单
usagepromptTokens / completionTokens / cachedTokens / totalTokens / latencyMs用量徽标
file_changepath / action本会话文件改动
checkpointid / label / fileCount检查点记录
subagentid / description / stage / tool / steps / message子代理进度(编码会话会额外落库以便回放)
search_results / knowledge_results / memory_resultsresults[]联网 / 知识库 / 记忆命中列表
noticekind / text系统提示(如上下文已压缩、工作树创建失败)
steeringtext运行中引导被接受
debugtext调试输出
done—本轮流结束(由调用方补齐)

工作流的独立事件

工作流运行走自己的事件名(同一套 SSE 传输,但语义不同):run_started / node_started / node_completed / node_failed / human_approval_required / agent_tool_call / run_completed / run_failed。

两条流的差异

维度对话页编码会话
错误帧直接渲染重新拼回 [ERROR] 前缀后交给统一解析器
subagent不特别处理额外落库,用于历史回放
检索类结果渲染命中列表当前未在时间线展示(解析器里是 no-op)
done由调用方补齐循环结束后显式补一帧

实现注意

  • 字段名大小写必须稳定:前端映射依赖后端原样输出的 JSON 键。
  • approval_request 与 question 都有 id,但语义不同(前者批工具,后者问人),不能混用。
  • usage 缺少字段时前端按 0 兜底;编码会话的 latencyMs 由前端自行计算/补零。
  • plainText 兜底是刻意设计:即使后端只写裸文本(例如某些代理/中间层),正文也不会丢。
  • 新增事件类型的做法:后端加 type 与字段 → 前端解析器加映射 → 各自 reducer 决定渲染,未适配期间事件不会丢。