3634 字
约 12 分钟
17
4.2 工具描述的写法决定了调用成功率
2026-10-25
无标签

4.2 工具描述的写法决定了调用成功率

一句话结论:工具描述不是文档,而是模型的决策依据。它要回答的不是"这个函数做了什么",而是"在什么情况下应该用它、什么情况下不该用"。写错这个,模型的工具选择就会系统性出错。

读这篇之前

需要先读 4.1(Function Calling 是结构化输出)。这一篇是工具设计里投入产出比最高的一篇:改描述比改代码便宜得多,效果却更明显。


一、先看一个失败案例

假设你有一个工具:

name: "search"
description: "搜索"

用户问:"帮我看看这个项目的依赖有没有安全隐患。"

模型的困境:search 搜什么?搜代码?搜网页?搜数据库?"搜索"两个字没有提供任何判断依据。

结果通常有三种:不调用、调用后传错参数、或者调用了一个不该调的工具。

再看一个写得好的:

name: "search_code"
description: "在项目代码库中按关键词搜索源码。适用于:查找某个函数/变量在哪里定义、
             某个字符串出现在哪些文件。不适用于:查找文档说明(用 search_docs)、
             或搜索互联网(用 web_search)。"
parameters:
  query: "要搜索的关键词或标识符,如 getUserById"
  path:  "限定搜索的目录,默认整个项目根目录"

差别不在"详细程度",而在它明确了边界:

要素 作用
做什么 在代码库按关键词搜源码
什么时候用 找定义、找引用
什么时候不用 找文档 → 用别的工具;搜网络 → 用别的工具
参数说明 + 示例 告诉模型怎么填

第三条是最关键、也最少人写的一条。


二、为什么"什么时候不用"如此重要

回到 2.1 的机制:模型在做的是从上下文里检索模式、选最匹配的那个。

当你有多个相似工具时,模型要判断"该选哪个"。如果每个工具的 description 只说"我做什么",那么:

  • 三个工具都描述得像"搜索"
  • 模型只能靠 name 猜
  • 猜错的概率很高

而如果你在每个工具里都写了"什么时候不该用我,该用谁",那么模型有了区分依据。

这本质上是把"工具选择"从推理题变成了检索题——而模型擅长后者(2.1、3.3 反复出现的规律)。


三、工具描述的六个要素模板

一个可靠的工具描述,覆盖六项:

# 要素 写法示例
1 一句话能力 "在项目代码库中按关键词搜索源码"
2 适用场景 "查找函数/变量定义、查找字符串出现位置"
3 不适用场景 + 指名替代 "找文档用 search_docs;搜网络用 web_search"
4 返回什么 "匹配文件路径 + 行号 + 该行内容,最多 50 条"
5 副作用等级 "只读,不修改任何文件"
6 失败情形 "参数为空或路径不存在时返回错误说明"

第 4、5、6 项常被忽略,但它们各自解决一个真实问题:

  • 第 4 项(返回什么):让模型知道"调用后能拿到什么",避免它为了获取不存在的信息反复调用
  • 第 5 项(副作用):让模型知道"这个操作危险不危险",也方便你在工具层做分级(2.5)
  • 第 6 项(失败情形):提前告诉它失败长什么样,减少把错误结果当成功用的情况

一个完整写法

name: "update_user_status"
description: |
  修改指定用户的状态(启用/禁用)。

  适用:用户要求启用、禁用、冻结某个账号。
  不适用:修改用户资料(昵称、头像)请用 update_user_profile。

  影响:写操作,会立即生效并记录审计日志。
  返回:成功时返回用户 ID 与新状态;用户不存在返回 error。
  注意:禁用操作需要用户明确指定目标用户,禁止批量禁用。
parameters:
  user_id: "目标用户 ID(数字)"
  status:  "目标状态,只能是 active 或 disabled"

这份描述里,.parameters.status 用了枚举(只能是两个值)——这是 4.3 要展开的关键技巧。


