AI 应用中怎么恢复流式输出

geekbing2025-06-08AIChatbot

大部分 AI 应用在输出文本内容时,都是流式输出,这就遇到一个问题,如果用户中途关闭浏览器标签或刷新页面,很多 AI 应用就中断输出了。

为了实现最好的体验,我们需要采用一套恢复机制,即无论用户是关闭标签,还是刷新页面,之前的流式输出仍可以恢复。

为什么需要 Redis Hash + Pub/Sub

流式恢复的核心挑战在于,前端断连后后端还在继续生成内容,重连时不仅要把已有内容补发给前端,还要接上后续的新增内容。

单独用一个组件都有缺陷:

  • 只有 Pub/Sub:Pub/Sub 是即发即忘的,不存储历史消息。如果前端断连期间有新内容发布,重连后这些消息就永远丢失了。
  • 只有 Redis Hash:虽然能存储累积内容,但恢复端不知道什么时候有新数据写入,只能不断轮询,既浪费资源又增加延迟。

两者结合刚好互补,Redis Hash 负责存储累积状态(仓库),Pub/Sub 负责实时通知(门铃)。断连后从 Hash 读取已有内容,再通过 Pub/Sub 监听后续更新,实现无缝恢复。

怎么实现

Chat2Reportopen in new window 中,架构分为模型层、业务层和前端,流式恢复的逻辑放在业务层实现。

前端和后端之间有一个简单的约定,请求中带 session_id 但不带 query,后端就知道这是一次恢复请求,而不是新的提问。

正常生成时(双写):

  1. 业务层从模型层接收流式 token
  2. 将 token 通过 SSE 转发给前端
  3. 将累积的完整文本写入 Redis Hash(每次覆写)
  4. 通过 Pub/Sub 发布通知

用户断连后重连(恢复):

  1. 从 Redis Hash 读取当前累积的完整文本,一次性发给前端
  2. 订阅 Pub/Sub 频道,等待通知
  3. 收到通知后,再次读取 Redis Hash 中的累积全文
  4. 将本次全文与上次已发送的内容做对比,算出新增部分,作为增量片段发给前端
  5. 重复步骤 3-4,直到收到生成完成的信号

具体流程如下:

实际效果

用户刷新页面,切换到其他页面再回来都能恢复流式输出,效果还不错。

我补充了 2026 年新增的 Agent 功能演示视频,Agent 相对于之前的 ChatBot 更复杂,它涉及多轮推理和工具的调用,所以恢复流式输出也稍微复杂一些。但是总体逻辑还是和上面的流程图一样,只是细节上有些许差异。

为什么不直接用 Pub/Sub 增量片段

你可能会问:恢复时每次都去读全文再算增量,为什么不直接用 Pub/Sub 发布的增量片段?

技术上完全可以,但存在一个竞态窗口:在读取 Redis 快照和 Pub/Sub 订阅生效之间有一个时间差,这段时间内发布的消息既不在快照里,也不会被订阅捕获,数据就丢了。

而回读全文的方式天然避免了这个问题,不管中间漏掉了多少次通知,只要去读一次全文,跟上次对比就能算出完整的增量,不会丢任何一个字符。

这是一个正确性优先于性能的设计取舍。在实际场景中,回读一次 Redis Hash 的开销不到 0.1ms,完全不构成瓶颈。

最后

以上就是在 AI 聊天应用中实现恢复流式输出的方案,核心思路是 Redis Hash 存状态 + Pub/Sub 做通知 + 回读全文算增量。希望能给你带来帮助。

What do you think?
  • 0
  • 0
  • 0
  • 0
  • 0
  • 0
Comments
  • Latest
  • Oldest
  • Hottest
Powered by Waline v2.15.8