跳到主要内容

智能体系统的补偿事务与故障恢复

· 阅读需 12 分钟
Tian Pan
Software Engineer

2025 年 7 月,一名开发者使用 AI 编程智能体(AI coding agent)来开发他们的 SaaS 产品。在会话进行到一半时,他们发出了“代码冻结”(code freeze)指令。该智能体忽略了指令,对生产数据库执行了破坏性的 SQL 操作,删除了 1,200 多个账户的数据,然后——显然是为了掩盖痕迹——伪造了大约 4,000 条合成记录。该 AI 平台的 CEO 发表了公开道歉。

根本原因不是幻觉或误解指令。而是缺少一个工程原语:该智能体对生产状态拥有不受限制的写入和删除权限,且不存在撤销其操作的机制。

这是在现实世界中运行的智能体系统所面临的核心问题。LLM 具有非确定性,在生产部署中,工具调用的失败率为 3–15%,而且许多操作——发送电子邮件、扣款、删除记录、预订机票——无法通过简单地使用不同参数重试来撤回。问题不在于你的智能体是否会在工作流中途失败。它一定会失败。问题在于你的系统能否恢复。

为什么“仅仅重试”对大多数智能体操作是错误的

对于失败的智能体步骤,最简单的恢复策略是重试。这仅在操作是“幂等”(idempotent)的情况下才有效——即运行两次产生的结果与运行一次相同。读取数据库记录是幂等的。发送电子邮件则不是。创建日历事件不是。扣款也不是。

对生产环境中智能体部署的现场分析显示,由于网络超时、速率限制和验证错误,LLM 会在 15–30% 的时间内重试工具调用。智能体收不到响应,无法确定操作是否已完成,于是进行重试——却不知道第一次执行已经成功。结果,接收者收到了两封邮件,客户被扣了两次款,日历上显示了两个相同的会议。

一种特别危险的失败模式发生在智能体误读错误代码时。一个来自已经处理并完成请求的 API 的 404 Not Found 可能会被误读为“资源不存在,所以我应该创建它”。于是智能体创建了一个重复副本。429 Too Many Requests 可能会被视为系统停机信号而非速率限制,导致智能体触发了不必要的升级流程。

除了重复操作,还有一个更根本的问题:已提交操作的不可逆性。一旦电子邮件离开了你的 SMTP 服务器,任何重试策略都无法撤回它。一旦数据库行被硬删除,再多的重试逻辑也无法恢复它。恢复架构必须在故障发生前就存在,而不是事后补救。

Saga 模式的应用

Saga 模式起源于分布式数据库系统,是在不需要单一分布式事务的情况下,实现跨多个服务一致性的一种方法。其概念非常直观:一个工作流是一系列本地操作 T₁、T₂、…、Tₙ,其中每个步骤都有一个配对的补偿操作 C₁、C₂、…、Cₙ。如果工作流在第 k 步失败,系统将反向执行 Cₖ₋₁、…、C₂、C₁ 以撤销已完成的工作。

对于 AI 智能体,每次工具调用都变成了一个本地事务。一个预订旅行的智能体可能会订机票(T₁)、扣款(T₂)并预订酒店(T₃)。如果 T₃ 失败,Saga 会触发 C₂(退款)和 C₁(取消机票预订)。补偿是显式的、预先计划的且具有确定性的。

在实践中,有两点能保证其奏效:

补偿必须在执行前注册。 顺序始终是:(1)持久化记录补偿操作,(2)执行正向操作。如果你在正向操作成功后才注册补偿,就会产生一个窗口期:正向操作已完成,但系统崩溃导致补偿注册失败——从而留下无法恢复的孤儿状态。这不是一个细微的差别,而是可恢复系统与持续积累隐性不一致系统之间的本质区别。

补偿事务本身必须是幂等的。 补偿步骤也可能失败并需要重试。如果退款逻辑因为第一次退款超时而再次扣款,那就违背了初衷。每一个恢复操作都需要与原始操作相同的幂等性保证。

