最近在尝试用Qwen2.5和Llama3.1本地搭一个简单的AI Agent,主要想让它调用几个自定义工具(比如查天气、发邮件)。但发现一个问题:模型经常在工具调用时“脑补”参数,比如明明工具要求city参数是字符串,它给我传个数字,或者干脆不传就瞎执行。我用的是function calling格式,也试过ReAct prompt模板,感觉不太稳定。想问下大佬们,这种情况是模型本身对工具调用的理解能力不行,还是我的prompt设计有坑?或者有没有推荐的专门优化过tool use的开源基座模型?求指点,调得有点自闭了。
用开源模型搭Agent,工具调用总翻车,是选型不对还是prompt没写好?
全部回复
共 177 条试试把工具描述里参数类型写成示例值,比如city: "北京",模型跟着学比纯定义稳多了。
别纠结prompt了,换GLM-4或Functionary这类专门调过的模型,省心不少。
说实话你这情况我也踩过坑,Qwen2.5的function calling对参数类型约束确实比较松,我后来在prompt里把每个参数的取值范围和示例都写死,甚至加了“如果拿不准就反问用户”的兜底逻辑,才稍微稳一点。Llama3.1相对好点但吃提示词,建议试试把工具描述改成JSON Schema那种带类型和必填标记的格式,比纯文本强不少。另外可以看看FireFunction-v2或者Glaive那类专门微调tool use的模型,不过本地跑起来要看显存够不够。
试试把工具描述写得更死板点,比如直接给示例参数格式,我之前这么干稳定多了。另外Qwen的function calling确实比Llama稳,你换个7B的qwen再试下。
说实话这俩模型在tool use上确实不算强项,尤其Qwen2.5的function calling对参数类型约束比较松散,容易自己发挥。你可以试试把工具描述写得更死板一点,比如直接写明“如果city不是字符串则拒绝执行”,再给个few-shot示例,效果会明显好一些。另外可以看看GLM-4或FireFunction V2,这两个对工具调用的格式遵循度会高不少,同参数下翻车率低很多。
说实话这俩模型在function calling上都不算强项,Qwen2.5对参数约束的敏感度确实一般,Llama3.1更吃prompt格式。你可以试试把每个工具的参数schema写得更狠一点,比如在描述里加“必须严格返回字符串否则报错”这种硬约束,比光靠类型声明管用。另外我最近换成glm-4-flash和functionary,工具调用成功率明显稳不少,特别是带few-shot示例的时候。要是你不想换模型,可以在解析输出层加个校验逻辑,参数不对就重试一次,比反复调prompt效率高。
试试把工具schema里加few-shot示例,比光改prompt管用,Qwen对格式敏感但逻辑容易飘。
或者直接换llama3.1的tool-use微调版,省心不少。
说实话这俩模型在function calling上都不是强项,特别是Qwen2.5不带tool-use微调的话确实容易瞎填参数。建议先试试把工具描述写得特别严格,每个参数都加上“必须为字符串”这种硬约束,再不行就换个思路用grammar约束输出格式。另外可以看看Qwen2.5的Coder版本或者专门搞agent的模型,比如Devstral或者FireFunction,它们在工具调用上会稳很多。
试试给每个工具加few-shot示例,把参数类型和边界写死在system prompt里,比纯描述管用得多。
试试把工具描述写得更死板一点,比如明确写“city必须是字符串”,Qwen对格式敏感但容易自由发挥。
试试给工具描述加上few-shot示例,Qwen对参数格式的敏感度比想象中高,我这么调完稳定多了。
把工具参数schema里加个"required"字段,再配合temperature调低到0.1,Llama3.1翻车率能降一半。
说实话这俩模型裸跑function calling确实容易飘,Qwen2.5对参数类型约束本来就弱,Llama3.1得把工具描述写得更死板才行。你可以试试在system prompt里加一句“参数必须严格匹配JSON Schema类型,缺了就拒绝执行”,会好一点。或者直接换GLM-4或DeepSeek的tool use版,这俩在工具调用上明显更稳。另外建议你log一下每次的完整输出,看是模型理解错还是解析层的问题,有时候是自己后处理把格式搞坏了。
说实话Qwen2.5在function calling上确实有点飘,特别是参数类型约束这块,我试过把工具描述写得更死板一点,比如“city必须是字符串,禁止数字”,效果会好一些。另外你换个思路,直接用LangChain的tool calling封装层,它内部会做一次参数校验和纠错,能挡住不少脑补。Llama3.1的话得看是哪个微调版,原版对中文工具理解更弱,建议试试OpenHermes或者Nous的变体。还有个小技巧,在prompt里加一个“不确定就反问用户”的指令,能让它少瞎猜。
说实话这俩模型裸跑function calling都不算强项,Qwen2.5对参数类型约束尤其容易放飞。你可以试试把工具描述里每个参数都加示例值,比如city: "北京"这样,模型跟着抄会稳很多。另外推荐看下Firefunction-v2或者ToolACE,这俩专门调过工具调用,本地部署也不难,能少踩不少坑。还有,ReAct模板里最好把“工具返回后必须检查再决定下一步”写进系统提示,能抑制瞎执行。
这俩模型裸跑function calling确实容易抽风,Qwen2.5对参数类型约束比Llama3.1强点但也没好哪去。建议先试试把工具描述里的参数格式写成JSON Schema那种带示例的,尤其city后面加个“比如北京”这种,模型会老实很多。另外可以看看Firefunction-v2或者Glaive那类专门调优过的模型,不过本地跑起来可能有点吃显存。你如果非要用这俩,ReAct模板里把工具返回的报错信息再次喂回去,多迭代一轮能救回不少翻车情况。
试试把工具描述写得更死板点,比如city字段直接写死成字符串格式示例,Qwen对强约束响应好很多。
建议换带tool-use微调的模型,Qwen2.5对参数约束确实弱,加个JSON schema校验能挡掉八成脑补。
试试把工具描述里每个参数都附上示例值,比纯写类型管用,Llama3.1对格式更敏感。
说实话这问题我太有共鸣了,Qwen2.5和Llama3.1我都试过,工具调用翻车基本是常态,尤其参数类型错乱这块,感觉模型压根没把schema当硬约束,更像是在“猜”你要啥。我后来折腾了一圈,发现prompt能救一部分,但救不了根子,比如你把工具描述写清楚、每个参数给个few-shot示例,能明显减少瞎传数字的情况,但偶尔还是会犯傻。如果你愿意换模型的话,可以看看那些专门在tool use上微调过的开源版本,比如基于Qwen的ToolACE或者FireFunction,实测比原版稳不少,不过本地部署要求也高一点。还有个偏方,就是别全指望模型自己规规矩矩输出,你在代码里加个强制校验层,参数不对就直接拒绝并让模型重新生成,虽然体验糙点,但至少不会瞎执行。最后想说,这真不是你prompt的锅,当前开源模型对工具调用的泛化能力就这水平,除非上超大杯,不然得接受“半自动”的现实。
说实话你这情况我太熟了,之前用Llama3.1搞工具调用的时候也是被那个参数幻觉折磨得够呛。我自己试下来感觉这俩模型在function calling上确实不是强项,Qwen2.5稍微好点但也就那样,它们更擅长对话生成,对结构化输出的约束力天生弱一些。你试试在prompt里把工具定义写得特别死,比如每个参数后面加上“必须是JSON字符串,禁止类型转换”这种强约束,然后few-shot给两个极端例子,一个对的一个错的,让模型模仿。不过更省心的路子是换专门调过的模型,像FireFunction-v2或者ToolACE这类开源模型就是冲着这个场景去的,我朋友用着说稳定性提升挺明显。另外还有个邪门技巧,把工具调用的结果接一个校验层,用正则或者pydantic硬卡参数类型,不合法就直接重试一次,比纯靠模型自觉靠谱多了。
大概率是模型本身对工具调用格式的敏感度不够,Qwen2.5在function calling上确实比Llama3.1稳一些,建议换个专门的tool-use微调模型试试。
说实话你这情况我太熟了,之前用Llama3.1调工具的时候也卡了好几天,最后发现是温度参数太高导致模型在生成JSON时瞎编。你试试把temperature降到0.1以下,或者直接用greedy decoding,能解决一大半“脑补”参数的问题。另外Qwen2.5的function calling其实比Llama3.1稳一些,但前提是你要把工具描述的格式写得极其严格,比如每个参数的类型、枚举值、默认值都得在prompt里用JSON Schema明确列出来,别让模型有任何自由发挥的空间。我之前还踩过一个坑,就是工具返回结果没做格式化,模型会把它当成对话内容继续生成,导致下一轮调用直接崩了。如果你愿意换模型,可以看看FireFunction V2或者Gorilla OpenFunctions,这些专门在tool use上做过微调,实测比通用模型强不少,但本地部署的话显存要求会高一些。最后建议你给每个工具加一个“required”字段,并且用few-shot的例子把“拒绝调用”和“错误调用”的情况也写进prompt里,效果比单纯改模板明显。