OrcaRouter

SSE API stream 消息格式怎么读与解析指南

更新于 2026-11-04 · orcarouter.cyou

简答:SSE(Server-Sent Events)流式消息以 <code>text/event-stream</code> 格式传输,每条消息由一个或多个键值对组成,并以双换行符(<code>\n\n</code>)作为单条事件的边界。读取时需注意网络传输的 HTTP 块(Chunk)不等于完整事件,必须通过缓冲区按行或按双换行符切分,提取 <code>data:</code> 后面的 JSON 字符串进行解析。

定义:SSE API stream 消息格式是一种基于 HTTP 的单向流式通信规范,通过文本事件流将 AI 模型的增量输出实时传输给客户端。

一、适用范围与核心结论

本文适用于所有采用 OpenAI 兼容接口、Anthropic Claude API 或自定义大模型 Agent 服务的开发者,在调试流式输出(stream: true)遇到消息粘连、解析中断或乱码时参考。

二、SSE 消息的完整结构与字段定义

一条标准的 SSE 响应由多行文本组成,每行以可选的字段名开头,后跟冒号和具体数值。字段定义需以各大模型供应商的官方文档为准。

字段名含义与用法
event事件类型(如 message、error 或自定义事件),未指定时默认为 message。
data消息负载。大模型接口的增量文本或 JSON 对象均在此字段,可多行出现。
id事件唯一标识符,可用于设置 Last-Event-ID。
retry客户端重连的时间间隔(毫秒)。
: (冒号开头)注释行或心跳保持行(Keep-alive),客户端应直接忽略。

以下是一个真实的 AI API SSE 响应片段示例:

event: message
data: {"id":"chatcmpl-123","choices":[{"delta":{"content":"你好"}}]}

data: {"id":"chatcmpl-123","choices":[{"delta":{"content":"世界"}}]}

data: [DONE]

三、为什么不能直接按行读?理解事件边界

初学者在处理流式 API 时最容易犯的错误是:直接按单行(\n)切割读取。由于底层 TCP/HTTP 分块传输的不确定性,一个 data: 的 JSON 可能会被拆在两个不同的 Chunk 中传输。

正确的分界逻辑:

  1. 将接收到的底层文本流追加到本地字符串缓冲区。
  2. 在缓冲区中搜索双换行符(\n\n 或 \r\n\r\n)。
  3. 一旦找到,截取出完整的一个事件块进行解析。
  4. 将已解析的部分从缓冲区移除,剩余部分留作下一次拼接。

四、JavaScript 手动解析 SSE 流示例

由于大模型流式请求通常使用 POST 方法并需要携带 Authorization 请求头,浏览器原生的 EventSource(仅支持 GET)往往无法直接满足需求。推荐使用 fetch 配合 ReadableStream 进行手动解析:

async function readSSEStream(response) {
  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 parts = buffer.split(/\r?\n\r?\n/);
    buffer = parts.pop(); // 留下最后不完整的一块

    for (const part of parts) {
      const lines = part.split(/\r?\n/);
      let dataStr = '';
      
      for (const line of lines) {
        if (line.startsWith('data:')) {
          dataStr += line.slice(5).trim();
        }
      }

      if (dataStr) {
        if (dataStr === '[DONE]') {
          console.log('流式传输结束');
          return;
        }
        try {
          const json = JSON.parse(dataStr);
          console.log('收到增量内容:', json.choices?.[0]?.delta?.content || '');
        } catch (e) {
          console.error('JSON 解析失败:', dataStr);
        }
      }
    }
  }
}

五、常见错误与排查清单

在对接或读取 SSE 消息格式时,如遇到异常可对照以下清单排查:

六、高级治理与统一网关建议

在生产环境中,直接处理不同大模型供应商(如 OpenAI、Anthropic、DeepSeek 等)略有差异的 SSE 事件格式和错误码会增加维护成本。通过引入标准化的 AI 网关,可以将多源模型的流式事件进行归一化处理,并提供统一的用量监控与限流保护。若您的项目正需要高效的 API 路由与网关支持,可以了解下犀速-AI的接入方案。

常见问题

SSE 流式响应必须以 [DONE] 结尾吗?

不一定。OpenAI 兼容接口广泛采用 <code>data: [DONE]</code> 作为结束标志,但部分其他厂商的模型 API 使用自定义的 <code>event</code>(如 <code>done</code> 或 <code>stop</code>)来标识结束。编写解析代码时应同时兼容 <code>[DONE]</code> 标记与连接正常关闭(<code>done: true</code>)。

为什么 EventSource 无法直接用来调用大模型 API?

浏览器自带的 <code>EventSource</code> 对象只支持发送 <code>GET</code> 请求,且无法在请求中携带自定义的 <code>Authorization</code>(API Key)请求头。因此调用 AI API 时,必须改用 <code>fetch</code> 配合 <code>ReadableStream</code> 自定义请求方法与头部。

如何避免代理服务器(如 Nginx)缓冲导致 SSE 变成一次性返回?

如果你的服务通过 Nginx 或其他反向代理转发,代理可能会等待整个响应生成完毕才一起下发。需在服务端和网关配置中关闭缓存(如设置响应头 <code>X-Accel-Buffering: no</code> 和 <code>Cache-Control: no-cache</code>)。

相关阅读

参考资料

  1. Using server-sent events - Web APIs | MDN
  2. Understanding the Event Stream Format — Server-Sent Events(server-sent-events.com)