四、四个具体的写法技巧

技巧一:参数用枚举,不要用自由文本

差:action: "要执行的操作"
好:action: "操作类型,只能是 create / update / delete 之一"

自由文本意味着无穷多的可能性,模型可能自创一个不存在的操作。枚举把它限制在有限集合里,既提升准确率,又天然防越权(第 14 章)。

技巧二:给示例值,不要只给类型

差:city: "城市名称,string"
好:city: "城市名称,如:北京、上海"

原因同 4.1:示例是可检索的模式。

技巧三:把"必须"和"可选"分开写清楚

差:parameters 里没有 required 字段
好:required: ["user_id"],并在描述里说明 status 默认值

漏写 required,模型会以为都可以省,于是关键参数缺失。

技巧四:长度要够,但不要变成手册

一个反直觉的实测规律:工具描述太短(一两个词)会明显降低准确率,但超过一定长度后收益就饱和了。

太短:「搜索」                    → 模型无法判断
合适:一句话能力 + 场景 + 边界    → 最优
太长:500 字的产品说明书          → 占上下文、无额外收益(2.2)

判断标准:让一个不了解你系统的同事读这段描述,他能不能说清"什么情况下该用、什么情况下不该用"。能,就够长了。


五、工具命名也在影响选择

命名不是风格问题,是准确率问题:

差 好 原因
search search_code / search_docs / web_search 名字自带边界
run run_python / run_tests / run_shell 区分执行对象
get get_weather / get_user_info 区分获取什么
do create_user / update_user / delete_user 动词前缀暴露副作用等级

最后一条特别有用:用动词前缀让副作用等级"写在名字里"。

read_*   / get_*  / list_*    → 只读
create_* / update_*           → 写入
delete_* / drop_* / deploy_*  → 危险

这样即便模型没仔细读 description,它也能从名字上感知风险。而你的工具层也可以按前缀做权限分级(2.5 第五节)。


六、一个常见反模式:工具集"大而全"

差的设计:
execute(sql)                        // 万能,无法校验
manage_user(action, data)           // 万能,参数组合爆炸
exec_command(cmd)                   // 万能,等于交出 shell

好的设计:
query_readonly(sql)                 // 只读,可静态校验
create_user(name, email, role)      // 参数明确,可枚举 role
list_users(page, size)              // 无副作用
disable_user(user_id)               // 单一动作,可审批

大而全的工具看起来"灵活",实际上带来了三个问题:

问题 后果
参数组合空间巨大 模型容易传错,校验困难
无法分级副作用 一个工具既读又写,无法单独审批
描述无法写清边界 什么都"能"做,等于什么都说不清

这条原则在 2.5 和后面 4.5 都会再出现:宁可多几个明确的小工具,不要一个模糊的大工具。


七、常见误区

误区一:工具描述是给人看的文档。
它的第一读者是模型。写给人看的详细文档可以另做。

误区二:描述越短越好,省 Token。
太短会让模型无法判断。描述是必要成本,不是浪费。

误区三:只写"我做什么"就够了。
不够。多个相似工具时,"什么时候不该用我"才是区分依据。

误区四:名字随便起,反正有描述。
名字是模型最先扫描的信号,且直接影响你的权限分级设计。

误区五:一个工具能覆盖越多场景越好。
参数空间越大,出错率越高,越难校验。

误区六:描述写好了就不管了。
工具是演进的,描述的边界要随实现同步更新。描述和实现不一致,比没有描述更危险。


八、动手:把一段烂描述改成好描述

改写前

name: "search"
description: "搜索"

模型能做什么? 只能猜。结果通常是:不调用,或者调用后传错参数。

改写后

name: "search_code"

description: |
  在项目源码中按关键词搜索。

  适用于:
  - 查找某个函数/类/变量在哪里定义
  - 查找某个字符串出现在哪些文件

  不适用于:
  - 查找文档说明(用 search_docs)
  - 搜索互联网(用 web_search)
  - 按文件路径读内容(用 read_file)

  只读操作,不修改任何文件。
  返回:匹配的路径 + 行号 + 该行内容,最多 50 条。
  若无匹配,返回空列表并说明。

