Skip to content

Agent 重复副作用与幂等补偿故障复盘

30 秒复盘结论: 这是一场故障演练:Agent 调用高风险工具已经完成外部副作用,但响应在回写前丢失,编排器把“结果未知”误判为“未执行”并重试,造成同一业务动作执行两次。止损顺序是暂停自动重试、冻结高风险工具并按 request_id 对账;长期修复是将 idempotency_key、执行台账、显式状态机与补偿任务组成不可绕过的副作用门禁。当前只有演练设计和合成证据,没有真实事故、实测成功率或个人线上处置记录。

目录

1. 复盘摘要

项目内容
事故类型故障演练,不是真实生产事故
用户现象同一付款、通知、建单或删除动作出现两次
直接原因工具成功后响应丢失,编排器自动重试
根本原因系统没有把“结果未知”建模成独立状态,也没有统一幂等门禁
临时止损暂停重试、冻结工具、按执行台账对账、人工接管
长期修复幂等键、执行台账、状态机、补偿与对账任务
当前状态文档设计完成;故障注入和维护者审图待执行

面试官真正关注的不是“知道幂等”这一个词,而是能否区分工具调用失败、响应失败和副作用结果未知,并给出不会扩大损失的恢复顺序。

2. 小白先看懂

想象小王通过代办员订酒店。酒店已经扣款并预订成功,但回执在路上丢了;代办员看到“没有收到成功消息”,又订了一次,于是产生两笔订单。此时最危险的动作不是等待,而是继续点“重试”。

生活场景技术映射
小王的请求Agent 任务与业务意图
代办员Agent 编排器
酒店扣款外部工具副作用
回执丢失超时、网络中断或结果回写失败
订单号idempotency_key 与外部 effect_id
查酒店订单执行台账和对账查询

专业机制上,“调用返回异常”并不能证明“副作用没有发生”。系统必须保留 UNKNOWN 状态,先按幂等键查询既有结果,再选择返回旧结果、补偿或人工接管。

类比没有覆盖并发 Agent、工具自身不支持查询、不可逆动作和跨系统最终一致性;这些边界正是工程验证重点。

3. 故障教学图片

图:故障教学图片|Agent 重复副作用的证据链与幂等恢复

替代文本: Agent 请求经过编排和工具调用产生外部副作用,响应丢失后重试造成重复;request_id、幂等键和执行台账支持止损、状态机与补偿恢复,事实边界为故障演练。

教学图片待人工审图Agent 请求经过编排和工具调用产生外部副作用,响应丢失后重试造成重复;request_id、幂等键和执行台账支持止损、状态机与补偿恢复,事实边界为故障演练 暂不公开,正文与 Mermaid 图可正常阅读。

读图结论: 外部动作返回超时后,第一步是确认副作用是否发生,而不是立即重试。

橙色回路表示“副作用已发生但回执丢失”,证据链负责关联同一业务意图;红色区域先停止风险扩散,绿色区域再恢复状态。图片只建立直觉,精确状态和调用顺序以下文 Mermaid 与表格为准。

图片生成记录: model=gpt-image-2generated=2026-07-15prompt_version=v1查看 Prompt。PNG C2PA 记录 softwareAgent=gpt-imageversion=2.0

质检状态: Agent 已逐字检查标题、标签、箭头与事实边界;维护者人工复核尚未完成,因此保持 generated-awaiting-review

4. 事实边界与证据

结论类型证据或验证动作
工具成功后响应可能丢失工程机制用故障注入在提交副作用后断开返回链路
缺少幂等门禁会导致重复执行示例假设并发发送相同 idempotency_key 并核对副作用计数
暂停自动重试可以缩小影响工程建议比较冻结前后的新增重复动作数,不预填数字
本文描述真实线上事故不成立没有工单、日志、Trace 或业务损失证据

合成 Trace 仅用于定义字段,不代表真实请求:

json
{
  "request_id": "req-drill-02",
  "agent_run_id": "run-drill-02",
  "tool_call_id": "call-01",
  "idempotency_key": "tenant:order:intent-01",
  "effect_id": "effect-unknown",
  "state": "UNKNOWN",
  "retry_count": 1,
  "evidence_boundary": "failure-drill"
}

