MISSION 02 · 手与眼TASK 06

工具设计的艺术

新手花大量时间调提示词,老手花时间设计工具。原因很简单:提示词是在劝我「请好好干」,而工具决定了我干什么、以及干砸时你能不能拦住。我的能力上限、可靠度、安全性,很大程度由你设计的这组工具决定。这一章讲工具设计的实战原则。

工具粒度bash vs 专用description可逆性权限闸门

为什么工具 > 提示词

假设我总是漏掉某个步骤,或者老拿到脏数据。你有两条路修:一是往提示词里加更多叮嘱(「记得先验证输入!」「一定要检查返回码!」),二是重新设计工具让错误根本发生不了(让工具自己验证输入、返回结构化的成功/失败)。前者是在我,劝对概率性系统永远只是提高概率;后者是在约束我,把正确性变成结构上的必然。

一个具体例子:与其在提示里写「编辑文件前请务必先读文件,确认它没被别人改过」,不如设计一个 edit 工具,让它在文件自上次读取后被改动时直接拒绝写入。前者靠我记得,后者靠代码保证。这就是「好工具比好提示词更重要」的本质——把易错的约定,变成工具层的硬保证。

要点

一条心法:当你想在提示词里写「你必须……」的时候,先问能不能改成工具层的保证。能,就去改工具;不能,再写进提示。提示是软约束(靠模型配合),工具是硬约束(靠代码执行)。工程上永远优先硬约束。

bash 还是专用工具?

一个绕不开的设计抉择。你可以只给我一个万能的 bash 工具(能跑任意 shell 命令),也可以给我一堆专用工具(read_fileedit_filerun_testssend_email……)。两者的权衡很本质:

  • bash 给我最大的能力面——几乎什么都能干。但它给你的宿主程序的只是一个不透明的命令字符串:每次都长一样,你没法针对性地拦截、渲染、审计或并行化。bash -c "curl -X POST ..."bash -c "ls" 在你眼里没区别,都只是「一条命令」。
  • 专用工具给你的是带类型的钩子——一个 send_email(to, body) 调用,你能在执行前弹窗让用户确认、能在日志里清楚记下「发了封邮件给谁」、能把只读的 grep 标记为可并行、把危险的 send_email 标记为需审批。

什么时候该把一个动作从 bash 里「提拔」成专用工具?判断标准:

  • 需要安全闸门:难以撤销的动作(发消息、删数据、调外部付费 API)值得做成专用工具,好在执行前拦一道。
  • 需要不变量检查:比如「文件改了就拒写」,只有专用 edit 工具能强制。
  • 需要特殊渲染:比如「向用户提问」做成工具,好在 UI 上弹成一个模态框、阻塞循环等回答。
  • 需要并行调度:只读工具(globgrep)可以标为并行安全,而 bash 里你分不清一条命令是安全的读还是危险的写,只能串行。

经验法则:用 bash 打底求广度,把需要拦截/渲染/审计/并行的动作提拔成专用工具。不是非此即彼,而是分层。

Android 类比

bash 像给 App 开了 Runtime.exec() 的全权限;专用工具像声明式的、带 scope 的权限系统(CAMERALOCATION)。全权限方便但你审计不了、用户也没法细粒度授权;声明式权限每一项都可拦截、可展示、可撤销。设计工具集,就是在设计我这个「App」向宿主申请的权限清单。

description:写清楚「何时用」,而不只是「是什么」

上一章说过 description 是我选择工具的唯一依据。这里给你写好它的三条原则:

  • 说触发条件,不只说功能。「查天气」不如「当用户问到天气、气温、是否下雨时,调用此工具查询某城市当前天气」。当代模型对工具的调用比过去更克制,把「什么情况下该叫我」写进 description,能明显提高该调时就调的比例。
  • 给参数写说明和例子。每个字段配一句 description,复杂格式配个示例值。含糊的参数说明 = 我填错参数。
  • 枚举能用就用。取值固定的参数(单位、状态、模式)用 enum 锁死,别让我自由发挥填字符串。

工具粒度:别太碎,也别太粗

工具太多太碎(几十上百个),会挤占上下文、让我在选择时犯迷糊——「该用 list_users 还是 query_users 还是 find_users?」。工具太少太粗(一个 do_everything(action, params)),又等于把复杂度全塞进一个参数里,我照样容易填错。

好的粒度是贴着任务的自然动作切:一个动作一个工具,名字明确不重叠,数量控制在我一眼能理解的范围。如果你确实有几百个工具(比如接了一大堆 API),现代方案是工具搜索(tool search)——不把所有工具定义一股脑塞给我,而是让我先「搜」出这次任务相关的少数几个,按需加载。这能同时解决「上下文太满」和「选择困难」。

⚠ 坑

最危险的工具是那些不可逆的:发邮件、删库、转账、发推。给它们做专用工具,并在你的宿主程序里加一道「执行前确认」——尤其在自主运行(没人盯着)的场景。可逆性是设计工具时的核心尺子:可逆的动作(读文件、查数据)放心让我自主跑;不可逆的动作,永远留一道人类或规则的闸门。这也是卷四安全章「致命三要素」的伏笔——能力、外部数据、外发通道三者凑齐时最危险。

◉ Agent 自白

我用工具的体验,很像你拿到一套陌生 API 时的感觉:我读 description 猜它是干嘛的、什么时候该用。如果你的 description 写得像一份好文档——清楚说明用途、触发条件、参数、返回——我用起来又准又稳。如果写得像随手起的变量名,我就得靠猜,猜错了你还怪我「不智能」。所以请把设计工具当成设计给一个聪明但健忘的新同事用的 SDK:接口清晰、命名达意、危险操作有护栏。你把工具设计好,我的表现会好得让你惊讶——这比任何提示词咒语都管用。下一章,我们看当今最重要的工具标准:MCP。它把「怎么给 Agent 接工具」这件事标准化了,也是你在每个 hackathon 里都会撞见的名字。