news 2026/7/27 5:23:56

Agent 工作流的异常处理:失败可恢复的执行设计

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Agent 工作流的异常处理:失败可恢复的执行设计

Agent 工作流的异常处理:失败可恢复的执行设计

一、多步执行的脏状态陷阱

Agent 一次任务往往分多步:查数据、调工具、写结果。走到第三步失败了,前两步的副作用已落地。数据库写了一半,文件改了一半,外部 API 调了。重跑?

从头来,副作用重复执行。不跑?脏状态留在系统里,下次接着错。进退两难,是 Agent 落地最痛的坑。

和单次推理不同,多步执行是"有状态"的。状态没管好,失败就不可恢复。本文探讨让 Agent 失败可回滚的执行设计。

二、事务式执行与检查点机制

可恢复的核心是"事务思维"。每步执行前,先存检查点(checkpoint)。检查点记录:当前状态、已完成的步骤、待补偿动作。失败时,按检查点回放补偿动作。

补偿是正向操作的反函数:写了就删,加了就减。不是所有操作都可补偿,不可补偿的必须前置校验。下面是事务式执行的链路:

flowchart TD A[Agent 任务] --> B[存检查点] B --> C[执行步骤] C --> D{成功?} D -->|是| E[更新检查点] E --> F{还有步骤?} F -->|是| B F -->|否| G[任务完成] D -->|否| H[读检查点] H --> I[执行补偿动作] I --> J[回滚到安全态] J --> K[断点续跑或告警] style G fill:#e8f5e9 style K fill:#ffebee

关键在"补偿的完备性"。不可补偿的操作(如发短信、转账)不能靠事后撤销。必须前置:要么改设计为可补偿,要么用两阶段提交。补偿设计不到位,回滚就是空谈。

补偿与回滚不是一回事。回滚是数据库事务的原生能力,依赖日志恢复到旧版本。补偿是应用层的正向操作,用“反向动作”抵消已产生的副作用。数据库写可以回滚,发出去的短信只能补偿,再发一条撤销通知。

补偿必须逆序执行。先执行的步骤副作用在底层,后执行的在上层。逆序撤销才不会破坏中间状态。比如先建文件再写内容,补偿要先删内容再删文件,顺序反了会报错。

检查点要存外部。进程内存里的检查点,进程一崩就没了。存数据库或对象存储,进程重启后能读回来续跑。检查点要带版本号,格式升级后旧检查点还能解析。

三、生产级带检查点的执行器

下面用 Python 实现一个带检查点与补偿的 Agent 执行器。

from dataclasses import dataclass, field from typing import Callable @dataclass class Step: """一个执行步骤:含正向动作与补偿动作""" name: str do: Callable[[], None] # 补偿是正向的反函数,失败时按完成步骤逆序调用 undo: Callable[[], None] = lambda: None @dataclass class Checkpoint: """检查点:记录已完成步骤,供失败时回滚与续跑""" completed: list[str] = field(default_factory=list) def save(self, name: str) -> None: if name not in self.completed: self.completed.append(name) def run(steps: list[Step], cp: Checkpoint) -> bool: """事务式执行:成功推进,失败按已完成步骤逆序补偿""" for step in steps: # 跳过已完成步骤,支持断点续跑 if step.name in cp.completed: continue try: step.do() cp.save(step.name) except Exception as exc: # 失败时逆序补偿已完成的步骤,回滚到安全态 for done in reversed(cp.completed): for s in steps: if s.name == done: try: s.undo() except Exception: # 补偿失败要告警,不能静默吞掉 print(f"补偿失败: {s.name}") print(f"任务失败于 {step.name}: {exc}") return False return True if __name__ == "__main__": cp = Checkpoint() def write_db(): print("写入数据库") def rollback_db(): print("回滚数据库") def call_api(): raise RuntimeError("API 不可用") steps = [ Step("write_db", write_db, rollback_db), Step("call_api", call_api), ] # 第二步失败,第一步的补偿会被自动触发 ok = run(steps, cp) print("成功" if ok else "已回滚")

真实系统会把检查点持久化到外部存储。进程崩了重启后,读检查点续跑,而非从头来。补偿动作要做幂等,重试不产生重复副作用。幂等靠业务 ID 兜底。

