tool-calling-loop-guard · ZH · 2026-10-28

在一把 key 上打断工具调用死循环:怎么发现 agent 反复发同一个调用

面向使用 LLM API 聚合站的开发者,讲解如何在一把 API key 上调用的多个模型后端上,通过识别重复工具调用签名、设置最大迭代预算和分后端循环阈值,在消耗过多 USDC 之前发现并终止 agent 的工具调用死循环。

死循环为什么值得专门处理

工具调用(function calling / tool use)让 agent 能自己决定下一步动作。代价是:一旦模型反复发起同一个调用,循环会在无人值守的情况下持续消耗 token。聚合站按官方价 ×1.3 从余额里扣费,每个来回都是真实成本;跑一夜的循环,账单会很明显。

更麻烦的是,这类循环往往不报错——HTTP 200、结构合法的 tool call、看起来一切正常,只是内容在重复。所以要靠主动检测,而不是等异常。

先定义:什么叫「同一个调用」

判断重复不能只看工具名。把一次工具调用归一化成一个调用签名(call signature),再比较签名是否相同:

  • 工具名
  • 参数对象的键排序后序列化(避免键顺序不同被当成新调用)
  • 去掉时间戳、随机 ID、request_id 这类每次都变的字段
  • 数值统一精度,字符串统一去空白

签名相同、连续出现同一签名,就是重复信号。若参数只在某个自动递增字段上变化(如 page=1,2,3),那是正常翻页,不应该判成循环——所以签名里要显式排除这类单调递增的游标字段。

三层防线:签名、预算、阈值

第一层:调用签名滑动窗口

维护一个最近 N 次工具调用的签名列表。当同一签名在窗口内出现次数超过设定值,判定为可疑重复。

注意区分两种重复:

  • 紧邻重复:连续两次一模一样,通常说明模型卡在某个观察结果上无法推进。
  • 周期重复:A→B→A→B,说明 agent 在两个动作之间来回摆动,往往是因为前一步的返回没被正确利用。

两种都该触发,但处理方式可以不同:紧邻重复直接中断当前回合;周期重复可以先注入一条提示,要求模型总结当前进展再继续。

第二层:最大迭代预算

给每个 agent 任务设一个硬上限:单次任务允许的最大工具调用轮数、最大总 token、最大累计花费。三者取先到者。

预算是最后一道闸,它保证即使检测逻辑漏判,成本也不会无限增长。预算应该按任务类型配置,而不是全局一个值——一个需要读十几个文件的重构任务,和一个只需查一次天气的任务,合理轮数差很多。

预算耗尽时的行为要明确:是直接失败返回,还是把当前上下文交给模型做一次「收尾总结」。后者对用户体验更好,也更省一次重跑。

第三层:分后端循环阈值

聚合站的一把 key 可以路由到不同模型后端,而不同模型对工具调用的行为差别很大:有的倾向一次给出多个并行调用,有的习惯一步一调用;有的在被工具报错后会立刻换参数重试,有的会原样重试。所以阈值不该全局统一。

实用做法:

  • 按后端(以及可选的模型)分别记录历史循环率,据此设定各自的窗口大小和容忍次数。
  • 对已知容易「原地重试」的后端,把紧邻重复的容忍度调到最低。
  • 对倾向并行调用的后端,窗口按调用批次而不是单次调用计数,避免并行被误判成重复。

阈值先给保守默认值,用线上真实数据再调,不要一上来就调得很激进——误杀正常任务同样有成本。

在代码里怎么落地

一次工具调用回合的检查顺序可以是:

  1. 模型返回 tool call 后,先算签名并入窗口。
  2. 查该签名的窗口计数与紧邻状态,超阈值则中断,不再执行工具。
  3. 未超阈值则检查任务预算(轮数 / token / 花费),超了走收尾分支。
  4. 都通过才真正执行工具,并把结果回传。

关键在于在执行工具之前判断。如果先执行再判断,重复的副作用(写文件、发请求、扣费)已经发生了。

中断时不要静默退出,把「检测到重复调用」作为一条明确事件记录下来:签名、出现次数、所用后端、当前轮数。这既是排查依据,也是后续调阈值的样本。

让模型自己跳出循环

检测到重复后,直接杀掉任务是最省钱的,但通常不是最优。更好的做法是先尝试一次「脱困」:

  • 把重复的调用签名和最近几次工具返回摘要一起回灌给模型,要求它说明为什么重复、下一步打算怎么改变策略。
  • 或者强制它换一个工具,或输出「无法继续」并给出原因。

很多循环源于工具返回的内容对模型不可读(过长被截断、错误信息含糊),模型只能原样再试。修工具返回的信息质量,往往比调阈值更能减少循环。

监控与成本视角

因为费用是按官方价 ×1.3 扣的,循环的边际成本是线性的,不会自己收敛。把以下指标纳入日常观察:

  • 每个任务的工具调用轮数分布,关注长尾。
  • 重复签名触发次数,按后端拆分。
  • 被预算截断的任务占比。

如果发现某个后端的重复触发率明显偏高,先看是不是提示词或工具 schema 的问题,再考虑调阈值。

对于自建服务的开发者,还可以在聚合站之外再套一层自己的配额:按 key、按任务类型分别限制单日工具调用总量。这和聚合站的余额机制互补——一个是事前限制,一个是事后扣费。

小结

掐断工具调用死循环的核心是把「重复」变成可计算的信号:归一化的调用签名、执行前的滑窗检查、任务级的迭代预算,以及按后端分档的阈值。这套组合不依赖具体模型,也不依赖某家供应商的实现细节,在单 key 多后端的架构下尤其适用。