【AI应用开发】Agent 无限 loop(反复调用同一个工具)如何规避?
【AI应用开发】Agent 无限 loop反复调用同一个工具如何规避1. Agent Loop 的四种模式模式A: 完全重复 — query_order(id001) × 8 次参数完全相同 模式B: 渐进重复 — search_docs(退货)→search_docs(退货流程)→search_docs(如何退货) 模式C: 乒乓循环 — query_order → search_kb → query_order → search_kb → ... 模式D: 发散循环 — 不断换工具但都不解决问题广度优先式尝试2. 根因分析根因说明LLM 不记忆历史上下文截断后忘记早期步骤工具返回无完成标记LLM 不知道什么算够了Prompt 鼓励过度探索如果不够详细请再次查询是坏指令工具设计缺陷缺少分页标记LLM 以为还有下页3. 七层防御体系第1层: 步数上限 (max_steps15) ← 最简单有效 第2层: 重复检测熔断 (相同调用≤2次) ← 模式A专用 第3层: Token 预算 (超80%预警) ← 兜底保护 第4层: 语义收敛检测 (余弦相似度0.95) ← 模式B/C/D 第5层: 智能终止信号 (完成标记/答案已覆盖) ← 主动终止 第6层: 超时控制 (全局120s/单步30s) ← 最后防线 第7层: 监控告警 (实时指标自动降级) ← 持续改进4. 第一层步数硬限制classStepLimiter:def__init__(self,default_max15,adaptiveTrue):self.limits{single_query:3,# 单次查询multi_step:8,# 多步执行research:20,# 深度研究}self.default_maxdefault_maxdefget_limit(self,task_typeNone):returnself.limits.get(task_type,self.default_max)defcheck(self,step,max_steps):ifstepmax_steps:returnFalse,达最大步数请立即给最终答案ifstepmax_steps*0.8:returnTrue,f仅剩{max_steps-step}步请尽快总结returnTrue,5. 第二层重复检测熔断classDuplicateDetector:def__init__(self):self.history[]self.max_exact2# 完全相同最多2次self.max_similar3# 相似调用最多3次self.circuit_openFalsedefcheck(self,step,tool_name,args,result_hash):# 检测1: 完全相同same[hforhinself.historyifh[tool]tool_nameandh[args]args]iflen(same)self.max_exact:self.circuit_openTruereturnf⛔ 熔断: {tool_name} 相同参数已调{len(same)}次# 检测2: 乒乓 ABABiflen(self.history)3:last4_names[h[tool]forhinself.history[-3:]][tool_name]iflast4_names[0]last4_names[2]andlast4_names[1]last4_names[3]:returnf⚠️ 检测到乒乓循环:{last4_names[0]}↔{last4_names[1]}# 检测3: 结果停滞same_tool[hforhinself.historyifh[tool]tool_name]iflen(same_tool)2andall(h[result_hash]result_hashforhinsame_tool[-2:]):return⚠️ 结果连续不变继续调用无意义self.history.append({step:step,tool:tool_name,args:args,result_hash:result_hash})returnNone6. 第三层Token 预算管理classTokenBudget:def__init__(self,max_total16000,reserve_output2000):self.max_totalmax_total self.reservereserve_output self.used0defcan_continue(self):remainingself.max_total-self.usedifremainingself.reserve:returnFalse,Token预算耗尽必须立即生成最终答案禁止调用工具ifself.used/self.max_total0.8:returnTrue,Token已用80%请精简returnTrue,defspend(self,tokens):self.usedtokens7. 第四层语义收敛检测importnumpyasnpclassSemanticConvergence:检测 Agent 输出是否还在取得进展def__init__(self,embed_model,threshold0.95):self.embedembed_model self.thresholdthreshold self.history[]# [(step, embedding)]defcheck(self,step,output_text):embself.embed.encode(output_text[:500])self.history.append((step,emb))iflen(self.history)3:returnTrue,recent[h[1]forhinself.history[-3:]]sims[]foriinrange(len(recent)-1):simnp.dot(recent[i],recent[i1])/(np.linalg.norm(recent[i])*np.linalg.norm(recent[i1]))sims.append(sim)ifall(sself.thresholdforsinsims):returnFalse,连续步骤结果高度相似信息增益趋零请停止并总结returnTrue,8. 第五层智能终止信号classTerminationManager:defshould_terminate(self,context):综合判断是否该停止reasons[]# 1. LLM 主动说完成了last_msgself._last_assistant_msg(context[messages])iflast_msgandany(winlast_msgforwin[已完成,以上就是,总结]):reasons.append(LLM声明完成)# 2. 问题已被覆盖回答ifself._question_covered(context):reasons.append(问题已完整回答)# 3. 工具返回完成标记forrincontext[tool_results][-2:]:ifisinstance(r.get(result),dict):ifr[result].get(completion_marker,{}).get(task_completed):reasons.append(f工具{r[tool]}标记完成)# 4. 用户取消last_userself._last_user_msg(context[messages])iflast_userandany(cinlast_userforcin[取消,停止,算了]):reasons.append(用户取消)returnlen(reasons)0,; .join(reasons)ifreasonselsedef_last_assistant_msg(self,msgs):forminreversed(msgs):ifm.get(role)assistantandm.get(content):returnm[content]returnNonedef_last_user_msg(self,msgs):forminreversed(msgs):ifm.get(role)user:returnm.get(content,)returndef_question_covered(self,context):# 简化: 检查问题关键词是否在结果中出现qcontext.get(original_question,)all_textjson.dumps([r.get(result,)forrincontext[tool_results]],ensure_asciiFalse)keywords[wforwinqif\u4e00w\u9fff]ifkeywords:coveredsum(1forkinset(.join(keywords)[:10])ifkinall_text)returncovered7returnFalse9. 第六层超时控制importasyncioclassTimeoutGuard:def__init__(self,global_timeout120,step_timeout30,llm_timeout20):self.global_timeoutglobal_timeout self.step_timeoutstep_timeout self.llm_timeoutllm_timeoutasyncdefrun(self,agent_fn,user_input):try:returnawaitasyncio.wait_for(agent_fn(user_input),timeoutself.global_timeout)exceptasyncio.TimeoutError:return{success:False,result:处理超时请缩小问题范围重新提问。,reason:global_timeout}10. 综合防御框架classLoopSafeAgent:集成所有防御机制的 Agentdef__init__(self,llm,tools,embed_model):self.llmllm self.toolstools self.step_limiterStepLimiter()self.dup_detectorDuplicateDetector()self.token_budgetTokenBudget()self.convergenceSemanticConvergence(embed_model)self.terminationTerminationManager()self.timeoutTimeoutGuard()self.terminated_earlyFalse# ← 关键标记是否提前终止asyncdefrun(self,user_input,task_typeNone):state{messages:[],tool_results:[],current_step:0,original_question:user_input}max_stepsself.step_limiter.get_limit(task_type)forstepinrange(1,max_steps1):state[current_step]step# 1. 步数检查can,warningself.step_limiter.check(step,max_steps)ifnotcan:self.terminated_earlyTruereturnself._force_summarize(state,warning)ifwarning:state[messages].append({role:system,content:warning})# 2. Token 预算检查can,msgself.token_budget.can_continue()ifnotcan:returnself._force_summarize(state,msg)# 3. 执行步骤responseawaitself._execute_step(state)# 4. 如果调用了工具记录并检测ifresponse.tool_calls:fortcinresponse.tool_calls:resultawaitself._call_tool(tc)# 重复检测loop_warningself.dup_detector.check(step,tc.name,tc.args,hashlib.md5(json.dumps(result).encode()).hexdigest())ifloop_warning:state[messages].append({role:system,content:f⚠️{loop_warning}})ifself.dup_detector.circuit_open:self.terminated_earlyTrue# ← 熔断强制终止returnself._force_summarize(state,工具熔断)# 语义收敛检测progress,msgself.convergence.check(step,str(result)[:500])ifnotprogress:state[messages].append({role:system,content:f⚠️{msg}})state[tool_results].append({step:step,tool:tc.name,args:tc.args,result:result})# 5. 终止条件检查should_stop,reasonself.termination.should_terminate(state)ifshould_stopandresponse.finish_reasonstop:returnresponse.content# 更新 Token 预算估算self.token_budget.spend(len(str(state[messages][-1]))//4)# 达到最大步数returnself._force_summarize(state,达到最大步数)def_force_summarize(self,state,reason):强制总结已获取的信息ifnotstate[tool_results]:return抱歉处理过程中遇到问题请重新描述您的问题。summary_prompt(f执行被终止原因:{reason}。f请基于以下已获取的信息给用户一个完整的回答\n\njson.dumps([{步骤:r[step],工具:r[tool],结果:str(r[result])[:300]}forrinstate[tool_results]],ensure_asciiFalse,indent2))returnself.llm.chat(summary_prompt)核心思想不要把防止 Loop 仅当作 Prompt 工程问题——必须在代码层面建立多层防线。LLM 不会主动停下来要靠系统强制停下来。

相关新闻

自动续费查询用什么App?续费藏挺深啊一键查清

自动续费查询用什么App?续费藏挺深啊一键查清

自动续费查询用什么App?续费藏挺深啊一键查清摘要:想搞清楚"我到底订阅了哪些付费软件、有哪些在悄悄扣钱",很多人第一反应是去微信、支付宝挨个翻。但平台割裂、入口藏着,往往半途而废。本文围绕"自动续费查询用什…

2026/9/30 9:48:02 阅读更多 →
Sinc函数:从理想低通滤波器到数字信号处理的工程实践

Sinc函数:从理想低通滤波器到数字信号处理的工程实践

1. 从“理想”到“现实”:Sinc函数为何是电气工程师的必修课 如果你在信号处理、通信系统或者图像处理领域工作过,哪怕只是上过相关的课程,大概率都见过一个长相奇特的函数:它在零点取值为1,向两边延伸时像衰减的波浪一…

2026/10/12 17:46:18 阅读更多 →
044、SimAMv2无参注意力在YOLOv12中的复现——基于神经科学启发的轻量涨点方案

044、SimAMv2无参注意力在YOLOv12中的复现——基于神经科学启发的轻量涨点方案

044、SimAMv2无参注意力在YOLOv12中的复现——基于神经科学启发的轻量涨点方案 兄弟们,今天这篇咱们聊点硬核又省事的东西。上周有个做工业质检的哥们儿找我,说他的YOLOv12在钢材表面缺陷数据集上跑到了mAP 78.4,死活上不去了,加了…

2026/10/2 19:15:51 阅读更多 →

最新新闻

RCE-labs命令注入绕过详解:换行符与IFS的shell解析原理

RCE-labs命令注入绕过详解:换行符与IFS的shell解析原理

老实说,我一开始在RCE-labs前几关过得很顺。命令注入嘛,不就是拼接分号、管道符、与符号这三板斧,页面把输入原样扔给系统命令,闭合掉前缀就能拿到执行结果。直到Level 5和Level 6启动,代码里直接把这些符号和空格全部…

2026/10/13 13:11:22 阅读更多 →
Linux screen命令详解:终端复用器解决SSH断线任务中断问题

Linux screen命令详解:终端复用器解决SSH断线任务中断问题

干过几年服务器运维和线上事故处理的人,几乎都经历过同一个噩梦:SSH连着一台服务器跑数据导入,半夜网络一抖,第二天发现任务挂在90%死了,几小时白干,连log都没捞回来。我最早学Linux screen命令&#xff0c…

2026/10/13 13:11:22 阅读更多 →
亚马逊棋:轻量级博弈沙盒训练LLM结构化推理能力

亚马逊棋:轻量级博弈沙盒训练LLM结构化推理能力

简介:本资源是面向人工智能与博弈算法研究者的亚马逊棋(Amazon Game)开源实现合集,聚焦机器博弈中的高复杂度策略游戏建模与AI求解,适用于算法工程师、高校AI课程实践者及强化学习初学者。压缩包含203个文件&#xff0…

2026/10/13 13:11:22 阅读更多 →
Hadoop MapReduce中Configuration与Job初始化深度解析

Hadoop MapReduce中Configuration与Job初始化深度解析

1. 为什么从Configuration和Job初始化开始跟踪MapReduce提交流程 很多人一上来就想直接跳进 submit() 方法里看YARN怎么分配Container,或者急着找Shuffle阶段的网络传输逻辑。我试过三次——每次都在 Job.submit() 之后就迷失在层层代理、动态生成的接口和一堆 …

2026/10/13 13:11:22 阅读更多 →
LeetCode Hot 100 普通数组题复盘:从双指针到原地哈希的避坑指南

LeetCode Hot 100 普通数组题复盘:从双指针到原地哈希的避坑指南

我刚开始刷 LeetCode Hot 100 的时候,并没把“数组题”放在眼里,总觉得数组不就是循环套循环,最多再加个排序嘛。直到有一次给一位学弟讲题,他随口问我“Hot 100 里的普通数组篇到底在考什么”,我卡住了——这才意识到…

2026/10/13 13:11:22 阅读更多 →
OpenClaw技能机制与PyCharm集成:自动化整理知识库的实操指南

OpenClaw技能机制与PyCharm集成:自动化整理知识库的实操指南

先说明我的身份:一个整天泡在代码和文档堆里的普通开发者。最近一个月,我把个人知识库的整理流程整个搬到了 OpenClaw 上,中文社区给它的昵称是“小龙虾”。这个称呼初听有点搞笑,但用顺手之后你会觉得,它确实像一只壳…

2026/10/13 13:10:21 阅读更多 →

日新闻

物联网系统集成避坑指南:从选型到验收的关键要点

物联网系统集成避坑指南:从选型到验收的关键要点

做了这么多年物联网系统集成的项目,我最大的感受是,大部分坑不是出在技术前沿,而是出在一些看起来很平常的地方。设备选了便宜的、协议没问清楚、现场没摸透、验收标准写得含糊——这些细枝末节,最后都会变成延期、超支和扯皮的大…

2026/10/13 0:00:57 阅读更多 →
ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

如果你玩ESP32有一阵子了,很难不碰到舵机控制这道坎。做智能小车要转方向,做机械臂要抬关节,做云台要偏航俯仰,背后都是同一个东西在干活:舵机。而我见过太多人卡在“代码能编译、舵机就是不听话”这个阶段&#xff0c…

2026/10/13 0:00:57 阅读更多 →
CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

1. 为什么CAN总线至今仍是工业现场的“硬通货”?——从汽车底盘到智能温室的真实需求你有没有拆过一辆老款燃油车的中控台?或者调试过某高校实验室里那台跑着Modbus协议却频频丢帧的PLC控制柜?又或者,在某个农业物联网项目里&…

2026/10/13 0:00:57 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/13 2:54:56 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/12 0:16:38 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/13 1:58:08 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 21:05:09 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 14:48:49 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 18:11:17 阅读更多 →