☰
Agent上线后账单暴涨?我用三层熔断,把无限循环调用这个炸弹拦在了门外|可写简历
2026/11/6 5:21:32 网站建设 项目流程

前面两篇,我们把 Agent 压测最容易踩的两个坑讲透了:
第一篇告诉你,只压 HTTP 接口不算压 Agent;第二篇告诉你,真实模型烧钱压测不如先做 Model 层仿真。

这一篇,我们来解决一个更隐蔽、也更致命的问题:Agent 无限循环调用。

很多团队上线 Agent 后,账单不是线性增长,而是突然翻倍、甚至几倍。
一开始大家会怀疑:是不是用户量涨了?是不是 Prompt 写得太长?是不是模型太贵?

查下来往往不是。
真正的原因,经常是 Agent 在某些输入下反复进入“思考—调用工具—再思考—再调用工具”的循环。

循环本身不一定报错。
接口可能一直返回 200,LLM 网关也可能没有明显异常,但 Token、工具调用次数、向量库查询量一直在涨。

等账单告警出来,损失已经发生了。

💡 框架迁移提示:本文熔断与上下文裁剪思路是通用方案,示例基于 Spring AI 实现;如果你使用 LangChain4j、AgentScope 等其他 Java Agent 框架,核心思路一致,只替换为对应框架的中断条件与上下文管理扩展点即可。

一、先搞清楚:Agent 为什么会无限循环?

Agent 和普通接口最大的区别,是它的执行路径不确定。

普通接口:输入固定,逻辑固定,结束条件明确。
Agent:输入同一个问题,可能一轮结束,也可能多轮推理;如果工具返回结果不收敛,模型就可能一直要求继续调用。

常见循环场景有三类:

1. 工具结果无法满足模型预期

比如模型要求“查到结果再回答”,但工具每次都返回“未查到”“请补充条件”。
模型理解为条件不足,于是继续调用同一个工具,进入死循环。

2. 输出格式不稳定

模型第一次调用工具失败,第二次换参数重试,第三次换说法再试。
如果没有次数限制,重试会被不断放大。

3. 上下文越来越长,模型越来越难收敛

每一轮工具结果都塞回上下文,Token 越来越多,模型注意力下降。
到后面,模型可能已经分不清自己有没有查过、有没有回答过,只能继续调用工具。

所以,要治 Agent 循环,不能只靠模型“自觉结束”,必须在工程上加三层边界:

  • 第一层:限制推理轮次
  • 第二层:限制工具调用次数
  • 第三层:限制上下文长度

二、第一层熔断:限制 Agent 最大推理轮次

最有效的第一道防线,是给 Agent 设置一个明确的最大轮次。

很多人觉得“设轮次限制会影响正常能力”。
实际上,绝大多数业务 Agent,正常回答都不需要几十轮。
真正异常的请求,往往会在第 5 轮、第 8 轮还不收敛。

我们可以维护一个轮次计数器,每完成一轮推理就加 1;超过阈值后,主动终止执行。

代码实现:RoundLimitAdvisor

package com.agent.demo.advisor; import org.springframework.ai.chat.client.advisor.api.AdvisedRequest; import org.springframework.ai.chat.client.advisor.api.AdvisedResponse; import org.springframework.ai.chat.client.advisor.api.CallAroundAdvisor; import org.springframework.ai.chat.client.advisor.api.CallAroundAdvisorChain; import org.springframework.stereotype.Component; @Component public class RoundLimitAdvisor implements CallAroundAdvisor { private static final int MAX_ROUNDS = 5; @Override public AdvisedResponse aroundCall(AdvisedRequest advisedRequest, CallAroundAdvisorChain chain) { Integer round = (Integer) advisedRequest.chatOptions().getMetadata().getOrDefault("round", 1); if (round > MAX_ROUNDS) { throw new IllegalStateException("Agent 超过最大推理轮次,已终止执行"); } advisedRequest.chatOptions().getMetadata().put("round", round + 1); return chain.nextAroundCall(advisedRequest); } @Override public String getName() { return "roundLimitAdvisor"; } @Override public int getOrder() { return 0; } }

