openai 兼容接口支不支持流式

接入与开发

讲清 OpenAI 兼容接口的流式支持情况:怎么判断一家到底支不支持、代码怎么写、断流丢字怎么排。

你按 OpenAI 的写法给 stream 传了 true,结果要么整段一次性吐回来,要么中途断掉、末尾少半句话。问题往往不在你的代码,而在中转层或上游模型对流式的处理方式。这篇给你一套判断顺序:先确认接口到底支不支持,再决定代码怎么写、怎么兜底。

你按 OpenAI 的写法给 stream 传了 true,结果要么整段一次性吐回来,要么中途断掉、末尾少半句话。问题往往不在你的代码,而在中转层或上游模型对流式的处理方式。先别急着改代码,先判断这家到底支不支持,再决定怎么写、怎么兜底。

OpenAI 兼容接口的「兼容」通常指路径、鉴权、请求体字段和响应结构对得上,流式属于常见参数,理论上会被保留。但兼容不等于每家都实现,聚合型中转站可能只做非流式转发,甚至把 stream 参数直接吃掉。所以这事得实测,不能看文档写没写。

先判断:这家到底支不支持流式

最省事的判断法:用同一条请求分别带 stream true 和 false 各发一次,重点看三个地方——返回头是不是事件流类型、返回体是不是分片推送、内容是不是逐块到达并带结束标记。一次性整段返回、头还是普通 JSON,基本就是不支持或没被透传。

  1. 发两次对照请求同一模型、同一 prompt,一次 stream true 一次 false,其余参数完全一致,方便对比。
  2. 看返回头事件流类型通常是 text/event-stream;如果头是 application/json 且立刻返回完整体,说明流式没生效。
  3. 看到达节奏正常流式是隔一小段来一块;如果等很久然后一次性全来,多半是上游攒完再发。
  4. 找结束标记正常结束会有明确的终止事件,最后一块带 [DONE] 之类的标记;没有标记就断,说明链路有问题。

模型层也要分开看:有的模型本身只走非流式,兼容接口自然不会帮你变出流式。遇到这种就把该模型单独标出来,别指望同一家所有模型表现一致。

代码侧几个容易踩的点

确认支持流式之后,代码上有几处不设好就会出玄学问题。最常见的是超时用一个值兜到底:慢模型首字等很久,被你提前掐断;快模型整段读很快,又浪费了额度。

  • 超时拆三档:连接超时、首字等待、整段读取,分别给不同档位,别一个值管到头
  • 逐块拼接按事件边界切,别按网络包切;一次到达可能含多段,也可能只有半段,缓存不完整的尾巴等下一片
  • 结束标记和错误事件单独处理,别只认正常分片,否则出错时会静默返回半截内容
  • 客户端和网关两侧都要允许流式透传,中间有反代或 CDN 时注意缓冲策略,别被攒批下发

还有一点容易被忽略:前端读取流的方式。用 fetch 拿 body reader 和用某些封装库,对分片边界处理不一样。换库之后表现变了,先去查是不是库帮你做了聚合。

curl -N -sS https://<你的接入点>/v1/chat/completions -H 'Content-Type: application/json' -H 'Authorization: Bearer $KEY' -d '{"model":"<模型名>","stream":true,"messages":[{"role":"user","content":"说三句话"}]}' | head -c 800

不支持的时候怎么兜

不是每家都支持流式,也不一定非流式就不可用。先看业务能不能等:能等就退回非流式,把逐字动画换成一次性展示或整段淡入,改造成本最低,稳定也最好。

兜底方式适用情况代价
退回非流式回答不长、用户能等没有逐字观感
前端假流式要保留打字机效果首字出现更晚,长文要限速
换接入点流式是硬需求要重新测稳定性和价格
双路并存重要业务不能断维护两套逻辑

要保留体验就自己在前端做假流式:拿完整结果按字或按标点切块定时吐出,观感接近真流式,但首字一定会晚,长回答场景还得设字数或时长上限,避免一次性返回把页面卡住。

选型阶段就把「是否支持流式」列进对比项。真到上线才发现不支持,换接入点的成本比一开始多测一家高得多。具体每家的支持情况和当时的报价,到 /sites 和 /price-watch 核对当时的数字。

排查顺序:不流式、断流、少内容

三类毛病表现像但原因不同,按顺序排查最省时间,别一上来就怀疑模型。

  1. 查请求看 stream 参数有没有被中间层吃掉或改名,比如网关转发时把字段过滤了,这是最常见的一层。
  2. 查返回头是不是事件流类型,有没有被压缩或缓冲导致攒批下发,反代和 CDN 都要看一眼。
  3. 查结束标记与错误事件判断是上游提前结束还是异常中断;只看正常分片会漏掉错误分支。
  4. 查落地侧前端读取、日志截断、代理超时都可能把完整流切掉一半,逐段加日志定位。

排查时把同一请求在直连和走中转两条路上各跑一遍,差异立刻能看出问题在中间层还是上游。想省事可以用站内的测速工具对比两条路的首字延迟和断流情况:/tools/speed-test。

下一步把你要用的接入点拉出来做一次双请求对照,再去核对支持情况和当时的报价,别等前端写完才发现不支持流式。

常见问题

兼容接口里 stream 传 true 但不逐字返回,是接口坏了吗

不一定。先确认返回头是不是事件流类型、内容是不是分片到达。若头是普通 JSON 且一次返回整段,多数是这一层没做流式透传,属于实现差异而不是报错。换非流式写法或换一个接入点通常就能解决。

流式写好了但偶尔少最后几句,从哪查

按「上游结束标记 → 中间代理超时与缓冲 → 前端读取循环」的顺序查。多数情况是中间层攒批或超时提前断开,导致尾部分片没到;也可能是前端没等到结束标记就停止渲染。分环节打日志最省时间。

不同接入点流式表现不一致,怎么统一

别追求完全统一,改成能力探测加降级:启动或首次调用时探一次是否支持流式,支持就走逐块渲染,不支持退回非流式。同时把首字等待和整段超时按接入点分别配置,比硬套一套参数稳。

本文依据各平台公开文档与官方公告整理,只做中文归纳;具体做法、参数与限制以官方文档为准。