Skip to main content

简短回答

三句话讲完:
  1. 流式还是非流式,完全由你的代码决定——请求体里的 stream 参数。同一个 Key、同一个模型、同一个端点,一会流式一会非流式,一定是客户端代码(或你用的 SDK / 上层框架)在切换,网关不会随机改
  2. 两者拿到的最终内容一致、计费口径也完全一致。差别只在「什么时候拿到」和「怎么解析」。
  3. 怎么选:有人盯着屏幕等输出 → 流式;程序自己吃结果(解析 JSON、批处理、工具调用)→ 非流式。

一张表看清差别

为什么我的请求一会流式一会非流式?

这是最常见的疑问,答案是:在你自己这一侧被改掉了。按下面几条从上到下排查,基本能命中:
最典型的情况:stream=config.get("stream", False)stream=is_web_request 这类写法。不同入口走到同一个函数,传进去的值不一样,日志上看就是”一会流式一会非流式”。排查:把实际发出去的请求体打印出来,看 stream 字段到底是什么。
同一份业务代码,换个客户端就变了:
  • 直接用 OpenAI SDK 的 chat.completions.create():默认 非流式
  • client.chat.completions.stream()with_streaming_response流式
  • LangChain / LlamaIndex 之类的封装:是否流式取决于你调的是 invoke 还是 stream,以及构造模型对象时有没有传 streaming=True
  • 各类桌面客户端、Agent 工具、工作流平台:一般在设置里有「流式输出」开关,默认值各不相同
排查:确认这次调用具体是哪个入口发出去的。
一个 Key 同时给「网页聊天界面」和「后台定时任务」用,前者流式、后者非流式,日志混在一起看就像是随机的。排查:给不同用途建不同的令牌,日志一眼就分得开。做法见 令牌管理
你确实发了 stream: true,但请求经过 Nginx、企业网关、某些代理软件时被缓冲了——服务端是一块块发的,代理攒够了才一次性给你,体感上就变成了非流式。排查:绕过代理直连测一次;Nginx 侧关掉缓冲(proxy_buffering off;)。注意这种情况下控制台日志的 is_stream 仍然是 true,因为网关这边确实是流式发出去的。
怎么确认某一次调用到底是不是流式:去控制台日志看 is_stream 字段,或用日志查询 API 批量拉取。这是”事实来源”,比凭体感判断可靠。

怎么选:按场景对号入座

用流式

  • 聊天界面、客服机器人——用户需要立刻看到反应
  • IDE 插件 / 编程助手(Claude Code、Cursor 等)
  • 长文本生成(万字文章、长翻译、大段代码)
  • 推理型模型的长任务——至少能看到进度,不至于”完全没动静”
  • 需要中途打断(用户点「停止」)的场景

用非流式

  • 结构化输出:要拿完整 JSON 去 json.loads()
  • Function Calling / 工具调用的参数解析
  • 批处理、离线跑批、定时任务
  • 只要最终结果、没有人在等的后台流程
  • 快速验证、调试、写测试用例
几个特殊场景单独说:

接入复杂度对比:同一件事的两种写法

Claude 原生格式(/v1/messages)的流式协议不一样:它用的是 Anthropic 的具名事件 SSEmessage_start / content_block_delta / message_delta 等),不是 OpenAI 那种统一的 data: chunk,usage 也分散在 message_startmessage_delta 两个事件里。完整解析方法见 Claude 原生格式:流式与非流式响应

计费与用量:两者完全一样

流式不会更便宜,也不会更贵。 计费按 token 走,与传输方式无关。中途断开也照常计费——你按 Ctrl+C 或客户端超时断开后,上游的生成任务仍会跑完,这次请求正常扣费。所以”流式看到一半不想要了就断开能省钱”是不成立的。
关于 usage 的两个坑:
  1. 流式默认不返回 usage。OpenAI 兼容端点要显式传 stream_options: {"include_usage": true},用量会出现在最后一个 chunk 里(那个 chunk 的 choices 是空数组,解析时要先判空)。本站多个模型已实测可用。
  2. 不要用 API 回显的 usage 去核对账单,尤其是缓存相关字段。回显值和实际计费不总是一致,缓存是否命中以控制台日志的「缓存计费详情」为准。详见 缓存计费说明
