1. 先弄清楚:Agent 任务里的"数据一致性"和数据库里的不是一回事
1.1 一次 OpenClaw 任务到底包含多少"副作用"
系列写到第 18 篇,这次聊聊一个特别容易被忽视、但一到生产环境就躲不开的问题:OpenClaw 任务执行过程中的事务管理,以及它和数据库事务的根本差异。
先描述一个我上个月真实踩过的场景。我们团队用 OpenClaw 跑了一个"数字员工",任务内容很简单:每天凌晨从 ERP 导出订单快照,清洗后同步到业务库,再给客户打标签。这个任务刚开始跑得很正常,第三天凌晨,负责同步的流程因为数据库连接池超时失败了一次,系统按默认策略自动做了重试。结果第二天早上,业务同事告诉我,有两千多条客户标签彻底乱了——不是少了,是重复、错位、新旧数据混在一起。
我后来仔细复盘才发现,OpenClaw 的数字员工执行一个真实业务任务,几乎从来不是"一次模型调用"那么简单。以我们跑的"ERP 订单快照同步 + 客户打标签"为例,完整链路是:任务启动后,先调用 ERP 接口拉取增量订单;接着把订单数据清洗、去重、落成中间文件放到 workspace;再连接业务库执行一批 upsert;然后按规则给客户打标签;最后把结果汇总发送到飞书群。这里每一步都是对真实世界的"副作用":拉数据没有副作用,但写中间文件、写数据库、改标签、发消息,全都是会产生持久影响的操作。
问题在于,这些副作用分散在文件系统、外部 API、数据库、IM 机器人等多个系统里,任何一个环节都可能失败。任务管理器把失败的任务重新调度起来时,它只有一个朴素的目标:把这个任务重新执行完。可它不知道的是,上一轮执行已经写入了 30% 的数据库记录、已经往外部系统发了请求。重试一旦启动,旧数据和新增数据混在一起,最终结果就会变得不可预测。
1.2 没有事务管理时的三类典型事故
我总结了一下,OpenClaw 任务如果不做事务管理,数据一致性事故基本逃不出这三类。
第一类,也是最常见的一类,是重复执行。任务失败后自动重试,上一个执行周期里已经成功写入的数据,又被当作新数据处理了一遍。如果目标是"插入客户记录",那就会出现重复客户;如果目标是"累加计数器",那数字直接翻倍。我们这次订单标签错乱,就是典型的重复执行叠加了脏数据。
第二类是半更新。一个任务要同时更新数据库里的订单状态、文件系统里的报表、某个外部系统的记录,结果写到第二个步骤时挂了,三个系统各留下不一致的状态。数据库里显示订单已同步,外部系统里根本没有这条记录,workspace 里的中间文件又停留在写入一半的状态。这种情况比重复执行更隐蔽,因为从单个系统里看每个数据都是"正常"的,只有跨系统比对才能发现问题。
第三类是脏工作区。OpenClaw 的数字员工依赖 workspace 里的文件作为中间产物,上一次失败执行留下的残缺文件,会被下一次执行当成有效输入。我见过最典型的一种:任务是"读取昨天的订单快照,生成今天的新报表",失败后中间文件只写到一半,重试时读取到的却是残缺数据,最后生成了一张看起来完整、实际上缺了 60% 数据的报表。
这三种事故,表面上看是 bug,本质上是没有事务管理的保护:任务没有明确的提交点,失败后没有回滚或补偿机制,重试时也没有幂等保障。任何跑真实业务的团队,迟早都会撞上一次,只是时间早晚的问题。
1.3 为什么不能直接把数据库那套 ACID 搬过来
很多人第一个念头是:数据库事务不是现成的吗?直接把任务里的所有操作包在一个大事务里不就行了?
可惜,ACID 那一套是针对单一存储系统设计的。数据库可以靠 undo log 把未提交的修改全部撤回来,但 OpenClaw 任务面对的是一个分布式环境:文件系统没有 undo log,外部 API 一旦调用了就无法撤销,飞书消息发出去了也不能撤回。这种场景下,传统的原子性根本做不到,一致性只能靠"业务层的最终一致"来找补。
数据库事务和 OpenClaw 任务事务的区别,我用一个表格来对比,大家感受一下:
| 维度 | 数据库事务 | OpenClaw 任务事务 |
|---|---|---|
| 作用范围 | 单一数据库/存储引擎 | 文件系统、外部 API、数据库、IM 等多个系统 |
| 回滚方式 | undo log 物理撤销 | 快照恢复、补偿动作、幂等重放 |
| 提交点 | COMMIT 一条指令 | 每个步骤的完成标记 + 人工审批门禁 |
| 隔离性 | 锁和 MVCC | 任务级隔离,依赖幂等键 |
| 一致性目标 | 强一致 | 最终一致 |
所以 OpenClaw 的事务管理,本质上是另一套思路:不追求全链路原子性,而是通过记录
