拓冰建站拓冰建站
首页 / 资讯中心 / 正文

Agent异常处理指南:重试、降级与护栏校验的容错设计

很多开发者在第一次做 Agent 项目时都会有这样一种错觉只要把 Prompt 写得足够清楚、把工具函数接得足够完整Agent 就能稳定工作。真正跑起来之后才发现问题大多不在“脑子不够聪明”而在“时好时坏”——模型偶尔超时、偶尔输出乱 JSON、偶尔调工具时参数错位、偶尔在某个分支上兜圈子。这个现象背后有一个非常关键的差异传统程序的异常是确定的、可复现的而 Agent 的异常是概率性的、依赖外部环境的。如果你沿用传统 try-catch 的思维来做 Agent 异常处理很快就会发现自己陷入两难。所以我先给出一个贯穿全文的判断Agent 的异常处理目标不是“让系统永不失败”而是“让失败可控、可恢复、可观测”。判断一个 Agent 容错方案是否合格标准只有一个——当异常发生时系统是按照预设路径优雅降级还是直接崩溃、卡死甚至把错误结果当正常结果用。很多 Agent 项目从 Demo 走向生产环境时最大的阻碍往往不是模型效果而是异常处理做得不够扎实。这篇文章会从三类最常见的异常处理方式展开重试机制、降级与回退、护栏校验与人工确认。这三者不是层层替代关系而是分别对应不同的失败类型。读完你会清楚什么情况下该重试、什么情况下该降级、什么情况下该把问题交给人工以及它们如何组合成一个完整的 Agent 容错体系。文章还会给出完整的 Python 示例代码帮助你直接在自己的项目里落地。补充一点这篇文章面向正在做 AI Agent 项目的开发者无论你用 LangChain、LlamaIndex 这类框架还是自己封装模型调用核心思想都适用。示例代码以 Python 为主但重试、降级、校验的思路完全可以平移到 Java、TypeScript 等其他技术栈。接下来我们进入正题。1. 为什么 Agent 的异常处理比普通编程更棘手1.1 传统异常是“可枚举的”Agent 异常是“概率性的”传统程序中try-catch 能生效的前提是异常类型有限、异常发生条件可预期。网络异常、空指针、非法参数这些都是编译器或运行时能明确告诉你的失败分支。你甚至在代码编译阶段就能判断哪些地方可能抛错然后针对性地处理。Agent 程序不是这样。Agent 程序里最大的不确定来源是模型输出。即使你把 Prompt 写得再严格模型也可能返回残缺 JSON也可能胡编一个不存在的工具名也可能对同一个问题给出质量完全不同的答案。这不是代码 bug而是模型推理本身的概率分布。这就导致了一个非常尴尬的局面你很难穷举所有失败模式因为模型输出的变化空间太大。比如你要求模型返回{action: query_order, params: {order_id: 123}}它可能返回{action: query_order, params: {order_id: 123}, reasoning: 用户想查询订单}也可能返回一大段解释文字后带上 JSON甚至可能因为安全策略拦截返回一段道歉说明。这些情况在写代码时往往无法提前预料只能设计一个通用的校验和修复机制去兜底。1.2 Agent 的失败链路比普通接口长得多一个普通接口从请求到响应可能只有一跳一次 HTTP 请求、一次数据库查询失败模式非常集中。一个 Agent 任务往往长得多任务拆解、规划、调用工具、观察结果、再规划、最终回复。链路很长任何一环出问题都有可能被后续环节放大。举一个具体的例子。假设 Agent 需要调用一个订单查询工具工具返回了一个空列表。这里有可能是真的没有订单也有可能是工具调用失败后返回了空对象还有可能是参数传错了导致查询条件不匹配。如果是真人程序员看到空列表会结合上下文判断到底哪个环节出了问题。但 Agent 很可能直接认为“查询无结果”然后给用户输出一个错误的结论比如“您没有订单”。这种“错误被当成正常输入继续传递”的现象是 Agent 异常处理中最容易被忽视、也最危险的一种失败。1.3 外部依赖的失败模式更加复杂Agent 几乎必然依赖外部组件大模型 API、工具接口、数据库、向量库。这些依赖各有各的失败模式限流、超时、网络抖动、上下文超长、内容安全策略拦截而且往往是间歇性的复现难度很高。你测试的时候一切正常上线后某个第三方服务突然慢了两秒整个 Agent 的响应时间就超标了。如果用传统“出错了就报错”的方式处理整个系统会频繁地暴露错误用户体感极差。更深一层的问题是这些外部依赖往往是不可控的。你无法修改模型 API 的限流策略也无法保证工具服务永远稳定。所以 Agent 的异常处理必须假设“外部组件随时可能出问题”并且在这种假设下仍然让系统具备可用的运行路径。这也是为什么我们说Agent 异常处理的本质是容错设计而不仅仅是错误捕捉。1.4 小结论从“精确捕捉错误”转向“系统容错设计”到这里我们可以给出一个更精确的定义Agent 异常处理不是 catch 住错误而是在失败发生时系统仍然能走通流程。这就要求你提前设计好重试时机、降级路径、输出校验和人工确认的接入点。本文后面三节就是围绕这几个关键点展开的。2. 先认识 Agent 的异常来源知道它会怎么挂2.1 四类常见异常来源在动手写异常处理代码之前先把 Agent 常见的失败类型梳理清楚。根据实践经验Agent 项目里的异常大体可以分为四类异常类别典型表现具体举例基础设施类请求超时、连接中断、限流模型 API 返回 429网络波动导致连接 reset模型输出类输出格式不符合预期、上下文超长、内容被安全策略拦截期望 JSON 却输出 JSON 加解释文字达到 token 上限工具调用类工具名不存在、参数类型错误、外部系统拒绝Agent 生成了不存在的函数名或把字符串参数传给数字参数编排逻辑类Agent 反复调用同一工具、多步推理结果矛盾Agent 陷入死循环反复查询同一个接口这四类异常的处理方式完全不同。基础设施类异常适合用重试来解决模型输出类异常适合用护栏校验和修复来解决工具调用类异常既可能是瞬时故障需要重试也可能是持续故障需要降级或停止编排逻辑类异常则要从架构层面设置限制比如最大步数避免死循环拖垮系统。2.2 从“什么时候会失败”反推处理方式简单来说重试主要针对“瞬时故障”比如超时、限流降级主要针对“持续故障”比如模型服务不可用、某个功能模块不稳定护栏校验主要针对“输出不符合预期”和“错误结果被当正常使用”的情况人工确认则更多用在“高风险操作”和“模型不确定该不该执行”的场景。这四个手段有一个清晰的递进关系。重试是成本最低的第一道防线它假设失败是暂时的过一会儿就好了降级是第二道防线它假设失败会持续一段时间需要换一条路走护栏校验是第三道防线它在拿到结果后做质量把关人工确认是最后一道闸门它把高风险决策交给人类来把握。理解了这条递进关系后面几节的代码就很容易读懂。2.3 异常处理的两个常见误区第一个误区是过度依赖重试。很多人习惯用一个大 try-catch 包住模型调用失败就循环重试结果限流越刷越严重token 成本飙升。之所以会这样是因为他们没有区分“瞬时故障”和“持续故障”。一个参数错误、鉴权失败、上下文超长的请求重试多少次都不会成功反而会不断消耗算力和费用。第二个误区是不设护栏。Agent 的返回结果完全没有校验下游系统直接消费。一旦模型输出格式漂移整个链路就崩了而且崩得非常隐蔽。大家可能遇到过这种情况模型 API 明明返回了 200但解析后得到的字段全是空的下游代码还以为查询不到数据。这种问题不加护栏是发现不了的只有等用户反馈“我的订单怎么没了”才意识到出了大问题。所以重试和护栏必须同时存在缺一不可。3. 第一种方式重试机制Retry3.1 重试解决什么问题重试是最直接、成本最低的异常处理方式核心适用场景是瞬时故障。所谓瞬时故障就是故障持续时间短可能只有几十毫秒到几秒过一会儿再试就能成功。比如模型 API 短时超时、服务重启窗口、网络抖动、单次限流这些都属于瞬时故障。遇到这类问题最简单有效的办法就是等一会儿再试一次。但这里要强调一个原则重试不是万能的它只对“暂时性失败”有效。如果一个请求因为参数错误而失败不管你重试多少次结果都一样如果一个请求触发了鉴权失败重试同样不会改变结果。所以设计重试机制的第一步是把可重试的异常和不可重试的异常区分开。3.2 重试的关键不是“重试”而是“重试策略”很多项目里的重试是写死的失败了就重试 3 次每次间隔固定 1 秒。这种做法在真实场景里远远不够。一个合格的重试策略至少要回答四个问题。第一个问题重试多少次重试次数太少瞬时抖动还没过去重试次数太多会放大后端压力。第二个问题等待多久是固定间隔还是指数退避如果大量请求同时失败同时重试固定间隔会让所有重试请求在同一时刻集中打到后端反而把偶发故障放大成限流风暴。第三个问题哪些异常值得重试只有可恢复异常才值得重试参数错误、授权失败这类异常重试多少次都没有意义。第四个问题重试有没有副作用如果 Agent 调用的工具不是幂等的比如“创建订单”“发送邮件”重试可能造成重复下单、重复发送。这四个问题没想清楚重试机制反而会引入新的问题。3.3 手写一个带退避的重试函数当你在一个不允许引入额外依赖的项目中可以自己实现一个带指数退避和随机抖动的重试函数。代码逻辑不复杂但已经包含了重试机制里最核心的几个要素。# 文件路径agent/retry.py import time import random from typing import Callable, Tuple, Type class AgentTransientError(Exception): Agent 瞬时错误适合重试 pass def retry_with_backoff( func: Callable, max_retries: int 3, base_delay: float 1.0, max_delay: float 10.0, retryable_exceptions: Tuple[Type[Exception], ...] (AgentTransientError, TimeoutError), ): 带指数退避和随机抖动的重试装饰器。 def wrapper(*args, **kwargs): attempt 0 while True: try: return func(*args, **kwargs) except retryable_exceptions as e: attempt 1 if attempt max_retries: raise delay min(base_delay * (2 ** (attempt - 1)), max_delay) delay delay * (0.5 random.random() / 2) # 抖动避免重试风暴 print(f第 {attempt} 次调用失败{e}{delay:.2f} 秒后重试) time.sleep(delay) return wrapper retry_with_backoff(max_retries3) def call_llm(prompt: str) - str: 模拟模型调用前 2 次抛瞬时错误第 3 次成功。 if random.random() 0.6: raise AgentTransientError(模型服务暂不可用) return 模型正常返回结果 # 运行验证 if __name__ __main__: print(call_llm(你好))代码里的退避策略是 1 秒、2 秒、4 秒这样翻倍增长并加入了随机抖动。抖动这一步在大量请求并发重试时非常重要它能把同步的重试请求打散避免在同一秒内集中冲击后端服务。如果你不加抖动指数退避在同一起点上的所有请求依然会“整齐划一”地在同一时刻重试。3.4 使用成熟的重试库tenacity实际项目中除非你想严格控制依赖数量否则更推荐直接用成熟的重试库。Python 生态里tenacity是最常用的选择它把重试策略抽象成了装饰器可以让业务代码保持干净。# 文件路径agent/llm.py from tenacity import ( retry, stop_after_attempt, wait_exponential, retry_if_exception_type, ) retry( stopstop_after_attempt(4), waitwait_exponential(multiplier1, min1, max15), retryretry_if_exception_type((AgentTransientError, TimeoutError)), ) def call_llm_with_retry(prompt: str) - str: # 开发环境中替换为真实模型调用 return call_llm(prompt)stop_after_attempt(4)表示最多尝试 4 次wait_exponential表示指数退避等待初始最小 1 秒最大 15 秒retry_if_exception_type表示只在指定异常类型上触发重试。使用这个库可以省去自己实现重试逻辑的时间同时它的策略配置清晰团队维护成本也更低。3.5 什么异常适合重试什么不适合适合重试不适合重试API 超时鉴权失败401/403限流429参数错误400服务重启窗口上下文超长且未优化网络抖动工具不存在或权限不足判断标准很简单这个请求换一个时间点执行是否有可能成功如果可能性很低就不值得重试。比如上下文超长你重试再多次也还是会超长正确的做法是截断上下文或切换到更大的上下文窗口而不是盲目重试。3.6 重试的边界什么时候停止重试重试有一个容易被忽略的边界问题什么时候停止重试我的建议是三层边界都要设。第一层是次数上限比如最多重试 3 次第二层是时间上限比如单次模型调用的总耗时不超过 30 秒超过则立即中断第三层是任务级上限比如整个 Agent 流程的总重试次数不能超过 5 次避免多个环节各自重试叠加后导致整体超时。这三层边界需要在配置中心里统一管理方便线上动态调整而不是散落在代码里。结论很简单重试只适合解决瞬时故障。如果一个问题连续重试多次仍然失败继续重试的意义会快速下降此时应该转入降级流程。这也是下一节要讲的内容。4. 第二种方式降级与回退Fallback4.1 降级解决什么问题降级是针对持续性故障的方案。什么场景会触发持续性故障模型服务不可用、某个参数组合反复失败、第三方接口升级导致不兼容、限流持续超过阈值。这些场景下重试已经没有意义你需要让流程“换个路走”而不是继续撞同一堵墙。降级的核心思想是主链路失败时用备选方案继续完成业务目标。它接受了一个前提——降级后的结果质量可能不如主链路但总比系统直接不可用要好。在生产者与消费者模型中这就是典型的“服务降级”在 Agent 场景里降级的对象变成了模型提供商、工具函数和功能模块。4.2 降级的三种层次第一层是模型降级。主模型不可用时切换到备选模型。比如从大型模型切换到小型模型或者从商业模型切换到开源模型。不同模型的推理能力、输出质量、成本都不一样但都能完成大部分任务。第二层是功能降级。Agent 的某项能力不可用时关闭它或替换为简化实现。比如语义检索失效时退化为关键词匹配比如记忆增强失效时退化为纯 Prompt 回答。第三层是结果降级。所有自动方案都失败时返回统一的兜底回复或者转人工处理。这是降级的最后一道防线保证用户至少能得到一个明确的反馈。4.3 多级降级链代码示例下面这段代码实现了一个典型的多级降级链。它按优先级依次尝试主模型、备选模型、规则引擎如果所有方案都失败最后抛出运行时异常。# 文件路径agent/fallback.py import logging logger logging.getLogger(__name__) class AgentTransientError(Exception): 瞬时错误适合重试或降级 pass def call_gpt4(prompt: str) - str: raise AgentTransientError(主模型不可用模拟异常) def call_gpt4_mini(prompt: str) - str: return 备选模型返回结果 def call_rule_engine(prompt: str) - str: return 无法自动回答请联系人工客服 def run_agent_with_fallback(prompt: str) - tuple: 多级降级链 依次尝试主模型、备选模型、规则引擎。 返回 (最终结果, 实际使用的提供商) providers [ (gpt-4o, call_gpt4), (gpt-4o-mini, call_gpt4_mini), (rule-engine, call_rule_engine), ] for name, handler in providers: try: result handler(prompt) logger.info(provider%s 调用成功, name) return result, name except Exception as e: logger.warning(provider%s 调用失败原因%s, name, e) continue raise RuntimeError(所有降级方案均失败)这段代码里有一个容易被忽略的细节降级方案返回的结果应该标明实际使用的提供商。原因很简单降级后的结果质量通常低于主链路如果调用方不知道这是降级结果就可能把低质量结果当成正常答案来使用。比如用户问“帮我查一下退换货规则”降级后被规则引擎回答了一个通用解释用户可能以为得到了准确答案实际上内容并不针对他的具体订单。所以降级结果必须带标记。4.4 降级的时机与代价降级不是越早越好。过早降级会让主模型形同虚设影响效果过晚降级又会拖慢响应用户等不起。比较稳妥的节奏是先快速重试 1 到 2 次再进入降级链。在降级链里也要设置整体耗时预算比如总耗时不超过 20 秒超过预算就直接走兜底回复。这里还要提一个关键指标——降级率。所谓降级率就是所有请求中触发降级的比例。如果一个系统长期处于高降级率状态说明主链路本身已经不稳定需要介入排查而不是让降级机制默默承担所有压力。降级机制是为了兜底不是为了掩盖故障。4.5 功能降级的实际场景举一个电商客服 Agent 的例子。它依赖订单查询接口、售后规则库、兜底知识库三个模块。如果订单查询接口持续故障让 Agent 反复调用失败接口只会浪费时间。更合理的做法是检测到订单接口故障后跳过订单详情查询直接向用户表达“订单查询暂时不可用请稍后再试”同时提供售后规则库的通用回答。实现这种功能降级要求开发者在编排 Agent 任务时想清楚“最小可用路径”。比如一个完整的客服流程原本是获取用户身份、查询订单、分析售后诉求、生成回答。最小可用路径可能只是获取用户身份、生成一个友好的提示。这个最小可用路径要在架构设计阶段就预留出来而不是等故障发生时临时拼凑。4.6 小结降级解决的是“持续失败”降级解决的是持续失败的问题。它的核心思想是不追求每次调用都用最强方案而是保障整个任务在弱方案下也能完成。降级的副作用是质量下降所以必须用日志、告警和标记来兜住质量感知。降级率应该被当作一个重要监控指标持续跟踪。5. 第三种方式护栏校验与人工确认Guardrails HITL5.1 护栏解决问题的关键错误结果被当成正常结果如果说重试解决瞬时失败、降级解决持续失败那么护栏解决的是另一类问题Agent 没有报错但返回的结果无法被下游解析或者内容不安全。很多初学者容易忽略这类异常因为程序运行过程中没有任何异常抛出代码正常执行但结果就是不对。更危险的场景是Agent 返回了一个看起来正常、实际上错误的结论并且它还会被后续流程继续使用。举个例子Agent 被要求从文本中提取用户意图然后调用对应的业务接口。如果模型把“退款”误识别成“退货”系统就会走错流程。这类异常没有堆栈不会触发 try-catch只能靠输出校验来拦截。这就是护栏机制存在的意义。5.2 三类护栏第一类是结构化校验。要求 Agent 的返回必须符合约定的 JSON Schema字段类型、必填项、取值范围都要校验。第二类是语义合规校验。比如结果长度是否合理、是否包含禁止词、是否超出业务范围。第三类是业务审计校验。高风险操作在真正执行前必须经过人工确认或权限校验。前两类针对的是模型输出的“格式”和“内容”第三类针对的是系统执行的“风险”。5.3 使用 Pydantic 做结构化校验Agent 的返回内容校验建议不要用零散的 if-else 判断而是定义数据模型让校验逻辑集中化。Pydantic 是 Python 生态里最常用的数据校验库它可以非常简洁地定义 Agent 输出的结构。# 文件路径agent/schema.py from pydantic import BaseModel, Field, ValidationError class AgentResponse(BaseModel): action: str Field(description要执行的行动如 query_order, refund, reply) params: dict Field(description行动参数) reasoning: str Field(description模型做出该决策的推理过程) confidence: float Field(ge0.0, le1.0, description置信度) def parse_agent_response(raw: str) - AgentResponse: 解析模型返回内容。 注意模型可能输出 JSON 之外的文本这里只演示核心解析逻辑。 if not raw or not raw.strip(): raise ValidationError(模型返回为空) # 实际项目中建议做 JSON 提取和预处理 return AgentResponse.model_validate_json(raw)通过 Pydantic 定义 Agent 返回的结构一旦模型输出缺少字段、类型错误、数值超出范围校验会立即失败并给出具体错误信息。这一步可以在异常扩散到下游之前拦截大部分格式问题。5.4 校验失败后的修复策略模型输出校验失败不一定要直接判失败。更实用的做法是把校验错误信息回传给模型让模型自己修复。比如 JSON 缺少字段就把错误信息拼进 Prompt 再让它重试一次。这比直接失败更高效也符合 Agent“可对话、可修正”的特点。# 文件路径agent/guardrail.py from pydantic import ValidationError class AgentOutputValidationError(Exception): Agent 输出多次校验失败 pass def call_llm(prompt: str) - str: # 真实项目中替换为模型调用 ... def call_llm_with_json_repair(prompt: str, max_repair_times: int 1) - AgentResponse: 调用模型并做结构化校验失败时让模型自动修复一次。 raw call_llm(prompt) for _ in range(max_repair_times): try: return parse_agent_response(raw) except ValidationError as e: print(f校验失败尝试修复{e}) raw call_llm( f你上一次的返回不符合要求请修正 JSON。 f原始返回{raw}错误信息{e} ) # 修复失败走失败兜底 raise AgentOutputValidationError(模型输出多次校验失败)这里要注意一个细节修复策略里一定要把上一次的具体错误信息传给模型。很多项目里的修复失败是因为修复 Prompt 只写了“你的输出格式不对”却没有告诉模型到底哪里不对。模型不知道自己缺了哪个字段、哪个字段类型错误自然不知道该修什么。5.5 人工确认与 HITL业务风险高的动作必须设置人工确认环节。典型场景包括发送付款、修改订单、删除数据、执行外部系统写操作。实现思路是Agent 先产出“待执行计划”系统拦截发送给人工审核审核通过后才真正执行。# 文件路径agent/hitl.py class HumanApprovalRequired(Exception): 需要人工确认的异常 def __init__(self, task_id: str, plan: dict): self.task_id task_id self.plan plan super().__init__(f任务 {task_id} 需要人工确认{plan}) def approve_task(task_id: str) - bool: 模拟人工确认接口。实际项目可以接企业微信、钉钉、邮件等审批渠道。 print(f请审核任务 {task_id}审核通过请回复 yes拒绝请回复 no) # 实际项目可以通过消息服务自动推送 return input(审核结果 (yes/no): ).strip().lower() yes def execute_with_human_approval(task_id: str, plan: dict): try: raise HumanApprovalRequired(task_id, plan) except HumanApprovalRequired as e: print(f请审核任务 {e.task_id}{e.plan}) if not approve_task(e.task_id): raise PermissionError(人工审核未通过任务已拦截) print(人工确认通过开始执行任务)这段代码使用异常来传递“需要人工确认”的信号实际项目中你可以把人工确认做成一个独立的状态机。但核心思想是一样的高风险操作必须经过一个审批入口而不能绕过。5.6 人工确认的取舍人工确认会带来延迟所以不能对每个请求都启用。更合理的做法是分级处理低风险操作自动执行中风险操作自动执行但留审计日志高风险操作强制人工确认。分级规则建议由业务方和研发共同制定并且在配置中心里可动态调整。比如“查询订单状态”属于低风险直接执行“修改订单收货地址”属于中风险执行后留日志“发起退款”属于高风险必须人工确认。这个分级可以在系统初始化时加载到内存里也可以做成一个配置表由运营人员维护。分级的核心是找到业务风险与自动化程度之间的平衡点。5.7 小结护栏解决的是“错误结果被正常使用”护栏校验解决的是“错误结果被正常使用”的问题。它通过结构化校验、语义校验和人工确认把 Agent 的自由发挥限制在一个可控范围内。护栏不是越多越好过重的护栏会让 Agent 失去灵活性关键是找到业务风险与自动化程度之间的平衡点。在实际项目中建议先做语法校验保证系统不被格式错误打崩再逐步增加语义和审计护栏。6. 三种方式如何组合成一个完整的容错框架6.1 组合原则按失败类型分流前面三节讲的是三种独立手段但真实项目里它们需要组合使用。当任务进入 Agent 流程后推荐按以下顺序处理。第一步调用主模型第二步确认是否出现异常。如果是瞬时故障走重试如果重试后仍然失败根据失败类型决定是否降级。第三步拿到模型输出后先做结构化校验校验失败则尝试修复修复失败则降级或返回兜底。第四步如果 Agent 的决策涉及高风险动作进入人工确认环节。第五步整个过程打日志、出监控。这套流程的核心是把异常按“失败类型”分流而不是笼统地抛给一个 catch 块。瞬时故障交给重试持续故障交给降级格式问题交给护栏风险问题交给人工。每个环节各司其职才能组成完整的容错链路。6.2 一个组合示例下面是一个把三种方式串起来的完整示例。它先走重试失败后走降级拿到结果后做结构化校验最后根据行动类型决定是否进入人工确认。# 文件路径agent/pipeline.py import logging from pydantic import ValidationError logger logging.getLogger(__name__) # 高风险动作清单 REQUIRES_APPROVAL_ACTIONS {refund, delete, send_message, modify_order} def generate_task_id() - str: import uuid return str(uuid.uuid4()) def run_agent(prompt: str) - str: # 第一层重试 try: raw call_llm_with_retry(prompt) except AgentTransientError: # 第二层降级 raw, provider run_agent_with_fallback(prompt) logger.info(已降级到 provider%s, provider) except Exception as e: logger.error(模型调用最终失败%s, e) return 抱歉当前服务繁忙请稍后再试。 # 第三层护栏校验 try: response parse_agent_response(raw) except ValidationError: response call_llm_with_json_repair(prompt) # 第四层人工确认 if response.action in REQUIRES_APPROVAL_ACTIONS: task_id generate_task_id() try: execute_with_human_approval(task_id, response.model_dump()) except PermissionError as e: logger.warning(任务 %s 被拦截%s, task_id, e) return 该操作需要审批当前未通过已取消执行。 else: execute_action(response) return response.reasoning def execute_action(response: AgentResponse): # 根据 action 分发到对应业务处理函数 print(f执行动作{response.action}参数{response.params})注意代码中的顺序重试在前降级在后然后才是结构化校验和人工确认。这个顺序对应的是失败发现的时间线。模型调用阶段可能发生瞬时或持续故障所以先处理重试和降级输出结果拿到后再校验它的格式和语义校验通过后再判断是否需要人工确认。6.3 正确的日志与监控是容错的一部分没有日志和监控的重试、降级是没有意义的。你要记录的信息包括异常类型、发生位置、重试次数、最终是否成功、是否降级、降级到哪个提供商、耗时、token 消耗、是否触发人工确认。这些数据可以用来持续调优重试参数和降级阈值。在多人协作的 Agent 项目里建议为每个 Agent 任务生成一个 trace_id贯穿重试、降级、校验、人工确认的全过程。每一行日志都带上 trace_id遇到用户投诉时可以根据 trace_id 快速拉出整条链路的执行情况。这一点在排查 Agent 问题时尤其重要因为 Agent 链路长、分支多没有统一的 trace_id定位问题会非常痛苦。6.4 容错的终极目标容错框架里出现“错误”不再只是坏事它同时是系统收集反馈、自我调整的机会。一个完整的容错框架应该具备四个能力失败可发现日志失败可恢复重试与降级失败可拦截护栏失败可审计人工确认与 trace。四者缺一不可。如果你目前的 Agent 项目只做了重试建议尽快补上降级和护栏如果只做了护栏也要回头想想模型调用失败时系统能不能自动恢复。7. 常见问题与排查思路在 Agent 项目里异常处理相关的坑比较集中。下面整理了一些常见现象、可能原因、排查方式和解决方案方便你在实际工作中快速对号入座。| 问题
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门