Agent 调用工具失败或者超时了怎么办?
面试官问你:“Agent 调用工具失败或者超时了怎么办?”如果你回答“加个 try-catch,失败了就重试三次”,恭喜你,出门右拐回家等通知吧。
为什么?
因为失败分好几种。网络抖动了,重试一次可能就好了;参数写错了,重试一百次还是错的;扣款的工具超时了,重试一次,用户可能就被扣了两次钱。而且真正能把错误改过来的,往往不是 try-catch,而是模型自己。
【截图:工具失败的三种类型;风格:whiteboard;截图目标:展示瞬时错误、确定性错误、超时三类失败,以及各自对应的处理方向;关键词:瞬时错误、确定性错误、超时】
恭喜看到这里的你,已经成功击败 30% 的学习者,给自己鼓个掌吧。接下来我问你,工具执行报错了,Harness 应该怎么处理?
A,直接抛异常,终止任务;B,Harness 自己重试;C,把错误信息作为工具结果,回传给模型。聪明的你可以把答案打在弹幕或者留言区。
我的答案是 C。先说一下 Harness,Agent 等于 Model 加 Harness,Harness 就是模型之外那套负责执行工具、拼接上下文的程序。
拿 Claude 的接口来说,模型每发起一次工具调用,就会产生一个 tool_use,Harness 执行完,必须紧跟着回一个对应的 tool_result,少一个,API 就直接返回 400 错误。工具出错了也一样要回,只是把 is_error 字段设为 true,再把错误原因写进内容里。
错误信息是写给模型看的。不要只写一个 failed,要写清楚出了什么错、下一步该怎么做,比如 Anthropic 官方文档里的示例,“Rate limit exceeded. Retry after 60 seconds.”。按照官方文档的说法,参数缺失或者无效时,Claude 会自己修正参数,重试 2 到 3 次,实在不行才向用户道歉。
Claude Code 的源码里有一行注释特别真实,大意是“没想到模型生成合法参数的能力这么差”。所以它每次执行工具之前,都会用 Zod 校验参数,校验失败就回一句“The required parameter command is missing”,明确告诉模型缺了哪个参数。
MCP,也就是 Anthropic 提出的模型上下文协议,也是这么规定的。未知工具、请求格式错误这类协议错误,走 JSON-RPC 的错误响应;接口失败、业务校验失败这类执行错误,放进工具结果里,标上 isError: true,交给模型自己纠正。
【截图:工具报错的回传流程;风格:swimlane;截图目标:展示模型发出 tool_use、Harness 执行失败、回传 is_error 为 true 的 tool_result、模型修正参数后重新调用的完整过程;关键词:tool_use、tool_result、is_error】
try-catch 是写给程序看的,错误信息是写给模型看的。恭喜你,已经成功击败 50% 的学习者了。
接下来继续问你,哪些错误应该重试?
A,所有错误都重试;B,网络抖动和限流;C,参数错误。聪明的你会选哪一个?
我的答案是 B,先分类,再处理。
网络断开、429 限流、5xx 服务端错误,这类叫瞬时错误,过一会儿可能就好了,适合重试。不管是工具请求外部接口,还是 Harness 请求模型接口,道理都一样。重试要用指数退避加随机抖动,等待时间每次翻倍,再随机加一点,避免成千上万个客户端在同一时刻一起重试。服务端如果返回了 retry-after 头,就按它给的时间等。
Claude Code 请求模型接口失败时,最多重试 10 次,等待时间从 500 毫秒开始翻倍,上限 32 秒,再加上最多 25% 的随机抖动。连续 3 次遇到 529 过载错误,如果配置了备用模型,就自动切换过去。
额度用完、上下文超限、参数错误,这类叫确定性错误,重试多少次结果都一样,所以不重试。Codex 的源码里有一张可重试错误的白名单,额度超限 QuotaExceeded、上下文窗口超限 ContextWindowExceeded 都不在上面。
重试也不能每一层都做。Amazon 的工程师 Marc Brooker 在 2019 年的 Builders' Library 文章里算过一笔账,五层调用,每层重试 3 次,最底层数据库的负载会放大 243 倍。所以重试只放在一层做。
【截图:三家 Harness 的重试参数对比;风格:data-board;截图目标:对比 Claude Code、Codex、Pi 的最大重试次数、初始等待时间、抖动方式和是否读取 retry-after 头;关键词:指数退避、随机抖动、retry-after】
重试只救得了运气不好,救不了写错了。恭喜你,成功击败 70% 的学习者了。
继续问你,工具超时了怎么办?
A,丢掉返回值,继续下一步;B,杀掉进程;C,把命令转到后台继续执行。聪明的你会选哪一个?
我的答案是 B 和 C 都对,看场景选,只有 A 是错的。
Codex 的一次性执行模式,默认超时 10 秒,到点之后用 killpg 给整个进程组发 SIGKILL 信号,连子进程一起结束,退出码统一改成 124,再告诉模型“command timed out after 10000 milliseconds”。
Claude Code 的 Bash 工具默认超时 2 分钟,最长可以设置成 10 分钟。超时后默认不杀进程,而是把命令转成后台任务,把任务 ID 和输出文件的路径交给模型,模型过一会儿可以再去读取结果。跑测试、跑构建这种耗时长的命令,就适合这么处理。
A 错在只是不等了,进程却还在执行,继续占着端口和内存,甚至还在修改文件。而且没有回 tool_result,下一轮请求 API 就会报错。
【截图:工具超时的处理策略对比;风格:three-layer;截图目标:对比丢弃返回值、杀掉整个进程组、转成后台任务三种做法的结果,标出 Codex 和 Claude Code 各自的默认超时;关键词:killpg、后台任务、退出码 124】
超时只是你不再等了,进程可没有停。恭喜你,成功击败 90% 的学习者了。
最后,面试官如果追问:“扣款这种工具超时了,能直接重试吗?”
告诉他,不能。超时不代表没有执行,可能钱已经扣了,只是响应没回来。这种有副作用的工具,必须先做幂等。
Stripe 的做法是每个请求带上一个 Idempotency-Key,服务端把第一次请求的状态码和响应体存下来,同一个 key 再来请求,直接返回第一次的结果,key 至少保留 24 小时。放到 Agent 里,就是调用工具之前先生成一个请求 ID,重试时复用同一个 ID。同一步连续失败几次之后,就停下来交给用户处理。
【截图:带幂等键的重试流程;风格:swimlane;截图目标:展示 Agent 生成请求 ID、第一次请求超时、带同一个 ID 重试、服务端直接返回第一次结果的过程;关键词:Idempotency-Key、幂等、请求 ID】
恭喜你升到王者段位了,成功击败 99% 的学习者。
最后简单总结下。
瞬时错误退避重试,确定性错误回传给模型改参数,超时要真正结束或者接管进程,有副作用的工具先做幂等再谈重试。
另外,自己写工具的时候,错误信息别只写一个 failed,写清楚错在哪、下一步该怎么做,模型会自己改。
这个知识点你学会了吗?想解锁更多 Agent 硬核知识,点赞关注,我是二哥,咱们下期见!
