工具是模型的手
给模型接上工具(tool use:把函数的名称、参数 schema 与描述交给模型,由模型决定何时调用哪个),它就从「只会说」变成「能做事」。但实践中大量「这模型不行」的案例,拆开看其实是工具设计不行:名字含义模糊、参数嵌套层层、描述缺失或互相打架、返回值一股脑倾倒——模型面对一个设计糟糕的接口,表现自然像一个被塞了一堆乱七八糟单据的实习生。工具接口的质量直接决定 Agent 表现的上限,模型能力只是其中一分。下面七条原则,逐条可执行。
七条原则
一、少而精。 功能相近的工具会让模型选择困难:search_orders 与 query_orders 同时存在,模型每次都要掷一次隐形的骰子,掷错一次就白跑一轮。两个工具的职责重叠超过一半,就合并成一个、用参数区分。还要警惕一个常见反模式——按界面拆工具:列表页一个搜索工具、详情页又一个搜索工具。工具应该按「能力」拆,而不是按「入口」拆;每加一个工具,都是在给模型的决策空间增加熵。
二、参数扁平化,必填最少化。 必填参数是模型的负担,也是出错的入口。能用一个字符串说清的,不要要求模型先构造嵌套对象:让模型填 {"query": {"filter": {"status": "paid"}}} 这样的三层结构,远不如直接给它 {"status": "paid"}。可选参数一律给默认值,schema 里能省的层级能省则省。
三、描述写给模型看,不是写给人看。 模型唯一能看到的就是工具的名字、描述与参数 schema——它不知道你的实现,也不知道你的业务背景。描述要说清三件事:这个工具做什么、边界在哪(不处理什么)、什么情况下不该用它。对比一下:「搜索文章的工具」 versus 「搜索站内文章的标题与正文,不支持按作者过滤;要按作者筛选请先调 list_authors」——后者把模型的下一步都安排好了。给一个参数示例,效果远超一堆形容词。判断描述写得好不好有个土办法:把工具列表交给一个没参与开发的同事,只看名字和描述,让他复述每个工具的用途与适用时机——他复述不出来的,模型也读不懂。
四、返回值信息密集且可读。 长输出要截断并说明「共 N 条,已显示前 10 条」,别把上万字符的原始 JSON 砸给模型,既挤占上下文又稀释关键信息。返回值还可以带上「下一步提示」:分页查询返回 next_cursor,模型自然知道怎么翻页。错误信息必须「可行动」:返回 {"error": "invalid_status", "allowed": ["pending", "paid"]},模型立刻知道该怎么改;只返回一个 "failed",模型就只能瞎猜着重试。
五、副作用显式化。 读和写分开成不同工具,写操作(尤其删除、支付、群发)单独成工具,并在参数里要求显式确认项(如 confirm: true)——给模型的「冲动」加一道刹车。会静默修改数据的工具是事故温床,也让模型无法判断哪些调用是安全的。
六、幂等与超时。 网络抖动之后,模型或框架会重试,工具必须能被安全重试:让调用方带上自己生成的请求标识,服务端据此去重,重试才不会变成重复下单。长任务不要让模型同步干等——立即返回一个任务句柄 task_id,另配一个查询进度的工具,把「提交」与「查询」解耦,模型的等待循环就有了明确节奏。
七、正交覆盖。 工具集合要覆盖任务的完整动作面:如果模型必须「先导出、再手工换算」才能完成一件本可以直接完成的事,说明工具面有洞。反过来也要定期用真实任务回归:有没有组合半天也做不到的事?有没有从上线起就没人调用的死工具?正交(彼此不重叠)且覆盖(合起来够用),是工具集的两个验收标准。
一句话看懂 MCP 的位置
MCP(Model Context Protocol,把工具标准化暴露给模型的开放协议)解决的是「怎么把工具递给模型」;而工具好不好用,取决于你在 schema 与描述里写了什么——协议是管道,设计是水质。换了协议,这七条原则一条都不会失效。
收尾
把每个工具当成一个 API 产品来做,目标用户是一个不太聪明、但极其认真照文档办事的实习生:文档里没写的它不会做,文档里含糊的它一定做错。名字自解释、参数最小、描述讲清边界、错误可行动、危险操作要确认、重试安全、集合正交覆盖——七条做完,很多「模型不行」会变成「模型其实挺行」。
读者留言
COMMENTS 暂无还没有留言,来说第一句?