在AI智能体(Agent)链路中,重复输出相同内容并不只是模型“变笨”了,而是执行循环、上下文管理、输出解析或工具调用中的某个环节失去了终止信号。要高效排查,必须先理解Agent典型的循环结构:模型接收提示词与历史消息,生成一段文本或一个工具调用,框架解析结果、执行工具、把结果回填,再进入下一轮推理。如果任何一步没有推进状态,模型就会在相似甚至完全相同的输入上反复生成,表现为用户看到一句回答被不断复制,或者工具调用日志里同一个动作连续出现几十次。

这种故障除了浪费token和增加延迟,还会污染后续记忆。若Agent将重复输出写入上下文,模型会误以为这些内容是有意义的进展,进一步强化循环。因此排查不能只看单条回复,而要结合轮次编号、输入指纹、输出哈希和工具返回结果一起分析。
一、先判断重复发生的层级:文本重复、动作重复还是状态重复
排查第一步不是改配置,而是确认重复发生在哪个层级。文本重复指模型在单次回复里连续输出相同句子,例如“收到,我将继续执行。收到,我将继续执行。”这通常与解码策略、停止序列未命中或模型自身退化有关。动作重复指Agent每一轮都调用同一个工具且参数不变,例如反复调用 search(query=同一关键词),这多半是工具返回结果没有进入上下文,或结果格式让模型无法提取有效信息,于是它只能重试。
状态重复更隐蔽:回复内容可能略有变化,但任务没有实质推进,例如翻译Agent反复输出“正在处理中”。这种循环经常被误判为模型幻觉,实际是系统提示词缺少明确的完成标准,或任务拆分粒度过粗。可以记录每轮响应的归一化哈希值,如果连续3轮哈希相同或编辑距离小于阈值,就应触发告警。
判断层级有助于选择修复方向。文本重复优先检查模型参数和停止条件;动作重复优先检查工具回填与解析器;状态重复优先检查提示词中的目标定义和终止规则。
二、六类常见诱因:从采样参数到上下文截断
第一类是采样参数过于确定。temperature设置为0或top_p过低时,模型倾向选择最高概率token,一旦进入一个稳定但不正确的输出路径,就可能持续生成相同内容。有人为了稳定性把temperature调成0,却忽略了重复风险。可以尝试temperature=0.2到0.4,或对Agent的规划步骤加入轻微随机性,但金融、代码生成等场景需要平衡。
第二类是停止条件缺失或未生效。API中的stop参数、平台默认停止词、自定义终止标记若设置错误,模型会在达到max_tokens前持续生成。对于Agent循环,还需要在框架层定义终止规则,例如连续N轮无新增信息、总步数上限、用户目标已满足等。很多框架只检查工具调用是否为空,无法识别语义上的完成。
第三类是上下文截断后信息丢失。当对话轮次变长,最早的系统指令、工具定义或示例被截掉,模型可能忘记终止条件,退回到默认的安全回答。尤其在长任务中,模型看不到之前已经尝试过的动作,只能重复相似尝试。解决办法是压缩历史、做摘要或使用向量检索保留关键约束。
第四类是工具返回结果格式异常。例如JSON中缺少必要字段、返回了超长HTML、错误信息被吞掉。模型读完这些内容后无法判断下一步,于是重新生成上一次的工具调用。应统一工具返回结构,控制长度,并在返回中明确写入成功、失败、数据摘要和下一步建议。
第五类是提示词中的循环诱导。类似“请反复检查直到满意”或“如果不确定就重试”这样的表述,会让模型在边界情况下不断重复。提示词应给出可验证的完成条件,例如“当检索结果包含发布时间且来源数量不少于3条时停止”。
第六类是框架的重试逻辑与模型自身重复叠加。网络超时、速率限制、工具异常时,有些框架会自动重发相同请求,而模型输出的原始内容又恰好重复。此时需要区分是网络重试次数过多,还是模型每一轮都产生新请求。
三、日志驱动的排查流程:四个关键证据
建议在Agent运行时记录四类日志:轮次编号、输入消息摘要、输出内容指纹、工具调用参数指纹。不要只保存完整文本,否则日志膨胀后排查困难。可使用MD5或SHA-256对归一化后的消息和参数计算短指纹。
第一步,定位首轮出现重复的编号。如果第N轮开始重复,查看第N-1轮到第N轮的输入变化。输入没有变化,通常是工具结果未回填或解析失败;输入变了,但输出仍相同,可能是采样或提示词问题。第二步,检查输出是否触达停止词。可临时开启API的logprobs或调试返回,观察结束原因字段是stop、length还是tool_call。
第三步,回放关键轮次。把模型输入、工具返回、输出和参数复制到调试环境,逐步对比。第四步,验证修复:只改变一个变量,例如只加stop序列、只调整temperature或只修改工具返回格式,观察重复率变化。多变量同时修改很难定位根因。
import hashlib
import json
def fingerprint(obj):
text = json.dumps(obj, ensure_ascii=False, sort_keys=True)
return hashlib.sha256(text.encode("utf-8")).hexdigest()[:12]
# 每轮记录示例
def log_round(round_no, messages, output, tool_args):
record = {
"round": round_no,
"input_fp": fingerprint(messages),
"output_fp": fingerprint(output),
"tool_fp": fingerprint(tool_args),
}
print(json.dumps(record, ensure_ascii=False))
上述代码将长文本映射为短指纹,便于在大量日志中快速发现哪些轮次输入输出完全一致。实际系统里可结合OpenTelemetry或自定义中间件输出,避免影响主流程性能。
四、修复方案:从即时止血到长期工程化
即时止血可先设置更短的max_tokens、明确stop序列、降低上下文注入噪声。如果问题是工具解析失败,给模型返回更清晰的错误摘要,并在下一轮提示中加入“上次操作失败,原因:参数缺少query字段,请修正后重试”这样的结构化反馈。必要时限制最大执行轮次,例如超过15轮直接返回已获得的中间结果,并要求用户确认。
中期方案是在框架层增加重复检测与熔断。维护最近K轮输出指纹,如果相同指纹连续出现M次,则强制插入一条打破循环的系统消息,或直接终止并返回诊断信息。去重不是简单丢弃重复内容,而是识别出没有信息增量,并给模型一个明确的“停止或换策略”信号。
长期工程化可引入状态机与任务进度模型。把Agent目标拆成可验证的状态,每轮更新进度,当状态连续两轮不变时触发健康检查。对于关键业务,还可以加入人工确认节点,例如支付、删除、发送等动作前必须由用户授权,从产品层避免Agent因循环而重复执行危险操作。
五、一个轻量级防循环示例
下面的Python伪代码展示了自研Agent循环中如何加入输出指纹检测、最大轮次和熔断逻辑。它不依赖具体框架,核心思想是在每一轮生成后比较当前输出与历史输出的相似度。
class SafeAgent:
def __init__(self, model, max_rounds=12, repeat_limit=3):
self.model = model
self.max_rounds = max_rounds
self.repeat_limit = repeat_limit
self.history_fingerprints = []
def run(self, task):
messages = [{"role": "system", "content": "你是任务执行Agent,完成目标后立即返回最终答案。"}]
messages.append({"role": "user", "content": task})
for round_no in range(1, self.max_rounds + 1):
output = self.model.generate(messages)
fp = fingerprint(output)
# 检查连续重复
if len(self.history_fingerprints) >= self.repeat_limit:
recent = set(self.history_fingerprints[-self.repeat_limit:])
if len(recent) == 1 and fp in recent:
messages.append({
"role": "system",
"content": "检测到连续重复输出。请停止当前策略,直接返回已有结论或说明卡住原因。"
})
# 熔断后允许再试一次,但不再执行工具
output = self.model.generate(messages)
return {"status": "repeat_stop", "answer": output}
self.history_fingerprints.append(fp)
messages.append({"role": "assistant", "content": output})
action = self.parse_action(output)
if action is None:
return {"status": "done", "answer": output}
result = self.execute_tool(action)
messages.append({"role": "tool", "content": result})
return {"status": "max_rounds", "answer": output}
这个示例里,如果连续3轮输出指纹相同,就注入一条校正指令,只允许再生成一次文本而不继续调用工具,从而快速打断循环。实际项目中还可以使用编辑距离代替哈希,容忍微小差异。
排查Agent重复输出问题,核心是弄清楚每一轮推理的输入输出是否产生有效增量。不管是调整参数、清洗工具返回还是加熔断,最终目的都是让模型在完成目标时能够明确停下来。建立好日志指纹和循环护栏后,这类故障通常会从难以复现的偶发问题,变成可监控、可预警的工程指标。