标准答案

  1. 使用 TextDecoder 流式解码并维护未完成缓冲区,按 SSE 或自定义协议的事件边界拆分;一次网络 chunk 可能包含多个事件,也可能只包含半个多字节字符。
  2. 事件至少区分 message_start、delta、tool、error 和 done,并用 requestId、messageId 和 sequence 保证只处理当前响应且不重复。
  3. 收到 done 后以服务端最终状态收口;网络断开、解析失败或 error 事件应保留已生成内容并显示可重试状态,不能假装完整成功。

题目解析

SSE 的 data 字段也可能分多行,JSON 可能跨 chunk。解析器应按照协议实现,不要用每次 fetch reader.read() 的结果直接 JSON.parse。

增量文本可能包含 Markdown 结构的一半,渲染器要允许临时不完整状态,结束后再做最终解析或安全过滤。

服务端应提供最终 usage、finish reason 和错误信息,客户端只靠“连接关闭”无法区分正常结束与异常断开。

代码示例

下面只展示读取边界的核心思路,生产实现还要包含协议解析、取消和重连。

TypeScript
const reader = response.body!.getReader()
const decoder = new TextDecoder()
let buffer = ''

while (true) {
  const { value, done } = await reader.read()
  if (done) break
  buffer += decoder.decode(value, { stream: true })
  const events = takeCompleteEvents(buffer)
  buffer = events.rest
  for (const event of events.items) handleEvent(event)
}

常见误区

  • 把一次 read() 当成一个完整事件。
  • 只监听连接 close,不区分 done 和异常断开。
  • 收到旧请求的分片仍追加到当前消息。

作者信息