OrcaRouter

OpenAI API stream=true 是什么意思?流式输出、代码与排错

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

简答:OpenAI API 的 stream=true 表示启用流式响应:模型生成内容时,服务器会通过 SSE 分块发送,客户端可以边接收边显示。它主要改善首个片段到达时间和交互体验,不等于模型生成更快,也不会因为这个参数本身减少 token 或费用。Responses API 通常读取带类型的事件,Chat Completions 则读取每个 chunk 的 delta,并结合 finish_reason 或 [DONE] 判断是否正常结束。

定义:stream=true 是 API 请求中的布尔参数,用于让模型响应以服务器发送事件(SSE)或增量数据分块返回,客户端需要循环读取并拼接内容。

先看结论:stream=true 改变的是返回方式

本文是本站对公开技术文档的整理,本站不是相关 API 品牌的官方网站;以下内容按 2026-11-04 检索到的资料核对。最短理解是:stream=true 不是让模型换一个生成模式,也不是保证生成更快,而是让已经生成的内容更早发送到客户端。

对比项stream=falsestream=true
返回方式通常等待完整结果后返回一个响应对象生成过程中持续返回事件或数据分块
客户端读取直接读取完整 JSON 中的结果字段循环读取 SSE、chunk 或 delta,再自行拼接
文本字段Chat Completions 常见为 message.contentChat Completions 常见为 delta.content;Responses API 常监听文本增量事件
用户体验实现简单,适合短请求和后台任务可边生成边显示,适合聊天窗口和长文本
首个片段时间通常要等完整响应准备好通常可以更早看到首个事件或文本片段
完整响应时间由模型、网络和服务端处理决定不会因参数本身保证更快完成,仍受模型、网络和中间层影响
费用与 token按实际请求和模型规则核算不能因为逐字显示就推断更省 token 或更便宜

OpenAI 的流式指南说明,默认请求通常在完整输出生成后一次性返回;设置 stream=true 后,客户端可以在模型继续生成时先处理开头内容,并通过服务器发送事件(SSE)接收数据。可查看 OpenAI Streaming API responses 指南。

可以先记住这个简化示意:非流式是“一次返回完整 JSON”;流式是“多次返回增量数据,最后以完成事件或结束标记收尾”。实际事件字段要按 Responses API、Chat Completions 或具体兼容接口分别核对。

最短主流程:启用、读取、判断完成

  1. 先用非流式请求确认基础配置。确认 API Key、Base URL、模型 ID、请求路径和普通响应都正常,再只增加 stream=true,不要同时修改多项配置。
  2. 按接口类型读取。Responses API 遍历类型化事件;Chat Completions 遍历 chunk,并从 choices[0].delta.content 读取增量文本。
  3. 持续拼接文本。每次收到非空片段就追加到本地缓冲区,同时可以立即刷新界面。
  4. 不要只看循环是否结束。Chat Completions 要读取最终 chunk 的 finish_reason;原始 SSE 还要确认收到 data: [DONE]。Responses API 则应关注 response.completed,并检查响应状态和错误字段。
  5. 区分正常完成和异常断流。如果连接断开、没有完成事件、没有 [DONE],或最后一个 chunk 的结束信息缺失,应把结果标记为未完成,而不是直接当作完整答案。

OpenAI 的 Responses 流式事件参考列出了 response.output_text.delta、response.output_text.done、response.completed 和错误事件等类型。Chat Completions 的事件参考则说明,流式 chunk 包含 delta、finish_reason 和可选的 usage 字段。分别参考 Responses streaming events 与 Chat Completions streaming events。

Responses API:Python 和 JavaScript 最小示例

Python:读取文本增量、完成事件和错误

下面的 MODEL_ID 只是占位符,不代表某个已确认的模型名称。运行前应通过实际平台的模型列表、权限说明和接口文档核对可用模型。

