SSE 流式输出原理

Server-Sent Events 的工作原理、格式,以及与 AI 流式输出的关系

生产方式:人写签发:晓黎学习复核:2026年9月19日被 4 个词条引用

什么是 SSE?

SSE(Server-Sent Events)是一种服务器向客户端推送数据的技术。

核心特点:服务器可以主动把数据「推」给客户端,不需要客户端反复问「有新消息吗?」。

为什么 AI API 要用 SSE?

AI 生成回复时,是一字一字「想」出来的。如果等全部想完再一次性返回,用户可能要等好几秒。

用 SSE 可以做流式输出:AI 每生成一个字,就立刻推给客户端,用户立刻就能看到。

服务器:你 ← 好 ← 世 ← 界
        (每生成一个字就推一次)

对比:

  • 不流式:等 AI 全部想完 → 一次性返回「你好世界」(等待时间长)
  • 流式:AI 每想一个字就推送 → 用户立刻看到「你」「你好」「你好世」「你好世界」(体验好)

SSE 的格式

SSE 基于 HTTP,服务器返回的是纯文本流,每一条消息以 data: 开头。

data: 你好

data: 世界

data: [DONE]

规则:

  • 每条消息以 data: 开头,后面跟一个空格
  • 消息之间用两个换行符(\n\n)分隔
  • 最后一条通常是 data: [DONE],表示「传输结束」

AI API 中的两种 SSE 封装

不同的 AI 服务商对 SSE 的封装方式不同:

OpenAI 方式:纯 data: 行

每条消息只有一个 data: 前缀,后面直接是 JSON 数据。

data: {"choices": [{"delta": {"content": "你"}}]}

data: {"choices": [{"delta": {"content": "好"}}]}

data: {"choices": [{"delta": {"content": "世"}}]}

data: {"choices": [{"delta": {"content": "界"}}]}

data: [DONE]

特点:简单直接,只有一种事件类型。

Anthropic 方式:命名事件

每条消息分两行:event:(事件类型)+ data:(数据内容)。

event: message_start
data: {"type": "message", "role": "assistant", ...}

event: content_block_start
data: {"type": "text", "text": ""}

event: content_block_delta
data: {"type": "content_block_delta", "delta": {"text": "你"}}

event: content_block_delta
data: {"type": "content_block_delta", "delta": {"text": "好"}}

event: message_stop
data: {"type": "message_stop"}

特点:事件类型丰富,能精确控制每个阶段的处理逻辑。

Anthropic 事件类型速查

事件类型含义类比
message_start开始生成一条新消息📋 打开一张新纸
content_block_start开始生成一个内容块📝 准备写一段
content_block_delta内容增量(每次推送的文字)✍️ 每写一个字推一次
content_block_stop内容块生成完毕✅ 写完一段
message_delta消息级别的增量(如停止原因)📊 补充元数据
message_stop整条消息生成完毕🏁 写完

OpenAI 的增量结构

OpenAI 的 data: 行中,增量内容在 choices[0].delta 里:

{
  "choices": [
    {
      "delta": {
        "content": "你"    ← 这是本次推送的文字
      }
    }
  ]
}

通俗比喻

把 AI 流式输出想象成老师现场板书:

  • 不流式:老师先想好整句话,然后一次性写出来。你只能等。
  • 流式(SSE):老师想一个字写一个字,你同步看到。黑板就是「客户端」,老师的粉笔就是「数据流」。

流式解析的关键逻辑

OpenAI 解析逻辑

const reader = response.body.getReader();
const decoder = new TextDecoder();

while (true) {
  const { done, value } = await reader.read();
  if (done) break;

  const chunk = decoder.decode(value);
  const lines = chunk.split('\n');

  for (const line of lines) {
    if (line.startsWith('data: ')) {
      const data = line.slice(6);
      if (data === '[DONE]') break;

      const json = JSON.parse(data);
      const text = json.choices[0]?.delta?.content || '';
      // 把 text 追加到界面上
    }
  }
}

Anthropic 解析逻辑

const reader = response.body.getReader();
const decoder = new TextDecoder();

while (true) {
  const { done, value } = await reader.read();
  if (done) break;

  const chunk = decoder.decode(value);
  const lines = chunk.split('\n');

  for (const line of lines) {
    if (line.startsWith('event: ')) {
      const eventType = line.slice(7);
      // 记住当前事件类型
    } else if (line.startsWith('data: ')) {
      const data = JSON.parse(line.slice(6));

      switch (eventType) {
        case 'content_block_delta':
          // data.delta.text 是本次增量
          appendToUI(data.delta.text);
          break;
        case 'message_stop':
          // 消息结束,可以做收尾处理
          break;
      }
    }
  }
}

与 WebSocket 的区别

特性SSEWebSocket
通信方向服务器 → 客户端(单向)双向(客户端也能发)
协议基于 HTTP独立协议(ws://)
重连自动重连需要手动处理
适用场景AI 流式输出、实时通知聊天、游戏、协作编辑
复杂度低高

为什么 AI 用 SSE 而不是 WebSocket?

  • AI 回复主要是服务器推数据给客户端
  • 客户端很少需要往服务器发中间数据
  • SSE 更简单,HTTP 基础设施(代理、CDN、防火墙)天然支持

常见问题

问题说明
SSE 和 Webhook 的区别Webhook 是「服务器 A 通知服务器 B」,SSE 是「服务器通知浏览器」
SSE 能传二进制吗?可以,但 AI 文本场景不需要
断线了怎么办?SSE 有自动重连机制,客户端重新连接后请求最后一条消息的 ID
能同时开多个 SSE 连接吗?可以,但浏览器对同一域名有并发限制(通常 6 个)

实战技巧

  1. 解析时跳过空行:SSE 消息之间有空行,要先过滤
  2. 处理 [DONE]:收到这个标志就停止读取
  3. 错误处理:网络断开时 reader.read() 会抛错,需要 try-catch
  4. 取消请求:用户点击「停止生成」时,调用 reader.cancel()

返回 REST API 基础概念 了解 HTTP 请求/响应的整体流程 返回 JSON 数据结构基础 了解 SSE 传输的 JSON 数据如何解析

互链

链接来自概念卡正文里的双链,编译时能解到本期词条集的才成为站内链接。

反链(4)

出现在哪些课时里(0)

判据与「课时正文里能不能点出来」是同一个:那一节确实提到了它。点课时走当前页跳转, 它是导航,不是词条链接。

还没有课时讲到这一条。它是词条跑在课程前面的那一部分,不是错链。

关联图

只画这一条与它直接相连的词条,边与上面两区同源。可拖拽、可缩放,点节点是新标签。

出处

路径是本地知识库(Obsidian 库)里的位置,正文与概念卡逐字对应。

  • 概念卡原文SSE 流式输出原理03_RESOURCES/概念/sse-流式输出原理.md
  • 所属知识地图栏目概念页索引 · API 与后端基础03_RESOURCES/概念/Index.md

编译入库:2026年11月2日。词条由知识库编译而来, 修正要回到概念卡改,再重新编译。