外观
Agent 执行到一半失败、超时或进程被杀,怎样恢复?
标准回答
我会把长任务保存成有状态的步骤:待执行、执行中、等待用户、成功、失败、已取消,以及结果待核实。记录 taskId、stepId、目标参数、尝试次数和已确认结果,而不是只保存一段聊天记录。
重试前先分类。只读查询遇到短暂网络问题,可以在总时限内退避重试;设置明确目标值的操作可结合当前状态重复确认;发送消息、提交订单这类操作可能已经产生副作用,超时后应使用同一个业务幂等键查询或重试。幂等键必须由真正执行副作用的一端识别,仅在客户端生成一个 ID 并不能防止重复。如果下游既不能去重也不能查询,我会停在结果待核实,而不是盲目再发一次。
进程重启后,先读取任务记录,再核对外部真实状态和当前权限。恢复的是尚未完成的工作,不是把整个任务从头再跑一遍。记录成功和外部动作之间可能有崩溃窗口,所以本地数据库事务也不能单独保证跨服务动作只发生一次。
用户取消后停止安排后续步骤,并尽量取消网络或设备操作;已经发生的动作不假装被撤销。可补偿的操作按业务规则补偿,不可逆的操作明确告知。模型循环还要设置最大步骤数、重复动作检测和总预算,避免失败后一直尝试。
关键机制
- 把“失败”与“结果未知”分开,超时和断连往往属于后者。
- 副作用边界需要下游幂等或可查询结果;检查点负责恢复上下文。
- 取消、补偿、回滚含义不同;每一步都给出真实可执行的规则。
通俗例子
创建提醒请求超时,但系统已经创建成功。恢复时通过业务标识找到原提醒并补记成功;若接口无法关联查询,就请用户核实,不直接再创建一个。
追问与回答
发送短信后程序崩了,恢复时怎么办?
我会先看下游是否支持业务标识去重或查询发送状态。如果能确认已经受理,就补齐任务状态,不再发送。如果只能知道本地还没记成功,但无法确认外部结果,就标记待核实并交给用户判断。因为“动作已发生、成功记录未写入”是可能存在的窗口,自动重发会带来重复消息。
怎样防止用户点取消后结果仍然出现?
取消时更新任务状态或 generation,并通知底层操作停止。回调到来时先核对任务是否仍有效,再决定是否更新页面或触发下一步。若底层动作已经完成,可以更新任务记录为“取消前已完成某一步”,但不能用迟到回调重新启动后续执行。
易混淆点
持久化不自动带来 exactly-once,cancel 也不自动撤回已发生的动作。执行器和 UI 都要识别迟到事件。
参考资料
内容来源
- Android 技术札记 · 原条目
system-recovery