AI agent Compensation Saga:工具副作用怎么补偿才不会越补越乱

HTMLPAGE 团队
15 分钟阅读

工作流真正难点在副作用补偿。本文给出 AI agent 的 saga 设计方法,覆盖可逆动作、不可逆动作与人工兜底策略。

#AI agent #Saga #Compensation #Side Effects

DAG 能保证顺序,但不能天然保证正确撤销。只要流程里有外部副作用,比如发邮件、创建工单、扣费、改状态,就需要补偿设计。

真正难点不在“写一个 undo 函数”,而在于你是否能证明补偿发生在正确时机、作用在正确对象、且不会二次伤害系统。

先把动作分级,再谈补偿

动作类型示例处理策略
可逆动作更新草稿状态自动补偿
条件可逆扣减额度先读状态再补偿
不可逆动作对外发送邮件人工兜底 + 证据包

不先分级,会把不可逆动作也放进自动重试,风险极高。

Saga 在 agent 里的最小状态机

建议至少定义以下状态:

EXECUTED -> COMPENSATING -> COMPENSATED | COMPENSATION_FAILED

不要只记录布尔值。布尔值无法表达“正在补偿”与“补偿失败待人工”的区别。

补偿记录的最小字段

  • 原动作 ID(actionId)
  • 目标对象 ID(targetId)
  • 补偿动作 ID(compensationId)
  • 补偿函数版本(compensationVersion)
  • 执行时间、执行人/执行体
  • 残留风险说明

这些字段必须进入 ledger,否则事故复盘会陷入“到底补偿没”的口水战。

设计原则:补偿不是反向重放

补偿动作应显式声明,而不是“把原步骤反着跑”。例如:

  • 原动作:创建工单
  • 补偿动作:关闭工单并标记原因

两者语义不同,不能靠自动推导。

失败案例:重复补偿引发二次事故

某团队在超时恢复时重复触发补偿,把已经回滚成功的订单再次撤销。根因是补偿链路没有幂等键,且重试策略把补偿当普通节点重跑。

修复动作:

  • 所有补偿请求强制携带 compensationId。
  • 补偿执行前读取目标状态并做“已补偿”判断。
  • 补偿失败进入人工队列,不再自动无限重试。

如何与审批流协同

高风险补偿建议引入审批节点:

场景是否审批
内部状态回滚
对外通知撤回
资金/额度回滚

审批并不是增加负担,而是避免补偿链路扩大事故面。

关键指标

  • 补偿成功率
  • 补偿幂等命中率
  • 补偿平均完成时长
  • 补偿后残留风险率

补偿成功率高但残留风险率高,说明你的补偿只是“技术成功”,不是“业务成功”。

补偿策略如何写进节点契约

不要把补偿逻辑藏在业务代码里。建议直接写入节点契约:

{
  "nodeId": "publish_response.v3",
  "sideEffects": [
    {
      "effectType": "external_message",
      "target": "crm.contact_thread",
      "idempotencyKey": "runId + nodeId + targetId",
      "reversible": false,
      "compensationPolicy": "manual_followup_with_audit_note"
    }
  ]
}

这段契约明确告诉 runtime:该节点不是简单可逆动作。失败后不能自动“撤回消息”,而应创建人工跟进任务,并附带审计说明。

补偿优先级不是按失败时间排

当多个补偿任务同时出现时,优先级应按业务风险排序:

优先级类型原因
P0资金、权限、对外承诺影响不可控且扩散快
P1外部状态写入影响客户和协作系统
P2内部状态回滚可控性较高
P3临时 artifact 清理可延后处理

这能避免团队把时间花在“清理漂亮”,却放任高风险副作用继续扩散。

Checklist

  • 每个副作用动作都有补偿声明
  • 补偿状态机可观测而非布尔值
  • 补偿流程支持幂等执行
  • 不可逆动作有人工兜底和审批策略
  • 补偿结果进入 run ledger 与复盘模板

延伸阅读: