AI Agent工具调用死循环成本控制

AI Agent 工具调用死循环怎么办?轮次、预算与状态机防护指南

2026-09-14 · 约 9 分钟阅读

Agent 接入搜索、数据库或订单工具后,最危险的故障往往不是一次报错,而是“看起来仍在工作”:模型反复提交相同参数、工具不断返回同一错误,或者两个工具互相触发,直到请求超时、配额耗尽。只在 Prompt 里写“不要重复调用”无法形成可靠边界,因为模型输出本身就应被视为不可信计划。

正确做法是由编排层掌握执行权。模型负责提出下一步,程序负责检查轮次、预算、状态与权限,并决定执行、拒绝还是转人工。本文是方法论教程,不依赖任何特定模型版本或价格。

先识别四类常见循环

  • 同参循环:工具名和参数完全相同,结果也没有变化
  • 近似循环:模型只修改无关字段,例如重复换页却始终命中同一批数据
  • 错误循环:工具持续返回参数错误或权限不足,模型没有改变修复策略
  • 振荡循环:在“查询—修改—再次查询”之间来回切换,状态没有向目标推进

监控时不要只看总轮数。应同时记录工具名、规范化参数哈希、结果摘要哈希、业务状态版本和累计消耗。这样才能区分合理的多步任务与无进展循环。

第一层:硬上限必须写在代码里

每个任务至少设置四类限制:最大模型轮次、最大工具调用次数、最大墙钟时间和最大预算。上限应按业务场景配置,而不是让前端任意提交。只要触发任意一项,就停止自动执行并返回可恢复状态。

type Limits = {
  maxTurns: number;
  maxToolCalls: number;
  deadline: number;
  maxCostUnits: number;
};
function assertWithinLimits(state: RunState, limits: Limits) {
  if (state.turns >= limits.maxTurns) throw new Error("turn_limit");
  if (state.toolCalls >= limits.maxToolCalls) throw new Error("tool_limit");
  if (Date.now() >= limits.deadline) throw new Error("deadline_exceeded");
  if (state.costUnits >= limits.maxCostUnits) throw new Error("budget_exhausted");
}

预算检查要在下一次调用之前完成,并为最坏输出和工具费用预留空间。若只在响应回来后记账,最后一次调用仍可能突破上限。

第二层:对工具请求做规范化去重

直接对 JSON 字符串求哈希并不可靠,因为键顺序、空白和默认值会变化。应先按 Schema 解析,移除无业务意义字段,统一 URL、时间与大小写,再稳定排序生成指纹。

function fingerprint(name: string, args: unknown) {
  const normalized = normalizeByToolSchema(name, args);
  return sha256(name + ":" + stableStringify(normalized));
}
function rejectRepeatedCall(state: RunState, fp: string) {
  const recent = state.calls.filter(x => x.fingerprint === fp);
  if (recent.length >= 2 && recent.every(x => x.progress === false)) {
    throw new Error("repeated_call_without_progress");
  }
}

“重复两次就拒绝”不是固定真理。分页抓取、轮询审批等任务可能合法重复,因此规则还要结合游标、资源版本和结果变化。重点不是重复本身,而是重复后是否产生可验证进展。

第三层:用状态机定义允许的路径

复杂任务不要只保存消息数组。把阶段显式建模为 planning、awaiting_approval、executing、verifying、completed 和 failed,并为每个阶段列出允许的工具。例如审批前可以生成退款草案,但不能调用真实退款接口;完成后禁止再次产生副作用。

每次工具返回后,由确定性代码更新业务状态。模型可以解释结果,却不能自行宣称“订单已修改”。状态转换应依据工具回执、数据库版本或任务 ID。若连续若干轮没有状态变化,就触发无进展保护。

第四层:副作用工具必须幂等

死循环最怕重复发邮件、重复扣款或重复创建工单。为每个副作用动作生成业务幂等键,例如 run_id + step_id + action_type,执行器先查询是否已完成,再决定调用外部系统。不要使用模型每轮生成的新随机值作为幂等键,否则无法识别同一业务动作。

工具还应返回机器可读错误:retryable、validation_error、permission_denied、conflict。只有明确可重试的瞬时错误才能进入有限重试;参数错误应把字段级原因交给模型;权限错误则直接停止,不能靠换一种措辞绕过。

如何设计安全退出与人工接管

停止不等于丢弃任务。保存最后一个已确认状态、已完成动作、未执行计划、失败原因和审计 ID,然后给用户三个明确选择:修改输入后继续、由人工审批某一步、取消并回滚可回滚动作。不要把内部堆栈或完整工具返回直接展示给终端用户。

人工接管时,审批对象必须绑定冻结后的工具名、参数摘要、资源版本和失效时间。审批通过后只执行这一份已审内容,不能让模型在按钮点击后重新生成参数。

上线验收清单

  • 编排层拥有轮次、工具次数、时间和预算四种硬上限
  • 相同及近似工具调用可按“是否产生进展”识别
  • 任务阶段与允许工具由状态机约束
  • 副作用操作有稳定幂等键,重放不会重复执行
  • 错误被划分为可重试、需修参、权限拒绝和永久失败
  • 达到限制后可保存检查点、人工接管并审计全过程
  • 中转站超时或切换模型不会重置整条任务预算

总结

Agent 死循环不是单纯的 Prompt 问题,而是执行编排缺少确定性边界。硬上限控制最坏损失,规范化指纹发现无进展重复,状态机限制路径,幂等键保护真实业务。四层同时存在,模型偶尔做出错误计划也不会演变成连续扣费或重复操作。准备选择支持用量查询、请求追踪和稳定工具透传的平台时,可前往 openairouter.net 查看 AI API 中转站对比与排行榜,再用本文清单完成压力与故障测试。

找到最适合你的 AI API 中转站

收录 125+ 服务商,按价格、模型、标签一键筛选

查看所有中转站 →