AI API 流式指南:SSE、首输出与超时
学习 Chat Completions 与 Responses 的流式事件、SSE 解析、首个有效输出、分阶段超时及 499 取消诊断。
AI API 流式响应会在模型生成过程中逐步发送输出,而不是等完整响应体生成后再一次返回。它可以改善用户感知速度,但不一定降低模型本身的延迟,同时还要求客户端正确实现一套流式协议。
第一条原则是让解析器与端点匹配。Chat Completions 返回流式 completion chunk,Responses 返回带类型的 response event。即使 HTTP 状态是 200,如果客户端期待了错误的事件结构,也可能完全渲染不出内容。
从不缓冲的请求开始
对于 Chat Completions 模型,curl -N 会关闭 curl 的输出缓冲:
curl -N -sS https://modelflare.dev/v1/chat/completions \
-H "Authorization: Bearer $MODELFLARE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "YOUR_CHAT_MODEL",
"messages": [{"role": "user", "content": "用三点解释 SSE。"}],
"stream": true
}'
对于兼容 Responses 的模型:
curl -N -sS https://modelflare.dev/v1/responses \
-H "Authorization: Bearer $MODELFLARE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "YOUR_RESPONSES_MODEL",
"input": "用三点解释 SSE。",
"stream": true
}'
请从模型与价格选择准确的模型 ID。先把同一请求以非流式方式执行一次,可以把请求校验问题与流式解析问题分开。
把事件流当成一套协议
Server-Sent Events 是有边界的事件记录,而不是任意切开的 JSON 片段。生产客户端需要:
- 确保 HTTP 库、代理和 UI 层都不会缓冲响应体;
- 累积不完整的网络读取,直到获得一个完整事件;
- 按所选端点解析正确的事件类型;
- 处理文本增量、推理或工具事件、完成事件和错误事件;
- 不要假设所有有效响应都包含可见文本;
- 保留取消状态与最终用量信息;
- 收到终止事件后主动关闭,而不是无限等待。
应优先使用原生支持目标协议的 SDK。自建解析器至少要测试事件被拆成多次读取、一次读取包含多个事件、仅工具输出以及正常终止等情况。
不要只测“首个 Token 时间”
对于 Agent 流量,“首个 Token 时间”经常不够精确。应该分别测量:
| 指标 | 含义 |
|---|---|
| 连接与鉴权 | 到达网关并完成 Key 校验所需时间 |
| 上游响应头 | 所选路由开始返回响应的时间 |
| 首个有效输出 | 首段有意义的文本、推理或工具事件 |
| 首段可见文本 | 用户真正可以看到的第一段文字 |
| 输出吞吐 | 可见输出开始后的生成速度 |
| 总响应时间 | 到完成、失败或取消为止的总耗时 |
工具调用可能在任何可见文字前就已经产生有效输出。因此,首个有效输出更适合运维判断,首段可见文本更适合用户体验评估。
Modelflare 用量日志保留请求级时序证据,可以把这些阶段与模型、分组、状态、Token 和费用一起比较。
按阶段设计超时
一个很短的全局超时会让失败原因变得模糊。流式客户端至少要考虑几类时间预算:
- **连接超时:**HTTP 连接未能建立。
- **响应头或首个输出超时:**请求已经接受,但迟迟没有响应或有效事件。
- **空闲超时:**流已经开始,但长时间没有新事件。
- **总体期限:**完整操作超过业务允许的最长时间。
重推理或工具驱动的请求,在出现可见文本前可能合理地等待更久。应根据真实工作负载的观测值设置预算;当用户不再需要结果时,再有意取消请求。
正确理解取消与 499 记录
如果客户端、反向代理、浏览器页面或调用方截止时间关闭了下游连接,网关可能把请求记录为客户端取消。请求记录中的 499 表示下游连接在完成前结束;仅凭这个标签不能证明所选模型或渠道发生故障。
排查取消时应比较:
- 客户端截止时间和 abort signal;
- 代理缓冲与空闲超时;
- 首个有效输出之前经过了多久;
- 客户端是否收到过任何事件;
- 实际选择的模型和分组;
- 请求 ID 与取消时间。
排查“流正常但没有可见文本”
按以下顺序检查:
- 使用 "stream": false 重复同一请求。
- 确认模型支持所选端点。
- 在 UI 转换前捕获原始事件。
- 检查响应是否只有工具调用或推理事件,而没有文本。
- 确认中间层没有缓冲响应体。
- 确认解析器能够识别该端点的终止事件。
- 在用量日志中核对状态、首个输出、总耗时与取消情况。
如果非流式调用成功,原始流式事件也能到达,问题通常位于客户端解析或渲染,而不是鉴权或模型权限。如果连原始事件都没有到达,请继续阅读AI API 错误指南。
生产流式检查清单
- 明确让解析器匹配 Chat Completions 或 Responses。
- 关闭 HTTP 客户端和所有可控代理层的缓冲。
- 测试应用会使用的文本、推理、仅工具和错误事件。
- 区分首个有效输出与首段可见文本。
- 使用分阶段超时和明确的取消信号。
- 除非已经设计好幂等,否则把重试视为一笔新请求。
- 为排查保留请求 ID、状态、所选模型、分组和时序。
- 结合Responses API 与 Chat Completions 对比确认端点选择。
常见问题
流式响应会让模型生成得更快吗?
不一定。流式只会更早暴露输出;生成速度和首个事件前的等待时间仍取决于模型、路由、上下文、推理和工具。
为什么 curl 能输出,但应用页面什么都没有?
应用或中间层可能缓冲了响应体,也可能用 Chat Completions 的解析器读取 Responses 事件。更换模型或路由前,先在应用边界捕获原始事件。
流断开后是否应该自动重试?
只有在重复执行安全时才应该重试。流虽然断开,上游可能已经完成部分工作或触发工具操作。对可能产生重复副作用的任务,需要有界重试和应用级幂等设计。