设计说明:

  • round存在 metadata 里,跟随整个 Agent 会话传递。
  • 每调用一次aroundCall,代表一轮交互。
  • 超过MAX_ROUNDS后直接抛异常,不再继续调用模型。
  • 异常会被全局异常处理器捕获,返回明确错误,而不是继续消耗 Token。

推荐阈值

不同业务阈值不同,但建议:

  • 普通问答 Agent:3~5 轮
  • 工具型 Agent:5~8 轮
  • 复杂任务 Agent:8~15 轮

不要一上来就设 20 轮、30 轮。
阈值越高,成本风险越大。先从保守值开始,再根据真实业务观察放开。


三、第二层熔断:限制工具调用次数

有些场景下,Agent 推理轮次还没到上限,但工具调用已经失控。
比如反复调用同一个检索工具、数据库工具、计算工具。

所以第二层熔断,要单独统计工具调用次数。

代码实现:ToolCallLimitAdvisor

package com.agent.demo.advisor; import org.springframework.ai.chat.client.advisor.api.AdvisedRequest; import org.springframework.ai.chat.client.advisor.api.AdvisedResponse; import org.springframework.ai.chat.client.advisor.api.CallAroundAdvisor; import org.springframework.ai.chat.client.advisor.api.CallAroundAdvisorChain; import org.springframework.stereotype.Component; @Component public class ToolCallLimitAdvisor implements CallAroundAdvisor { private static final int MAX_TOOL_CALLS = 4; @Override public AdvisedResponse aroundCall(AdvisedRequest advisedRequest, CallAroundAdvisorChain chain) { Integer toolCallCount = (Integer) advisedRequest.chatOptions().getMetadata() .getOrDefault("toolCallCount", 0); if (toolCallCount >= MAX_TOOL_CALLS) { throw new IllegalStateException("Agent 超过最大工具调用次数,已终止执行"); } AdvisedResponse response = chain.nextAroundCall(advisedRequest); boolean hasToolCall = response.response().getResults().stream() .anyMatch(result -> result.getMetadata().containsKey("toolCalls")); if (hasToolCall) { toolCallCount++; advisedRequest.chatOptions().getMetadata().put("toolCallCount", toolCallCount); } return response; } @Override public String getName() { return "toolCallLimitAdvisor"; } @Override public int getOrder() { return 1; } }

设计说明:

  • 工具调用次数单独统计,不与推理轮次混在一起。
  • 每轮响应里如果包含toolCalls,计数加 1。
  • 超过阈值后终止,避免 Agent 一直查库、一直检索。

更细一点:按工具类型隔离

如果业务里工具很多,建议不只统计总次数,还要按工具名隔离。
比如:

  • 查询类工具:最多 3 次
  • 计算类工具:最多 2 次
  • 写入类工具:最多 1 次

这样能防止某一个工具被无限打爆。


四、第三层熔断:上下文长度裁剪

Agent 循环调用的另一个放大器,是上下文越来越长。

工具结果、历史对话、中间推理步骤,如果全部保留,Token 会迅速膨胀。
上下文越长,模型越容易分心,越容易继续调用工具。

所以第三层,不是等循环发生再拦,而是主动把上下文“瘦身”。

策略 1:只保留必要上下文

不要把所有历史都塞进每一轮。
很多业务只需要保留:

  • 用户原始问题
  • 当前任务目标
  • 最近一次工具结果
  • 必要的约束条件

中间过程、调试信息、重复描述,能不保留就不保留。

策略 2:超过长度自动裁剪

可以设置上下文窗口,超过后只保留最近 N 轮,或保留关键结果。

代码实现:ContextTruncationAdvisor