无论流式与否,控制台日志都会完整记录本次调用的 token 数、耗时和计费,不受传输方式影响。日志字段含义见 日志计费明细怎么看

六个常见误区

不成立。 流式只是让”第一个 token”来得早,它不缩短总生成时间,也不保证中途一直有数据。推理型模型(gemini-3.1-pro-previewgpt-5.6-solgpt-5.5-pro 等)在思考阶段可能长时间不吐任何 token,客户端的 read timeout 一样会被触发。正确做法是按场景分档设置 timeout,见 如何避免接口超时
快的是首字节,不是总耗时。 同一个模型、同一段提示词,流式和非流式跑完的总时间基本一致。流式的价值是体感:用户 1 秒就看到有东西在动,而不是盯着转圈等 30 秒。如果没人在看屏幕,这份价值等于零。
不是。 见上方「计费与用量」一节:计费口径完全相同,中途断开也照常扣费。
不是。 文本对话类模型基本都支持;图片生成、Embedding、Rerank 这类端点没有流式概念,传 stream 要么被忽略要么直接报错。个别模型对流式下的某些参数组合有额外限制,不确定时先用非流式跑通,再加 stream: true
各有各的坑。
  • 非流式的风险:整段生成期间连接是”静默”的,中间的代理、CDN、企业网关容易按空闲超时把连接掐掉。另外响应体很大时(出图返回 base64 动辄十几 MB)还可能遇到收尾卡住,见 请求收尾卡住日志显示已完成却收不到响应
  • 流式的风险:对不支持 SSE 或强制缓冲的中间层不友好;客户端解析逻辑更复杂,容易漏掉边界情况。
另外注意:api-cf.apiyi.com(CDN 节点)有约 100 秒的请求上限,流式和非流式都受影响,长请求请改用 api.apiyi.comvip.apiyi.com,见 Base URL 配置指南
能拿到,只是要自己拼。 把每个 chunk 的 delta.content 按顺序累加起来,就是非流式那个 message.content如果你发现拼出来的内容不完整,先查这三点:是否漏处理了 finish_reason、是否在收到 data: [DONE] 前就退出了循环、是否被中间层截断。

流式接不通?按这四步查

1

确认请求体真的带了 stream: true

打印实际发出的 JSON。用了封装库时,“你以为传了”和”真的传了”经常不是一回事。
2

用 curl -N 直连测一次

绕开你自己的代码和代理,直接用上面「cURL 对照」里的命令跑。如果 curl 能看到一块块吐出来,说明服务端侧没问题,问题在客户端或中间层。
3

检查中间层缓冲

Nginx 加 proxy_buffering off;;企业网关 / 安全设备可能对 text/event-stream 做整包扫描,需要联系网络管理员放行。
4

核对解析逻辑

按行读 SSE,跳过空行和 : 开头的注释行,遇到 data: [DONE] 结束;最后一个带 usage 的 chunk 里 choices 是空数组,别在这里下标越界。
排查到这一步还没结论时,带上 request_id 联系客服——控制台日志里能直接看到这次调用是不是按流式处理的、耗时和首字节时间各是多少。

相关文档

如何避免接口超时

分场景的 timeout 推荐值,以及流式为什么救不了超时

Base URL 配置指南

各接口地址的差异,CDN 节点的 100 秒限制

日志显示已完成却收不到响应

非流式大响应的经典问题,含分段计时方法

Claude 流式与非流式响应

Anthropic 原生格式的具名事件 SSE 协议解析

文本生成接口说明

完整参数列表与调用示例

日志计费明细怎么看

控制台日志各字段含义,含 is_stream