每个副作用操作带唯一键,执行前先查是否已做过。已做过直接跳过,未做过才执行。补偿动作同理,带键查重,避免重复撤销把正确的状态也撤掉。检查点写入要原子。

检查点与副作用操作不能跨网络分两次写,否则中间崩了会出现“副作用做了但检查点没记”的脏状态。应先写检查点标记“待执行”,再执行副作用,最后更新检查点为“已完成”。这套写前日志模式,借鉴自数据库的 WAL。

四、Agent 工作流的异常处理的代价与边界

事务式执行稳妥,但不是银弹。

补偿的不可行性。发短信、发邮件、真实扣款。这些操作无法撤销,补偿无从设计。必须前置校验,或用 saga 模式拆成可补偿的子事务。

检查点的存储成本。每步存检查点有 I/O 开销。高频小步任务,检查点开销可能抵消 Agent 的效率。应在关键节点存,而非每步都存。

补偿失败的雪崩。补偿本身也可能失败。补偿失败不告警,脏状态更深。必须对补偿失败做告警,并留人工介入入口。

幂等性的要求。续跑时已执行步骤可能重复执行。正向与补偿动作都必须幂等。否则重试一次,副作用翻倍。

事务式执行的"补偿完备性审计"要做在前面。上线前逐步骤检查:这个操作能否撤销?撤销失败怎么办?答不上来的步骤就是定时炸弹。

另一个被忽视的点是"检查点的可见性":检查点存在哪、什么格式、怎么读,要文档化。否则故障时想人工续跑,连状态都看不懂。最后,对外部系统的调用要设超时与重试上限,避免一步卡死整个事务,补偿动作也跟着永远等不到触发,脏状态无限期残留。

五、总结

Agent 可恢复执行,本质是用"检查点 + 补偿"换"失败可回滚"。机制上每步存检查点,失败时逆序执行补偿动作。工程上补偿需幂等,不可补偿操作前置校验。落地路线:先识别每步的可补偿性;关键节点存检查点;失败逆序补偿并告警;持久化检查点支持断点续跑。Agent 可以失败,但失败后系统必须能回到安全态。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/27 5:23:11

C++高并发Channel模块:无锁环形缓冲区与混合同步策略实现

1. 项目概述:为什么我们需要一个C高并发Channel模块?在构建现代高性能服务器、游戏引擎或者任何需要处理海量异步消息的系统时,我们常常会面临一个核心挑战:如何在多个线程或协程之间,安全、高效、有序地传递数据&…

作者头像 李华
网站建设 2026/7/27 5:23:00

大模型训练全流程:从数据准备到部署优化

1. 大模型训练全流程总览大模型训练就像培养一位专业顾问,需要经历完整的成长阶段。让我们用一个企业顾问的成长案例,贯穿整个训练流程的讲解。1.1 训练阶段全景图从零开始训练一个大模型,需要经历五个关键阶段:数据准备&#xff…

作者头像 李华
网站建设 2026/7/27 5:21:46

拍照检测软件 显示器泄密 2026企业终端物理防泄密系统实力TOP6排行

在政企涉密办公、医疗数据管理、企业商务研发场景中,显示器泄密已经成为内部数据泄露的核心隐性渠道。区别于传统文件传输外泄,显示器画面可通过手机、相机快速抓拍,无文件痕迹、排查难度大、传播速度快,是多数企业数据安全治理的…

作者头像 李华
网站建设 2026/7/27 5:19:34

嵌入式开发外设时序解析:从eHRPWM到I2C/UART的实战配置与避坑指南

1. 项目概述:为什么外设时序是嵌入式开发的“生命线”?在嵌入式系统,尤其是像TMS320C6743这样的高性能数字信号处理器(DSP)开发中,我们常常把精力集中在算法优化、内存管理和中断响应上。然而,有…

作者头像 李华
网站建设 2026/7/27 5:18:03

TMS320F281x DSP串行Flash编程:基于Boot ROM的自主可控烧录方案

1. 项目概述在嵌入式DSP系统开发,尤其是工业控制、电机驱动或数字电源这类对可靠性和现场维护有高要求的领域,如何安全、高效地将最终的应用代码“烧录”进处理器的内部Flash,是一个贯穿产品开发、测试到量产的硬核问题。直接使用JTAG仿真器配…

作者头像 李华