from openai import OpenAI client = OpenAI() stream = client.responses.create( model="MODEL_ID", input="请用一句话解释 SSE。", stream=True, ) parts = [] completed = False for event in stream: if event.type == "response.output_text.delta": parts.append(event.delta) print(event.delta, end="", flush=True) elif event.type == "response.completed": completed = True print("\\n完成") elif event.type == "error": print(f"\\n流式错误:{event}") answer = "".join(parts) if not completed: print("\\n结果未收到 response.completed,需按未完成处理")

JavaScript:用异步迭代读取事件

import OpenAI from "openai"; const client = new OpenAI(); const stream = await client.responses.create({ model: "MODEL_ID", input: "请用一句话解释 SSE。", stream: true, }); let fullText = ""; let completed = false; for await (const event of stream) { if (event.type === "response.output_text.delta") { fullText += event.delta; process.stdout.write(event.delta); } else if (event.type === "response.completed") { completed = true; process.stdout.write("\\n完成\\n"); } else if (event.type === "error") { console.error("流式错误:", event); } } if (!completed) { console.error("结果未收到 response.completed,需按未完成处理"); }

Responses API 使用语义化事件,文本通常从 response.output_text.delta 的 delta 字段中读取;收到 response.completed 只能说明服务端发出了完成事件,应用仍应结合本地拼接结果、异常状态和取消状态判断最终业务结果。

Chat Completions:delta.content、finish_reason 和 [DONE]

Chat Completions 的流式响应与非流式响应字段不同。非流式结果通常从完整消息的 message.content 读取;流式 chunk 使用 delta 表示本次增量,某些 chunk 可能只有角色、工具调用信息、结束原因或 usage,并不包含文本。

Python:拼接文本并读取 finish_reason

from openai import OpenAI client = OpenAI() stream = client.chat.completions.create( model="MODEL_ID", messages=[ {"role": "user", "content": "请用一句话解释流式输出。"} ], stream=True, ) parts = [] finish_reason = None for chunk in stream: if not chunk.choices: continue choice = chunk.choices[0] text = choice.delta.content if text is not None: parts.append(text) print(text, end="", flush=True) if choice.finish_reason is not None: finish_reason = choice.finish_reason answer = "".join(parts) print(f"\\nfinish_reason={finish_reason}")

JavaScript:读取增量和结束原因

import OpenAI from "openai"; const client = new OpenAI(); const stream = await client.chat.completions.create({ model: "MODEL_ID", messages: [ { role: "user", content: "请用一句话解释流式输出。" } ], stream: true, }); let answer = ""; let finishReason = null; for await (const chunk of stream) { const choice = chunk.choices?.[0]; const text = choice?.delta?.content || ""; answer += text; process.stdout.write(text); if (choice?.finish_reason) { finishReason = choice.finish_reason; } } console.log(`\\nfinish_reason=${finishReason}`);

finish_reason 常见值包括 stop、length、tool_calls 和内容过滤相关结果,具体取值应以实际接口参考为准。原始 HTTP SSE 场景下,流还会以 data: [DONE] 消息终止;因此,业务完成判断不应只依赖客户端循环是否自然结束。可核对 Chat Completions 流式事件参考 和 Chat Completions 创建接口参考。

usage、token 和费用:不要把流式显示当成计费变化

stream=true 主要改变传输时机,不会因为该参数本身减少模型生成的 token,也不能单凭逐字显示判断请求更便宜。端到端总耗时仍可能受到模型处理、网络、客户端、代理缓冲和重试影响。

Chat Completions 的流式 usage 不能简单理解为每个 chunk 都有完整统计。官方接口参考说明,只有在请求中设置 stream_options: {"include_usage": true} 时,才会额外发送包含整次请求 token 用量的 chunk;其他 chunk 的 usage 可能为空。这个最终 usage chunk 位于 [DONE] 之前,如果流被中断或取消,可能收不到它,因此不能把“没有 usage”直接等同于“没有产生用量”。

示例配置如下,是否被兼容接口支持仍需按实际服务文档核对:

stream = client.chat.completions.create( model="MODEL_ID", messages=[{"role": "user", "content": "你好"}], stream=True, stream_options={"include_usage": True}, )

