Agent推理死循环的典型表现是:模型在思考、调用工具、观察结果之间来回反复,既不退出也不产出最终答案。这种状态比单纯报错更危险,因为它会持续消耗API费用和计算资源,还可能在循环中覆盖关键状态。要解决这个问题,不能只依赖模型自觉,必须在执行层加入边界控制。步数限制负责给推理链路上限,状态回滚则保证一旦发现异常可以回到安全位置。

一、推理死循环从何而来
死循环很少因为模型出现了底层故障,更多时候是任务结构、提示词和工具反馈共同造成的。比如当Agent不确定某个工具参数是否正确时,会不断尝试不同组合;当观察结果与预期不符时,它可能重复执行同一个调用,期望得到不同输出;还有些情况是模型在两个候选答案之间来回推翻,因为每一步都以为新的思考更接近目标。
如果工具存在副作用,这种循环会带来额外风险。例如一个文件写入工具被重复调用,第一次成功创建文件,第二次可能覆盖内容,第三次则报错或继续覆盖。模型看到错误信息后再次调整参数重试,形成更长的失控链路。因此识别循环不能只看步数,还要观察状态是否发生实质性变化。
提示词工程可以缓解部分问题,比如在系统消息中强调发现问题立即返回最终结论、禁止无意义重试。但提示词是软约束,模型仍有概率忽略。尤其是长上下文下,早期指令容易被后续观察结果稀释。执行层的步数限制与回滚机制,相当于给Agent加上硬保险。
二、步数限制:先给循环画一条硬线
步数限制是最直接的控制手段。实现上可以在Agent主循环中维护一个计数器,每执行一次模型调用或工具调用就加一。当计数超过预设阈值时,强制终止当前任务并返回错误或部分结果。计数器可以只统计工具调用,也可以把思考步骤一起纳入,具体取决于计费方式和控制粒度。
class AgentLoopLimit:
def __init__(self, max_steps=10):
self.max_steps = max_steps
self.current_step = 0
def check(self):
self.current_step += 1
if self.current_step > self.max_steps:
raise RuntimeError("Agent step limit exceeded")
return True
上面的代码是硬限制,达到上限直接抛异常。它的好处是逻辑简单、不会遗漏,缺点是一刀切可能把即将完成的正常任务打断。因此可以引入软限制:接近上限时发送警告消息,让模型在下一轮优先给出最终答案,只有继续超限才强制终止。
soft_limit = max_steps - 2
if agent_state.current_step >= soft_limit and agent_state.current_step < max_steps:
messages.append({
"role": "system",
"content": "注意:执行步数即将达到上限,请直接给出最终答案,不要继续调用工具。"
})
步数限制虽然有效,但不能解决所有问题。如果阈值设置过小,复杂任务可能刚展开就被切断;如果设置过大,失控时已经浪费了大量资源。更合理的做法是与状态检测结合,让系统既能尽早发现循环,又允许真正有进展的推理继续运行。
步数指标还可以细分。比如将模型推理步数与工具调用步数分开记录,分别设置阈值。某些Agent的思考步骤很多但工具调用很少,这可能是正常推理;相反,工具调用频繁但参数几乎不变,通常意味着循环。细粒度统计能帮助更准确地判断故障类型。
三、状态回滚:让错误链条可以被撤销
回滚机制的核心是定期保存快照。快照内容至少包括当前消息列表、已执行的工具结果、关键变量和游标位置。对于基于无状态API的Agent,最简单的方式是记录整个messages数组;如果涉及外部存储或数据库,还需要保存事务标识或版本号,确保回滚后外部状态也能恢复。
import copy
class AgentSnapshot:
def __init__(self, messages, tool_results, step):
self.messages = copy.deepcopy(messages)
self.tool_results = copy.deepcopy(tool_results)
self.step = step
def restore(self):
return copy.deepcopy(self.messages), copy.deepcopy(self.tool_results), self.step
快照不是越多越好。每一步都保存完整消息列表会带来内存和序列化开销,尤其当上下文较长时。常见的折中方案是每N步保存一次,或者只在状态发生重要变化时保存,例如工具返回成功结果、用户确认关键信息、进入新任务阶段等。快照粒度要能覆盖最可能的回滚位置。
循环检测是触发回滚的前提。可以通过比较最近几次动作的相似度来判断。如果连续三次工具调用名称相同且参数一致,或者模型连续产生相同思考内容,系统可以判定进入循环。检测逻辑不必非常复杂,关键是能够快速识别重复模式并执行恢复动作。
def detect_repetition(history, window=3):
if len(history) < window:
return False
recent = history[-window:]
return all(item == recent[0] for item in recent)
如果检测到循环,应当回滚到最近一个快照,同时给模型追加明确的循环提示。这样做能避免模型继续在错误轨迹上消耗资源。恢复后是否允许继续执行,可以根据回滚次数决定。连续回滚超过两到三次,就应直接终止任务,进入人工处理或降级流程。
回滚也可能引入新问题。比如某些工具调用已经产生外部副作用,仅恢复上下文并不能撤销真实操作。这时需要在工具层实现幂等或补偿逻辑。对于非幂等操作,可以在快照中记录是否已经执行过该操作,回滚时跳过重复执行,或调用反向操作进行补偿。把外部影响纳入回滚设计,才能保证恢复后的状态真正安全。
四、在ReAct风格Agent中落地完整控制
ReAct风格Agent的流程通常包括推理、行动、观察三个环节。将步数限制和回滚机制嵌入这个循环,并不需要改变模型本身,只需在调度层增加控制逻辑。调度器每次循环先检查步数,再调用模型生成下一步;执行工具后判断是否出现重复动作;如果检测到循环,则恢复快照并注入提示。
class SafeReActAgent:
def __init__(self, model, tools, max_steps=12, snapshot_interval=3):
self.model = model
self.tools = tools
self.max_steps = max_steps
self.snapshot_interval = snapshot_interval
self.messages = []
self.tool_results = []
self.step = 0
self.snapshots = []
def run(self, task):
self.messages.append({"role": "user", "content": task})
last_actions = []
while self.step < self.max_steps:
self.step += 1
if self.step % self.snapshot_interval == 0:
self.snapshots.append(AgentSnapshot(self.messages, self.tool_results, self.step))
response = self.model.generate(self.messages)
self.messages.append(response)
if response.get("finish"):
return response["content"]
action = response["action"]
last_actions.append(action)
if detect_repetition(last_actions):
if not self.snapshots:
raise RuntimeError("Loop detected with no snapshot available")
snapshot = self.snapshots.pop()
self.messages, self.tool_results, self.step = snapshot.restore()
self.messages.append({
"role": "system",
"content": "检测到重复操作,已回滚到之前状态。请重新分析并给出不同方案。"
})
last_actions = []
continue
tool_result = self.tools[action["name"]](**action["input"])
self.tool_results.append(tool_result)
self.messages.append({"role": "tool", "content": str(tool_result)})
raise RuntimeError("Max steps reached without final answer")
这段流程展示了完整控制思路:步数上限保证任务不会无限运行;快照按固定间隔保存;重复动作检测触发回滚并追加提示。实际项目中还需要考虑异常处理、超时、日志记录和人工介入入口。例如在检测到两次回滚后,可以停止自动重试,将上下文转交给人工审核。
步数限制与回滚并不是孤立策略。前者从全局控制资源消耗,后者从局部修正错误轨迹。二者配合能显著降低Agent在复杂场景下的失控概率。更重要的是,这些机制应该在系统设计阶段就纳入,而不是等线上出现费用激增后再补丁式修复。