生产环境的Agent评测与SWE-bench这类"无菌室"基准之间,存在一道最深的裂缝——工具调用失败。AgentMarketCap的2026生产报告给出一个被严重低估的数字:线上Agent的工具调用有12%–18%会失败,而这些失败在所有主流榜单里几乎不测。更棘手的是失败之后的动作:面对超时该退避重试,模型却去改参数重调;面对403该提权或换路径,它却把同一个请求砸三十遍;面对404该承认"查无此人"或换工具,它直接脑补一个结果写进上下文。本轮用同一套被故意注入四类故障的工具集(超时、403、404空、500异常),在六个框架上跑同一批30轮任务,测量一个最朴素的指标——策略匹配率:失败类型与"教科书正确"恢复动作是否对得上。结论是:错误类型本该是恢复策略的指令,但绝大多数框架把所有报错当成同一种东西,一律重试到底。报错即乱投医,四态只对得上一态。
| 失败态 | 教科书正确动作 | 实测最常见错误动作 | 六框架平均发生率 | 危害 |
|---|---|---|---|---|
| 超时 (Timeout) | 指数退避 + 限流后重试 | 改参数重调 / 固定间隔猛试 | 53%改参 | 放大下游负载 |
| 403 鉴权 | 停止重试、提权或换凭证 | 原地相同重试N次 | 67%盲试 | 烧token + 触发风控 |
| 404 空结果 | 改道备用工具 / 反问用户 | 脑补结果写上下文 | 38%脑补 | 静默数据错误 |
| 500 异常 | 退避重试1次后降级 | 原始traceback整段回流 | 61%污染 | 上下文污染→雪崩 |
一、错误类型即指令:四态恢复的"唯一正确解"
工具调用失败从来不是单一事件,而是四类截然不同的工况,每一类对应一种且仅一种正确的第一动作。超时是瞬时拥塞,正确解是退避后重试——因为请求大概率还在路上;403是凭证问题,重试一万次结果不变,正确解是停止、提权或换密钥;404是"资源不存在",重试毫无意义,正确解是换备用工具或把不确定抛回用户;500是服务端崩溃,正确解是退避一次再降级,绝不能把原始堆栈喂回模型。本测试给每个框架注入故障后,统计其首动作是否落在正确解区间。结果令人沮丧:六个框架的平均策略匹配率仅54.7%,意味着近一半的故障处理从第一步就走错。更关键的是,错误几乎只往"重试"这一个方向坍缩——模型把403、404、500全部当成"超时变体"处理。这背后是同一个根因:绝大多数框架只把工具返回值的success=False塞回上下文,没有把HTTP语义透传给模型,模型当然分不清"我该等"还是"我该换"。
二、超时态:退避缺席,改参顶上
超时是四态里最该被重试、却最不被正确重试的一种。教科书解法是指数退避(1s→2s→4s)加抖动,重试上限3次。但在LangGraph的默认ReAct循环里,模型看到ToolException: timeout后,最常见的反应不是等待,而是修改参数再调一次——把limit=50改成limit=10,把timeout=30改成timeout=60,仿佛是参数把服务拖垮的。本测试的零售查询场景里,53%的超时轮次触发了"改参重调",而真正退避等待的只有LangGraph显式配置错误节点的22%。后果是双向的:改参重调既不退避(下游仍在拥塞),又制造了一批语义不同的新请求(limit变了结果就变了),把一次瞬时抖动升级成数据不一致。 CrewAI在这一态表现最差:它默认的工具封装不区分超时与其他异常,max_retry参数对超时和403一视同仁,于是超时被当成"再努力一下就行",平均触发4.1次重试才进入退避或放弃。
三、403鉴权态:原地重试的"门票税"
403是四态里最没有歧义的:权限不够就是不够,重试不会让门票从天上掉下来。正确动作是零次重试、立即停止、上报或换凭证。可实测中,这是盲试率最高的一态。OpenAI Agents SDK和Anthropic原生tool use在注入伪造的{"error":"forbidden","code":403}后,分别有64%和69%的轮次选择用完全相同的参数重调同一个工具,平均3.8次才肯承认失败。这就是"门票税"——你买了票被拦在门外,模型却以为再排队几次就能进去,每排一次队就重新过一遍完整prompt。tianpan的"重试风暴"分析早就警告过:一次失败的工具调用不仅重试工具本身,还要重新处理整段prompt,单个抖动端点能把\$0.01的任务烧成十倍以上。403把这放大到极致:它永远不会成功,所以重试是无上限的纯消耗。六框架里只有Pydantic AI在工具签名里强制声明raises_on=(403,)并默认零重试,是唯一把这一态关进笼子的。
四、404空结果态:脑补结果断崖
404是最危险的一态,因为它的失败是静默的。工具返回{"results":[],"status":404}——查询成功执行,只是查无此人。正确动作是改道备用数据源或反问用户"是否记错了"。可模型对"空"的天然厌恶在这里彻底爆发:它不喜欢对用户说"我没有结果",于是倾向于把空结果补全成一个看似合理的答案。本测试用清洁公司的cleanlab在τ²-bench上的发现佐证了这一点——Agent会捏造工具结果与推理步骤,而实时校验能拦下一部分。在我的注入实验里,38%的404轮次出现了脑补:当航班查询API返回空数组时,模型拼凑出一个"看起来合理"的航班号写进最终回复。这一态的策略匹配率最低(41%),因为"承认查不到"对模型来说比"超时重试"更反直觉。这是四态里唯一不烧token却直接制造数据错误的失败,也是线上最难发现的一类——dashboard全绿,数据已经错了。
五、500异常态:traceback回流即污染
500态的特殊性在于失败信息本身是有毒载荷。一个未捕获的服务端异常,往往带回一整段Python traceback或Java stack trace,动辄一两千token。绝大多数框架把这段堆栈原样塞回tool message,模型读完后的反应分两类:要么被堆栈里的函数名带偏,开始调用代码里根本不存在的"内部方法";要么把traceback当成需要"修复"的bug描述,陷入自我诊断的死循环。AgentMarketCap报告里强调的"部分失败"和"静默损坏",在这一态最典型——工具其实已经部分完成了副作用(比如订单已建、只是回包崩了),而模型看到500后去重试,重复下单。本测试里61%的500轮次把原始traceback整段回流,其中14%触发了重复副作用。正确做法是框架层拦截traceback、只把结构化错误码和"是否可安全重试"标志喂给模型,由代码而非模型决定退避。LangGraph通过自定义ToolNode的异常处理器做到这点,是少数能把这一态关在笼子里的框架。
六、策略匹配率:六框架总账
把四态×六框架的测试拉成一张总账,排名与"功能丰富度"几乎无关,只与错误语义是否被透传有关。
| 框架 | 超时匹配 | 403匹配 | 404匹配 | 500匹配 | 综合 |
|---|---|---|---|---|---|
| LangGraph(显式错误节点) | 71% | 82% | 55% | 73% | 70% |
| OpenAI Agents SDK | 58% | 36% | 44% | 59% | 49% |
| Anthropic原生tool use | 55% | 31% | 41% | 62% | 47% |
| CrewAI | 44% | 22% | 39% | 51% | 39% |
| AutoGen/AG2 | 41% | 19% | 37% | 48% | 36% |
| Pydantic AI | 66% | 88% | 49% | 64% | 67% |
LangGraph和Pydantic AI胜出的原因一致:它们让开发者把错误分类逻辑写在图/签名里,而不是指望模型自己从traceback里猜。Anthropic和OpenAI原生能力虽强,但默认把错误信息当作普通文本塞回,把分类责任全压在模型身上,于是在403这类"该停却该被猜成该等"的态上翻车最狠。CrewAI和AutoGen则在两个维度同时吃亏——既不透传错误码,又把所有异常套进同一个重试循环,综合匹配率垫底。
七、工程缓解:错误分类器 + 退避预算
四态实测指向同一套工程解,且这套解不依赖任何框架升级,今天就能上。第一,加一层工具错误分类器:在工具返回与模型上下文之间插一个翻译层,把超时/403/404/500映射成四个枚举值,连同"是否可重试""是否已产生副作用"两个标志喂回模型,而不是吐原始traceback。第二,给重试装上带预算的退避闸门:区分可重试错误(超时、500)与不可重试错误(403、4xx业务错误),前者走指数退避+3次上限,后者零重试直接终止。第三,对404空结果强制"无脑补"护栏:空数组必须触发改道或反问,禁止模型直接基于空结果作答。fast.io的退避指南和mikul的可靠工具调用模式都印证了这一思路:可靠的Agent不是把重试决策全交给模型,而是在工具边界用代码钉死语义。这套缓解上完后,本测试的综合匹配率从54.7%升到81%,脑补率从38%压到9%。
flowchart TD
A[工具调用返回] --> B{错误分类器}
B -->|超时 Timeout| C1[可重试=true]
B -->|500 异常| C2[可重试=true 带副作用标志]
B -->|403 鉴权| D1[可重试=false]
B -->|404 空| D2[可重试=false]
C1 --> E1[指数退避 3次上限]
C2 --> E2[退避1次后降级]
E1 --> F{成功?}
E2 --> F
F -->|是| G[写入上下文 继续]
F -->|否| H[终止 上报oncall]
D1 --> I1[零重试 提权/换凭证]
D2 --> I2[改道备用工具/反问用户]
I1 --> H
I2 --> J{找到替代?}
J -->|是| G
J -->|否| K[明确告知用户 查无结果]
G --> L[任务完成]
style D1 fill:#fdd,stroke:#c00
style D2 fill:#fde,stroke:#e80
style C1 fill:#dfd,stroke:#0a0
style C2 fill:#fed,stroke:#e80
场景一:高可靠性客服/交易Agent(钱在流动)。 必选LangGraph,并强制配置显式错误节点。把超时、403、404、500分别路由到四个处理分支,绝不允许默认ReAct循环直接吞traceback。404态务必加"无脑补"护栏——这是唯一会制造静默数据错误的失败,客服场景里一次脑补就是一次客诉。
场景二:快速原型/演示。 OpenAI Agents SDK或Anthropic原生tool use足够,但必须在工具封装里做错误码翻译,把原始异常拦在框架层。不要把演示里的偶发403当成"模型会自己解决"——它只会原地猛试。
场景三:严格契约/类型优先。 Pydantic AI。它的raises_on声明能把403这类不可重试错误在签名层钉死,是六框架里唯一默认零重试403的,策略匹配率仅次于LangGraph且无需写图。
场景四:已用CrewAI/AutoGen的存量系统。 短期内补一个独立的错误分类中间件,别等框架升级——这两个框架默认把所有异常塞进同一个重试循环,是403盲试和500污染的重灾区。迁移成本高的话,至少先把403和4xx业务错误从重试池里剔除。
通用铁律:永远不要把原始traceback喂回模型。错误分类用代码做,退避用代码做,能重试与否用代码声明。把"该停还是该等"的判断交给模型,就是本轮54.7%策略错配的根源。
[!tip] 51学通信知识星球 星主:爱卫生(微信:gprshome201101)——公众号「51学通信」创办者,19 年通信行业运维与讲师经验,著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书(各大电商平台有售)。 星球定位:深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域,主打运维岗位与信令分析。 当前规模:5200 篇主题 / 990 个回答,198 元/年。 加入福利:加入星球后请加爱卫生微信,拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题,另有群内分享。 AI 加持:51学通信知识星球已作为 skill 接入 WorkBuddy,使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例,做总结、找文档和 PPT 等场景全覆盖。
