03 · 给它手:工具怎么设计
模型本身什么也做不了。它能干的每一件事,都来自你给的工具。所以工具这一层完全由你控制——也意味着它干错的事,多半也是你给的工具设计得不好。
工具调用是怎么回事
一句话:模型只负责"提出要调什么",真正执行的是你的程序。
① 你告诉模型:手上有这几个工具,各自能干嘛、要传什么参数
② 模型判断:这一步该调 search_docs,参数是 {"query": "定价"}
③ 你的程序执行:真的去搜
④ 结果塞回上下文:模型看到搜索结果
⑤ 模型继续判断下一步第 ② 步就是 Function Calling。注意第 ③ 步——执行权在你手上,这是你能设边界的物理基础(05 章)。
工具描述就是它的说明书
模型靠什么决定调哪个工具?靠工具的名字和描述。 这跟 Skill 的 description 是完全一样的机制——写不好,它就选错、或者根本不选。
一个工具的定义通常包含三样:
| 作用 | 写不好会怎样 | |
|---|---|---|
| 名字 | 标识 | 起名含糊(get_data),它分不清几个工具的差别 |
| 描述 | 决定它什么时候调这个 | 最关键。糊了就乱调或不调 |
| 参数说明 | 每个参数是什么、什么格式 | 糊了它就乱传,或者漏传 |
对照
❌ 名字:search
描述:搜索。
参数:query(字符串)它不知道搜的是什么、搜到的是哪个范围、query 该写关键词还是自然语言。
✅ 名字:search_internal_docs
描述:在公司内部文档库中做全文检索,返回最多 10 条匹配片段及其文档标题和链接。
用于查找内部规范、流程说明、历史方案。**不能搜到公开网页**——
需要公开信息时用 search_web。
参数:
query(字符串):检索关键词,用空格分隔,不要写成完整句子。
例:"报销 差旅 标准"
limit(整数,可选):返回条数,默认 10,最大 50三处改进:说清返回什么、说清不能干什么并指向替代工具、参数给了格式和例子。
写工具描述时,把Skill 篇 04 章那一整套原封不动搬过来:
- 既写"做什么",也写"什么时候用";
- 工具多的时候加排除条款("公开信息用 search_web");
- 用具体的词,别用抽象的词;
- 参数给例子,比给类型有用。
工具描述和 Skill description 是同一件事的两个位置。
工具设计的五条规矩
一、一个工具一件事
❌ manage_file(action, path, content) action 可以是 read / write / delete / move
✅ read_file(path)
write_file(path, content)
delete_file(path)拆开的三个理由:它选错的概率低;你能对 delete 单独加确认(05 章);日志里一眼看出它干了什么。
那个 action 参数版本,日志里全是 manage_file,你得点进去看参数才知道它是读还是删。
二、数量控制在十几个以内
工具太多有两个问题:每个的描述都要常驻上下文,几十个工具就是不小的开销;而且选择困难——相似的工具一多,它选错的概率明显上升。
超过二十个,考虑分组:按任务阶段给不同的工具集,或者用一个"目录型"工具让它先查有哪些工具可用。
三、返回结果要精简,但要够判断
工具返回的东西会原样进上下文。一个返回整页 HTML 的工具,几圈就能把桌子占满(07 章)。
❌ 返回:整个网页的 HTML
✅ 返回:页面标题 + 正文纯文本(截断到 2000 字)+ 原始 URL
截断时注明"(已截断,完整内容见 URL)"但也别精简过头——它得能判断"这次调用成功了没有、要不要换个方式再试"。返回一个空字符串,它不知道是没找到还是出错了。
四、错误要说人话
工具报错时,返回的信息是给模型看的,目的是让它知道下一步该怎么办:
❌ Error: 400
✅ 错误:query 参数不能为空。请提供检索关键词后重试。
❌ Error: not found
✅ 未找到匹配文档。建议:换更宽的关键词,或改用 search_web 查公开信息。第二种写法里那句"建议",能直接省掉它两三圈瞎试。
五、危险的动作,工具层面就拦住
别指望靠提示词说"不要删文件"。 提示词是软约束,工具是硬约束:
- 不想让它删,就别给
delete_file这个工具; - 必须给,就让这个工具返回一个待确认的动作,而不是真的执行——由人点头之后再执行。
这是本章最重要的一条
提示词里的"不许做 X",和"根本没有做 X 的工具",是两种完全不同强度的东西。
能靠不给工具解决的,就不要靠说服解决。
常见的一组工具
大多数 Agent 的工具集是这几类的组合:
| 类别 | 例子 | 风险 |
|---|---|---|
| 读 | 搜索、读文件、查数据库、读网页 | 低。主要风险是读到不该读的(05 章) |
| 写 | 写文件、建记录、发消息 | 中。要能撤回,或者要人确认 |
| 删 / 改 | 删除、覆盖、更新状态 | 高。默认不给,或者必须人确认 |
| 执行 | 跑命令、跑代码、调外部 API | 最高。必须沙箱 + 白名单 |
| 对外 | 发邮件、发消息、提交表单、支付 | 最高。一律人工确认,无例外 |
设计工具集时先做一次分类:把你打算给的工具按上面五类摆一遍。
"高"和"最高"那两类,逐个问一句:"这个真的必须给它吗?"
多数时候答案是:这一步可以由人来做,或者可以让 Agent 只产出一个"待执行清单",由人批量执行。
一个折中做法:先产出计划,再执行
对付高风险动作,有个很实用的模式:
Agent 不直接执行危险动作,而是产出一份「计划文件」:
- 要删哪几个文件
- 要发哪几条消息,发给谁,内容是什么
人看一遍这份计划 → 确认 → 由程序按计划批量执行好处:Agent 的自主性保住了(它自己想清楚该干什么),风险也控住了(真正的动作由人点头)。而且计划是文本,好看、好改、好留档。
这跟 Workflow 篇 07 章"把要判断的和已经没问题的分开列"是同一个思路。
下一步
04 · MCP:一个统一的插头 —— 工具从哪儿来,以及怎么不用每接一个系统就写一遍。