中级工具调用技术一面

工具调用失败:重试策略怎么设计才不死循环

考察候选人对Tool Calling异常处理的工程思维——可恢复与不可恢复错误的区分、退避策略、以及如何防止Agent进入无限重试

#Tool Calling#重试策略#异常处理#容错设计

情景对话

大J
大J
工具调用失败了,你的Agent会怎么处理?
小Y
小Y
第一反应是先搞清楚为什么失败——这个问题的核心不是「要不要重试」,而是「这个错误值不值得重试」。 我会把错误分两类:一类是「可恢复的」,比如网络抖动、限流、超时,这些重试是有意义的;另一类是「不可恢复的」,比如参数格式错误、鉴权失败、资源不存在,重试只是在浪费时间和token,应该直接放弃并告知用户。 这个判断如果不做,Agent就会对着一个永远会失败的工具反复调用,直到耗尽轮次。
大J
大J
怎么判断一个错误是可恢复的?
小Y
小Y
主要看两个维度:HTTP状态码和错误语义。 5xx基本都是可恢复的候选——服务端临时故障;429是限流,可恢复但要退避等待;4xx里面,400/422是参数问题,不可恢复,重试没意义;401/403是权限问题,也不可恢复;404是资源不存在,要分情况——如果是查询类操作,404是正常结果不是错误。 另外有些工具会在error message里带上语义信息,比如「quota exceeded」和「invalid input」,也可以用来辅助判断。 坦白说,边界情况挺多的,我之前的项目里是维护了一个错误码白名单,只有在白名单里的才走重试逻辑。
大J
大J
好。那重试本身怎么设计?直接重试还是有等待?
小Y
小Y
肯定不是直接重试,特别是对限流场景。我用的是指数退避加抖动:第一次失败等1秒,第二次等2秒,第三次等4秒,但每次加一个随机抖动,避免多个Agent同时重启的「惊群效应」。 最大重试次数我设3次,超过就走降级。次数不宜太多,因为每次重试都要把工具调用结果带回LLM做推理,context会持续膨胀。 还有一个细节:重试时要带上上一次失败的错误信息给LLM,让它在下一个Thought里能感知到「我刚才失败了,原因是XXX」,而不是盲目重试。有时候LLM会自己调整参数,比如上次超时它会减少查询量。
大J
大J
LLM自己调整参数——这个靠谱吗?不会调出更烂的结果?
小Y
小Y
这个确实是个风险点。我们的做法是限定LLM能修改的参数范围——比如查询工具,它可以调整时间范围或关键词,但不能修改鉴权信息或目标接口。通过工具的schema描述来约束,readonly字段就不暴露给LLM改。 另外对高风险工具,我们完全禁止LLM在重试时修改参数,必须保持原始调用参数,只允许它决定「要不要继续」。低风险工具才允许参数微调。这是在灵活性和安全性之间做的权衡。
大J
大J
说说实际踩过的最深的一个坑。
小Y
小Y
最典型的一次是一个数据库查询工具,返回的是空结果而不是错误码。LLM把「空结果」当成「查询失败」处理,然后不断修改查询条件重试,条件越改越宽,最终查出了不相关的数据,还很「自信」地把这些数据当作答案返回给了用户。 那次之后我们规定:空结果是正常语义,必须在工具的schema里明确标注「空数组是合法返回值,代表无匹配记录」。LLM看到这个说明就不会把空结果误判为错误了。 本质上这类问题都是工具契约不清晰导致的——工具告诉LLM的信息越精确,LLM的行为就越可预期。

核心概念

评分维度

延伸追问