parameters:
  query:
    type: string
    description: "搜索关键词或标识符,如 getUserById、TOKEN_EXPIRED"
  path:
    type: string
    description: "限定搜索目录,默认项目根目录,如 src/services"

七个改动点

# 从 到
1 search search_code(名字自带边界)
2 无 适用场景
3 无 不适用场景 + 指名替代
4 无 副作用声明(只读)
5 无 返回什么 + 数量上限
6 无 无结果时返回什么
7 参数无说明 参数带示例值

第 3 条最关键——它把"选哪个工具"从推理题变成检索题(见本篇第二节)。

一个检查方法

把你的工具描述给一个不了解这个项目的人看,问他:

  • "什么情况下你会用这个?"
  • "什么情况下你不会?"

两个都答不出来,说明描述还不够。

一个反例:描述写太长也会失效

差:一个工具的描述写了 800 字,包含实现细节、历史沿革、参数逐个举例

问题:模型需要的是"什么时候用",不是"它内部怎么实现"。

判断标准:描述里每一句都应该能回答"这影响我用不用它吗"。不能的,删掉。


九、一个容易忽略的点:描述里的"否定"要谨慎

工具描述里写"不要做什么",有时会起反效果。

风险写法:「不要用这个工具删除文件」
问题:模型可能把"删除文件"当成使用场景之一

更安全的写法:

好:「本工具只读。删除请用 delete_file(需审批)。」

原理:模型对"否定"的把握不如"肯定"(2.3 幻觉研究中的常见现象)。指向替代工具,比单纯禁止更有效。

三条描述写作规则

规则 好 差
用肯定句描述边界 "只读" "不要写"
指名替代工具 "搜索文档用 search_docs" "不要用它搜文档"
描述副作用 "会通知仓库成员" "注意有影响"

十、一个实战总结:描述写不好的三种典型后果

后果 表现 根因
该用不用 模型自己瞎猜而不调工具 描述没写清"何时用"
不该用硬用 简单问题也去调工具 描述太泛
用另一个 调了相似但错的工具 缺"不适用时用谁"

三种后果都指向同一件事:描述里缺"边界"。

一个修法模板

【做什么】一句话
【何时用】2-3 个具体场景
【何时不用】+ 指名替代工具   ← 最关键
【副作用】只读 / 写 / 不可逆
【返回什么】结构 + 数量上限
【失败时】返回什么

六个小节,每次都写全。 写顺了之后,它比"两句话描述"只多花三分钟,但省掉大量调试。


十一、一个补充:描述长度的实测建议

工具类型 建议描述长度 理由
单一动作、名字自解释 1-2 句 多了是噪声
与别的工具有重叠 必须写清边界(3-5 句) 否则误选
副作用不明确 必须写副作用 影响审批
参数复杂 每个参数一句 减少参数错

没有统一字数,只有统一标准:

让读者能判断"何时用、何时不用、用完会怎样"。

满足这三条就够,多写的都是噪声(2.2 的信噪比)。


十二、一句话收尾

工具描述是"写给模型的说明书",不是"写给同事的文档"。

写文档时可以假设读者会通读、会查资料;写工具描述时只能假设模型只读这一遍,就决定用不用、怎么填参数。

这一个假设差异,决定了所有写法细节。


十三、下一篇

描述解决了"该不该用、用哪个"。下一个问题是:模型真的调用了,但参数传错了怎么办?

这看起来是个小问题,实际上是 Agent 稳定性的主要来源之一。下一篇讲参数校验与失败回填——包括怎么让模型自己把错误改过来。

4.2 工具描述的写法决定了调用成功率
https://clxhxhhr.top/posts/4883/
作者
clxstart
发布于
2026-10-25
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。