IIWAB

Agent循环重试问题及应对机制

IIWAB 3天前 ⋅ 22 阅读

一、先对齐核心问题

你提到的典型case:工具结果反馈缺失 → Agent 判断任务未完成 → 重复调用同一个工具,不断消耗迭代轮次,直到预算耗尽。 本质是一类稳定不动的局部循环(Agent 状态没有向前推进,动作重复、目标不收敛),区别于正常多轮探索(每次输入/工具参数不一样、信息在增加)。 下面先枚举生产环境里除「工具无返回/反馈缺失」之外的所有常见诱因,再给检测机制、分级终止策略、配套兜底设计,贴合 Agent 生产落地(FunctionCall / ToolCall 型 Agent)。

前置定义:循环 = 在连续多轮迭代中,任务目标、外部环境没有正向变化,但 Agent 持续输出高度相似的工具调用动作。


二、其他会导致 Agent 陷入重试死循环的场景(生产高频)

1. 工具返回结果有效,但结果无法满足校验断言(语义校验失败)

工具正常返回了 payload,不是空,但内容不满足 Agent 内置的成功判定规则:

  • 例子:查询数据库,返回空列表 [];查接口返回 200 OK,但数据为空;搜索工具返回摘要但没有需要的关键字段。
  • Agent 逻辑:“没有拿到想要的信息 → 再调用一次这个工具试试”,参数几乎不变,反复调用。

和你说的「反馈缺失」区分:这里有反馈,只是反馈被Agent判定为无效,是生产里最高发的一类循环。

2. 工具返回成功,但业务状态本身不会变化(幂等只读工具)

只读类工具(查询、检索、读配置)本身没有副作用,多次调用结果完全一致:

  • Agent 理解偏差:误以为“多查几次、换个轻微改动的参数,就能拿到不一样结果”;
  • 例如:查订单、查库存、向量检索、读文件,结果固定,Agent 反复微调关键词重试。

写操作(提交单据)不一样:写操作重试可能触发重复下单,不仅是循环,还有资损风险。

3. LLM 自身状态遗忘/上下文漂移,丢失“已经试过这个方案”的记忆

上下文窗口限制、摘要压缩、历史裁剪、记忆召回失败:

  • Agent 前面已经调用过A工具失败,但是这一段历史被压缩丢掉了;
  • 新一轮思考时,LLM 完全不知道之前已经试过,再次生成一模一样的工具调用;
  • 特征:间隔几轮之后再次回到同一个动作,不是连续紧挨着的循环,属于间断型循环,更难检测。

4. 提示词/规划逻辑缺陷:缺少备选分支,只有单一修复路径

规划器(Planner)的分支设计问题:

  • 判定失败后,只有一种修复策略:重试当前工具,没有“换工具、换参数策略、向用户提问、降级”分支;
  • 典型prompt写法:如果调用结果不符合预期,请重试工具,没有重试上限和逃逸出口。
  • 哪怕结果明确不可用,Agent 也只会重复同一个动作。

5. 参数扰动式伪循环:参数轻微改动,语义目标完全一致

不是完全一模一样的调用,但是工具、核心查询意图不变,只改无关紧要的参数:

  • 例子:搜商品,每次只换几个同义词;SQL每次微调limit/offset但查询条件不变;API调用换一个无关header;
  • LLM 自以为“我在换策略”,实际访问的数据源、得到的信息不会有增量;
  • 难点:简单字符串匹配检测不到,需要语义级相似度判断。

6. 依赖外部不稳定信号: transient error 没有分类,一律重试

工具返回可重试报错(超时、503、限流),但是 Agent 没有退避策略,无脑立即重试:

  • 短时间高频轰炸工具服务,触发服务熔断,进而持续报错,形成闭环;
  • 区分:合理指数退避重试是正常逻辑;无退避、无限次重试就是死循环。

7. 目标不可达(无解问题),Agent 没有“承认失败”的能力

问题本身在当前工具集、权限、数据源下无法完成:

  • 用户要求查不存在的数据、执行无权限操作、需求矛盾;
  • LLM 不愿意输出“无法完成”,持续尝试各种工具调用,不断试错,在一小撮动作里来回打转。

8. 状态机/规划器的跳转逻辑死锁(结构化Agent,比如ABSD/分层Agent)

