昂贵的生产AI故障常常是这样的:周二凌晨3点,一个客服智能体陷入无限循环。它调用同一个工具,收到同一个错误;稍微修改参数后重试,仍然收到同一个错误;随后继续重复,每分钟多达数百次。到了早上,团队突然面对一笔四位数甚至五位数的账单,具体金额取决于所用模型和调用频率。
这种情况并不少见。智能体陷入循环,是最常见、后果也最严重的生产故障之一。这类问题很隐蔽,因为智能体往往“看起来仍在工作”:它不断采取行动,调用要么成功,要么按相同方式失败。只有查看运行轨迹时,才会发现同一种模式在反复出现。
要构建不会无限循环的智能体,必须有意识地作出架构选择。智能体的大多数故障都可以预见,防范方法也早已为人所知。能够可靠上线智能体的团队,正是那些严格落实这些方法的团队。
本文将介绍智能体为何陷入循环、哪些架构方法可以避免循环,以及漏网的循环出现时,如何通过运营防护及时发现。
智能体为何陷入循环
智能体循环通常源于以下几种机制。
1. 无法判断是否取得进展
智能体不清楚“我取得进展了吗?”它尝试一种办法、观察结果,然后决定换一种办法。如果没有明确的进度跟踪,“换一种办法”可能只是“用略有不同的方式重复同一件事”。
2. 缺少终止条件
智能体的提示词只说“帮助用户”,却没有说明“当X成立时停止”。没有明确的终止条件,智能体就会一直“帮下去”:再搜索一条信息,再尝试一个工具,再继续优化一点。
3. 病态重试
遇到失败时,智能体会自然地重试。如果没有重试次数限制,同一种失败就可能被无限重试。智能体只意识到“我还没有成功”,却意识不到“我已经试过10次了”。
4. 状态遗忘
智能体的工作记忆只包含最近几轮。如果一个循环已经经历了20次尝试,智能体在上下文中可能只看得到最后5次,因而无法发现外部观察者一眼就能看出的重复模式。
5. 工具结果不一致
工具返回含混或相互矛盾的结果,智能体于是再次尝试。工具仍然给出含混的结果,智能体便推断“也许我上次理解错了”,换种方式重试。结果仍然含混,于是形成循环。
6. 过度乐观
智能体在训练中学会了坚持不懈,即使更合理的策略是停下来求助,它也会继续尝试。这在长周期任务中尤其危险,因为微小的疑惑会不断累积。
7. 目标漂移
智能体逐渐偏离原本要完成的目标。它不断分出子任务,再分出更小的子任务,在与目标略有关联的方向上越走越远,却始终没有回到主任务。
不同智能体出现故障的方式各不相同,但防范措施大多相通。
方法1:硬性步骤预算
最简单也最重要的防线,是设置最大步骤数。例如,智能体最多可以调用工具20次。达到20次后,它必须给出最终答复,或者升级给人工处理。
实现示例:
def agent_loop(query, max_steps=20):
messages = [{"role": "user", "content": query}]
for step in range(max_steps):
response = call_llm(messages, tools=available_tools)
if response.is_final_answer:
return response.content
result = execute_tool(response.tool_call)
messages.append(response)
messages.append({"role": "tool", "content": result})
# 已用尽预算——强制给出最终答复
return force_final_answer(messages)
预算应根据任务校准。简单任务可以设置5–10步,复杂的多来源任务设置20–30步,开放式研究则可设置50步以上,但无论如何都必须有上限。
达到预算后,智能体应根据已有信息给出尽可能好的答复,或者升级给人工处理。
仅这一项措施,就能避免大多数灾难性的循环。因此必须始终落实。
变体
token预算。 除了限制步骤数,也可以改为或同时限制token总数。这样可以防止智能体虽然步骤不多,每一步却产生50K token的推理轨迹。
成本预算。 将步骤和token预算换算为欧元,让超支风险变得具体可见。
时间预算。 限制实际经过的时间。这对面向用户的流程很有用,例如要求“30秒内答复”。
大多数生产智能体都会以某种形式同时设置这四种预算。触及其中任何一项,都应终止本次运行。
方法2:进度跟踪
预算本身并不能让智能体意识到自己陷入停滞,只能保证它最终会停止。进度跟踪则能帮助智能体识别循环并主动脱离。
一种简单做法是让智能体维护明确的“进度”日志,每一步都记录获得了什么新信息,或者实际发生了什么变化。
第1步:搜索姓“Smith”的客户,找到12个匹配项。
第2步:筛选出活跃账户,还剩4个。
第3步:查看近期活动,发现客户234最近提交过一张价格相关工单。
第4步:读取工单详情,投诉内容与近期调价有关。
第5步:起草回复,已可发送。
每一步都增加了新信息。如果智能体执行第6步后,进度日志没有新增内容,仍是相同的搜索、结果和结论,就说明它正在原地打转。
提示词可以加入:
决定下一步行动之前,总结你在最近几步中了解到的内容。如果最近3步都没有获得新信息,请停止,并选择以下一种方式:
- 根据现有信息给出尽可能好的答复。
- 升级问题:说明你尝试过什么,以及还缺少什么。
这样一来,智能体就能看到自己“没有取得进展”,并采取相应措施。
方法3:重复检测
有时,智能体会原样重复同一个工具调用。这很容易通过程序检测。
def detect_repeat(history):
recent_calls = [c for c in history[-5:] if c.is_tool_call]
if len(recent_calls) < 3:
return False
call_signatures = [(c.tool, json.dumps(c.args, sort_keys=True)) for c in recent_calls]
return len(set(call_signatures)) < len(call_signatures) / 2
检测到重复后,可以介入:
- 注入一条消息:“你最近已经用这些参数调用过该工具,结果没有变化。请换一种方法或终止任务。”
- 或者直接强制终止。
这种方法可以自动捕获最明显的循环。
方法4:停滞状态检测
除了完全相同的重复,还可以检测更隐蔽的停滞状态:
模式识别。 单独调用一次LLM,让它评估:“查看最近5个步骤,这个智能体是否在取得进展?”如果没有,就中止循环。
def is_stuck(history):
recent = format_history(history[-5:])
response = call_llm(
system="你负责评估一个智能体是否在取得进展。",
user=f"智能体最近的步骤:\n{recent}\n\n智能体是在取得实质性进展,还是陷入了循环?回答:progressing | stuck"
)
return response.content.strip() == "stuck"
每隔几步运行一次检查。如果返回“stuck”,就进行干预。
工具多样性。 如果智能体连续5步以上只调用了1个工具,就值得警惕。应强制它尝试其他方法或停止。
错误模式。 如果同一个工具已经3次以上返回相同错误,就停止使用该工具。继续重试并不能让智能体猜出缺少的输入。
方法5:定期反思
在智能体运行到特定节点时,强制它明确反思。
每完成5个步骤,智能体都必须进行一次反思:
1. 我的原始目标是什么?
2. 到目前为止,我了解到了什么?
3. 我还需要知道什么?
4. 我是在取得进展,还是在重复?
5. 我应该继续还是停止?
反思会迫使智能体暂时跳出只考虑下一步行动的思维,重新审视全局。
这对长周期任务尤其有效。没有强制反思,智能体容易逐渐偏离目标;加入反思后,它们可以自行发现这种偏离。
方法6:目标锚定
智能体运行时间一长,最初的目标就容易被遗忘。上下文窗口逐渐被中间步骤占满,原始问题只占庞大上下文中的一小部分。
可以通过反复强调目标来应对:
- 在每条系统消息顶部写明原始目标。
- 让智能体每隔N步重述一次目标。
- 使用独立的“目标跟踪器”,确认每一步都与目标一致。
提示词可以加入:
原始目标:[verbatim user request]
每次行动前,请确认:
- 这项行动是否有助于实现原始目标?
- 如果有,请继续。
- 如果没有,请直接回到原始目标。
方法7:划定子任务边界
长周期智能体会自然地把工作拆分成子任务。如果缺少结构,子任务可能递归产生更小的子任务,最终让智能体彻底迷失。
应提供明确结构:
- 智能体明确列出子任务。
- 每个子任务都有自己的预算。
- 子任务完成或失败后,智能体回到主任务。
- 子任务不得无限产生更深层的子任务。
LangGraph等框架试图将这种结构形式化:用状态机表示流程,每个节点都是一个清晰步骤,节点之间有明确的状态转换。
这种结构对复杂智能体至关重要,对简单智能体则可能有些过度。
方法8:退出路径
智能体陷入停滞时,需要有明确的停止方式:
升级处理。 “我无法完成这项任务。以下是我尝试过的方法和仍然缺少的信息。”智能体停止运行,并把问题交给其他人处理。
部分完成。 “我已完成A和B,C因X而受阻。”智能体不必完全成功,也可以交付有用的部分结果。
请求澄清。 “我需要用户补充以下信息:……”智能体暂停并提问。
这些应该是智能体可以优先选择的常规路径,而不是迫不得已的最后手段。提示词应明确列出这些选项,并鼓励智能体在停滞时使用。
可以加入以下提示词:
如果遇到以下任何情况,请停止尝试并作出恰当回应:
- 某个工具持续返回相同错误。
- 你已尝试3种不同方法,但仍没有进展。
- 你需要只有用户才能提供的信息。
- 任务复杂度超出了现有工具的能力。
遇到这些情况时:
- 如果是工具错误:说明问题,并建议用户联系支持人员。
- 如果是没有进展:报告你尝试过的方法,并请求指导。
- 如果是缺少信息:向用户提出一个具体问题。
- 如果是任务过于复杂:提供摘要,并升级给人工协助。
方法9:根据置信度决定行动
智能体应知道自己何时有把握、何时没有把握。在置信度不足时贸然行动,往往是循环的起点。
一种方法是要求智能体在每次采取重大行动前,明确给出置信度。
调用delete_record之前,请用1–5分表示你对这项操作正确性的置信度。如果低于4,不要调用,而应请求人工确认。
这种方法尤其适合破坏性操作或成本高昂的操作。智能体必须确认自己有充分把握,才能执行。
与反思机制结合后,这种做法可以发现智能体只是在“胡乱试办法”,而不是“按计划执行”的情况。
方法10:工具层防护
除了智能体层面的措施,工具本身也可以设置防护:
按会话限流。 一个工具在单次会话中最多只能调用N次。超过N次后返回“rate limit”,迫使智能体采取其他行动。
幂等性。 完全相同的重复调用直接返回缓存结果,不再重新执行,避免循环持续冲击工具。
成本上限。 对昂贵工具,例如高负载数据库查询或按量计费的第三方API,设置每次会话的使用上限。
故障熔断。 某个工具在本次会话中失败3次后即被禁用,智能体无法再调用它。
这些防护与智能体层面的措施互为补充。智能体即使试图重复调用,也会被工具阻止。
方法11:外部监控
无论智能体内部设置了多少防护,仍需要由外部监控捕获漏网的问题。
监控进程观察所有运行中的智能体,并检查:
- 每个智能体的步骤数。
- 每个智能体的token用量。
- 每个智能体的成本。
- 每个智能体的运行时间。
- 工具调用模式。
任何智能体超过阈值时,立即将其终止并发出警报。
这是最后一道防线。即使智能体本身已经失常,监控程序也能在它耗尽预算之前将其拦下。
具体实现可以是:
- 用时序数据库跟踪智能体指标。
- 通过规则触发终止命令,例如“智能体运行超过5分钟就终止”。
- 用一个小型服务持续监视并执行这些规则。
对于同时运行大量智能体的系统,这项措施不可或缺。
方法12:人在回路检查点
对于高风险智能体,应设置人工检查点。智能体运行至检查点后暂停,等待人工批准。
典型检查点包括:
- 执行破坏性操作之前。
- 作出不可逆决定之后。
- 长周期任务的重要里程碑。
- 置信度下降时。
这不是因为不信任智能体,而是为了在修复成本尚低时发现错误。
一种实用工作流是:智能体自主完成准备工作,提交摘要和拟执行的操作,由人工批准后再执行。人工只参与决策,不必介入每一个步骤。
完整示例:长时间运行的研究智能体
以下展示如何把这些方法应用到一个真实智能体中。
任务: 调研一家竞争对手并撰写简报。
预计工作量: 搜索网页10–30次,阅读页面20–50个,综合写成一篇1,000词的简报。
采用的方法:
-
步骤预算: 共60步。
-
token预算: 300K token(上下文和操作)。超出预算后,先总结当前发现再继续。
-
成本预算: 每次运行€2。超出后停止,并返回部分简报。
-
时间预算: 实际经过时间5分钟。
-
进度跟踪: 每一步都向“发现日志”加入新信息。如果连续3步没有新发现,就退出。
-
重复检测: 如果同一搜索词运行两次且结果相似,就强制改用其他方法。
-
定期反思: 每10步反思一次当前进展和剩余工作。
-
目标锚定: 在每条系统消息顶部写明原始简报目标。
-
退出路径: “信息已经足够”或“无法找到足够信息”都可以让智能体正常结束。
-
外部监控: 由独立监控程序终止超出预算的智能体。
结果: 运行时间中位数为3分钟,成本中位数为€0.40;循环或超时导致的失败率低于1%。生成的简报为700–1,200词,有事实依据,可作为实用的初稿。
如果不采用这些方法,偶尔会出现运行30分钟、花费超过€20,甚至会话彻底卡死的情况。这些方法能显著降低长尾风险。
在生产环境中检测
即使采用了上述方法,偶尔仍会有问题漏过,因此还要进行检测:
对长时间运行的智能体发出警报。 运行时间超过中位数2倍的任何智能体都会触发警报。
对成本激增发出警报。 单个智能体或总体成本超过阈值时发出警报。
对重复模式发出警报。 发现疑似循环的工具调用模式时发出警报。
每天审查长时间运行的轨迹。 人工每天快速查看耗时最长的10条运行轨迹,以发现评估没有覆盖的问题。
汇总指标: 跟踪循环率随时间的变化。当模型更新或提示词变更等因素导致循环频率上升时,就能及时发现。
一个实用的仪表盘应展示智能体运行长度的分布。分布的长尾能反映循环发生率。
常见错误
以下错误经常出现:
错误1:不设步骤预算。 团队常说“需要时再加”,直到智能体凌晨3点陷入循环,才后悔没有提前设置。从第一天起就必须加入步骤预算。
错误2:预算过高。 “100步肯定够了”,但循环照样会把预算用满。预算应按中位数的2–3倍来设,而不是按最坏情况来设。
错误3:没有外部监控。 只相信智能体会自行停止,但它有时不会。生产环境必须有外部监控。
错误4:发现循环后不分析。 监控程序终止循环后,团队就继续做其他工作,结果同样的循环下周再次发生。每次发现循环都要进行事后分析:是什么触发了循环,什么发生了变化,能否从根本上防止这一类故障?
错误5:在简单任务上过于频繁地反思。 对一个总共只有5步的任务强制每5步反思一次,只会增加开销,没有实际收益。反思频率应与任务复杂度相匹配。
错误6:在长上下文中遗失目标。 只在第1步提过一次的目标,到了第50步往往已经淡出上下文。必须定期重新强调目标。
错误7:相信智能体自报的进度。 智能体可能声称自己正在取得进展,实际却并非如此。只要条件允许,就应从外部验证。
错误8:允许智能体递归调用自己。 “把这项任务拆分给多个子智能体”可能导致智能体数量呈指数增长。如果允许这种行为,就必须严格限制预算。
哪些循环可以接受
并非所有循环都有问题。有些任务确实需要多轮迭代:
- 迭代改进代码:编写、测试、修复,再重复。
- 带有分支的多步骤研究。
- 优化任务:尝试不同方案、评估,再改进。
在这些任务中,循环本身就是工作,并非故障。相应方法也需要调整:
- 提供更充足的步骤预算,例如50–200步。
- 明确把过程定义为“迭代”,而不是“循环”。
- 跟踪质量改进,每轮迭代都应提升某项指标。
- 改进停滞时硬性停止。
关键在于区分“预期内的迭代工作”和“意外循环”,并分别采用适当措施。
生产检查清单
智能体无限循环是可以预见、很常见,也能够避免的问题。相应方法早已明确:步骤预算、进度跟踪、重复检测、定期反思、目标锚定、退出路径、工具防护和外部监控。
这些并非可有可无的锦上添花,而是决定智能体能否上线,还是会带来意外四位数账单的关键措施。
任何生产智能体都应检查以下项目:
- 最大步骤预算。
- 最大token预算。
- 最大成本预算。
- 最大时间预算。
- 重复调用检测。
- 进度跟踪。
- 定期反思。
- 目标锚定。
- 多种退出路径。
- 具备终止能力的外部监控。
每一项都很容易实现。结合起来,它们决定了一个智能体是“无人看管就很危险”,还是“在生产环境中可靠运行”。
把这些方法落实到系统中,并认真测试。由此消除的长尾风险,远远抵得上投入的工作。



