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 中转站对比与排行榜,再用本文清单完成压力与故障测试。