很多生产Agent不是裸LLM,是带状态机、子任务DAG的:

  • 子任务的完成条件互相依赖、循环等待:A子任务等待B输出,B又等待A输出;
  • 或者子任务完成断言写错,永远判定未完成,反复调度同一个子任务。

这个是框架层循环,和LLM推理无关,纯代码逻辑bug,消融实验很容易漏掉。

9. 工具回调/结果解析器异常:结果被解析成“未拿到结果”

工具返回正常JSON,但是解析层(parser、schema校验、pydantic)抛异常,直接向上抛出「获取结果失败」:

  • Agent看不到原始返回,只能收到解析失败信号,触发重试;
  • 属于中间层的隐形“有效反馈被吞掉”,区别于底层工具没有返回。

10. 用户输入模糊,Agent持续追问同类问题(追问循环)

属于特殊的动作循环:Agent反复问同一个/语义相同的问题,期望用户补充信息,而不是调用工具; 例如:一直追问“请问你要查询哪个日期?”,用户没感知、或者持续给无效回答,形成对话循环。


三、检测机制:分层检测(从轻量低成本 → 语义重检测)

核心原则:优先低成本检测,再上语义模型;区分【正常多轮探索】和【无增量循环】

正常探索特征:新增信息、动作集合在扩大、查询目标在变化;循环特征:信息增量≈0、动作空间收敛到很小集合。

层级1:精确匹配检测(成本最低,优先部署)

对每一轮 ToolCall 做签名: 签名key = tool_name + 规范化后的入参(排序json key,去掉随机字段、traceId、时间戳这类噪声) 维护滑动窗口(比如最近 N=3~5 轮)的调用签名集合:

  • 规则:同一个签名在窗口内重复出现 ≥K次 → 标记为精确重复循环 适用:完全一模一样参数重复调用,你原始消融实验的场景、简单重试场景。 缺陷:抓不住“轻微改参数、语义不变”的伪循环。

层级2:结构化特征相似度检测(中等成本,推荐生产标配)

提取工具调用的结构化特征向量,而不是原始字符串:

  1. 工具名称;
  2. 核心业务参数(白名单:查询主体、id、过滤条件;忽略噪声参数:timeout、request_id);
  3. 目标资源标识(订单号、文件路径、知识库id); 计算特征相似度,设置阈值,窗口内高度相似就告警。

好处:比纯字符串鲁棒,又不需要大模型语义推理。

层级3:语义相似度检测(高成本,用来抓参数扰动伪循环)

当精确、结构化检测没有命中,但监控发现连续多轮都是同一个工具时,触发轻量embedding/小分类模型:

  • 输入:本轮工具调用意图 + 前几轮调用意图;
  • 判断:是否在尝试达成完全一样的子目标,没有新的查询方向;

触发条件要加前置限流,不要每一轮都跑embedding,避免成本爆炸。

层级4:信息增量检测(最本质的判断,区分重试和有效探索)

循环的根本是:连续多轮没有新增有效信息。 实现方案:

  1. 维护全局信息摘要池/向量库,把每一轮工具返回的有效事实向量化入库;
  2. 新结果和历史摘要的最大相似度 > 阈值,且没有新增事实 → 判定无信息增益;
  3. 组合规则:【同一个工具 + 连续多轮无信息增量】→ 循环。

优势:可以识别“换了工具但一直在查同一批无效信息”这类更隐蔽的循环;缺点是计算成本最高。

层级5:状态机/DAG子任务死锁检测(针对结构化Planner Agent)

对分层规划Agent单独做:

  • 子任务依赖图环检测(拓扑排序判环);
  • 每个子任务增加最大调度次数;
  • 子任务完成断言长时间不翻转(状态 stuck)告警。

附加:异常信号辅助判断

  • 工具错误码分布:连续返回同一类错误(403、空结果、解析失败);
  • token消耗速率异常上涨,但是输出动作高度同质化;
  • 连续多轮没有向用户输出自然语言,只静默调用工具。

注意:检测要做滑动窗口,而不是全局累计。任务中间隔很久再调用一次同一个工具,属于正常行为,不能误杀。


四、终止/逃逸机制:分级策略,不要一刀切直接杀掉(生产核心)

不能只有一个“超过次数直接终止”,要分层:先降风险、再引导逃逸、最后强制终止、上报。 推荐 4 级逃逸流水线:

L1:局部重试配额 + 指数退避(第一道防线,处理瞬时错误)

