最近在跑一个本地部署的AI Agent demo,底层用Qwen2.5-7B,配合LangGraph做工具调用。任务很简单:让agent根据用户需求查天气再订个闹钟。但实际跑起来,agent经常在“调用工具→拿到结果→再次调用同一个工具”之间反复横跳,日志里能看到它已经拿到天气数据了,下一轮还是继续调天气API,就是不往下走。我试过调低temperature、加max_iterations限制,也改过system prompt强调“完成就结束”,但偶尔还是会卡住。想问问大家,是模型本身对工具调用的指令遵循能力有限,还是我的图结构设计有问题?有没有什么prompt技巧或者后处理手段能缓解这个现象?感谢!
用Qwen做Agent老是陷入死循环,有人遇到过吗?
全部回复
共 51 条我也踩过这坑,LangGraph里给工具结果加个去重缓存,命中就直接跳过,能治标不少。
这个问题我太有同感了,之前用Qwen2.5-7B跑类似的多步工具调用时也撞见过一模一样的鬼打墙。我后来排查发现,问题多半出在LangGraph的节点状态设计上——模型其实“知道”任务完成了,但你的图结构可能没给它一个明确的“终止信号”,它只能靠惯性继续调工具。你可以试试在拿到天气结果后,强制把工具列表从状态里清空,或者单独加一个“判断是否满足用户全部需求”的LLM节点,输出一个布尔值再决定走结束分支,这样比单纯靠prompt硬掰要稳得多。另外,7B模型对复杂指令的跟随确实有天花板,尤其是当工具返回的JSON里带了一堆无关字段时,它容易误以为“还有信息没处理完”。我自己的workaround是把工具返回结果用正则压缩成一行纯文本摘要,再塞回给模型,减少干扰。还有个小技巧,在system prompt里把“完成”的动作具体化,比如“当天气和闹钟都设置成功后,只回复一句话,不再调用任何函数”,然后配合一个简单的规则检查——如果最后一条消息里包含“已设置”这种关键词,就直接强制跳出循环,别给模型选择权。反正这种问题得靠工程手段兜底,纯指望模型自觉不太现实。
我这边也遇到过,多半是图结构里工具结果没喂给决策节点,试试把天气输出直接拼进下一轮prompt。
加个工具结果去重缓存,同一参数下重复调用直接跳过,实测能砍掉大半死循环。
这个太常见了,Qwen系列在短链条工具调用上确实容易“上头”,尤其7B版本对“已完成”的判定不够果断。我试过在工具返回结果里加一个“is_final”字段,配合LangGraph的条件边判断,比纯靠prompt强不少。另外你可以在拿到结果后强制把对话历史里最后一条工具消息替换成“已完成,请继续”,能骗过模型让它往下走。死循环本质是模型对状态机的理解弱,用图结构硬约束比调参更靠谱。
遇到过一次,当时是让它查完天气再算个时间差,结果它疯狂重复查天气。后来我把工具返回格式改成带“status:success”标记,然后代码里检测到重复调用就自动注入一条“天气已获取,现在执行下一步”的伪造系统消息,基本能打断循环。不过说实话,7B模型对这类多步推理确实勉强,你试试把任务拆成两个独立的agent节点,中间用路由连接,比单agent硬扛稳定得多。
我猜你用的是ReAct那种循环结构吧?Qwen对“调用后必须改变状态”的感知很弱,容易把工具结果当成新输入。建议在tool节点后面加一个“意图确认”节点,用规则判断结果是否满足用户原始需求,满足就直接走结束分支,别给模型自由决策的机会。另外可以试试把temperature调到0
我之前跑类似demo也踩过这个坑,后来发现不完全是模型的问题,LangGraph里工具节点返回后如果没显式更新状态,模型会误以为还要继续调。你可以试试在工具调用后加个条件边,判断结果里如果有“已完成”标记就直接走结束节点。另外Qwen对“最终回答”这种词挺敏感的,我在system prompt里写死“当用户需求全部满足时,必须输出final_answer”,效果好了不少,但还是建议配合一个简单的规则:如果连续两次调用同一个工具,就强制截断走回复分支。
这个问题我碰到过太多次了,Qwen2.5-7B在工具调用上确实有这种“惯性”,就是它一旦开始调某个工具,那个动作的权重会特别高,哪怕结果已经拿到了,它还是倾向于重复那个模式。我之前试过在LangGraph里加一个状态检查节点,看到天气结果已经存在就不再让工具节点有执行权限,直接强制路由到下一步,比单纯靠prompt管用。另外你可以在工具返回的content里加一句“这是最终结果,不要再次请求”,有时候模型会读这个来修正自己的行为。还有个小技巧,把工具描述里的“查询”改成“查询一次”,然后系统提示里明确写“每个工具只能调用一次,调用后立即执行后续计划”,比“完成就结束”这种模糊指令要有效得多。至于图结构,建议把工具调用和结果处理拆成两个独立节点,中间加个条件边判断结果是否包含“success”标记,这样能物理上切断循环路径。不过说实话,7B模型本身指令遵循的上限就在那,你要是任务再复杂点,可能得上RAG或者微调,或者干脆换Qwen2.5-14B试试,参数量上来这个问题会明显减少。
这个问题我太有同感了,之前用Qwen做多步工具调用时也踩过这个坑,尤其是7B这个量级,模型对“已经拿到结果就该换下一步”的隐含逻辑理解得确实比较弱。我后来发现一个比较管用的办法是在工具返回的内容里强制加上一个字段,比如“数据已获取,请勿重复调用”,同时把LangGraph的边设计成根据工具结果的关键词做条件路由,而不是让模型自己决定下一步。另外我怀疑你那个循环可能跟图结构里节点间的状态传递有关,如果工具节点和决策节点之间没有显式清理历史消息,模型很容易把之前tool call的上下文当成最新状态,导致它觉得“还得再调一次”。你可以试试在每次工具调用后把对应的消息历史截断,或者只保留最后两轮对话,这样能减轻干扰。还有就是prompt里别光说“完成就结束”,最好给一个明确的终止动作示例,比如“当天气和闹钟都已确认时,输出FINAL_ANSWER”,模型对具体符号的响应比抽象指令更稳定。不过说实话,Qwen2.5-7B对复杂指令遵循的上限就在那,实在不行可以试下把部分判断逻辑抽出来用规则写死,毕竟Agent框架本来就是拿来兜底的。
这个问题我最近也踩过,跟你情况几乎一模一样,用的也是Qwen2.5-7B加LangGraph。后来我仔细看了下追踪日志,发现它其实不是“没理解”任务,而是生成的下一个动作里仍然带着天气查询的tool_call_id,相当于它把上一轮的observation又当成新指令的一部分给复述出来了。我猜这跟模型在长上下文里对“当前状态”的注意力衰减有关,尤其是当工具返回的结果比较长时,它更容易被中间信息带偏。
我试过几个缓解办法,最有效的是在每次工具返回后,强制把结果里跟任务无关的冗余字段(比如温度单位、风速这种)先裁剪掉,只保留核心数值,再塞回给模型。另外我在system prompt里加了一条类似“你已经拥有所有必要数据,如果用户没有提出新需求,请直接输出最终答案”的规则,并且用正则去拦截连续两次调用同一工具的循环,一旦命中就直接强制跳到结束节点。
不过说实话,这更像是模型本身的指令遵循天花板问题,7B在复杂多步工具调用上确实容易丢状态。如果你有条件,可以试试把Qwen2.5-32B或者更大参数量的版本跑一下同一套图结构,对比下是不是循环率大幅下降。还有一个思路是调整LangGraph的conditional_edge,不单纯依赖模型自己决定下一步,而是根据工具结果类型做一个硬编码路由,比如天气查完就直接进闹钟节点,别给它选择空间。我这么改完之后,循环基本消失了,就是灵活性会牺牲一点。
我之前跑类似流程的时候也踩过这个坑,后来发现光靠改prompt解决不了根本问题,得在LangGraph里加一个状态机判断,比如检测到同一工具连续返回相同结果就直接强制跳转下一步,比调温度参数管用多了。另外Qwen2.5-7B对工具结果的语义理解确实偏弱,有时候它把“拿到数据”当成“还得再确认一次”,所以我会在工具返回里显式加一句“这是最终结果,请直接继续”,效果能好不少。你试试看会不会减少死循环频率?
试试在LangGraph里加个状态机判断,工具结果重复就直接强制跳下一步,光靠prompt拦不住这种循环。
同款问题,我跑Qwen2.5-7B接API的时候也经常这样,尤其任务稍微复杂点,它就像个复读机一样抓着同一个工具不放。我之前试过在system prompt里直接写“如果已经拿到所需数据,禁止再次调用该工具”,但效果不稳定,有时候它还是会犯迷糊。后来我怀疑是不是LangGraph的循环节点设计太松了,给了模型太多自由选择的空间,试着把工具调用的结果缓存起来,在下一轮判断时如果发现相同参数就直接跳过,这才好了一些。另外我也试过在工具返回的内容里加一句“这是最终结果,请据此行动”,对Qwen有点用但也不是百分百,感觉模型对“完成”这个概念的理解还是不够稳固。你有没有试过在调用工具前先让模型输出一个“意图确认”步骤?我加了这个之后死循环少了很多,代价就是多一次推理延迟。还有个偏方,就是检测到重复调用时强制注入一个“请总结当前状态并给出最终答案”的提示,虽然粗暴但偶尔能救回来。说到底7B模型在这种多步推理上确实有点勉强,可能换Qwen2.5-14B或者用带tool-use微调的版本会好一些,但成本就上去了。