← 返回 V-API 是什么?功能、接入步骤与使用注意事项 首页

OpenAI API 流式输出中断怎么办?SSE 连接与重试排查

更新于 2026-10-31 · gpt.boats

简答:OpenAI API 流式输出中断,先不要直接重试:确认请求是否收到正常结束信号,再用同一 Base URL、API Key 和模型分别测试非流式请求与 curl SSE 请求。Chat Completions 重点检查 finish_reason 和 data: [DONE];Responses API 重点检查 response.completed、response.failed 或 response.incomplete。若没有结束事件就断开,再按客户端解析、代理超时、网关返回和上游状态逐层定位。

定义:SSE 流式输出是服务器通过一个持续的 HTTP 连接,以事件或数据块逐步发送模型结果,而不是等待完整结果后一次性返回。

先试这一步:用最小请求判断断在哪一层

本文面向已经在使用 stream=true 的开发者。本站是第三方 AI API 接入指南站,不代表任何 API 平台官方。核验时间为 2026-10-31。

先固定四个变量:实际请求地址、API Key、模型 ID、请求体。不要一边排查一边更换模型、SDK、网关和提示词,否则无法判断是哪一层发生变化。

  1. 先把流式参数关闭,发送一次非流式请求。如果非流式也失败,优先检查 Key、Base URL、模型、权限、额度和网关响应。
  2. 非流式成功后,用 curl 直接测试同一个端点,绕过业务代码和前端。
  3. 记录 HTTP 状态、Content-Type、首字节时间、最后一个完整事件、断开时间、请求 ID 或响应 ID。
  4. 只有在确认请求尚未完成、且操作没有外部副作用时,才执行有限次数重试。

Chat Completions 的流式响应是数据型 SSE,客户端应按事件边界读取,而不是把每个 TCP 或网络 chunk 直接当成一个完整 JSON。官方示例显示,正常结束通常包含最后一个带 finish_reason 的数据块以及 data: [DONE]。可参考 OpenAI 流式响应指南 和 Chat Completions API 参考。

curl 基线示例:

curl --no-buffer -i https://api.openai.com/v1/chat/completions -H "Authorization: Bearer $OPENAI_API_KEY" -H "Content-Type: application/json" -d '{"model":"YOUR_MODEL","messages":[{"role":"user","content":"只回复:测试成功"}],"stream":true}'

如果 curl 能持续收到事件而应用代码中断,问题更可能在 SSE 解析、读取超时、线程取消或前端转发;如果 curl 也中断,再检查网关、代理链路和上游响应。

三种结束状态必须分开判断

观察到的现象应如何判断下一步
收到正常结束事件Chat Completions 出现 finish_reason,并收到 data: [DONE];Responses API 收到 response.completed不是断流。检查前端是否误把完成事件当成错误,或是否因内容拼接逻辑造成显示不完整。
收到错误事件Responses API 可能出现 response.failed 或 response.incomplete,其中包含状态或错误信息先保存响应 ID、错误码和已完成内容,再按错误类型决定是否修复参数、等待后重试或查询最终状态。
没有结束事件,连接直接关闭已收到部分文本,但没有结束标志,可能是客户端、代理、网关或上游连接被关闭保留已显示文本并标记“未完成”,检查超时、缓冲、连接重置和 SSE 解析;不要把半截结果当成完整回答。

Responses API 的事件参考明确列出了 response.completed、response.failed 和 response.incomplete。其中 response.completed 表示响应完成,response.failed 表示响应失败。相关字段应以 Responses 流式事件参考 为准。

如果是 Chat Completions,还要区分 finish_reason:stop 表示自然停止,length 表示达到输出长度限制,tool_calls 表示模型产生工具调用。它们都不是同一种“网络断流”,不能只看界面上是否停止输出。

按故障层级排查:客户端、代理还是上游

1. 客户端 SSE 解析错误

SSE 事件可能跨越多个底层网络读取块,一个 JSON 也可能被拆成多段。解析器应先按 SSE 事件分隔规则组装完整事件,再解析其中的 JSON。不要对每次 read() 返回的数据直接调用 JSON 解析。

排查时打印脱敏后的事件类型、事件长度、最后一条完整事件和解析异常位置。API Key、完整 Authorization 头、用户输入和工具参数中的隐私数据不要写入日志。

2. 首字节超时与流中空闲超时

首字节超时表示请求发出后迟迟没有收到第一段响应;流中空闲超时表示连接已经建立,但连续一段时间没有新事件。两者应分开配置和记录。长上下文、复杂工具调用或网关排队可能影响首字节时间,但不能仅凭一次超时断言是上游故障。

3. 反向代理缓冲

如果服务端已经生成内容,但浏览器长时间收不到,检查反向代理是否对响应进行了缓冲、压缩或聚合。对 SSE 应确认代理按流式方式转发,并检查读取超时、连接超时和上游响应超时。具体配置名称取决于代理产品,不能把某一个代理的配置直接套用到所有网关。

4. 502、连接重置与网关错误

如果 HTTP 响应尚未建立就收到 502,优先检查网关到上游的连接、DNS、TLS 和上游超时;如果 HTTP 200 已经开始输出后连接被关闭,则要结合最后事件、代理日志和 TCP 错误判断。状态码本身不能证明一定是模型服务问题。

5. 429 与重试放大

