DEBUG.WIKI
V.PS:让项目靠近用户,亚太、欧洲、北美 KVM VPS
IPPeak:从目标地区访问网页,住宅代理、SEO 监测、广告验证
ZgoCloud:多地区部署,按需选节点;日本、美国、香港、德国

KeyError 也被重试

ToolRetryMiddleware 只想重试 TimeoutError,KeyError 却也调用多次。核对单异常类的 callable 分支,用尾逗号元组保留真实过滤与失败策略。

LangChain 的 ToolRetryMiddleware 已写 retry_on=TimeoutError,工具抛出 KeyError 却仍调用三次。ModelRetryMiddleware 也可能出现同样表现。这时先检查 retry_on 是否只传了一个异常类,而没有按接口写成元组。

这一表现有 langchain 1.4.0 的实际反馈。已核对的正式 1.4.5 仍先判断参数是否 callable,类型约定也仍是异常类型元组或布尔判断函数。升级主包后仍要核对实际传入的配置形式。

异常类也能被调用

Python 的异常类是 callable。旧判断拿到 TimeoutError 时,把它当成用户提供的判断函数,执行 TimeoutError(exc)。

这个调用构造了一个异常对象,并没有检查当前错误是不是 TimeoutError。返回的对象为真,重试循环便继续。原本应立即抛出的 KeyError、配置错误等,也可能耗完同一份重试预算,最后进入 on_failure。

保留实际中间件配置、异常类型与调用次数,区分中间件再次调用工具、HTTP 客户端重试,以及模型重新决定执行工具。日志出现多个相同请求,不一定都来自这一个判断分支。

单一类型也写成元组

在这组正式接口上,使用带尾逗号的单元素元组:

from langchain.agents.middleware import ToolRetryMiddleware

retry = ToolRetryMiddleware(
    retry_on=(TimeoutError,),
    max_retries=2,
    on_failure="error",
)

这段只构造中间件,不注册或执行工具。实际合入时保留原来允许重试的工具范围、退避与延时设置;on_failure='error' 是示例的耗尽策略,应与应用约定一致,不能因为改了元组就顺手改变失败后的 Agent 流程。

元组最后的逗号不能省略:(TimeoutError) 仍然是同一个异常类。多个类型可以放进同一元组,也可以提供明确返回 bool 的判断函数;判断依据应来自应用本来允许重试的异常,不扩大成所有错误都可重试。

ModelRetryMiddleware 使用同一个匹配辅助逻辑,单一类型也应按元组传入。不匹配的异常会立即传播,不经过“已匹配错误耗尽重试”那条 on_failure 分支;这个区别需要在上层错误处理里保留。

重试预算不是总调用次数

max_retries=2 表示首次调用之后最多再重试两次,因此匹配错误持续失败时,总次数最多是三。验收时用获准的无副作用工具检查两条路径:不匹配的 KeyError 只调用一次;匹配的 TimeoutError 才按原预算与退避策略处理。

同步和异步调用都要核对,不能只看构造器接受了配置。中断或父图命令等控制信号有自己的传播路径,也不应为了重试把它们统统捕获成普通工具错误。

修正类型过滤,只说明不该重试的错误不再进入这份预算。TimeoutError 并不证明远端没有执行操作;涉及提交、创建或其他副作用的工具,仍需要幂等或结果确认契约。不要通过增加重试次数、吞掉所有异常或伪造成功 ToolMessage 来验证这项调整。