5. 影响、时间线与指标

演练范围覆盖可产生付款、发信、建单、删除和授权变更的工具;只读查询不在副作用影响域,但仍需受超时预算约束。未知项包括具体工具是否原生支持幂等键、查询结果和撤销接口。

相对时间事件决策
T0工具收到调用并完成副作用外部系统生成 effect_id
T0+1返回链路被注入中断Agent 记录 UNKNOWN,不得写 FAILED
T0+2旧策略触发自动重试演练复现重复风险
T0+3冻结工具并对账根据幂等键查询既有结果
T0+4修复后重放验证返回旧结果或进入补偿

核心指标先定义口径:

text
duplicate_effect_rate = 同一业务幂等键对应多个有效 effect_id 的数量 / 已确认产生副作用的幂等键数量
unknown_state_age = 当前时间 - 首次进入 UNKNOWN 的时间
reconciliation_success_rate = 被对账任务收敛到终态的 UNKNOWN 数 / 进入对账的 UNKNOWN 总数

本文不填写阈值和改善比例;需要在具体业务的可接受损失、工具 SLA 和人工接管能力下确定。

6. 技术栈与故障域架构

6.1 技术点清单

技术点 ID技术点/环节类型采用方案链路职责版本/证据边界
TP-01副作用身份协议业务级 idempotency_key关联同一意图的多次调用演练 Schema
TP-02执行台账存储唯一约束加状态记录保存请求、结果与外部 effect_id设计稿
TP-03状态控制状态机PENDING/RUNNING/SUCCEEDED/UNKNOWN/COMPENSATING/FAILED防止把未知结果当失败设计稿
TP-04恢复机制服务对账任务加显式补偿查询外部事实并收敛终态待故障注入

6.2 故障域架构

图:架构|Agent 副作用、执行台账与恢复边界

替代文本: 用户请求进入 Agent 编排器,副作用门禁先读取执行台账,再调用外部工具;状态机、对账器和补偿器共享台账,日志与 Trace 贯穿调用链。

图表加载中…

读图结论: 幂等能力必须位于所有副作用工具之前,并以外部业务事实而非 Agent 内存判断最终结果。

7. 故障与恢复调用流程

图:技术调用流程|响应丢失后的停止重试、对账与补偿

替代文本: Agent 用幂等键调用门禁,外部工具完成副作用但返回丢失;门禁记录 UNKNOWN,停止自动重试并由对账器查询外部结果,存在结果则复用,不存在才按策略重试或补偿。

图表加载中…

读图结论: UNKNOWN 是需要取证的独立状态,既不能直接重试,也不能直接宣告失败。

8. 止损与根因分析

止损按风险从高到低执行:暂停高风险工具自动重试;冻结新副作用请求;保留只读查询;导出 UNKNOWN 台账;按外部事实对账;不可逆动作进入人工接管。

假设证据结论
Agent 规划了两次动作tool_call_id 与计划 Trace若只有一次计划,排除
工具第一次没有执行外部 effect_id 与业务记录已存在则排除
客户端重复提交上游幂等键与请求日志同键请求可定位,但门禁仍应兜底
响应丢失触发重试工具提交 Span 成功、返回 Span 中断演练主假设
  • 直接原因: 副作用成功后响应丢失,编排器自动重试。
  • 根本原因: 没有统一副作用协议和 UNKNOWN 状态,工具各自处理幂等。
  • 促成因素: 重试策略只看异常类型,不看副作用阶段;缺少跨系统对账。
  • 非原因: LLM 输出随机性不是本演练的直接原因;相同工具参数仍需幂等保护。
  • 防线失效: 测试只覆盖“调用前失败”,没有覆盖“提交后响应丢失”。

9. 长期修复与横向选型

