流式输出怎么接:从第一段文字到完整回答
理解流式响应与普通 JSON 的差别,掌握 SSE 分帧、增量拼接、取消请求与异常收尾,让聊天界面的等待体验更自然。

用户发出问题后,等待完整答案才显示内容,往往会显得迟钝。流式输出允许应用在生成过程中逐步展示文本。它能改善感知等待时间,但不保证总生成时间一定更短。
先确认模型支持流式请求#
对于支持聊天补全协议的模型,通常可以通过 stream: true 请求流式响应。不同协议的事件结构可能不同,接入前应查看当前模型文档,并用最小请求确认实际返回的 Content-Type 与数据格式。
不要把普通响应的处理方式直接搬过来:流式响应不是一个等到结束后再调用 JSON 解析的完整对象,而是一系列按协议发送的事件。
网络数据块不等于完整事件#
一次网络读取可能只得到半个事件,也可能包含多个事件。UTF-8 字符也可能跨数据块,因此需要持续使用同一个流式解码器,保留尚未解析完的文本。
一个稳妥的处理顺序是:
- 将收到的字节交给流式文本解码器。
- 把解码结果追加到缓冲区。
- 按 SSE 的空行边界提取完整事件,兼容协议规定的换行形式。
- 合并事件中的
data:行,忽略注释或心跳。 - 根据当前协议解析事件,将不完整的尾部留到下次读取。
不要直接对每次读取结果执行 JSON 解析,也不要假设每个数据块只有一条 data:。生产应用优先选用维护良好的 SSE 解析器。
拼接文本,也要识别结束信号#
在聊天补全兼容协议中,文本增量通常位于 choices[].delta.content。有些事件只提供角色、结束原因或其他信息,文本字段为空并不一定是错误。
以下是协议示意,不代表所有模型都返回完全相同的字段:
data: {"choices":[{"delta":{"content":"你好"}}]}
data: {"choices":[{"delta":{"content":",很高兴见到你。"}}]}
data: [DONE][DONE] 是部分兼容协议使用的结束标记,不能推广到所有接口。工具调用的参数也可能分片返回,应按协议合并完整后再校验,不能看到一段参数就立即执行。
界面至少需要四种状态#
| 状态 | 界面建议 |
|---|---|
| 等待首段 | 显示正在连接或等待响应,保留取消入口 |
| 正在输出 | 追加文本,避免每个字符都触发昂贵渲染 |
| 正常结束 | 结束加载,启用复制等后续操作 |
| 中断或失败 | 保留已经收到的内容,并明确标记未完成 |
收到第一段文本之前,应检查 HTTP 状态。开始传输后,也可能出现错误事件或断连;不能仅凭最初的 200 状态就把任务标成成功。
处理取消与中途断线#
用户切换会话、关闭页面或点击停止时,客户端应停止读取并取消对应请求。若中间经过自有服务端,还应把取消信号传递给上游处理逻辑,避免无意义的持续工作。
已经展示部分内容后,不要静默重发并把新答案接在旧答案后面。更清晰的做法是标记本次输出中断,让用户选择重新生成,并将新尝试作为独立回答展示。
最后检查反向代理是否缓冲响应、连接是否有合理的空闲超时,以及 Markdown 渲染是否会因未闭合代码块反复跳动。相关排错步骤见API 报错排查指南。

