一个 search_hotels 工具要在 schema 里放下 location、check_in、check_out、max_price 四个字段。这个定义看起来很平常,但粒度、描述、参数约束、返回格式、错误语义五个决策其实已经全部埋在里面。CSDN 上署名 sinat_41617212 的一篇文章正是从这个角度切入,它的核心判断是:实际工程中约 80% 的 Agent 调用错误不是因为模型不够聪明,而是工具定义本身有问题。
需要先说清楚一点:这是一篇工程社区文章,不是任何厂商或规范的官方文档。文中的例子、数量和阈值都是作者的经验主张,不能当作经过验证的官方结论。下文会尽量区分哪些是文章原本的论述,哪些是进一步的分析。
粒度:两个方向的失败
文章把粒度错误分成两类。
太粗的典型是“万能工具综合症”。它举的例子是 database_tool——把增删改查甚至 drop_table 都塞进一个函数,用 operation 参数区分。后果有四个:描述变得模糊、参数发生耦合、权限无法单独收窄、错误处理逻辑混杂。其中权限问题最容易被低估:当读和写共用一个入口,调用方一旦拿到工具权限,就等于同时拿到了删除能力。
太细则表现为“工具爆炸”。文章举的数字是:20 个端点定义出 20 个工具,模型的候选空间过大。它同时给出一个阈值判断——工具数量超过 15–20 个时,多数 LLM 的选择准确率显著下降。
这个阈值是文章的经验主张,没有给出测试方法、模型清单或数据来源,因此只能当作调参起点而非结论。更稳妥的做法是在自己的场景里做对照实验:固定任务集,逐步增加工具数量,观察选择准确率的变化曲线,而不是直接照搬 15–20 这个数字。
文章给出的四条黄金法则是:一个工具做一件事;工具之间保持正交,能合并就合并;数量控制在 5–15 个;按业务领域分组。正例是把 20 个端点收敛成三个:
- search_users(query, field="all", limit)
- create_user(name, email, 可选 phone/address/profile)
- update_user(user_id, updates)
从工程角度看,这个收敛的真正价值不只是减少数量,而是让权限边界和错误语义跟着一起收敛——查询工具天然只读,创建和更新可以各自挂不同的校验与审计。
描述决定选择,而不是函数名
文章有一个明确的判断:description 是 Agent 选择工具的唯一依据,函数名、参数名、返回类型都不是。这个说法在措辞上偏绝对,但它指出了实践中最常见的疏忽——人靠函数名理解,模型靠描述理解。
它把好的描述拆成三个问题:做什么(What)、什么时候用(When)、什么时候不用(When NOT)。反例是只写一行 “Send an email”;正例是明确写出“用户明确要求发邮件时使用,聊天消息、SMS、通知请改用 send_message”。
When NOT 这一项在实践中最容易被省略,但它往往比 When 更有效。原因很直接:工具之间的混淆通常发生在语义相邻的场景,显式写出排除条件,等于在描述层面预先做了一次消歧。
参数、返回与错误
文章在参数维度上讨论的是类型选择、必选与可选的判断框架、默认值策略、嵌套与扁平结构。它给出的反面案例是一个叫 api_call 的工具:description 只写 “Call the API”,endpoint 没有合法值约束,body 被设为必选——但 GET 请求根本不需要 body。
这个例子里至少有三层问题,值得分开看:
- 描述缺失,模型无法在候选工具中做出判断;
- endpoint 缺少枚举约束,等于把合法值校验推给了运行时;
- 必选 / 可选划分与 HTTP 语义冲突,会让模型在 GET 场景下被迫编造一个 body。
第三条属于硬性错误,前两条属于可修复的设计缺陷。
返回格式方面,文章的主张是结构化返回优于非结构化返回,且返回格式应当稳定一致。这一点在工程上比听起来更重要:如果同一工具在不同条件下返回结构不同的对象,调用方的解析逻辑和模型的理解都会随之波动。
错误返回方面,文章强调要给 Agent 可操作的信息,并区分可重试与不可重试信号。这里真正值得展开的是“可操作”的含义——返回“操作失败”对模型没有任何指导价值,返回“缺少 user_id,请先调用 search_users 获取”才可能触发有意义的下一步。错误信息实际上是给模型的第二个提示词。
工具组合
文章把工具关系分成两种:正交工具是理想设计,重叠工具是灾难,并主张做正交性检查。
正交性检查可以落成一个具体动作:列出所有工具及其负责的实体与操作,逐对检查是否存在同时满足两个工具描述的任务。如果存在,说明描述或粒度需要调整。
边界
最后需要重申文章没有回答的问题:15–20 这个阈值在什么模型、什么任务分布下成立,文章没有说明;80% 这个比例来自何处,也没有给出统计口径;六个维度之间出现冲突时如何取舍,同样没有展开。
从工程角度看,这篇文章的价值不在于它给出的数字,而在于它把工具定义从“随手写的 schema”提升为需要逐项审查的设计对象。可以把它当作一份检查清单的起点:粒度是否单一、描述是否覆盖 What/When/When NOT、参数约束是否完备、返回结构是否稳定、错误是否可操作、工具之间是否正交。至于每个维度上的具体阈值,需要在各自场景里实测。