流式响应
SSE 流式形态、chunk 分片结构、usage 末帧、断流与重试建议,以及一次流式请求的接口视角时序
当您希望边生成边返回、尽快把首批内容呈现给终端用户时,将请求体的 stream 字段设为 true,平台即以流式分片形态逐段返回。本页说明流式的传输形态、分片结构、usage 末帧的读法,以及断流与重试建议。
流式适用于 对话补全 与 消息接口 两类发起查询的接口;本页以对话补全为例。
何时使用流式
| 场景 | 是否建议流式 |
|---|---|
| 面向终端用户的实时对话、长文生成 | 建议:尽早呈现首批内容,降低用户感知等待 |
| 后台批处理、只取最终完整结果 | 不必:用非流式更简单,一次拿到完整响应 |
| 需要逐字渲染打字机效果 | 建议:分片即到即渲染 |
传输形态(SSE)
开启流式后,平台以 Server-Sent Events(SSE) 形态返回:响应头为 Content-Type: text/event-stream,响应体是一串以 data: 开头的事件行,每行承载一个分片(chat.completion.chunk)。
- 每个分片是一个独立的 JSON 对象,前缀
data:。 - 分片按生成顺序逐个下发,客户端逐行解析、即到即渲染。
- 流以一行固定的
data: [DONE]标记结束,收到即表示本次生成完毕。
curl -N https://<平台入口地址>/v1/chat/completions \
-H "Authorization: Bearer <您的 API 密钥>" \
-H "Content-Type: application/json" \
-d '{
"model": "<模型名>",
"messages": [
{ "role": "user", "content": "用三句话介绍一下你的能力。" }
],
"stream": true
}'
curl的-N关闭缓冲,便于观察分片实时到达。大多数 OpenAI 兼容 SDK 已内置流式解析,您通常只需把stream设为true并迭代返回的分片。
分片(chunk)结构
每个分片为 chat.completion.chunk 对象,增量内容放在 choices[].delta 中(而非非流式的 message):
| 字段 | 说明 |
|---|---|
id | 本次响应标识,同一次流的各分片一致 |
object | 固定为 chat.completion.chunk |
created | 时间戳 |
model | 本次实际路由的模型名 |
choices[].delta | 本分片的增量内容:首帧通常含 role,其后各帧含 content 增量片段 |
choices[].finish_reason | 非结束分片为 null;最后一个内容分片给出结束原因(stop / length / tool_calls / content_filter) |
usage | 仅在 usage 末帧出现(见下文),承载本次完整的统一计价词元用量 |
典型的分片序列如下(示意):
data: {"id":"<响应标识>","object":"chat.completion.chunk","model":"<模型名>","choices":[{"index":0,"delta":{"role":"assistant"},"finish_reason":null}]}
data: {"id":"<响应标识>","object":"chat.completion.chunk","model":"<模型名>","choices":[{"index":0,"delta":{"content":"我可以"},"finish_reason":null}]}
data: {"id":"<响应标识>","object":"chat.completion.chunk","model":"<模型名>","choices":[{"index":0,"delta":{"content":"帮您……"},"finish_reason":null}]}
data: {"id":"<响应标识>","object":"chat.completion.chunk","model":"<模型名>","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}
data: {"id":"<响应标识>","object":"chat.completion.chunk","model":"<模型名>","choices":[],"usage":{"prompt_tokens":0,"completion_tokens":0,"total_tokens":0,"cached_input_tokens":0,"price_tier":"<本次适用单价档>","thinking":false}}
data: [DONE]拼接增量。 把各分片
choices[].delta.content按到达顺序依次拼接,即得完整回复;首帧的role不重复出现。
usage 末帧
流式响应的统一计价词元用量在最后一个携带 usage 的分片中给出(紧邻 data: [DONE] 之前)。该帧的 choices 通常为空数组,仅承载 usage:
| 字段 | 含义 |
|---|---|
prompt_tokens | 本次输入的统一计价词元数 |
completion_tokens | 本次输出的统一计价词元数 |
total_tokens | 输入与输出合计 |
cached_input_tokens | 命中上游模型供应商缓存的输入词元数 |
price_tier | 本次适用的单价档(6 档计量口径之一) |
thinking | 本次是否走深度推理(thinking)档 |
计量口径与非流式一致。 流式末帧的
usage与非流式响应的usage字段、口径完全一致;缓存是否命中以上游模型供应商返回为准。该笔用量同样落入 用量明细 的逐条记录,三处数字同源。6 档口径详见 用量明细与计量说明。
安全护栏照常生效。 流式路径上,每个分片同样经平台安全护栏处置后才下发——脱敏与应拒答复核不因流式而绕过。若中途触发护栏,末分片的
finish_reason可能为content_filter,或转为结构化护栏处置结果。详见 安全防护处置。
流式时序
断流与重试建议
流式连接跨越较长时间,网络抖动可能导致连接中途断开。建议在应用侧按下表处理:
| 情形 | 建议处理 |
|---|---|
收到完整 [DONE] 前连接断开 | 视为本次流式未完成;可携带相同 messages 重新发起请求,重试一次完整流式生成 |
| 已收到部分分片后断流 | 已到达的增量内容仍可呈现;重试时重新发起完整请求,不要尝试「续传」未完成的流 |
| 重试前 | 建议加入短暂退避(如数百毫秒到数秒),避免对刚抖动的连接立刻密集重试 |
| 反复断流 | 检查客户端是否关闭了响应缓冲、代理或负载均衡是否对 SSE 长连接有超时限制 |
返回 429 | 表示额度暂停或限速,不应立即重试;识别后暂缓并触发内部告警,详见 错误码与重试 与 额度管理 |
重试的计量。 流式因断流重试时,只有真正返回了
usage末帧的那次生成才计入用量;中途断开、未产出usage的尝试不重复计费。如对某次用量有疑问,可凭响应标识在 用量明细 中按逐条记录核对。
幂等建议。 若您的业务对「同一查询不应重复生效」敏感,建议在应用侧自行以业务键去重,避免断流重试导致同一查询被多次成功生成。