小睿AI导航

ARTICLE DETAIL

大模型API流式输出怎么接?SSE解析、界面更新与中断处理指南

本文以一个明确的SSE事件协议为例,展示浏览器如何读取后端转发的流式响应,处理跨网络分块的中文文本、结束信号和错误事件,并说明取消请求、连接断开、部分结果保留以及服务端密钥保护等实践要点。

📁 大模型与开发 浏览 1 2026-10-12 作者 小睿AI
📝

文章正文

样式 7 内容详情排版

阅读提示当前文章有1089字,阅读完大概需要3分钟。

和一次性返回的区别,在于客户端是否能在完整答案生成前收到增量数据。一次性请求通常要等服务端完成后才得到完整JSON;流式请求则会连续发送多个事件,界面可以先显示已生成的内容。需要区分两个时间:首段响应时间是从发起请求到收到第一段可显示内容的时间,总生成时间则是到结束事件到达的时间。流式传输不必然降低总耗时或费用,但能改善等待过程中的反馈。

大模型API流式输出怎么接?SSE解析、界面更新与中断处理指南

先约定一套明确的SSE协议

不同API的字段和事件名称可能不同。下面只演示一套由业务后端约定的协议,不把它当成所有厂商的通用格式。客户端请求后端的/api/answer,请求体包含message和stream:true。后端返回Content-Type: text/event-stream,每个事件之间用空行分隔,事件只包含一行data::

大模型API流式输出怎么接?SSE解析、界面更新与中断处理指南
  • 文本增量:{"type":"text_delta","text":"你好"}
  • 工具调用增量:{"type":"tool_delta","name":"search","arguments":"{..."},参数可能跨多个事件,需要继续拼接。
  • 正常结束:{"type":"done"}
  • 错误:{"type":"error","message":"..."}

真实项目应以所接入API的协议文档为准,不要把其他服务的字段直接套进来。

大模型API流式输出怎么接?SSE解析、界面更新与中断处理指南

浏览器读取并逐步更新界面

应只放在服务端,由后端调用模型服务并转发结果。浏览器只访问自己的业务接口。下面的代码使用Fetch读取响应体,并用TextDecoder处理可能被拆开的UTF-8中文字符:

大模型API流式输出怎么接?SSE解析、界面更新与中断处理指南
const controller = new AbortController();
const output = document.querySelector('#output');
let buffer = '';
let;

async function start(message) {
  const res = await fetch('/api/answer', {
    method: 'POST',
    headers: {'Content-Type': 'application/json'},
    body: JSON.stringify({message, stream: true}),
    signal: controller.signal
  });
  if (!res.ok || !res.body) throw new Error('流式请求失败');

  const reader = res.body.getReader();
  const decoder = new TextDecoder();
  while (true) {
    const {value, done} = await reader.read();
    buffer += decoder.decode(value || new Uint8Array(), {stream: !done});
    const events = buffer.split('\n\n');
    buffer = events.pop() || '';
    for (const event of events) {
      const line = event.split('\n').find(x => x.startsWith('data:'));
      if (!line) continue;
      const item = JSON.parse(line.slice(5).trim());
      if (item.type === 'text_delta') {
        text += item.text;
        output.textContent = text;
      } else if (item.type === 'error') {
        throw new Error(item.message);
      } else if (item.type === 'done') {
        output.dataset.complete = 'true';
      }
    }
    if (done) break;
  }
}

这里的关键不是把每次read()当作一个完整事件。网络层可能把一个事件拆成多个数据块,也可能一次读到多个事件,所以必须保留未完成的buffer,按协议分隔符拆分。TextDecoder的stream:true也不能省略,否则多字节中文恰好跨块时可能出现乱码。

大模型API流式输出怎么接?SSE解析、界面更新与中断处理指南

区分文本、工具调用、结束和错误

文本增量可以直接追加到回答区域;工具调用增量则应单独缓存,等参数完整后再校验JSON并执行,不能把工具参数当作用户可见文本。收到done后再把回答标记为完成。错误事件需要展示可理解的提示,同时保留已经收到的文本,便于用户复制或重试。若协议允许注释行或多行data,解析器还应按该协议合并多行字段,而不是只取第一行。

大模型API流式输出怎么接?SSE解析、界面更新与中断处理指南

停止、断开与重试

用户点击“停止”时调用controller.abort(),前端应把当前结果标记为“未完成”,而不是伪装成正常结束。服务端也要监听客户端断开,尽早取消对上游模型的读取,避免无意义地继续生成。网络断开时同样保留已显示内容,但不要默认重试能够接续原输出:除非服务端协议提供可恢复的请求ID、游标或明确的续传机制,重试通常只是一次新的生成。

大模型API流式输出怎么接?SSE解析、界面更新与中断处理指南

让更新稳定而不是频繁闪烁

示例为便于理解在每个文本事件后刷新界面,生产环境可以把增量先放入队列,每隔约几十毫秒批量更新一次,减少布局和渲染开销。服务端还应设置合理的读取超时、错误日志和请求取消逻辑,并严格校验事件JSON。这样既能让用户尽早看到首段响应,也能在异常发生时准确区分“未开始”“部分完成”和“正常完成”。

大模型API流式输出怎么接?SSE解析、界面更新与中断处理指南