技术点 ID候选方案优点缺点/代价适用场景不适用场景选择结论与依据
TP-01随机请求 ID接入简单重试会生成新 ID,不能表达业务同一性只做追踪幂等控制不采用为幂等键
TP-01业务幂等键能跨重试关联同一意图需定义稳定业务边界有明确业务主键无法识别业务意图默认采用
TP-02内存去重延迟低重启丢失、无法跨实例无副作用的短任务生产副作用仅用于加速,不能做事实源
TP-02持久化唯一约束跨实例、可审计增加存储写入生产副作用极低价值只读请求采用
TP-03成功/失败二态简单无法表达结果未知纯本地原子操作远程副作用不采用
TP-03显式 UNKNOWN 状态机恢复路径清晰状态和迁移更复杂分布式工具调用无恢复需求采用
TP-04自动无条件补偿恢复快补偿也可能重复或不可逆可证明补偿幂等付款、删除等不可逆动作不默认采用
TP-04对账后受控补偿以外部事实为准恢复较慢、需查询能力高风险副作用外部系统完全不可查询默认采用,必要时人工接管

10. 验证与防复发

注入场景预期行为验收证据
调用前断网可安全重试台账无 effect_id,外部无副作用
提交后响应丢失进入 UNKNOWN,不自动重试同键只有一个有效 effect_id
两个 Agent 并发同键只有一个执行者唯一约束冲突被复用为既有结果
对账接口超时保持 UNKNOWN 并告警状态不被误写为 FAILED
补偿任务重复执行补偿本身幂等最终业务状态唯一且可审计

防复发资产:CI 增加副作用契约测试;Trace 强制记录 idempotency_key/effect_id/state_transition;告警监控 UNKNOWN 数量与年龄;Runbook 固定“冻结—取证—对账—恢复”顺序;新工具接入必须声明查询、撤销和幂等能力。

难点卡: 最难的不是加一个唯一键,而是在网络结果未知和工具能力不一致时,同时保证不重复执行与可恢复。验证边界是故障注入,不是普通成功用例。

亮点卡: 把重试判断从异常类型升级为副作用状态机,并以外部事实对账;代价是台账、补偿和人工接管流程增加。

反模式: 把所有异常都交给 Agent 自主重试;用 Prompt 提醒“不要重复”代替系统幂等;只记录自然语言思考而不记录业务键和状态迁移。

11. 面试表达

11.1 30 秒

这是一次故障演练:Agent 的工具副作用已成功,但响应丢失后自动重试,可能造成重复付款或建单。我先暂停重试、冻结高风险工具并按幂等键对账,根因是系统没有统一幂等门禁和结果未知状态。长期修复采用执行台账、显式状态机与补偿任务,并通过提交后断链、并发同键和补偿重放验证;当前没有真实事故数据。

11.2 60~90 秒

我把失败拆成调用前失败、调用后失败和副作用已提交但响应丢失三类。第三类不能直接重试,所以 Trace 必须串联 request_id、tool_call_id、idempotency_key 和 effect_id,台账进入 UNKNOWN 后由对账任务查询外部事实。方案没有选择内存去重或成功/失败二态,因为它们不能跨实例恢复结果未知;代价是增加存储和状态治理。验证重点是故障注入后同一业务键只有一个有效副作用,并且不可查询时能转人工接管。

11.3 2~3 分钟展开要点

按“演练边界—工具副作用—响应丢失—取证字段—冻结止损—根因与防线失效—方案横评—故障注入—Runbook”展开。第一人称只用于描述演练方案设计,不能声称处理过真实线上事故。

递进追问:幂等键由谁生成?工具不支持查询怎么办?补偿失败如何恢复?同一业务意图参数变化是否复用同一键?如何处理跨工具事务?为什么 Prompt 不能替代幂等?

12. 总结

一句话记忆: 副作用结果未知时,先对账事实,再决定重试或补偿。

  • 调用异常不等于外部动作未发生;
  • UNKNOWN 必须成为显式、可观测、可恢复的状态;
  • 幂等键、执行台账、对账和补偿共同构成副作用门禁;
  • 修复必须覆盖提交后断链、并发重放和补偿重复;
  • 本文是故障演练设计,不是已验证生产经历。