流式输出中途断流:AI 回复到一半停住,先分清是“断了”还是“停了”
流式输出(SSE)中途断流,表现为 AI 应用的回复写到一半就不动了。这个现象背后至少有两类完全不同的原因:一类是连接真的断了,需要重连或续写;另一类是模型已经正常结束或撞到输出上限,前端却把它当成了故障。分不清这两类,重试策略就会写错。本文对照 Anthropic、OpenAI 与 MDN 的官方文档(核验日期 2026-09-20),按“先判断、再定位、后重连”的顺序,梳理回复中途停住的排查方法和可落地的重连策略。
先判断:是连接断了,还是响应已经结束
定位问题的第一步是看最后收到了什么,而不是先怀疑网络。下表把常见现象与首要检查项对应起来。
| 现象 | 常见原因 | 首先检查什么 |
|---|---|---|
| 停在句子中间,没有任何报错 | 输出上限已用完,含推理消耗的部分;或模型已正常结束 | 最后一个事件里的结束原因,如 Anthropic 的 stop_reason 是否为 max_tokens,OpenAI 的状态是否为 incomplete |
| 收到 error 事件后停止 | 上游过载等错误在 200 之后才发生 | 事件里的错误类型,例如 overloaded_error |
| 连接被直接关闭,浏览器报分块不完整 | 反向代理或网关超时、缓冲、进程重启 | 网关与应用日志,对照断开时间点 |
| 长时间没有数据后断开 | 空闲连接被中间设备回收 | 是否有周期性的心跳或 ping |
| 只在特定网络环境下发生 | 链路丢包、路由抖动、出口不稳 | 换网络对比,并记录每次断开时已接收的字节数 |
第一行最容易被误判。Anthropic 文档说明,思考 Token 与回答共用同一个 max_tokens,OpenAI 文档也提到达到 max_output_tokens 时状态会变成 incomplete,甚至可能在出现任何可见文字之前就已耗尽。这类“停住”不是断流,重连只会重复浪费一次调用。
服务端与链路上的四个常见断点
反向代理的缓冲与超时
流式响应经过 Nginx 之类的反向代理时,默认的响应缓冲会让数据攒成一大块才发出,前端看到的是“卡住再一次性吐出”;读超时则会在上游长时间无数据时主动断开。常见的处理是对流式路由关闭缓冲,并把读超时调到大于模型可能的最长静默时间。具体配置项因网关而异,请以你所用网关的官方文档为准。
空闲连接被中间设备回收
Anthropic 的错误文档指出,某些网络会在一段不定的时间后丢弃空闲连接,导致请求失败或超时而收不到任何响应;对直接接入的调用,建议设置 TCP 保活。流式响应里,服务端也会穿插 ping 事件,MDN 则说明 SSE 可以用以冒号开头的注释行周期性发送,避免连接因超时被关闭。自建转发层时,别把这些心跳数据过滤掉。
浏览器对 HTTP/1.1 连接数的限制
MDN 指出,不使用 HTTP/2 时,SSE 受到浏览器对同一域名的并发连接数限制,数值很低,为 6。用户开多个标签页各自挂一条流,就可能让新的流一直排队,看起来像“没有响应”。使用 HTTP/2 可以避开这个限制。
上游过载在 200 之后才报错
Anthropic 文档说明,流式响应的错误可能出现在 API 已经返回 200 之后,此时错误处理不走标准机制,而是以 error 事件的形式出现在事件流里,例如相当于 HTTP 529 的 overloaded_error。所以只在 HTTP 状态码上做判断是不够的,事件流本身也要监听 error 事件。
重连策略:三种做法与各自的边界
浏览器自带的 EventSource 重连
MDN 说明,EventSource 默认会在连接关闭后自动重连,服务端可通过 retry 字段指定等待毫秒数,通过 id 字段让浏览器记住最近的事件 ID。需要注意两点:一是 EventSource 只支持 GET 请求,而大模型接口通常是 POST,聊天应用多半用 fetch 读取流并自行实现重连;二是重连后能否从断点继续,取决于服务端是否按事件 ID 保存并回放事件,不是浏览器能替你完成的。
应用层续写
Anthropic 文档给出的恢复思路是:捕获已经收到的部分响应,构造一个新请求,让模型从中断处继续。对 Claude 4.5 及更早的模型,可以把部分响应放进新的助手消息;对 4.6 及之后的模型,则改为用一条用户消息说明“上一次回复在某处中断,请从断点继续”,并附上已收到的内容。文档同时提醒:工具调用块与思考块无法部分恢复,只能从最近一个完整的文本块继续。
后台模式加游标续传
OpenAI 的后台模式让长任务异步执行,并允许在连接断开后,凭记录下来的 sequence_number 通过 starting_after 参数从最后的游标继续读取流。文档同时说明,后台响应的数据只会临时保存约 10 分钟用于异步执行与轮询,且首字延迟高于同步请求。这种方式适合任务很长、断线代价高的场景,不适合对首字延迟敏感的对话。
落地清单:把重连做成可验证的流程
- 为每次流式请求记录已接收的字节数、最后一个事件类型和结束原因,出问题时才有证据。
- 区分结束原因:正常结束、达到输出上限、收到 error 事件、连接直接断开,四种走不同分支,只对后两种重试。
- 重试采用指数退避加随机抖动,设置次数上限,避免在上游过载时放大压力。
- 续写时保存已展示的文本,用“从断点继续”的提示词拼接,并在界面上标注这是续写结果,避免出现重复或断句错位。
- 对代理层做一次端到端演练:人为在中途切断连接,确认前端能恢复且不会重复计费同一段内容。
关于计费,续写会产生新的输入与输出用量,重复发送已收到的内容也要计入输入,所以重连策略最好有次数上限和成本上限。
总结:先定位,再决定重连还是续写
回复停在一半,先看结束原因,再看网关与链路。输出上限、error 事件、代理超时、空闲回收、浏览器连接数,是五类最常见的原因,每一类的处理方式并不相同。把已接收字节数与结束事件记录下来,重连才有依据。如果你的中断集中在特定网络环境,说明链路层不稳定是主因,这也是 Tonbo AI 的 AI 安全隧道与内置 AI Chat 面板面对的使用场景,可以先用本文的分层方法确认问题落在哪一层,再决定是否需要更换接入方式。
延伸阅读









