智能体副作用的自动回滚
智能体副作用自动回滚无法撤销每一项外部操作。了解快照的边界,以及何时必须开始审批或补偿。

智能体回滚可以恢复代码、配置或选定的应用数据,却无法让另一家机构忘记一个请求,无法从收件箱中收回已送达的邮件,也无法假装信用卡扣款从未到达支付处理商。把这些操作都称为“回滚”的团队,构建的是一种令人安心的控制措施,而它恰恰会在后果最重要时失效。
安全的设计应区分四种机制:应用快照、Git revert、数据库恢复和补偿操作。每种机制负责不同的边界。任何跨越应用边界的操作,在智能体执行前都需要自己的审批、证据、重试规则和恢复路径。如果没人能用一句话说明这条路径,这项操作就还不适合无人值守地执行。
回滚有四种不同含义
只有团队说清楚要恢复什么状态、无法触及什么状态时,回滚才有用。这个词往往掩盖了四种保障完全不同的机制。
快照会恢复应用或工作区已捕获的版本。视产品而定,快照可能包含生成的代码、配置和选定的受管状态。除非快照契约明确写明,否则它与外部服务无关。
Git revert 会记录一个新提交,其改动与较早提交引入的改动相反。它修复源码历史,不会删除那段历史。它不会联系旧代码运行时调用过的服务。
数据库恢复会更改由数据库保存的记录。事务回滚会丢弃单个事务中尚未提交的写入。恢复备份或使用时间点恢复的范围大得多,它会将数据库集群推向较早的状态。两者都不会自动协调该数据库之外的系统。
补偿操作会产生一个新影响,目的是抵消旧影响。退款补偿已捕获的付款,取消请求补偿订单。更正邮件可以减轻错发邮件的影响,却无法移除第一封邮件。补偿保留了一个不舒服的事实:原始操作已经发生。
我会在设计评审中使用影响归属表,因为它强迫团队给出准确答案:
| 变更 | 恢复责任方 | 常见机制 | 能抹去原始影响吗? |
|---|---|---|---|
| 生成的源码 | 应用或仓库 | 快照恢复或 Git revert | 通常可以,对未来执行而言 |
| 已提交的记录 | 数据库运维方 | 逻辑更正或恢复 | 有时可在本地实现 |
| 已送达邮件 | 邮件服务商和收件人 | 后续邮件或抑制待发送邮件 | 不可以 |
| 已捕获付款 | 支付处理商 | 撤销或退款 | 不可以 |
| 外部 API 请求 | 接收服务 | 服务商特定的取消或补偿 | 通常不可以 |
最后一列最重要。存在一个反向操作,并不代表原始操作已经消失。审计记录、收件人的副本、结算记录、webhook 和实体工作都可能仍然存在。
代码 revert 改变程序,不会改变过去
代码 revert 能阻止或改变未来行为,不能逆转旧版本已经造成的行为。无论团队使用 Git、平台快照还是部署回滚,这一点都成立。
Git 文档将 git revert 描述为记录一些提交,用以逆转较早提交引入的变更。这个表述很准确:Git 对仓库内容应用反向补丁。Git 不知道已回滚提交运行时创建的邮件、付款、云资源、支持工单或合作方 API 请求。
假设某个智能体修改了计费函数、部署后,在监控发现缺陷前调用了两次。回滚提交可以阻止错误函数再次运行,却会在处理商那里留下两次付款尝试。如果本地数据库只记录了一次尝试,revert 甚至可能因移除了能理解第二次响应的代码路径,让调查变得更困难。
部署回滚也有同样的边界。把流量切回旧构建能恢复可执行行为。已被替换构建接受的请求会保留后果。排队任务也可能在部署后继续存在,并依照旧假设在恢复后的版本上执行。
回滚前,应保存旧构建产生的运维证据:
- 部署标识符和源码提交
- 智能体运行标识符和意图标识符
- 队列消息标识符和租约状态
- 外部请求标识符
- 服务商响应和时间戳
然后停止新的执行,核对未完成操作,再恢复代码。先 revert、事后再追问,往往会破坏最容易查明哪些影响已经越界的线索。
快照的范围可能比 Git 提交更广,但同一条规则仍然适用。快照契约必须准确说明包含哪些资源。如果它包含代码和配置,就称它为代码与配置恢复机制。不要靠界面中充满希望的措辞,把它包装成万能撤销。
数据库恢复的职责比人们想象中更窄
数据库恢复恢复的是数据库状态,不是交易所有参与方共同构成的业务现实。一次事务可以在一个数据库内保持原子性,而周围操作仍会分散在多个系统中。
考虑以下过程:
- 智能体插入一条发票记录。
- 它调用支付 API。
- 处理商接受扣款。
- 数据库提交失败。
本地回滚会移除发票记录,扣款仍然存在。若不先对账就重试整个操作,可能再次向客户扣款。这就是经典的双写失败:应用试图让一项业务操作在不共享事务协调器的系统之间保持原子性。
调换顺序也解决不了问题。如果应用先提交发票,随后支付调用失败,数据库中会留下一张未付款发票。这个状态更容易检查,但应用仍需要状态机来区分 payment_pending、payment_confirmed、payment_failed 和 payment_unknown。
PostgreSQL 文档将时间点恢复解释为:恢复基础备份,并将预写日志记录重放到选定的恢复目标。这是恢复数据库集群的运维流程,不是针对某次智能体运行的选择性撤销,也不能要求支付处理商或邮件服务回到同一时间点。
把数据库回退还可能制造第二种不一致。设想故障后恢复到 10:00。某服务商一直在接受请求,直到 10:07,而恢复后的数据库已没有那些记录。看到“缺失”记录的智能体可能重建整整七分钟的工作。因此,恢复后工作人员继续运行前,需要先进行外部对账。
发件箱模式能缩小一个危险缺口。应用会在同一笔本地事务中提交业务变更和影响意图:
BEGIN;
INSERT INTO invoices (invoice_id, customer_id, status)
VALUES ('inv_2048', 'cust_91', 'payment_pending');
INSERT INTO effect_intents
(intent_id, operation, subject_id, status)
VALUES
('eff_7f31', 'capture_payment', 'inv_2048', 'pending');
COMMIT;
之后,工作进程领取 eff_7f31,带着稳定的幂等值调用服务商,并记录结果。发件箱不会让外部调用变成原子操作。它为系统留下了工作原本要执行的持久证据,使重试和对账成为可能。
外部操作需要补偿,有些操作没有补偿办法
外部副作用需要服务商特定的补偿方案,或明确说明不存在有意义的补偿。把所有操作都当成可逆,比承认某些操作必须审批更糟。
邮件是最简单的例子。服务商接收消息前,应用或许可以取消排队任务。接收后,服务商可能允许在短暂的内部阶段取消,但无法对所有收件人和邮件系统提供普遍的撤回保证。一旦送达,第二封邮件只能更正记录,无法抹掉第一封。敏感数据、法律通知和声誉损害仍可能暴露在外。
支付有多种状态,团队常常把它们都压缩成“已扣款”。预授权会预留支付能力。捕获会请求从该授权中划转资金。尚未结算的授权或许可通过撤销来释放。退款则是在捕获后创建一笔返还资金的后续金融记录。这些操作的时机、费用、权限和对客户的影响各不相同。通用的 undo_payment 工具会隐藏智能体安全行动所需的信息。
其他 API 的配合程度更低。请求可能订购库存、配置基础设施、发布内容、授予访问权限、寄出包裹,或触发某人开始工作。DELETE 端点不能证明可逆性。删除资源后,审计日志、复制的数据、通知、依赖资源或实体后果都可能留下。
应按补偿能诚实实现的结果分类:
- 精确的本地反向操作会将受控状态恢复为原值。
- 服务商取消会停止尚未完成的工作。
- 金融补偿会创建退款或贷记。
- 更正沟通承认第一条消息仍然可见。
- 人工处置负责那些无法装进安全自动规则的影响。
补偿也可能失败。退款端点可能超时,取消窗口可能关闭,收件地址可能拒收更正邮件,智能体所用账户可能没有权限。因此,系统必须把补偿作为另一项操作跟踪,拥有自己的意图标识符、状态、尝试次数、证据和审批策略。
不要构建递归的“回滚回滚”功能。应将历史建模为操作账本。若补偿引发新错误,应在审查当前状态后发起另一项明确操作。这样的历史更长,却能在事故中保持清楚。
幂等性阻止重复,不会逆转成功
幂等性能保护重试,避免产生重复的预期影响,却不会撤销第一次成功的影响。团队经常混淆这两个概念,随后在超时发生后才发现差别。
RFC 9110 按预期影响来定义幂等请求方法:多次相同请求的预期影响,与一次此类请求相同。它将 PUT、DELETE 和安全方法列为协议语义层面的幂等方法。POST 通常不具备幂等性,不过 API 可以通过自身契约加入幂等行为。
这个限定很重要。幂等的 DELETE 每次仍可能产生新的日志记录、指标或响应。服务商的幂等实现也可能会让记录过期、将标识符限定在一个账户内、拒绝参数变更,或只缓存部分结果。应阅读服务商契约,不要从 HTTP 动词推断保证。
每个影响意图在首次尝试前都应获得一个稳定的幂等值。同一意图的重试继续使用它。新的业务意图使用新的值。绝不能只从客户、金额和日期等可变参数生成它,因为两次合法购买可能有相同的这些值。
超时意味着结果未知,不等于失败。请按以下顺序处理:
- 将尝试标记为
outcome_unknown,不要创建替代意图。 - 用幂等值或操作引用向服务商查询。
- 如果服务商确认成功,在本地记录成功。
- 如果它确认没有操作,用相同的值重试。
- 如果它无法回答,将操作暂停,等待对账或人工审查。
以下响应结构让智能体能够区分已被接受与传输不确定:
{
"intent_id": "eff_7f31",
"attempt": 2,
"idempotency_key": "eff_7f31",
"transport_status": "timeout",
"provider_status": "unknown",
"provider_reference": null,
"next_action": "reconcile"
}
幂等值应在支持的情况下贯穿日志、队列消息、API 请求头和服务商元数据。如果运维人员无法在边界两侧用它搜索,恢复时就只能猜测。
审批应放在影响边界处
审批必须发生在智能体形成准确的外部操作之后、第一项不可逆请求离开系统之前。对一个宽泛任务在开始时审批,会给智能体太大空间,让它后来变更收件人、金额、范围或工具。
“处理这位客户的账户”不足以批准扣卡或向所有用户发邮件。有效审批应描述具体操作:收件人、金额和币种、消息或载荷摘要、目标账户、工具、过期时间和允许尝试次数。任何获批字段发生变化,审批就不再匹配。
有用的策略按影响后果分类,而不是按哪个智能体或模型提出请求分类。读取权限也可能暴露私密数据,却没有外发操作那样的恢复难题。起草邮件在本地且可逆,发送则跨越边界。创建付款提案在本地,捕获资金则跨越边界。
下列操作应要求明确审批:
- 转移资金或产生金融义务
- 向个人或外部机构发送信息
- 在受控存储之外发布、删除或披露数据
- 更改身份、访问权限、所有权或安全设置
- 启动无法可靠撤回的实体工作或其他流程
后果较低、重复性的操作可以使用有边界的长期审批。边界应写明最高金额、收件人集合、允许的工具、过期时间、频率和总操作次数。“已批准用于计费”没有可执行的边界。
审批还要防止重放。将它绑定到操作的不可变摘要,并在策略只允许执行一次时标记为已使用。如果执行结果未知,不要重新申请审批并创建第二个意图。应先核对已获批的意图。
审批页面应以日常语言说明恢复的事实。“此消息送达后无法撤回”很有用。当实际恢复是退款,且退款可能需要时间并留在财务账单上时,“此操作可逆”就会误导人。
工具契约应公开完整的影响生命周期
智能体工具契约应把意图、执行、观察和补偿描述为不同操作。一个执行影响并返回 success: true 的单一函数,为重试、审批和事故响应留下的证据太少。
以下策略片段足够小,可以强制执行,也足够具体,便于审查:
tools:
send_email:
effect: irreversible
approval: required
idempotency_field: intent_id
evidence_field: provider_message_id
compensation: null
capture_payment:
effect: compensatable
approval: required
idempotency_field: intent_id
evidence_field: provider_payment_id
compensation: refund_payment
update_draft:
effect: local_reversible
approval: none
compensation: restore_version
effect 告诉规划器适用哪类恢复。approval 会阻止执行,直到准确参数获得授权。幂等字段让重试复用同一个身份。证据字段告诉运维人员哪些信息必须保留。补偿字段指向独立工具,而不是假装原调用可以倒着执行。
执行应接受不可变信封:
{
"intent_id": "eff_7f31",
"operation": "capture_payment",
"arguments": {
"invoice_id": "inv_2048",
"amount_minor": 12900,
"currency": "USD"
},
"approval": {
"approval_id": "apr_662",
"scope_hash": "sha256:8b4f...",
"expires_at": "2026-07-27T18:00:00Z"
}
}
执行器会计算操作摘要,将其与 scope_hash 比较,检查过期时间,保留意图,然后才联系服务商。它在调用前保存请求元数据,调用后保存响应。若在这两次写入之间崩溃,持久意图仍可用于对账。
以下四种情况应被执行器拒绝,不要临场变通:参数已变更、审批已过期、用同一意图执行不同操作,以及试图补偿尚未确认成功的影响。智能体擅长找到看似合理的后续行动。金融和通信控制必须优先选择清晰的停止,而不是看似合理的猜测。
应为观察提供独立工具,例如 get_payment_status(intent_id)。观察不应产生影响。将其分开,智能体便能解决结果不明的情况,而不会因此获得重试原操作的权限。
一次失败运行可能跨越所有恢复边界
单次智能体运行可能让代码、数据库记录和外部系统停留在不同时间点。上线前走一遍这类失败过程,能暴露通用回滚控制所掩盖的缺口。
假设某智能体构建了会员应用、部署改动、导入客户列表、收取年费并发送欢迎邮件。任务听起来统一,实际至少跨越四条恢复边界。
14:00,智能体部署了从错误列计算年费的代码。14:02,它向数据库写入 40 个付款意图和邮件草稿。14:03,工作进程捕获了几笔付款。14:04,邮件服务商接收了包含错误费用的欢迎邮件。14:05,监控停止工作进程。一些付款调用在到达处理商后超时,因此本地状态无法说明它们是否成功。
恢复 13:59 的代码快照能阻止未来运行继续使用错误计算,却不会改变已复制到现有意图中的费用。revert Git 提交记录了源码修正,但也有同样限制。
若将数据库恢复到 13:59,会移除包含服务商引用和幂等值的本地意图记录,使外部不一致更严重。更好的数据库操作是逻辑更正:保留意图,将不确定操作标记为待对账,只有在匹配服务商状态后才更正记录。
随后,响应团队应按影响类别处理。它用稳定操作身份查询每一笔不确定付款。确认捕获的付款进入退款审查,失败尝试不重试即可关闭,未解决尝试继续阻塞。对已送达邮件发送经过仔细审批的更正。尚未到达服务商的排队消息则取消。每项补偿都创建新的意图,并关联原始意图。
这个例子也说明了为什么自动补偿很危险。系统若立即退款所有本地 payment_unknown 记录,可能会为从未发生的扣款退款,或使用错误引用调用退款端点。数据库恢复后,若重发所有缺失邮件,收件人可能收到重复邮件。只要本地与服务商状态不一致,就必须先对账,再补偿。
只有每个意图都到达 succeeded、confirmed_failed、compensated 或 manual_exception 等终态时,这次运行才算完成。“应用已回滚”只描述了事故的第一部分。
只有证据留存,恢复才有效
当回滚删除了判断发生过什么所需的记录时,恢复控制就会失效。应在不受常规快照或恢复替换的应用状态之外,保存一份只能追加的影响账本,并保留足够的服务商证据来核对每项操作。
账本应记录意图创建、参数摘要、审批、执行租约、尝试、传输结果、服务商引用、观察到的服务商状态和补偿关联。应限制变更只能发生在状态转换中,而不是允许智能体覆盖旧条目。更正应新增事件,不应编辑历史。
应监控未解决状态,而不只是显式错误。持续十分钟的 outcome_unknown 可能比明确拒绝更危险,因为运维人员可能手动重试。还应在执行期间审批过期、一个幂等值带着不同参数摘要出现,或补偿失败时告警。
应通过刻意设置棘手故障点来演练恢复。在服务商接收请求后、本地写入成功前终止工作进程。在保留影响账本的情况下,从较早快照恢复应用数据。让审批在规划和执行之间过期。让观察 API 不可用。只有系统能停止、对账、公开未解决决策且不重复产生影响时,演练才算通过。
使用 Koder.ai 时,应将其快照和回滚视为应用恢复层,然后为数据库历史及应用可能触发的每项外部操作设计独立控制。源码导出、部署控制和回滚有助于恢复软件,而应用的工具契约仍负责审批和补偿。
回滚按钮旁应注明它的边界:代码、配置、受管数据或外部影响。如果界面无法把这句话说准确,就不应承诺回滚。诚实的控制看起来也许没那么神奇,却能在凌晨两点给事故团队最需要的东西:一份可信的说明,讲清楚改了什么、哪些影响已经越界,以及接下来哪项操作安全。
常见问题
回滚 AI 智能体能撤回已发送的邮件吗?
不能。它可以恢复请求发送邮件之前的代码或应用快照,却无法撤回邮件服务商已经接收的消息。系统应在发送前进行审批,并持久保存服务商的响应记录。
自动回滚能撤销信用卡扣款吗?
通常不能。回滚无法从支付处理商的记录中抹掉已结算的付款,系统必须以一笔新的金融交易发起退款。尚未捕获的预授权或许可以撤销,但这仍是一项明确的支付操作,不是代码回滚。
Git revert 实际撤销了什么?
Git revert 会创建一个新提交,对较早的代码改动应用相反的变更。它不会恢复数据库记录、取消 API 请求、删除已送达的消息或退还付款。应只把它当作修复源码历史的手段。
数据库回滚能撤销外部 API 调用吗?
数据库事务可以回滚尚未提交的本地写入。提交后,恢复操作可能将数据库还原到更早的状态,但这也可能移除不相关但有效的写入,且无法逆转外部系统中的操作。应将它视为恢复流程,而不是万能撤销按钮。
幂等键和回滚是一回事吗?
不能。幂等性可在服务商正确实现时,防止重复尝试产生重复影响。它不会取消首次成功的请求,也不能保证使用相同标识符的不同请求仍然安全。
哪些智能体操作需要人工审批?
当某项操作可能在应用外造成法律、财务、隐私、声誉或运营后果时,应要求审批。例如发送消息、扣款、发布数据、更改访问权限、下单,以及调用会启动实体工作的 API。读取操作和本地草稿通常不需要同样的关卡。
调用外部 API 前,智能体应记录什么?
记录意图标识符、准确参数、授权信息、审批范围、幂等值、尝试次数、服务商引用、响应状态和补偿状态。执行前先保存记录,并在每次尝试后更新。仅依赖应用日志很容易在您正在调查的回滚中丢失。
智能体怎样安全地重试超时请求?
只有当操作真正具备幂等性,或者接收服务能识别稳定的幂等值时,重试才安全。超时结果不明确,因为首次请求可能已成功,只是调用方没有收到响应。尽可能先用同一操作标识符向服务商查询,再发送新请求。
何时应自动执行补偿操作?
当外部操作支持有意义的反向操作,且策略允许时,可以使用补偿。退款、取消请求和更正消息都属于补偿,因为它们新增了历史,而不是删除旧历史。若可能造成另一笔扣款、另一条消息、权限变更或法律承诺,就不要盲目自动执行。
如何测试智能体工具的回滚安全性?
在预发布环境中演练一次故障:让服务商接收请求后、智能体记录成功前发生超时。确认系统能按操作标识符对账、避免重复,并记录所有补偿。还应测试审批过期、参数变更、服务商部分故障,以及应用数据恢复后的处理。