最近在跑一个本地部署的AI Agent demo,底层用Qwen2.5-7B,配合LangGraph做工具调用。任务很简单:让agent根据用户需求查天气再订个闹钟。但实际跑起来,agent经常在“调用工具→拿到结果→再次调用同一个工具”之间反复横跳,日志里能看到它已经拿到天气数据了,下一轮还是继续调天气API,就是不往下走。我试过调低temperature、加max_iterations限制,也改过system prompt强调“完成就结束”,但偶尔还是会卡住。想问问大家,是模型本身对工具调用的指令遵循能力有限,还是我的图结构设计有问题?有没有什么prompt技巧或者后处理手段能缓解这个现象?感谢!
用Qwen做Agent老是陷入死循环,有人遇到过吗?
全部回复
共 51 条我也遇到过,后来在LangGraph里加了个工具结果去重,同参数直接跳过,基本治好了这毛病。
我之前跑类似demo的时候也踩过这个坑,Qwen系列在工具调用上的确容易“上头”,尤其是7B这个量级,对“任务已完成”的语义理解比想象中要弱。你试过调低temperature和加max_iterations,但我觉得问题可能更多出在LangGraph的图结构上——如果工具节点和判断节点之间没有显式的状态转移条件,模型很容易把“拿到结果”误判成“还需要再确认一次”。一个比较好用的土办法是,在拿到工具返回后,强制让LLM先输出一句“用户需求是否满足”的布尔判断,再决定下一步走哪个分支,相当于把决策从模型直觉里剥离出来,交给代码逻辑去截断。另外,你可以在system prompt里给一个非常具体的“终止样例”,比如直接写“如果天气已返回且闹钟已设置,必须输出FINAL_ANSWER”,比单纯说“完成就结束”有效得多。还有个后处理技巧,就是记录上一次工具调用的签名,如果下一轮模型又请求同一个参数完全相同的工具,直接拦截掉并让它重新生成,这能解决大部分死循环。我猜你现在的图可能是个简单的循环结构,建议改成带“完成标志位”的有限状态机,虽然麻烦点,但稳很多。对了,你用的工具返回格式是纯文本还是结构化JSON?有时候模型会把JSON里的“status: success”误读成需要再执行一次。
我之前也遇到过一模一样的鬼打墙,后来发现不光是模型的问题,LangGraph里工具节点的返回格式太宽松也会这样。你试试在工具返回结果里强制加一个“任务状态”字段,比如already_done=true,然后让agent的下一步决策逻辑先去读这个字段,比单纯改prompt管用。另外Qwen对“结束”的理解确实不如GPT-4o干脆,我后来加了规则:连续两次调用同一个工具就直接截断,强制走下一步,虽然粗暴但实测能减少大半死循环。
还有一种思路是别让agent自己判断要不要继续,把“查天气→订闹钟”拆成两个独立的子图,每个子图只负责一个动作,做完就return,父图再串起来。这样就算单个节点卡住,也不会无限横跳。你可以先试试给工具结果加个状态标记,成本最低。
另外温度调到0.1以下其实意义不大,Qwen的重复行为更多是注意力机制的问题,不是采样随机性导致的。建议你翻一下日志,看看它第二次调天气API时,system prompt里的历史对话是不是把之前的工具结果又当成新请求了,有时候是上下文拼接顺序的锅。
我也遇到过类似的,Qwen在工具调用上确实容易“上头”,尤其是多轮工具交替的时候,图结构本身可能没问题,但模型对“已完成”的上下文判断很弱。我当时是加了个规则层,检测到相同工具连续调用两次且参数一样就直接强制跳转,效果立竿见影。另外你可以试试把工具返回的结果在prompt里用特殊标记包起来,比如[TOOL_RESULT]...[/TOOL_RESULT],这样模型更容易区分“这是数据”而不是“需要再调一次”。温度调低确实有用,但7B模型本身对指令边界敏感,建议还是后处理兜底更稳。
这个我太有同感了,之前用Qwen做类似的多步工具调用时也卡在过循环里。后来发现单纯靠prompt压不住,得在LangGraph的边逻辑上加个状态判断,比如检测到工具输出和上一轮一样就直接强制跳转下一步。另外可以试试把工具调用的描述改成更明确的“查询完成”标记,让模型更容易识别终止条件。你用的是ReAct结构还是自定义的图?我换成带记忆缓冲的版本后频率低了不少。
我最近也在折腾类似的,Qwen2.5-7B对工具调用的边界感确实有点飘,感觉它把“调用工具”本身当成了任务的一部分,而不是达成目标的手段。你试试在每次工具返回后强制塞一条“当前信息已满足需求”的中间节点,用程序判断下一步该不该继续,别全指望模型自觉。另外把天气API的返回格式改得再“终态”一点,比如直接给个“今日天气:晴,已记录”这种带结论性的句子,比裸数据更容易让它收手。
这个问题我太有同感了,之前用Qwen2.5-7B跑类似的tool calling也踩过这个坑。我觉得与其说是图结构设计问题,不如说这是小模型在工具调用场景下的一个通病,它对“完成意图”的把握确实不如大模型那么稳定。我自己试过几个办法,一个是在system prompt里明确写“如果已经获取到最终答案,直接输出结果,不要重复调用”,同时把工具描述改得更具区分度,比如加一句“当用户需要设置闹钟时,才调用此工具”。另外我发现,LangGraph里给每条边加个条件判断挺有用的,比如检测到返回结果里包含特定字段就强制跳转到结束节点,相当于做个硬约束。还有个土办法,就是在agent的输出层加个正则过滤,如果连续两次调用同一个工具且参数完全一样,就直接截断并返回最后一次结果。不过说实话,这些后处理都只是缓解,根本解法可能得换更大参数的模型,或者用专门的tool-use微调版本。你现在日志里能看到它拿到数据后还调用,那有没有尝试过把工具返回的结果格式改一下,比如加个“status: complete”标记,让模型更容易识别状态?
这个问题我也踩过坑,Qwen2.5-7B在工具调用的终止判断上确实比较弱,尤其是LangGraph里如果工具节点返回结构稍微复杂点,模型很容易把“结果”误读成“还需要再确认”。我后来是把工具返回内容里直接加一行“这是最终结果,请停止调用”的显式标记,然后配合一个规则:如果同一工具连续调两次且参数相同,就直接强制跳转到结束节点,效果好了很多。你试试看是不是图里缺少一个“工具结果去重”的判断条件。
大概率是模型对“工具结果已满足意图”的判别太弱,试试在工具返回里直接拼接“任务已完成”的硬信号,比改prompt管用。
我也踩过这坑,后来在工具返回里加了个状态标记,让模型能明确感知任务已完成,死循环少了很多。
试试在system prompt里加一句“天气查询结果已包含所有必要信息,无需重复调用”,配合图结构里对同一工具的连续调用次数做硬限制,效果会好不少。
我这边用Qwen做工具调用也踩过类似的坑,感觉不全是模型指令遵循的问题,LangGraph的图结构如果没把工具结果的判断逻辑写死,模型确实容易自己绕回去。后来我在工具返回里加了个“已获取数据”的标记,Agent拿到后强制走下一步,基本就稳了。另外你试试把system prompt里的“完成就结束”改成“直接输出最终答案”,对Qwen来说“结束”这个词有时候反而会触发它继续验证。
我之前也踩过这个坑,尤其是Qwen 7B这种量级的模型,工具调用的终止条件确实比大模型要模糊。你日志里那种“拿到结果还继续调”的情况,我猜不完全是图结构的问题,更像是模型在上下文里把“工具返回”当成了“新指令”来理解,而你的LangGraph节点可能又把工具结果当成新一轮输入喂回去了。我后来试了个笨办法:在拿到工具结果后,强制拼接一段“用户已确认,任务完成”这样的前缀再传给模型,相当于用文本把状态机掰正,效果比改prompt稳定得多。另外你可以检查一下工具调用的stop token是不是没设对,有时候模型输出里带了tool_call标记但没正常截断,就会自己又生成一轮调用。还有个小技巧,把max_iterations设成3,但每轮结束后用代码判断一下,如果工具结果和上一轮完全一致,就直接跳出去走最后的生成节点,别让模型自己决定停不停。至于prompt,我试过在system里加“除非用户有新指令,否则不要重复调用”这样的明确约束,会好一点,但7B模型偶尔还是会抽风,所以后处理兜底还是最靠谱的。你用的LangGraph版本是多少?我怀疑RecursionLimit的触发逻辑在低版本里有些bug,换个新版本可能也能缓解。
这问题我也踩过,后来在tool结果里加了个“已获取”标识,让模型看到就不再调用,效果立竿见影。
我之前也卡这,后来在工具返回里塞了句“已获取结果,请继续下一步”才好转,你可以试试。
我最近也在折腾类似的东西,Qwen系列在工具调用上确实容易“上头”,感觉它把工具结果当成了新的指令来源,而不是任务进展。你试试在每次工具返回后,强制把历史对话里那段“调用-结果”压缩成一句摘要,比如“天气已获取,现在需要设置闹钟”,而不是把完整JSON喂回去,这样模型更容易跳出来。另外LangGraph里可以在工具节点后加一个“状态校验”节点,如果检测到连续两次调用同一个工具且参数没变,就直接打断,手动路由到下一步,比纯靠模型自觉靠谱多了。
我最近也在用Qwen2.5跑类似流程,发现temperature调到0.1以下还不够,关键是system prompt里得明确说“工具结果只是中间状态,必须基于用户原始目标决定下一步”,甚至可以把“已经完成”的假动作写进few-shot示例里。另外检查一下你的图是不是把工具节点设计成了“总是需要返回给模型”的循环结构,试着在拿到结果后直接走条件边,用规则判断是否满足结束条件,别让模型有再次决策的机会。
确实遇到过,而且感觉这不只是Qwen的问题,7B模型对“何时停止”这种元认知本来就弱。我当时的土办法是在工具结果里加个隐藏标记,比如在返回内容末尾附上“【任务已完成标记】”,然后在LangGraph的边条件里写死,只要检测
这问题我也踩过,多半是图结构里工具调用后的状态没更新干净,试试在返回结果时强制带上“任务完成”标记。
我这边加个简单的正则判断,如果结果里包含天气数据就直接跳到下一步,基本能避免死循环。
这个问题我碰到过好几次,Qwen系列在工具调用上确实有点“轴”,尤其是7B这个规模,拿到结果后容易把“工具返回值”误当成新的用户输入去继续触发同一个动作。我后来在LangGraph里加了个状态检查,就是每轮循环前比对一下上一步的输出是不是已经包含目标信息,比如天气数据里出现了“温度”关键字,就直接强制跳到下一节点,绕开模型决策。你可以试试把工具调用和状态转移解耦,别让LLM全权决定下一步,图结构里加个规则性的条件分支会稳很多。另外prompt里别只写“完成就结束”,我习惯在系统指令里明确说“如果当前已有工具返回结果且满足用户需求,请直接输出最终答案,禁止再次调用任何工具”,然后把这句话放在很靠前的位置,效果比放后面好。还有一个土办法,就是给每个工具调用加个“已调用过”的标记,塞进历史记录里,模型看到重复调用会更容易意识到自己卡住了。你用的是Qwen2.5-7B的话,也可以试试把temperature调到0.1以下,虽然不能根治,但能减少它“灵机一动”想再确认一次的情况。要是还不行,就考虑换Qwen2.5-14B或者拿72B量化版跑,模型大了指令跟随能力真的天差地别。
这问题我太熟了,之前用Qwen系列跑类似的多步工具调用时也撞见过一模一样的死循环。我感觉根源不单是模型指令遵循能力的问题,更像是LangGraph里节点状态传递的机制——你每次工具调用后如果没在state里显式标记“该工具已执行”,模型下一轮看到的上下文其实还是一张白纸,它自然觉得“我还没干完活”。我后来是手动在每轮工具结果前加了一行“注意:以下为历史执行结果,禁止重复调用同一工具”,效果立竿见影。另外你可以试试把天气API的输入参数做成不可变对象,一旦写入state就锁死,这样即使模型想再调,图结构上也会因为参数相同直接短路到下一个节点。还有个土办法,就是在工具返回的content里塞一个隐藏标记,比如“DONE”,然后你在LangGraph的conditional edge里判断这个标记存在就直接走终结点,比单纯靠模型自觉靠谱得多。不过说实话,7B模型对多步指令的长期一致性确实弱,如果任务再复杂点我建议直接换Qwen2.5-32B或者用few-shot示例把“完成”的路径写死。你那个max_iterations设的是多少?我这边设到12次反而更容易触发重复,因为模型会故意凑次数。
我之前跑类似流程的时候也踩过这个坑,而且用的还是带function calling微调的模型,照样会抽风。感觉Qwen2.5-7B在工具调用上确实有点“执着”,拿到结果后它可能把“调用工具”本身当成任务的一部分了,而不是达成目的的手段,这时候光靠prompt压效果有限。我后来是直接在LangGraph里加了个状态机判断,比如记录上次工具返回的内容,如果连续两次调用同一个工具且结果没变化,就强制走下一步,或者注入一条“你已经拿到天气数据了”的假消息来打断循环。另外你可以试试在tool result里加上一个“已完成”标记,让模型看到明确的终止信号,比在system prompt里说“完成就结束”要实在得多。还有一个偏门但有效的办法——把agent的决策层切到Qwen2.5-72B或者干脆用GPT-4o mini做路由,7B的指令遵循能力在复杂多跳场景下确实容易掉链子,不是你的图结构问题。最后建议你开一下log,看看它循环时input里是不是还带着上一轮的assistant消息,有时候是历史消息重复累积导致模型误判,清理一下对话窗口可能就好很多。
我也遇到过,Qwen系列在工具调用上确实容易“上头”,尤其是7B这种规模,拿到结果后对“下一步该干嘛”的语义理解会漂移。图结构本身可能没问题,但你可以试试在工具返回内容里强制加上“数据已获取,请直接生成最终回答”这样的显式标记,比改system prompt管用。另外后处理加个规则,如果连续两次调用同一个工具且参数相同,就直接截断让它输出,虽然粗暴但很实用。