package com.agent.demo.advisor; import org.springframework.ai.chat.client.advisor.api.AdvisedRequest; import org.springframework.ai.chat.client.advisor.api.AdvisedResponse; import org.springframework.ai.chat.client.advisor.api.CallAroundAdvisor; import org.springframework.ai.chat.client.advisor.api.CallAroundAdvisorChain; import org.springframework.ai.chat.messages.Message; import org.springframework.stereotype.Component; import java.util.List; @Component public class ContextTruncationAdvisor implements CallAroundAdvisor { private static final int MAX_HISTORY_ROUNDS = 6; @Override public AdvisedResponse aroundCall(AdvisedRequest advisedRequest, CallAroundAdvisorChain chain) { List<Message> messages = advisedRequest.messages(); if (messages.size() > MAX_HISTORY_ROUNDS) { List<Message> truncated = messages.subList(messages.size() - MAX_HISTORY_ROUNDS, messages.size()); return chain.nextAroundCall(new AdvisedRequest( advisedRequest.userText(), advisedRequest.systemText(), advisedRequest.advisors(), advisedRequest.chatOptions(), advisedRequest.functionNames(), advisedRequest.memoryId(), truncated, advisedRequest.advisorParams() )); } return chain.nextAroundCall(advisedRequest); } @Override public String getName() { return "contextTruncationAdvisor"; } @Override public int getOrder() { return 2; } }

设计说明:

  • 只保留最近MAX_HISTORY_ROUNDS条消息。
  • 减少无效上下文堆积,降低 Token 消耗,也降低模型“反复纠结”的概率。
  • 裁剪策略要按业务调整,不能机械一刀切。

策略 3:工具结果摘要

如果工具返回结果很长,不要原样塞回上下文。
先做摘要,再送入模型。

例如:

  • 数据库返回 100 条记录:只保留关键字段和统计结果
  • 检索返回 20 个片段:只保留 Top 5 相关片段
  • 计算结果很大:只返回最终结论

工具结果摘要,是很多团队容易忽略的成本优化点。


五、三层熔断怎么配合使用?

这三层不是选一个,而是要同时开。

推荐执行顺序:

第一步:先限制轮次和工具次数

这是最快见效的两道硬边界。
只要 Agent 出现异常循环,先被拦在这两层。

第二步:再做上下文裁剪

上下文短了,模型出错概率下降,轮次和工具调用自然也会下降。

第三步:最后加监控告警

配合前两篇的 Agent 可观测体系,监控这几个指标:

  • 平均推理轮次
  • P95 推理轮次
  • 工具调用次数
  • 单请求 Token 消耗
  • 异常终止率

如果这些指标突然上涨,说明 Agent 正在进入异常模式,需要立刻排查。


六、生产避坑清单

❌ 不要只靠 Prompt 要求模型“结束后不要继续调用工具”
模型不可靠,必须工程兜底。

❌ 不要只做全局异常捕获
异常捕获只能止损,不能阻止循环发生。

❌ 不要把上下文全量保留
上下文越长,成本越高,模型越容易失控。

❌ 不要阈值设置过大
轮次、工具次数、上下文长度,都要从保守值开始。

✅ 必须设置最大推理轮次
✅ 必须设置最大工具调用次数
✅ 必须做上下文裁剪或摘要
✅ 必须配合监控观察指标变化
✅ 异常终止后,记录输入、上下文、工具结果,方便复盘


七、简历描述片段

基于 Spring AI 搭建 Agent 生产稳定性治理体系,通过多层 Advisor 扩展实现最大推理轮次熔断、最大工具调用次数限制与上下文动态裁剪,有效防止 Agent 无限循环调用;配合 Agent 可观测指标监控推理轮次、工具调用频次与 Token 消耗,在保证业务能力的前提下,显著降低大模型成本异常波动,提升多 Agent 生产稳定性。

📦 落地资源推荐

跑Agent的应用,尤其是做压力测试,更吃服务器资源,通常本机是不够用的,建议使用云服务器,能大大减少我们做其他事的精力,提高学习代码的专注力和学习AI的效率,免除后顾之忧,新用户首年特惠+专属渠道,https://www.aliyun.com/daily-act/ecs/activity_selection?userCode=fzoabro9

💡领取完整工程

找到图片上的水印即Java-AI工程师,然后在Java-AI工程师中练习打字【agent-metric】即可,本篇完整熔断与上下文裁剪源码

下期预告

下一篇,我们继续把成本治理往前推一步:前面解决了“异常循环”,下一篇解决“正常请求也很贵”的问题。我们会把缓存、工具结果摘要、模型降级、Token统计归因放到一个方案里,做一套可直接上线的 Agent成本控制清单。


需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询