ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

AI Agent容错实战:校验、暂停、回滚与人工接管

AI Agent容错实战:校验、暂停、回滚与人工接管 1. 为什么AI Agent的“出错处理”比模型能力更值得关注很多人第一次搭AI Agent注意力全在模型选型上——用哪个大模型、上下文窗口多大、推理能力多强。但真正把Agent放到生产环境跑上一周你会发现一个扎心的事实决定Agent能不能用的往往不是它有多聪明而是它出错之后你怎么收拾。我自己的经历很典型。早期做的一个Agent负责自动整理工单、调用内部接口改状态、再回写数据库。测试环境跑得挺顺上线第三天就出事了模型把一条“待审核”的工单误判成“已关闭”直接调了写接口。等发现的时候几十条记录已经被改。没有校验、没有暂停、没有回滚只能人工一条条对着日志往回捞。那次之后我才真正理解Agent和普通程序最大的区别在于——它的执行路径是概率性的不是确定性的。普通代码你写if a then b它就一定走bAgent是“大概率走b偶尔抽风走c极端情况下走一个你根本没想过的d”。所以这篇东西我想聊的不是“怎么让Agent更聪明”而是怎么让它在犯错时可控。核心就四件事校验Validation、暂停Pause、回滚Rollback、人工接管Human Takeover。这四个词听起来像运维术语但放到Agent语境里含义和实现方式都不太一样。适合谁看如果你正在从0到1搭Agent或者已经把Agent跑起来了但总担心它闯祸那这篇基本就是给你写的。我会把每个环节的原理、为什么这么设计、具体怎么落地、踩过哪些坑都摊开讲代码和配置尽量给到能直接抄的程度。先说一个贯穿全文的判断Agent的可靠性不来自模型来自外围的约束层。模型是发动机校验、暂停、回滚、接管是刹车、安全带、气囊和备用方向盘。发动机再强没有这四样你不敢让它上高速。2. 校验层在Agent动手之前拦住它2.1 校验到底校验什么三层校验模型很多人一提“校验”就想到表单校验规则那种东西——检查输入是不是邮箱、是不是数字。但Agent场景下的校验要复杂得多因为它校验的不只是数据格式还有意图、权限和后果。我一般把它拆成三层输入校验Agent拿到的任务描述、参数、上下文是否合法、完整、无歧义。决策校验Agent打算执行的动作是否符合预设的规则和边界。输出校验Agent执行完的结果是否在可接受范围内是否和预期一致。这三层的顺序不能乱。输入校验是入口关决策校验是核心关输出校验是兜底关。任何一层没过都应该触发后续的暂停或接管流程而不是硬着头皮往下走。举个具体例子。假设Agent的任务是“把库存低于10的商品下架”。输入校验要确认商品ID存在吗库存数据来源可信吗决策校验要确认这个Agent有没有下架权限下架这个动作是不是高危操作、需不需要二次确认输出校验要确认下架后商品状态真的变了吗有没有误伤其他商品2.2 规则校验的落地从硬编码到可配置最朴素的校验就是硬编码if判断但Agent的动作空间一大硬编码会爆炸。我的做法是把校验规则抽成独立的规则表用配置驱动。规则表大概长这样规则ID适用动作校验类型条件失败处理R001修改订单状态决策校验目标状态 ∈ {待审核,已关闭}暂停并转人工R002调用支付接口决策校验金额 ≤ 5000拒绝执行R003删除数据决策校验永远禁止直接拦截R004任意写操作输出校验影响行数 ≤ 100回滚并告警这样设计的好处是规则可以热更新不用改代码重新部署。新增一条“禁止在凌晨2点到4点执行批量操作”的规则运维同学自己就能加。规则引擎的选型上轻量场景我推荐直接用JSON/YAML配置加一个简单的表达式求值器比如用Python的asteval或者JS的expr-eval没必要上Drools那种重型规则引擎。Agent的校验规则通常不复杂重引擎反而增加维护成本。2.3 数据完整性校验别忽略那些“看起来没问题”的输入Agent经常要处理外部数据——从数据库读、从接口拉、从文件解析。这些数据在格式上可能完全合法但内容上是错的。比如一个金额字段是-100格式没问题但业务上不可能。一个时间戳是0能解析但那是1970年。这类问题靠格式校验抓不住得靠业务语义校验。我的经验是维护一份“字段语义约束表”对关键字段定义合理范围FIELD_CONSTRAINTS { amount: {min: 0, max: 1_000_000, type: decimal}, timestamp: {min: 1_600_000_000, max: None}, # 不早于2020年 status: {enum: [待审核, 处理中, 已关闭, 已取消]}, user_id: {pattern: r^U\d{8}$}, }Agent在读取数据后、做决策前先过一遍这张表。任何字段越界直接标记为“数据可疑”暂停流程。这一步能拦掉相当一部分因为脏数据导致的误操作。注意校验规则本身也会出错。我见过有人把max写成1_000_000结果单位是“分”而实际数据是“元”导致所有正常订单都被拦。规则上线前一定要用真实数据跑一遍回归别拍脑袋定阈值。2.4 校验失败的降级策略不是所有失败都要停校验失败不等于世界末日。根据严重程度我一般分三档处理硬失败违反安全红线如删除数据、越权操作直接拦截记录审计日志通知负责人。软失败数据可疑但动作可逆如金额略超阈值暂停并请求人工确认确认后可继续。警告轻微异常如字段格式不规范但可自动修正记录警告自动修正后继续。这个分级很重要。如果所有校验失败都触发人工人会累死Agent也会因为频繁暂停而失去自动化价值。关键是把“必须人管”和“可以自动兜”分开。3. 暂停机制让Agent在关键时刻“踩刹车”3.1 暂停的三种触发方式Agent的暂停不是简单地把进程kill掉那样状态全丢。真正的暂停要保留上下文支持后续恢复。触发方式主要有三种规则触发校验层判定需要暂停主动调用暂停接口。人工触发监控人员发现异常手动按下暂停按钮。系统触发外部依赖不可用如数据库连接断开、资源超限如Token消耗超预算自动暂停。这三种触发最终都汇聚到同一个暂停管理器。暂停管理器负责冻结当前执行状态、保存上下文快照、通知相关方、等待恢复指令。3.2 状态快照暂停的核心是“可恢复”暂停如果不可恢复那和终止没区别。所以暂停时必须做状态快照。快照要包含什么我的清单是当前任务ID和任务描述已执行的步骤序列每一步的输入、输出、时间戳当前待执行的步骤及其参数模型的对话历史如果Agent是基于对话的外部调用的中间状态如已发起的请求、已获取的锁快照存哪里小规模用Redis就行HSET存结构化字段RPUSH存步骤序列。大规模或者需要长期保留的落库到PostgreSQL用JSONB字段存上下文。关键是快照要原子写入不能写一半崩了。def pause_agent(task_id, reason): snapshot { task_id: task_id, reason: reason, timestamp: time.time(), steps: get_executed_steps(task_id), pending: get_pending_step(task_id), context: get_agent_context(task_id), } # 原子写入先写临时key再rename redis_client.set(fsnapshot:{task_id}:tmp, json.dumps(snapshot)) redis_client.rename(fsnapshot:{task_id}:tmp, fsnapshot:{task_id}) redis_client.set(fstatus:{task_id}, PAUSED) notify_operator(task_id, reason)3.3 暂停的粒度步骤级还是动作级暂停粒度是个容易纠结的点。步骤级暂停是“执行完当前步骤后停”动作级暂停是“当前动作执行到一半也能停”。后者实现难度大得多因为很多外部调用不是原子的。我的建议是默认用步骤级暂停只对高危动作做动作级暂停。比如“调用支付接口”这种要么完整成功要么完整失败中间状态没法暂停。而“批量更新100条记录”可以做成每10条检查一次暂停信号支持中途停。实现上步骤级暂停就是在每个步骤执行前检查一个pause_flagdef execute_step(step): if is_paused(step.task_id): save_snapshot(step.task_id) return StepResult(statusPAUSED) # 执行步骤...动作级暂停则需要在动作内部埋检查点复杂度高只在必要场景用。3.4 暂停后的通知与等待别让人干等暂停之后得让人知道。通知渠道看团队习惯邮件、IM、工单系统都行。通知内容要包含哪个任务停了、为什么停、当前状态、需要人做什么决策。等待恢复的机制有两种轮询和回调。轮询简单Agent暂停后定期查status字段变成RESUMED就继续。回调更实时人工在管理后台点“恢复”后后台直接调用Agent的恢复接口。我倾向回调因为轮询有延迟而且Agent进程一直挂着也占资源。实操心得暂停状态一定要设超时。我遇到过人工忘了处理任务暂停了三天恢复的时候外部数据早就变了快照里的上下文全失效。现在我的做法是暂停超过2小时自动转“待人工接管”超过24小时自动标记为“废弃”释放资源。4. 回滚把Agent闯的祸收回来4.1 回滚的前提可逆性设计回滚能不能做取决于动作本身可不可逆。发出去的邮件收不回来调用第三方接口改了对方数据也未必能改回来。所以回滚能力是在设计Agent动作时就要考虑的不是出事之后才想。我的原则是能设计成可逆的绝不设计成不可逆。具体做法写操作优先用“软删除”或“状态标记”而不是物理删除。批量操作前先备份原数据或者记录反向操作。外部调用尽量选幂等的接口或者自己维护一个“补偿操作”映射。比如“下架商品”这个动作不要直接DELETE而是UPDATE status offline。回滚就是UPDATE status online。简单、可靠、可审计。4.2 回滚的三种实现模式根据场景不同回滚有三种模式模式一反向操作Compensating Action每个正向操作配一个反向操作。正向是“扣库存”反向是“加库存”。正向是“发通知”反向是“发更正通知”。这种模式适合操作序列明确、每步都有对应逆操作的场景。模式二快照恢复Snapshot Restore操作前对受影响的数据做完整快照回滚时用快照覆盖。适合批量修改、数据结构复杂的场景。缺点是快照占空间大表快照成本高。模式三事务回滚Transaction Rollback如果所有操作都在同一个数据库事务里直接ROLLBACK就行。但Agent的操作往往跨系统、跨接口很难包在一个事务里。所以这种模式只适合单库单事务的简单场景。实际项目中我通常是模式一为主模式二为辅。关键数据操作前做快照日常操作靠反向操作。4.3 回滚的触发与执行流程回滚不是随便就能触发的。我的流程是检测异常输出校验失败、人工发现错误、监控告警。评估影响范围哪些数据被改了改了多久有没有下游依赖决定回滚策略全量回滚还是部分回滚用反向操作还是快照恢复执行回滚按操作序列的逆序执行每步校验。验证回滚结果确认数据恢复到预期状态。记录审计回滚的原因、范围、结果全部留痕。第4步的“逆序”很关键。Agent的操作是有依赖的先创建订单再扣库存回滚就得先恢复库存再删订单。顺序错了会出问题。def rollback(task_id): steps get_executed_steps(task_id) for step in reversed(steps): if step.reversible: inverse get_inverse_action(step) result execute_inverse(inverse) if not result.success: # 回滚失败升级为人工接管 escalate_to_human(task_id, f回滚步骤{step.id}失败) return mark_task_rolled_back(task_id)4.4 回滚不干净怎么办那些“回滚了但没完全回滚”的坑回滚最怕的是“回滚了但没回干净”。我踩过的坑包括部分回滚批量操作回滚到一半失败数据处于半新半旧状态。级联遗漏改了主表忘了改关联表或者改了数据库忘了清缓存。外部系统不同步内部数据回滚了但已经推送给外部系统的数据没撤回。副作用残留操作触发了通知、日志、计费等副作用回滚时没处理。应对这些我的经验是回滚操作本身也要有校验和重试不能假设一次成功。维护一份“副作用清单”回滚时逐项检查。对于无法回滚的外部副作用生成“更正说明”而不是假装没发生。回滚后跑一遍数据一致性检查别信“应该没问题”。注意回滚不是万能的。有些操作就是不可逆的比如已经发给客户的邮件、已经执行的支付。这种情况下回滚的目标不是“当没发生过”而是“把状态修正到可接受”。这个心态要摆正不然会在不可逆操作上浪费大量精力。5. 人工接管最后的防线怎么设计5.1 什么情况下必须人工接管不是所有暂停都需要人工接管。我划的线是涉及不可逆操作删除、支付、对外发送必须人工确认。涉及高价值数据金额超过阈值、影响核心业务必须人工确认。Agent置信度低模型自己都拿不准的决策转人工。连续失败同一任务重试超过N次仍失败转人工。规则明确要求某些业务场景合规要求必须人工复核。反过来低风险、可逆、Agent置信度高的操作暂停后可以自动恢复不用惊动人。5.2 接管界面的核心要素人工接管不是让人去看日志猜发生了什么。接管界面要让人在最短时间内理解现状并做出决策。核心要素任务概览任务是什么、谁发起的、跑了多久。执行轨迹每一步做了什么、结果如何、耗时多少。暂停原因为什么停、哪条规则触发的。待决策项需要人做什么选择选项有哪些。影响预览每个选项会导致什么后果。操作按钮继续、回滚、修改参数后继续、终止。我见过太多接管界面只给一个“发生了什么”的日志然后让人自己判断。这是偷懒。好的接管界面应该把决策所需的信息都摆好人只需要点按钮。5.3 接管后的状态同步别让Agent“失忆”人工接管后Agent恢复执行时必须知道人工做了什么。比如人工修改了某个参数Agent得用新参数继续人工回滚了某步Agent得从回滚后的状态重新规划。实现上接管操作要写回Agent的上下文。我的做法是维护一个“人工干预记录”Agent恢复时先读这个记录把人工的修改合并进当前状态。def resume_after_human(task_id, human_action): intervention { task_id: task_id, action: human_action.type, # continue / rollback / modify / abort modifications: human_action.modifications, timestamp: time.time(), } save_intervention(intervention) context load_context(task_id) context apply_intervention(context, intervention) save_context(task_id, context) set_status(task_id, RUNNING)5.4 人机协作的边界哪些交给Agent哪些留给人长期来看人工接管的比例应该越来越低但不是降到零。我的经验是把“判断”留给人把“执行”交给Agent。人负责决定“要不要做”“做到什么程度”Agent负责“怎么做”“做多少”。具体分工上决策类型交给Agent留给人常规数据查询是否低风险状态更新是否批量数据修改校验后执行超阈值时确认对外发送否是资金相关否是删除操作否是异常处理尝试自动恢复恢复失败时接管这个边界不是一成不变的。随着Agent可靠性提升和校验规则完善可以把更多决策下放给Agent。但下放的前提是有对应的校验和回滚能力兜底。6. 把四件事串起来一个完整的容错流程6.1 从任务开始到结束的完整链路把校验、暂停、回滚、接管串成一条线一个任务的生命周期大概是这样任务进入输入校验确认任务合法、参数完整。规划阶段Agent生成执行计划决策校验逐条检查计划中的动作。执行阶段每步执行前检查暂停信号执行后做输出校验。异常分支校验失败 → 按严重程度决定拦截/暂停/警告。暂停 → 保存快照通知人工。人工决策 → 继续/回滚/修改/终止。回滚 → 逆序执行反向操作验证结果。任务结束记录完整审计日志更新任务状态。这条链路里每个环节都要有日志。Agent的审计日志不是“出了事才看”而是“随时能看”。我习惯把日志结构化每条包含时间、任务ID、步骤ID、动作类型、输入摘要、输出摘要、校验结果、耗时。6.2 一个可复用的容错框架设计如果每个Agent都从头实现这四件事重复劳动太多。我一般会抽一个容错框架Agent开发者只需要定义“动作”和“校验规则”框架负责暂停、回滚、接管。框架的核心接口class FaultTolerantAgent: def register_action(self, name, forward, inverseNone, reversibleTrue): 注册动作及其反向操作 def register_rule(self, action_name, rule): 注册校验规则 def execute(self, task): 执行任务内置校验、暂停、回滚 def pause(self, task_id, reason): 暂停任务 def resume(self, task_id, interventionNone): 恢复任务 def rollback(self, task_id): 回滚任务这样Agent开发者写业务逻辑框架管可靠性。新Agent接入成本低容错能力也统一。6.3 监控与告警让问题在爆发前被发现容错机制再完善也是事后处理。更好的做法是提前发现苗头。我关注的监控指标校验失败率突然升高说明输入数据或规则有问题。暂停次数频繁暂停说明Agent决策质量下降。回滚率回滚率高说明校验不够前置。人工接管率接管率是Agent自主性的反向指标。平均恢复时间从暂停到恢复的耗时反映人工响应效率。这些指标设阈值告警。比如校验失败率超过5%就告警暂停次数一小时超过10次就告警。告警不是目的目的是让人在问题变大之前介入。6.4 常见问题速查表问题现象可能原因排查方向处理建议Agent执行了不该执行的动作决策校验缺失或规则未覆盖检查规则表是否覆盖该动作补充规则回滚已执行动作暂停后无法恢复快照不完整或上下文丢失检查快照字段是否齐全完善快照必要时重跑任务回滚后数据不一致反向操作遗漏或顺序错误对比回滚前后数据快照补做遗漏的反向操作人工接管后Agent行为异常干预记录未正确合并检查上下文合并逻辑修正合并逻辑重跑频繁暂停影响效率校验规则过严分析暂停原因分布放宽低风险规则改为警告回滚耗时过长批量操作数据量大评估回滚性能瓶颈分批回滚或改用快照恢复实操心得这套机制刚上线时暂停和接管会特别频繁别慌。这是正常的说明校验在起作用。随着规则调优和Agent决策质量提升暂停率会降下来。我第一个Agent上线第一周接管率30%一个月后降到5%以下。关键是每次接管后都要复盘看是规则问题还是Agent问题针对性优化。7. 一些不那么显然的经验7.1 校验规则要“可解释”校验失败时不能只告诉人“失败了”要告诉人“为什么失败”。规则引擎返回的结果要包含触发了哪条规则、规则的条件是什么、实际值是什么、期望值是什么。这样人工接管时能快速判断是数据问题还是规则问题。我见过有人把校验结果写成{passed: false}然后人工一脸懵。改成{passed: false, rule: R002, condition: amount 5000, actual: 8000, expected: 5000}排查效率天差地别。7.2 回滚要考虑“回滚的回滚”回滚操作本身也可能失败或者回滚之后发现回滚错了。所以回滚也要有审计也要能“再回滚”。虽然这种情况很少但设计时留个口子比事后抓瞎强。7.3 人工接管不是越多越好接管率高不代表安全可能代表Agent太笨或者规则太严。目标是让Agent处理绝大多数常规情况人只处理真正的例外。如果接管率长期高于10%得回头看看是Agent能力问题还是流程设计问题。7.4 容错机制本身要轻量我见过有人为了做容错引入了一堆中间件、消息队列、分布式事务框架结果系统复杂度爆炸容错机制自己成了故障源。我的建议是能用简单方案就别上重型框架。Redis存快照、数据库存审计、一个管理后台做接管对大多数Agent场景足够了。等规模真的上来了再考虑升级。7.5 测试容错机制比测试Agent更难测试Agent的正常路径容易构造异常路径难。我的做法是故意注入故障在测试环境随机让校验失败、随机暂停、随机让回滚失败看整个流程能不能正确处理。这种“混沌测试”能发现很多正常测试发现不了的问题。最后分享一个我自己的习惯每次Agent上线新功能我都会先问三个问题——这个动作可逆吗校验覆盖了吗出错了人怎么接管三个问题有一个答不上来就不上线。这个习惯帮我省了无数次半夜爬起来救火。Agent的能力可以慢慢加但容错的底线一开始就得守住。
返回列表