如果目标是降低成本,应检查输入长度、输出上限、模型选择、重复请求和缓存策略,而不是只切换 stream 参数。最终用量应结合实际响应中的 usage、模型计费规则和服务商文档核验。

设置 stream=true 仍然一次性显示:按链路排查

现象优先检查处理方法
客户端没有任何输出是否真的遍历了流对象;是否把流对象当普通 JSON 解析先打印事件类型或 chunk 摘要,确认代码进入迭代循环
有 chunk 但文本为空是否读取了错误字段;是否把 message.content 和 delta.content 混用先打印单个 chunk 的结构,再按接口类型读取字段
命令行能分块,网页最后才显示应用服务器、反向代理、压缩层或浏览器链路是否缓冲 SSE依次测试上游、应用接口、代理和浏览器,定位第一层开始缓冲的位置
输出中途停止是否收到 finish_reason、[DONE]、response.completed 或错误事件记录最后一个事件、HTTP 状态、连接断开时间和请求标识,不要只记录“请求失败”
usage 偶尔缺失是否启用 stream_options.include_usage;流是否提前中断按接口文档启用 usage,并允许最终统计因断流而缺失
输出重复或缺字是否重复消费同一个流;重试时是否把旧缓冲区继续追加每次请求创建独立缓冲区,重试时重新建立结果状态

如果你通过 OpenAI 兼容接口或 API Router 接入,必须单独核对它是否支持 SSE、是否透传 finish_reason、是否支持 stream_options.include_usage、是否会缓冲响应,以及它采用的是 Responses API 还是 Chat Completions 的字段语义。请求格式兼容,不代表所有流式事件和错误字段都完全一致。

安全边界与上线前自查

上线前可以按这个顺序核验:先用非流式请求确认 Key、Base URL、路径和模型;再只增加 stream=true;然后用短文本测试正常完成;最后测试长文本、客户端取消、代理链路、超时和断流恢复。

常见问题

stream=true 是不是让模型生成速度更快?

不是。它让已经生成的内容更早发送到客户端,主要改善首个片段到达时间和感知延迟;完整输出仍受模型、网络、代理和客户端处理影响。核验时应分别记录请求开始、首个事件、最后事件和完整结果时间。

Chat Completions 流式响应什么时候算完成?

不能只看循环是否结束。应读取最终 chunk 的 finish_reason,并在原始 SSE 场景确认收到 data: [DONE];如果没有这些完成信号就断开,应按未完成或异常中断处理。

stream=true 会减少 token 用量或 API 费用吗?

不能这样推断。stream=true 主要改变响应传输方式。实际用量取决于输入、输出、模型和服务规则;Chat Completions 可能需要显式设置 stream_options.include_usage 才能在流末获得 usage,而且流中断时可能收不到最终 usage chunk。

Responses API 和 Chat Completions 的流式读取方式一样吗?

不一样。Responses API 使用带 type 的语义化事件,例如 response.output_text.delta 和 response.completed;Chat Completions 通常从每个 chunk 的 choices[0].delta.content 读取,并结合 finish_reason 和 [DONE] 判断结束。

为什么设置 stream=true 后网页还是一次性显示?

常见原因是应用服务器、反向代理、压缩层或浏览器链路缓冲了 SSE。先用原始命令行客户端确认上游是否连续返回,再逐层测试应用接口、代理和浏览器,找到开始缓冲的那一层。

流式响应中途断了,已收到的文字能直接当最终答案吗?

不建议。除非收到正常完成信号并确认没有错误,否则应把已拼接内容标记为未完成。记录最后一个事件、finish_reason、HTTP 状态和断开时间,再决定重试、续接或重新生成。

相关阅读

参考资料

  1. OpenAI API:Streaming API responses
  2. OpenAI API Reference:Responses streaming events
  3. OpenAI API Reference:Chat Completions streaming events
  4. OpenAI API Reference:Create chat completion
  5. OpenAI API:Migrate to the Responses API