断线恢复、超时与终态
为什么需要
移动网络切换、浏览器休眠、电话转接、provider 连接时限和进程重启都会中断会话。“socket 重新打开”不能证明旧输出停止、上下文恢复或工具没有执行两次。没有显式终态的会话还会长期占用麦克风、资源和审批。
怎样实现
把恢复拆成三步,并分别留下证据:
- 传输恢复:重新建立 WebRTC/WebSocket/电话媒体路径;
- 会话恢复:使用短期、绑定用户/租户的 resume handle,协商最后确认的 event/sequence;
- 业务核对:从 durable event log/checkpoint 重建 turn、response、未决 call,查询副作用 receipt。
第 2 步成功后不是“恢复监听”,而是进入一个副作用核对门。该门只处理旧调用的查询、receipt 或明确的超时/人工交接,不接受新 turn、response 或写工具:
| 核对结论 | runtime 可做什么 | 不能做什么 |
|---|---|---|
| 没有未决调用 | 解除门禁,接受新工作 | 把旧 response 当作仍可播放 |
| 有 receipt,结果为已提交/失败 | 持久化结果、向用户说明已知状态,再按策略继续 | 靠模型文字假定已回滚 |
| 确认未开始 | 记录结论;若仍有业务需求,作为新的有授权动作发起 | 自动重放旧写请求 |
unknown、receipt 冲突或权限失效 | 保持门禁,转 needs_human 或受控 reconciliation | 接受新写工作、宣称结果确定 |
checkpoint 至少保存 session/version、最后已确认事件、活动 turn/response、播放 generation、未决工具及 intent digest、审批、deadline 和可接受终态。原始音频通常不应因方便恢复而默认长期保存;可保存受控指针、摘要或必要窗口,并按隐私策略到期。
断线时先冻结新副作用,取消/隔离旧 response 并让客户端清缓冲。重连后旧 generation 的 chunk 即使迟到也因 response ID/version 不匹配被丢弃。resume token 不是永久身份凭据;应有短期有效期、作用域、轮换和服务端验证。
终态至少区分:
completed:verifier 已证明目标完成;canceled:用户/runtime 明确取消,副作用已核对;timed_out:deadline 到期,未决动作进入 reconciliation;failed:不可恢复错误并记录失败阶段;needs_human:状态未知或风险不允许自动继续。
timeout 不是异常字符串,而是确定性状态迁移:停止继续生成/调用、清播放、持久化未决项、释放媒体资源,并向用户说明已知/未知边界。
常见失败
- 只依赖供应商 session;其过期后审批、receipt 和业务进度一起丢失。
- 重连后自动重放最后工具请求,造成重复扣款/预约。
- 客户端断线但服务端仍生成并执行高风险动作。
- resume token 写入可公开日志或长期存储,且不绑定用户/租户。
- 把 timeout 标记为 failed 后直接丢弃未知副作用。
怎样验证
在关键 crash window 注入故障:提交 turn 前后、tool request 发出前后、外部 commit 前后、receipt 保存前后、response audio 缓冲期间。每次恢复都验证副作用次数、旧音频泄漏、未决状态、终态和资源释放。还要测试错误/过期/跨用户 token 与断线期间迟到事件。
实践任务
画出一个 10 分钟实时会话的 checkpoint 结构,并为“网络断线时付款状态 unknown”写恢复协议:哪些字段持久化、先查询谁、何时允许重试、何时转人工、用户听到什么。
动态实现样例与下一步
Google Live session management在 2026-07-18 展示了连接终止通知、session resumption handle 和 context compression;这些是产品机制,不替代应用 checkpoint。其他服务的有效期和兼容性可能完全不同。下一步:隐私、安全与信任边界。