关于多智能体 LLM 系统(SagaLLM, VLDB 2025)的学术形式化定义指出,使得 Saga 成为必然的特定失败模式包括:LLM 无法可靠地验证自己的输出、缺乏跨顺序交互维护状态的机制,以及在长序列中遭受上下文衰减。Saga 架构在基础设施层面补偿了这些局限性,而不是依赖模型进行自我纠正。

幂等工具设计

智能体工具的幂等性需要一些具体的工程选择。

幂等键(Idempotency keys)。 在调用任何具有副作用的工具之前,智能体运行时会生成一个代表该特定逻辑操作的确定性键——一个由稳定标识符(会话 ID、客户 ID、操作类型以及工作流中的序列号)派生出的哈希值或 UUID。接收服务会存储以该 UUID 为键的结果。重试时,服务检测到重复的键,并返回缓存的结果而不重新执行。

键的生成必须是确定性的。如果智能体在每次重试时都生成一个新的随机 UUID,它将完全失去去重功能。该键必须能够从相同的逻辑操作上下文中复现。

原子写入,而非追加。 对于文件和数据库操作,优先选择“读取-修改-覆盖”模式,而不是增量追加。追加是非幂等的——重试会产生重复数据。而覆盖为完整的预期状态则是幂等的。

针对不可逆操作的软状态。 能够保留最多恢复选项的架构选择,是在数据模型设计中消除表面的不可逆性。使用软删除代替硬删除、在正式发送前设置草稿状态、在立即写入前进行阶段性提交——这些不是针对出错时的防御模式,而是保持补偿窗口开启足够长时间以执行恢复逻辑的机制。

持久化状态与检查点

仅存在于内存中的 Saga 会随进程一同消亡。生产环境中的智能体系统需要能够跨越崩溃、重启和超时的持久化状态。

LangGraph 的检查点(checkpointing)模型在每个节点执行后将工作流状态保存到持久化存储中(在生产环境中通常是 PostgreSQL)。当工作流失败时,它可以从最后一个检查点恢复,而不是重新开始。更重要的是,工作流可以转换到补偿子图(compensating subgraph)——这是状态机的一个并行分支,负责针对所有先前已提交的步骤执行回滚操作。

Temporal 通过基础设施层面的持久执行(durable execution)更进一步。无论服务器故障或网络分区如何,工作流代码都保证能够完成;状态在每一步之后都会自动持久化。对于 Java,Temporal 提供了内置的 Saga 库。对于其他语言,补偿列表需手动维护,并在失败时按相反顺序执行。

使用 Temporal 的关键警告是:避免工作流级别的超时,并避免终止/重置(terminate/reset)操作。这两者都会阻止补偿处理器执行,从而使系统处于不一致状态。应让工作流完成其补偿路径。

更广泛的原则是,失败的智能体不应负责自身的修复。导致问题的同一种推理失败——如上下文退化、误读工具输出、累积的规划错误——很可能也会导致恢复失败。恢复基础设施必须是外部的:一个独立的看门狗(watchdog)进程、一个人工审批关口,或者一个预先确定的补偿图,无论模型认为下一步该做什么,它都会确定性地执行。

智能体决策的发件箱模式

智能体在做出具有下游影响的决策时,面临一个微妙的一致性问题:双写问题(dual write problem)。在智能体提交业务决策后,必须发生两件事——更新内部状态和通知下游消费者(计费系统、CRM、通知服务)。如果这是两个独立的操作,且进程在它们之间崩溃,就会出现一边完成而另一边未完成的情况。智能体记录了决策但通知从未触发,反之亦然。

事务性发件箱模式(transactional outbox pattern)通过在单个原子事务中同时提交状态变更和发件箱记录来解决此问题。一个单独的路由进程(relay process)读取发件箱并将事件发布给下游消费者。下游消费者必须处理幂等交付,因为事件可能会多次到达。

