可恢复的长任务:SSE 断流、页面刷新与双路径恢复

少于 1 分钟阅读时长

发布时间:

Agent 任务可能持续数分钟甚至更久。负载均衡器空闲超时、浏览器刷新、移动网络切换或服务抖动都会让 SSE 连接中断,但后端任务可能仍在运行。

可靠系统必须把传输连接和业务任务分开建模。

连接断开后的第一原则

不要立即重建任务。重新提交相同需求可能重复调用外部工具、重复写数据库,或创建两个相互竞争的任务。客户端或上游 Agent 应首先查询已有任务状态,并使用稳定 task ID 尝试恢复。

订阅优先,轮询兜底

一种实用的双路径方案是:

  1. 查询任务状态,确认任务仍在运行;
  2. 使用原 task ID 重新订阅实时事件;
  3. 服务端先返回最新快照,再推送后续增量;
  4. 订阅不可用时退化为消息轮询;
  5. 直到任务进入 completed、failed 或 input-required。

订阅适合低延迟恢复,轮询对流式基础设施要求更低。两条路径必须共享相同的任务状态和消息序号。

快照与增量

仅重放全部增量会导致重复,只推送新事件又可能丢失断开期间的内容。恢复接口应先返回当前完整正文和结构化状态快照,再从快照版本之后继续发送实时增量。客户端收到快照后覆盖本地状态,每个后续事件带单调递增序号或版本号。

页面刷新后的恢复

前端可以携带恢复标识和已有 thread/task ID。历史对话通过 history API 重放,当前轮通过任务订阅续接,不再次调用创建任务。如果任务正在等待输入,则恢复对应确认卡或表单。

这要求 Task、Session 与 Activity 状态存放在可恢复介质中,而不是只存在于单个进程内存。

设置循环与超时边界

上游有时会持续返回处理中而没有终态。恢复循环必须设置单次请求超时、退避轮询、最大连续失败次数和最大自动继续轮数。达到上限后应转为 input-required,而不是假装 completed。

恢复机制还必须建立在幂等之上:创建任务使用请求号,写操作使用业务幂等键,续订只读取和订阅,工具响应携带待恢复调用 ID,终态通过状态条件保证只写一次。

长任务恢复的核心,是让用户重新获得对原任务的观察和控制,而不是把一次网络故障放大成一次业务重做。