给前端 Agent 加离线 outbox 时,我会先把草稿和提交账本分开
最近在做一个前端 Agent 工作台时,我遇到一个很具体的问题:用户在弱网环境里让 Agent 改草稿、补字段、提交检索任务,页面短暂断线后又恢复。早期实现只在请求失败时提示“稍后重试”,结果用户确认过的修改留在页面 state 里,刷新后消失;后台偶尔收到重复提交,任务面板也说不清哪一次才是最终结果。

原创示意图:把草稿、outbox、网络探测、重放 runner、提交回执和恢复账本拆成可恢复模块。 来源:Codex image generation
问题背景
MDN 的 IndexedDB 文档把它描述成浏览器内可存储结构化数据的低层 API,适合保存离线草稿、队列项和提交回执。Service Worker 能拦截网络请求,Background Synchronization API 提供网络恢复后延迟执行任务的能力。不过 MDN 同时标注 Background Sync 存在兼容限制,所以它只能做增强通道。Chrome 的 Workbox Background Sync 文档给了一个实用方向:失败请求先进队列,稍后再由同步逻辑重放。
踩坑和关键难点
第一个坑是把草稿和提交混在一起。输入框内容代表用户当前想法,outbox 操作代表已经确认、等待送达的副作用。两者混用后,用户继续编辑会污染待提交 payload。
第二个坑是过度相信在线状态。navigator.onLine 只能给出浏览器判断的在线状态,真实请求仍可能因为代理、企业网关、服务端限流或临时 DNS 问题失败。
第三个坑是重放没有回执。断线期间用户可能点了三次保存,恢复后队列依次重放。如果每条操作没有 operationId、baseRevision 和 receipt,界面就很难判断哪些已经落地,哪些需要人工复核。
解决思路
我把链路拆成五层。draft store 只保存当前可编辑草稿和本地 revision。outbox queue 只收已经确认的操作,字段包含 operationId、entityId、baseRevision、payloadHash、createdAt、retryCount 和 nextAttemptAt。network guard 监听 online、visibilitychange 和手动重试按钮,但最终仍以真实探测请求为准。replay runner 按顺序提交 pending 操作,并用 AbortController 控制取消。receipt ledger 记录服务端返回的 committedRevision、conflictReason 和 committedAt。
这样拆完后,页面刷新只需要先 hydrate draft store 和 outbox queue,再恢复面板状态。用户继续编辑草稿时,不会改写已经入队的操作;网络恢复时,runner 只处理 pending 项;服务端确认后,receipt ledger 再把实体推进到新的 revision。
关键步骤
第一步,提交前冻结 payload。用户点保存时,从当前草稿生成一次不可变操作,写入 IndexedDB 后再发请求。请求成功前,UI 可以显示等待同步,但不能把它当成已提交状态。
第二步,重放要有顺序和退避。相同 entityId 的操作按 createdAt 串行执行,不同实体可以小并发。遇到网络错误时递增 retryCount 并计算 nextAttemptAt;遇到校验失败或版本冲突时停止自动重放,转入 needs_review。
第三步,服务端也要参与幂等。每个操作带 operationId,服务端如果处理过同一键,应返回同一份 receipt。前端拿到 receipt 后再更新本地 committedRevision,避免重复点击造成多次副作用。
第四步,Background Sync 只做增强。支持时把队列注册到 Service Worker;不支持时用页面恢复、手动重试和定时探测兜底。核心可靠性仍由 outbox 和 ledger 保证。
可复用经验
离线 outbox 的重点是把用户意图变成可审计的端侧事实。草稿负责继续编辑,outbox 负责等待提交,runner 负责重放,receipt ledger 负责证明结果。只要这几个边界稳定,AI 表单助手、RAG 导入向导、Electron Web 协作面板和自动化规则编辑器都能复用同一套离线提交模型。
我现在会把三个问题放进验收清单:刷新后 pending 操作还在不在,恢复网络后是否只提交一次,版本冲突时能不能给用户一个可解释的复核入口。上线前再补一组断网脚本,用同一份 operationId 连续提交、刷新、恢复、取消,确认队列、回执和 UI 展示能对上。三项都稳定后,离线能力才算进入工程可维护状态。
主要来源
MDN Background Synchronization API