Appearance
工具调用补偿机制专题
版本:
v1.1最后更新:
2026-07-07适用对象:需要为 Agent、工作流、外部写操作、审批流和长任务链路设计失败恢复、补偿、回退和人工兜底机制的产品、平台、架构、工程与运维同学
只要 Agent 开始调用外部工具,系统就会面对一个传统工作流里很常见、但在 AI 场景里更容易被忽视的问题:
- 某一步执行成功了
- 后面一步失败了
- 那前面的副作用怎么办
这类问题在 Agent 系统里尤其常见,因为工具链通常具备:
- 多步骤串联
- 多系统参与
- 模型决策存在不确定性
- 长任务与人工介入并存
- 部分动作不可逆
所以失败时经常不是“重试一下就好”,而是:
- 要判断已经做过哪些事
- 这些事是否要撤销、反向修复、人工接管,还是继续推进
Microsoft 的 Compensating Transaction 模式、Temporal 的 Saga 模式、AWS Step Functions 的 Retry/Catch/redrive 文档,以及 OpenAI 的 orchestration / background mode 文档,都在讲同一个工程事实:
- 分布式、多步骤、带副作用的流程里,失败恢复必须被设计成系统能力,而不是事后人工救火
一句话理解:
补偿机制不是失败后的补丁,而是 Agent 工具链的正常组成部分
1. 什么是补偿机制
更实用的理解通常是:
- 当某个工具链路无法完整成功时,通过额外动作把系统尽量拉回到可接受状态
重点有两点:
- 它不一定等于严格回滚
- 它关注的是“整体业务状态可接受”,不只是“技术调用结束”
例如:
- 创建工单成功,但发送通知失败
- 审批提交成功,但 CRM 更新失败
- 扣减额度成功,但下游记录没写进去
这时系统要做的,通常不是简单抛错,而是明确决定:
- 是重试
- 是补一笔反向操作
- 是转人工
- 还是接受部分成功并继续修正
2. 为什么 AI 工具链尤其需要补偿
AI 工具链比普通单体业务流程更容易遇到半完成状态,原因通常包括:
2.1 决策链更长
- planner
- executor
- tool
- validator
- human approval
- compensation
2.2 外部系统更多
- CRM
- ERP
- 工单系统
- 邮件和 IM
- 浏览器自动化
- 文件系统与对象存储
2.3 模型可能会调整路径
- replan
- fallback
- retry
- handoff
2.4 长任务跨时间执行
- 人工审批可能几分钟甚至几小时后才返回
- 外部任务可能异步完成
这些特征叠在一起时,失败往往不是二元的“成/败”,而是:
- 有些事已经做了
- 有些事还没做
- 有些事做了但状态不确定
补偿机制就是用来处理这种现实复杂性的。
3. 重试、回滚、补偿、人工兜底不是一回事
很多系统会把这几个概念混在一起,结果设计时就会越来越乱。
| 概念 | 回答的问题 |
|---|---|
| 重试 | 这一步能不能再跑一次 |
| 回滚 | 能不能把技术状态恢复到之前 |
| 补偿 | 前面已经有副作用时,如何把整体业务状态拉回可接受范围 |
| 人工兜底 | 当系统无法可靠修复时,谁来接手 |
这四者经常一起出现,但不能互相替代。
例如:
- 网络超时时,优先考虑重试
- 幂等写入失败时,可能重试即可
- 已经发出去的邮件无法撤回,就不能谈真正回滚
- 退款已经发起但通知失败,可能需要补偿写记录或人工跟进
4. 一个更实用的动作分类方式
补偿设计前,先把工具动作分成三类最稳。
4.1 幂等可重试
特点:
- 重跑不会造成额外副作用
例子:
- 查询状态
- 幂等更新带 request id
- 可覆盖写入草稿
4.2 可补偿
特点:
- 不能简单重试,但可以通过反向动作修复
例子:
- 创建工单后可以关闭工单
- 添加标签后可以删除标签
- 写草稿后可以撤销草稿
4.3 不可逆
特点:
- 无法真正撤销
- 只能靠后续动作减损或人工接管
例子:
- 对外邮件已发送
- 付款已执行
- 消息已推送给客户
- 第三方不可撤销动作已触发
先把动作分清,后面很多设计就会清爽很多。
5. 为什么补偿机制通常依赖状态机
如果不知道流程停在哪一步,就很难判断:
- 哪些工具已经执行
- 哪些补偿已经做过
- 哪些动作处于待确认状态
- 接下来该补偿还是该继续
所以补偿机制通常和这些能力一起出现:
- 状态持久化
- 步骤日志
- 事件模型
- 任务恢复策略
一个简单状态机示意可以是:
text
draft_created
-> approval_requested
-> approval_granted
-> external_write_started
-> external_write_succeeded
-> notification_failed
-> compensation_required
-> compensation_succeeded没有状态模型,补偿逻辑经常只能靠“猜当前大概到哪了”。
6. 补偿设计的核心是定义业务可接受状态
很多人一说补偿就默认:
- 一定要恢复到最初状态
现实里常常做不到。
更实用的做法是先定义:
- 什么叫“可接受状态”
例如:
工单创建成功,但客户通知失败 可接受状态可能是:工单保留、打补发标签、转人工补通知
退款审批成功,但 CRM 没同步 可接受状态可能是:退款状态保留、创建数据修复任务、阻止后续结案
补偿真正要追求的,不是神话般的“彻底没发生过”,而是:
- 系统状态清晰
- 风险可控
- 后续可继续处理
7. 一个更实用的补偿对象模型
建议对每个关键工具步骤至少记录:
json
{
"step_id": "s3",
"tool": "create_ticket",
"action_type": "write",
"idempotency_key": "req_1001",
"side_effect_level": "medium",
"status": "succeeded",
"compensation": {
"available": true,
"tool": "close_ticket",
"preconditions": ["ticket_status=open"],
"requires_human_approval": false
}
}这个模型至少能帮助回答:
- 这一步有没有副作用
- 可不可以重试
- 有没有补偿动作
- 补偿前需要满足什么条件
没有这些信息,失败恢复只能非常临时。
8. 幂等性是补偿之前最该先做好的东西
很多团队一上来就讨论补偿,但真正省心的第一步其实是:
- 能幂等的动作,先尽量设计成幂等
为什么:
- 幂等可以显著降低重复执行带来的损害
- 幂等可以让很多“补偿问题”直接退化成“安全重试问题”
常见做法包括:
idempotency_key- 外部写操作 request id
- 去重表
- 状态版本号
- 幂等 update endpoint
补偿机制很重要,但它不应该替代幂等设计。
最好的补偿之一,往往是:
- 让系统不那么容易进入难以补偿的状态
9. 什么时候优先重试,什么时候优先补偿
一个粗略但实用的判断框架是:
9.1 优先重试
适合:
- 工具超时
- 短暂网络故障
- 幂等读请求
- 幂等写请求
- 已知 provider 短暂抖动
9.2 优先补偿
适合:
- 上一步成功,下一步失败,且已产生副作用
- 不希望链路保持半完成状态
- 前面动作可反向修复
9.3 优先人工兜底
适合:
- 不可逆动作
- 状态冲突无法自动判断
- 高风险、多系统不一致
- 自动补偿可能带来更大风险
不要把所有失败统一写成:
text
retry 3 times那通常只是把复杂问题暂时藏起来。
10. Saga 思路对 Agent 工具链很有帮助
Temporal 官方文档把 Saga 视为一种处理长事务失败的模式:
- 每走一步业务动作,就为这一步预留相应补偿动作
映射到 Agent 工具链里,可以理解成:
text
步骤 A: 创建工单
补偿 A: 关闭工单
步骤 B: 更新 CRM
补偿 B: 恢复旧值
步骤 C: 发送消息
补偿 C: 不可撤回,只能人工补救然后在执行链路里明确:
- 哪些补偿是自动执行
- 哪些补偿需要审批
- 哪些补偿只能人工接管
这个思路特别适合多步骤、多系统、长任务场景。
11. 补偿顺序通常和执行顺序相反
这是 Saga / compensating transaction 模式里很重要的一个工程习惯。
如果执行顺序是:
text
1. 创建工单
2. 更新 CRM
3. 发通知失败后常见补偿顺序往往是:
text
3 的补救
2 的修复
1 的关闭或保留原因很简单:
- 越后面的动作,往往越依赖前面的状态
当然这不是绝对规则,但在绝大多数有依赖关系的链路里都很实用。
12. 补偿前必须先确认当前真实状态
补偿失败的高频原因之一是:
- 以为前一步成功了
- 实际它可能只是超时,不确定是否成功
这时如果直接补偿,可能会把系统弄得更乱。
更稳妥的做法通常是:
- 先查当前状态
- 确认前一步是否真的成功
- 判断是否已经部分补偿
- 再决定重试、补偿还是跳过
也就是说:
- 补偿前最好有一次“写后读”或状态确认
这和 工具结果校验专题 其实是强耦合的。
13. 补偿动作本身也可能失败
这是很多设计里容易忘掉的一层。
现实里,补偿动作也可能:
- 超时
- 权限不足
- 状态不满足
- 已经被别人修改
- 外部系统不可用
所以“补偿逻辑”本身也需要:
- 重试策略
- 失败记录
- 人工接管路径
- 幂等性
不要默认:
- 一旦进到补偿步骤,事情就一定能收好
很多事故就是卡在“原动作失败 + 补偿也失败”的双重半完成状态里。
14. 人工兜底不应被视为失败,而应是设计的一部分
对于高风险或不可逆动作,人工介入本来就应该是预设路径之一。
典型场景包括:
- 客户消息已发出,无法撤回
- 退款已提交第三方处理
- 多系统状态冲突
- 补偿前需要业务判断
更成熟的做法通常是:
- 自动系统负责把状态整理清楚
- 人工负责做最后业务判断
也就是说,好的补偿机制不会让人工“从一堆碎日志里自己猜”,而是会把这些信息准备好:
- 哪一步成功了
- 哪一步失败了
- 已有哪些副作用
- 可执行哪些补偿候选
15. 补偿机制和人工纠偏工作台应当配套设计
人工纠偏工作台专题 真正需要的,往往就是这些补偿上下文:
- 当前状态
- 已执行步骤
- 可重试步骤
- 可补偿步骤
- 不可逆步骤
- 需要业务判断的原因
如果补偿上下文没有结构化沉淀,工作台就很难做成稳定工具,只能变成人工翻日志页面。
16. 补偿机制为什么和事件模型强绑定
工具观测事件模型专题 解决的是:
- 发生了什么
而补偿机制最需要知道的正是:
- 哪些动作已经发生
- 哪些动作还没发生
- 哪个失败触发了补偿
- 哪次补偿是否成功
建议至少有这些事件:
text
tool.execution.succeeded
tool.execution.failed
tool.retry.scheduled
tool.compensation.started
tool.compensation.succeeded
tool.compensation.failed
human.handoff.started补偿设计没有事件模型,后期追踪和复盘会非常痛苦。
17. 一个更实用的补偿流程示例
text
创建工单
-> 更新 CRM
-> 发送客户通知
如果发送通知失败:
1. 查询 CRM 是否已更新成功
2. 查询工单是否已创建成功
3. 决定是否:
- 重试通知
- 给工单打“待人工通知”标签
- 发送内部告警
- 转人工工作台这里最重要的一点不是“是否有一个反向 API”,而是:
- 系统是否知道自己已经做到了哪一步,以及可接受的恢复路径是什么
18. 不同工具类型的补偿重点
| 工具类型 | 补偿重点 |
|---|---|
| 只读查询 | 通常不需要补偿,重点是重试与降级 |
| 幂等写入 | 重点是安全重试和状态确认 |
| 可撤销业务写入 | 重点是反向操作和顺序控制 |
| 不可逆外部动作 | 重点是减损、标记、人工兜底 |
| 审批工具 | 重点是撤销审批单、关闭流程或标记失效 |
| 批量任务 | 重点是部分成功识别、逐项补偿和重入能力 |
不要用一套“失败了就 rollback”思路覆盖所有工具。
19. 线上应该监控哪些补偿指标
19.1 基础指标
| 指标 | 用途 |
|---|---|
| compensation_trigger_rate | 补偿触发频率 |
| compensation_success_rate | 补偿是否真的收住了 |
| compensation_failure_rate | 补偿本身是否经常失败 |
| retry_before_compensation_rate | 是否过度重试后才补偿 |
19.2 业务指标
| 指标 | 用途 |
|---|---|
| partial_success_rate | 半完成状态出现频率 |
| unrecoverable_action_rate | 不可逆动作导致的人工接管比例 |
| stale_orphan_record_count | 遗留脏状态数量 |
| human_handoff_after_compensation_fail | 补偿失败后的人工负担 |
19.3 成本与时延指标
| 指标 | 用途 |
|---|---|
| mean_time_to_recover | 从失败到恢复耗时 |
| compensation_cost | 恢复动作本身带来的成本 |
| p95_recovery_time | 长尾恢复耗时 |
如果没有这些指标,团队往往会低估失败恢复对系统成本和体验的影响。
20. 补偿逻辑一定要进入测试和演练
一个非常常见的反模式是:
- 正常路径有测试
- 失败恢复和补偿几乎没演练过
结果就是:
- 真的出问题时,补偿逻辑第一次被执行就是在线上
建议至少覆盖三类验证:
20.1 单步失败测试
- 某一步失败时是否触发预期补偿
20.2 部分成功测试
- 前两步成功、第三步失败时是否能判断真实状态
20.3 补偿失败测试
- 补偿本身失败时是否能正确升级到人工
Temporal 和 AWS Step Functions 相关文档都在强调:
- 恢复逻辑必须被明确建模和演练,而不是寄希望于线上第一次就成功
21. 常见失败模式
21.1 所有失败都只靠重试
后果:
- 半完成状态越来越多
21.2 有副作用却不记录状态
后果:
- 系统根本不知道哪些动作已经发生
21.3 补偿前不确认真实状态
后果:
- 补偿可能误伤已经正确完成的步骤
21.4 补偿逻辑没有幂等
后果:
- 补偿重跑又产生新副作用
21.5 不可逆动作没有人工兜底
后果:
- 失败后只能到处找人手工查
21.6 补偿没有监控和演练
后果:
- 平时看起来没问题,出事时一塌糊涂
22. 优化补偿机制的常见手段
22.1 先提高幂等性
- 能幂等就先幂等
22.2 给关键步骤显式声明补偿动作
- 不要临时想“失败后怎么收”
22.3 引入状态确认步骤
- 补偿前先查真实状态
22.4 把补偿上下文结构化
- 为人工和系统都留出可解释上下文
22.5 高风险动作提前设计人工路径
- 不把所有恢复责任都压给自动系统
22.6 给补偿逻辑本身建观测与评测
- 补偿不是隐形逻辑
这样恢复能力才会从“经验活”变成“系统能力”。
23. 一个可执行的落地流程
23.1 先盘点有副作用的关键工具
列出:
- 写数据库
- 发消息
- 创建工单
- 发起审批
- 调第三方业务 API
23.2 给每个动作分级
至少分:
- 幂等可重试
- 可补偿
- 不可逆
23.3 为可补偿动作定义补偿操作
包括:
- 谁来补偿
- 补偿前条件
- 补偿顺序
- 是否需要审批
23.4 为不可逆动作定义人工兜底
包括:
- 何时升级
- 提供什么上下文
- 谁来接手
23.5 把补偿事件接入状态机和观测系统
能看到:
- 哪步触发补偿
- 补偿是否成功
- 最终是否恢复到可接受状态
23.6 做失败演练和回归集
把真实线上故障沉淀成长期样例。
24. 推荐搭配阅读
25. 落地检查清单
- 是否梳理过关键工具动作的副作用,而不是只看工具名?
- 是否区分了幂等可重试、可补偿和不可逆动作?
- 是否为关键步骤显式定义补偿动作、前置条件和顺序?
- 是否在补偿前确认当前真实状态,而不是凭上一步返回值盲补?
- 是否为高风险和不可逆动作设计了人工兜底路径?
- 是否记录了补偿触发原因、补偿动作和最终恢复状态?
- 是否对补偿逻辑本身做了重试、幂等和失败监控?
- 是否把补偿链路纳入事件模型、状态机和人工纠偏工作台?
- 是否定期演练“部分成功”“补偿失败”“人工接管”这些场景?
- 是否把真实线上恢复案例沉淀成回归样例?
26. 推荐资源
以下资源在 2026-07-07 检查时可访问,适合作为补偿事务、Saga、重试与恢复设计的官方参考。
OpenAI
- Agents orchestration:https://developers.openai.com/api/docs/guides/agents/orchestration
- Running agents:https://developers.openai.com/api/docs/guides/agents/running-agents
- Background mode:https://developers.openai.com/api/docs/guides/background
- Evaluate agent workflows:https://developers.openai.com/api/docs/guides/agent-evals
- Trace grading:https://developers.openai.com/api/docs/guides/trace-grading
Temporal
- Use cases and design patterns:https://docs.temporal.io/evaluate/use-cases-design-patterns
- Error handling - Python SDK:https://docs.temporal.io/develop/python/best-practices/error-handling
Microsoft
- Compensating Transaction pattern:https://learn.microsoft.com/azure/architecture/patterns/compensating-transaction
AWS Step Functions
- Error handling:https://docs.aws.amazon.com/step-functions/latest/dg/concepts-error-handling.html
- Restarting executions:https://docs.aws.amazon.com/step-functions/latest/dg/redrive-executions.html
27. 最后总结
工具调用补偿机制不是“失败后再想想怎么办”,而是 Agent 工具链能否稳定进入生产环境的关键前提。
它真正要解决的是:
- 某一步成功、后一步失败时,系统如何知道自己已经做到哪
- 前面的副作用是该重试、该补偿、该保留,还是该转人工
- 恢复动作本身失败时,系统是否还有后手
如果这些问题没有被机制化,系统就会不断积累半完成状态、脏数据和人工救火成本。
更成熟的做法,是把动作分类、幂等设计、状态确认、补偿顺序、人工兜底、事件观测和失败演练一起做进去,让恢复能力本身也成为可设计、可监控、可评测的一等公民。
这样 Agent 系统面对失败时,不再只是“重试几次看看”,而是能够更稳定地把业务拉回可接受状态。