给单工具、单子目标独立重试预算,不是全局迭代预算:

  • 例:同一个查询目标最多重试2次;
  • 重试之间加指数退避 + jitter;
  • 区分错误类型: ✅ 瞬时错误(503、超时)→ 允许少量退避重试; ❌ 稳定空结果、权限错误、不存在资源 → 禁止重试,直接走逃逸;

重点:错误分类器是这里的关键,很多Agent死循环就是把所有报错都当成可重试错误。

L2:策略扰动逃逸(检测到疑似循环,自动换方案,优先不中断任务)

触发循环告警后,不直接终止,先强制干预Planner:

  1. 强制切换工具:“不要再使用工具A,请尝试用B/C工具或者换一种查询方式”;
  2. 强制改写查询视角:prompt注入约束:总结已经尝试过的方案,禁止重复同类意图;
  3. 拆分/降目标:缩小查询范围,或者把大任务拆成别的子任务;
  4. 只读工具:强制要求LLM先对已有结果做总结,而不是继续查询;

这是最有价值的一层:很多时候只是LLM思维卡壳,给一个“换思路”的约束,就能跳出循环,保住任务。

L3:人机移交(逃逸失败,把问题抛给用户,而不是静默耗尽预算)

自动逃逸失败后,Agent必须向用户说明:

  • 已经尝试了哪些动作;
  • 多次尝试没有拿到有效信息;
  • 给出选项:继续尝试其他方式 / 修改需求 / 放弃;

禁止在后台静默无限重试,用户完全感知不到,直到超时或者扣费爆炸。追问循环优先走这一级。

L4:硬终止 + 熔断兜底(最后防线,保护预算和下游服务)

两层预算隔离,非常重要:

  1. 全局迭代预算:整个Agent会话最大轮次(防止整体失控);
  2. 单目标/单工具组预算:局部子目标独立限额(你现在要解决的循环就是靠这个局部预算,而不是靠全局预算耗尽才停);

触发硬终止时执行:

  • 停止生成新ToolCall;
  • 输出收敛失败总结;
  • 埋点上报循环事件(标签:循环类型、工具名、prompt版本、错误分类);
  • 下游限流熔断:同一个trace短时间大量调用同一个工具,直接拦截请求,保护依赖服务不被打崩。

L5:框架级静态校验(左移,上线前减少循环可能性)

在提示词、规划模板里预埋约束,从源头降低概率:

  1. 强制要求Planner每一步列出【已经尝试过的方案,本次避免重复】;
  2. 每个工具的system prompt里写明:稳定无结果时禁止无限重试,优先询问用户;
  3. 禁止“结果不符合预期就重试”这种无上限规则;
  4. 子任务DAG上线前做依赖环静态检查。

五、落地参考的组合规则示例(可直接写进Agent调度器)

当满足:
  滑动窗口最近4轮
  AND 工具名称相同
  AND 结构化核心参数相似度 > 0.9
  AND 工具返回结果的信息相似度 >0.85(无新增信息)
THEN:
  1. 先标记【疑似循环】,上报监控
  2. 检查该子目标重试次数:
     - ≤2次:注入提示词,强制换查询策略,禁止继续使用该参数查询
     - >2次:进入人机移交,向用户说明多次尝试未获取有效信息
     - 用户选择继续且再次循环:触发局部硬终止,不再调用该工具

六、补充:消融实验对照设计建议(呼应你开头的消融)

如果你要做消融验证这套机制,建议设置几组变量:

  1. 基线:无循环检测,仅全局迭代预算(就是你观察到的反复重试耗尽预算)
  2. 增加:单工具局部重试配额
  3. 增加:结构化相似度检测 + 策略扰动逃逸
  4. 增加:信息增量语义校验 指标观测:循环发生率、平均token浪费率、任务成功率、下游工具QPS峰值。

七、简短总结

除了「工具反馈缺失」,最常见的循环根源是:返回结果有效但不可用、只读工具结果固定、LLM遗忘历史、只有重试一条修复分支、参数扰动伪重试、目标无解、子任务依赖死锁、错误不做分类无脑重试。 检测上:从签名匹配 → 结构化特征 → 语义embedding → 信息增量四层递进; 终止上:优先分类重试→自动换策略→移交用户→硬熔断,用子目标局部预算而不是单纯依赖全局迭代预算兜底,这是和朴素“等预算耗尽”方案最大的区别。


全部评论: 0 条

    我有话说: