流式输出中断了怎么办?断点、重发与幂等的三种处理

2026-08-06

流式输出中断的尴尬在于:你已经为生成的那半截付了钱,但拿到的是残缺内容。整体重发意味着两边都付费,直接展示残缺内容意味着体验受损。这中间有几种折中方案,选哪种取决于你的场景能不能容忍”接不上”。

这篇讲三种处理策略。超时相关的通用处理见 API 超时与中断

中断是怎么发生的

网络层断开:移动网络切换、代理超时、负载均衡器的空闲连接回收。长回答尤其容易撞上中间设备的超时设置——因为流式连接会保持很久。

服务端主动结束:触发限流、内部错误、达到 max_tokens 上限(这个严格说不算中断,但表现类似:内容戛然而止)。

客户端主动取消:用户关掉页面、切走、点了停止。这种情况反而最好处理,因为是预期内的。

区分它们的关键是看有没有收到结束事件。正常结束会有明确的完成信号和 finish_reason;异常中断则是连接直接断掉,什么都没收到。所以务必判断 finish_reason,别只看有没有内容。

策略一:保留残缺内容 + 提示用户

最简单的做法:把已经收到的部分保留下来,在界面上明确提示「生成中断」,给一个「继续生成」按钮。

适用:内容型场景(写作助手、长文生成),用户能看懂残缺内容,也愿意点一下继续。

优点:不浪费已付费的内容,实现简单。

注意:一定要在 UI 上区分「完整」和「中断」,别让残缺内容看起来像是模型的完整回答。这类静默残缺最容易招投诉。

策略二:把已生成内容作为上下文续写

用户点「继续」时,把原始问题加上已生成的部分一起发过去,让模型接着写。

做法:在新请求里把已有内容作为助手消息放进历史,并在指令里明确「从中断处继续,不要重复已有内容」。

优点:用户拿到的是完整内容,已生成的部分没有白付。

代价:续写请求的输入包含了已生成内容,这部分要按输入价再付一次。不过输入价通常远低于输出价,所以总账仍比整体重发划算。

风险:接缝处可能重复或跳跃。缓解办法是在续写提示里给出最后一小段原文作为锚点,并在拼接时做重叠去重。

策略三:整体重发(幂等重试)

丢弃已生成内容,用同样的参数重新请求一次。

适用:短输出场景(几百 token 以内),重来的成本很低;或者对内容连贯性要求极高、无法接受接缝的场景。

代价:两次输出都要付费。输出越长越亏,所以长内容不建议用这个策略。

必须配套幂等设计:如果这次调用有副作用(写数据库、发消息、扣费),重发前要确保不会重复执行。常见做法是给每次业务操作一个幂等键,落库前先查。

三种策略怎么选

按输出长度和场景性质分:

  • 短输出 + 无副作用 → 策略三,最简单
  • 长输出 + 内容型 → 策略二,最省钱
  • 长输出 + 用户能接受残缺 → 策略一,实现成本最低
  • 有副作用的调用 → 无论选哪种,先把幂等做好

工程上要配套的几件事

一、边流边存。 流式内容要实时落盘或落缓存,别只放在内存里等结束后统一保存——中断时内存里那份可能一起丢了。

二、记录中断点。 存下已生成的 token 数和最后一段内容,续写时要用。

三、把中断计入监控。 统计「中断率」和「中断时的平均已生成长度」。中断率突然上升,通常是超时配置、网络链路或厂商侧出了问题。

四、区分 finish_reason。 因为达到 max_tokens 而停止,和网络中断,处理方式完全不同:前者是你的参数设小了,后者才需要重连。混在一起统计会掩盖真实问题。

前端和后端各该做什么

流式场景的中断处理是前后端配合的事,职责划分不清就会互相等对方处理。

后端要做的

  • 逐块接收并立即转发给前端,同时写入持久化存储
  • 记录已发送的 token 数和最后一段内容
  • 判断结束原因(正常完成、达到上限、异常中断),把这个信号明确传给前端
  • 中断时不要静默吞掉,要发一个明确的错误事件

前端要做的

  • 区分「还在生成」「正常结束」「异常中断」三种状态,UI 上表现不同
  • 中断时保留已收到的内容,不要清空
  • 提供「继续生成」入口,而不是让用户重新提问
  • 处理连接断开后的重连(如果产品需要)

最容易出问题的是结束信号的传递。后端知道是异常中断,但如果没把这个信息传给前端,前端只会看到流突然停了,无法区分「写完了」和「断了」,于是只能按写完处理——用户就看到一段莫名其妙的残缺内容。

怎么减少中断本身

处理中断固然重要,但更根本的是减少中断发生。几个常见的着力点:

一、检查链路上所有中间设备的超时设置。 负载均衡、反向代理、API 网关、CDN,每一层都可能有空闲超时。流式连接在两个数据块之间如果间隔较长,会被误判为空闲而断开。把这些超时调到大于你的典型生成时长。

二、发送心跳。 有些实现会在数据块之间发送空注释行保持连接活跃,能有效对抗中间设备的空闲回收。

三、控制单次输出长度。 生成时间越长,撞上任何一层超时的概率越高。把一次超长生成拆成几次短生成,中断率会明显下降,见 max_tokens 该设多大

四、监控中断率的分布。 按输出长度分组看中断率,如果长输出的中断率显著更高,那就是超时问题而不是随机故障。

一个常被忽略的点:中断也要计费

已经生成的 token 是要付费的,即便你没用上。所以高中断率不只是体验问题,也是成本问题——你在为一堆没交付的内容付钱。

如果中断率长期偏高,优先查两件事:中间设备的超时设置(负载均衡、网关、代理的空闲超时是否短于你的典型生成时长),以及输出长度是否过长(把一次超长生成拆成几次短生成,能显著降低单次中断的损失)。相关的成本影响见长文本任务的四个成本陷阱

三个高频问题

问:怎么区分是模型写完了还是断了? 看 finish_reason。正常完成、达到 max_tokens、异常中断是三种不同的信号,务必分开处理。

问:续写会不会重复已有内容? 有可能。缓解办法是在提示里给出最后一小段原文作为锚点,并在拼接时做重叠去重。

问:用户主动取消要计费吗? 已经生成的部分照常计费。所以频繁被取消的场景,值得考虑先返回摘要再按需展开。

想系统学会用 AI?报名体系课或加入会员,照着学、照着用。