429 可能对应请求或 Token 速率限制,也可能是额度、组织使用上限或项目支出限制。官方建议先读取错误详情和 Retry-After;临时限流时使用带随机抖动的指数退避,并限制重试次数和总时长。额度或支出限制不能靠重复发送恢复,应先处理对应限制。详见 OpenAI API 429 排查说明。

Python 与 Node.js 的最小流式处理示例

Python:保留已输出内容,区分完成与异常

from openai import OpenAI client = OpenAI() text = [] completed = False try: stream = client.chat.completions.create( model="YOUR_MODEL", messages=[{"role": "user", "content": "只回复:测试成功"}], stream=True, ) for chunk in stream: choice = chunk.choices[0] piece = choice.delta.content or "" if piece: text.append(piece) print(piece, end="", flush=True) if choice.finish_reason is not None: completed = True print("\n完成状态:", completed) except Exception as exc: print("\n流式请求异常,已输出字符数:", len("".join(text))) raise

这个示例把“已经展示的文本”和“是否收到结束原因”分开保存。出现异常时,调用方应把结果标记为未完成,而不是自动拼接一句看似完整的结尾。

Node.js:记录最后事件与读取异常

import OpenAI from "openai"; const client = new OpenAI(); let output = ""; let finished = false; try { const stream = await client.chat.completions.create({ model: "YOUR_MODEL", messages: [{ role: "user", content: "只回复:测试成功" }], stream: true, }); for await (const chunk of stream) { const choice = chunk.choices?.[0]; const piece = choice?.delta?.content || ""; output += piece; process.stdout.write(piece); if (choice?.finish_reason) finished = true; } console.log("\n完成状态:", finished); } catch (error) { console.error("流式连接中断,已输出长度:", output.length); throw error; }

如果你使用的是 Responses API,应根据 SDK 返回的事件类型处理完成和失败事件,不要把 Chat Completions 的字段名直接套用到 Responses API。官方流式指南同时给出了两种 API 的流式调用方式。

安全重试:先确认状态,再决定是否重放

流式连接断开不等于服务端一定没有完成请求。官方错误恢复说明要求在断开后先检查已保存状态、已完成操作或会话最终状态,再决定重连或重试;对于可能调用外部工具、写入数据库、发送消息或产生其他副作用的请求,不能无条件从头重放。参考 OpenAI 错误与恢复指南。

  1. 先查结果:保存请求 ID、响应 ID、会话 ID、模型、最后事件和错误内容,优先查询可查询的最终状态。
  2. 区分可重试与不可重试:临时超时、过载、部分 429 或暂时性服务器错误,可以进入有限重试;认证、权限、模型不存在、参数无效和额度错误,应先修复原因。
  3. 遵守 Retry-After:有有效值时至少等待该时长;没有时使用指数退避加随机抖动。
  4. 设置边界:同时限制最大尝试次数、总重试时间和并发数量,避免多个组件各自重试造成请求风暴。
  5. 改善用户体验:保留已收到的文本,明确显示“输出未完成,请重试”,不要把中断结果伪装成正常答案。

一个通用的退避思路是:第 1 次失败等待较短时间,后续逐步增加等待,并叠加小幅随机值;实际延迟、次数和总截止时间应根据业务可接受时延、接口限制和是否存在副作用来设定,不能照搬固定数字。

一张自查清单:把日志补齐再判断

完成这份清单后,通常可以把问题收敛到三类:业务代码没有正确解析 SSE、链路中间层提前关闭连接,或上游确实返回了错误。若仍无法定位,应提交脱敏后的时间线、请求 ID、响应 ID、HTTP 状态和最后事件,而不是只提供“页面卡住了”。

如果你正在为多个 AI API 配置统一的 Base URL、模型和故障切换,可以在完成上述最小测试后,再考虑接入自己的路由层或兼容接口;所有端点、模型和认证方式仍应以对应服务的当前文档为准。

常见问题

OpenAI API 流式输出只返回半句话,是模型生成不完整吗?

不一定。先确认是否收到正常结束事件。如果没有 finish_reason、data: [DONE] 或 Responses API 的 response.completed,就只能判定为未确认完成;可能是客户端解析、代理超时、网关断开或上游错误。应保留已输出文本并继续查日志。

为什么非流式请求成功,stream=true 却超时?

流式请求会保持连接并持续传输事件,因此会额外受到 SSE 解析、代理缓冲、连接空闲超时和客户端读取逻辑影响。先用 curl 绕过业务代码测试,再检查中间层是否按流式方式转发,并分别记录首字节时间和流中空闲时间。

流式连接断开后能不能直接重新发送同一个请求?

不能默认直接重发。断开不代表服务端没有完成请求;如果请求触发工具调用、写入或其他副作用,重放可能造成重复操作。先查询可用的响应或会话状态,再决定是否只继续未完成部分,且设置最大尝试次数和总截止时间。

Chat Completions 和 Responses API 的流式结束标志一样吗?

不一样。Chat Completions 通常通过数据块中的 finish_reason 和 data: [DONE] 判断结束;Responses API 使用事件类型,例如 response.completed、response.failed 或 response.incomplete。应根据实际接口和官方参考处理,不能混用字段。

相关阅读

参考资料

  1. Streaming API responses | OpenAI API
  2. Create chat completion | OpenAI API Reference
  3. Responses streaming events | OpenAI API Reference
  4. Errors and recovery | OpenAI API
  5. Troubleshooting API rate limits and 429 errors | OpenAI Help Center