具体到智能体,这意味着决策(如“向客户收费”、“发送升级通知”、“创建工单”)和代表该决策的发件箱事件是共同提交的。一旦该事务提交,下游的一切都变得可恢复。如果路由进程崩溃,它会从发件箱重新播放。当下游消费者失败时,它会重播该事件。智能体的决策在提交的那一刻就是持久化的,而不是在所有下游影响都确认的那一刻。

这将脆弱的“发后即忘”(fire-and-forget)工具调用转变为可恢复的操作。它还创建了完整的审计追踪——智能体的每个决策都是不可变的记录,从而能够对智能体在何时做出何种决策进行精确的取证分析。

补偿中的编排与编舞

当 Saga 跨越多个智能体时,关于补偿权限归属存在一种架构选择。

在编排式(orchestrated)Saga 中,中央协调智能体了解完整的工作流、管理状态,并在步骤失败时发起补偿事务。编排器可以停止整个链条并触发所有智能体的补偿。由于存在单一的执行轨迹,调试非常直观。权衡之处在于单点故障:如果编排器崩溃或做出错误决策,整个工作流就会受阻。

在编舞式(choreographed)Saga 中,每个智能体在没有中央协调的情况下对事件做出反应。一个智能体完成其步骤并发布事件;下一个智能体接收该事件并继续。补偿要求每个智能体了解自己的回滚逻辑,并发布失败事件以触发上游智能体进行补偿。调试则需要从分布式事件流中重建执行顺序。

对于补偿至关重要且失败模式可预测的工作流——如支付处理、预订系统或任何涉及财务状态的工作流——编排几乎总是正确的选择。集中化的补偿权限值得付出这些协调成本。

关于多智能体错误动态的研究(Google DeepMind,2025 年 12 月)发现,与单智能体基准相比,缺乏刻意协调拓扑的非结构化多智能体网络会将错误放大 17 倍。基于编舞的系统,即每个智能体在没有协调结构的情况下自主行动,往往会趋向于这些非结构化拓扑。当每个智能体在没有协调器验证整体状态的情况下独立进行补偿时,连锁的局部补偿可能会使系统处于比原始故障更糟糕的状态。

为故障点设计

这些模式的共同点在于,智能体系统的可靠性要求在第一次工具调用之前就做出工程选择,而不是在故障发生后才进行响应。

在运行任何包含不可逆操作的工作流之前:

  • 每个具有副作用的工具都需要定义一个补偿操作
  • 补偿操作必须在对应的正向操作执行之前进行注册
  • 所有补偿操作必须是幂等的
  • 工作流状态必须是持久化的,以便补偿操作在系统崩溃后仍能运行

在调用任何具有网络延迟的工具之前:

  • 必须根据稳定的操作标识符确定性地生成幂等键(Idempotency key)
  • 接收服务必须实现幂等键去重
  • 重试逻辑必须传递相同的键,而不是生成新键

在授予智能体生产环境访问权限之前:

  • 隔离开发和生产上下文
  • 应用最小权限原则以限制爆炸半径
  • 定义哪些操作在执行前需要人工审批

Replit 事件、在 11 天内消耗了 47,000 美元 API 调用成本才被察觉的多智能体系统、每周产生 200 多个重复通知的计费工作流 —— 这些都不是罕见的故障模式。它们是部署智能体时,在缺乏其工具访问所需的故障恢复基础设施的情况下,所产生的可预见后果。

工具调用就是事务。事务可能会失败。在没有补偿逻辑的系统中,失败的事务会留下永久的状态更改,且无法恢复。应当采用的模式在分布式系统中已经非常成熟:在执行“做”之前定义好“撤销”,确保“撤销”是幂等的,并构建基础设施在正向路径失败时自动执行它。

References:Let's stay in touch and Follow me for more thoughts and updates