Skip to content

第 5 章:工具不是外挂,而是 Agent 的手和脚

本章目标:讲清工具层在 Agent 系统中的真实地位,解释为什么“接了工具”不等于“做好了工具系统”。
本章对应总纲:docs/ebook-outline.md 中“第 5 章正文写作提纲(2026-03-31 归档)”。


5.1 为什么工具层决定 Agent 能不能真正做事

这一章要拆掉的误解也很常见:

只要接上几个 API 或 function calling,工具层就算做完了。

这也是错的。

如果说模型负责想,那工具就负责把想法变成动作。

没有工具,系统只能:

  • 给建议
  • 写说明
  • 列计划
  • 模拟执行

有了工具,系统才开始能:

  • 读文件
  • 搜索代码
  • 修改内容
  • 运行检查
  • 请求外部服务
  • 调起其他执行单元

所以工具层真正带来的,不是“功能变多”,而是:

系统第一次具备了干预外部世界的能力。

这就是为什么工具是 Agent 和普通对话系统之间第一道很硬的分水岭。


5.2 但“接上工具”本身,其实很廉价

现在很多系统喜欢宣传:

  • 支持 function calling
  • 支持外部 API
  • 支持工具调用
  • 支持自动执行

这些都不稀奇。

真正难的不是“能不能接工具”,而是下面这些事:

  • 模型知不知道什么时候该用哪个工具
  • 工具描述是否清楚到足以被正确使用
  • 工具输出是否稳定到足以进入下一轮判断
  • 工具之间是否有清晰边界
  • 风险动作有没有权限控制
  • 失败结果能不能变成有用反馈

也就是说,工具层的难点不在接线,而在设计。


5.3 一个好工具系统,至少要回答五个问题

设计工具层时,最少要把五件事说清楚:

  1. 能做什么
    工具能力范围是什么?

  2. 什么时候用
    在什么场景下它是合适的选择?

  3. 怎么调用
    输入参数、调用格式、前置条件是什么?

  4. 返回什么
    输出是文本、结构化数据、状态信号,还是副作用结果?

  5. 风险是什么
    这个工具会不会改外部状态?是否需要确认?

如果这五件事说不清,工具系统大概率会烂。

因为模型不是靠读心术调用工具,它只能根据描述和上下文去猜。


5.4 工具描述为什么比很多人想得更重要

5.4.1 模型是通过描述理解工具,不是通过源代码理解工具

这是一个常被忽略的现实。

在运行时,模型并不是先读工具实现源码,再决定怎么用。它通常先接触的是工具的接口定义和说明信息。

所以工具描述如果写得差,就会直接导致:

  • 该用的时候不用
  • 不该用的时候乱用
  • 参数填错
  • 输出解释错
  • 明明有更合适的工具,却选了更笨的路径

5.4.2 好工具描述的标准是什么

一个好工具描述至少应该做到:

  • 能力边界明确
  • 输入输出明确
  • 适用场景明确
  • 不该怎么用也明确

最糟糕的写法就是那种“万能型描述”:

  • 可以做很多事情
  • 适合多种场景
  • 灵活处理各类任务

这种描述看起来很高级,实际上等于没说。

好系统不靠模糊描述装聪明,而靠清晰边界减少误判。


5.5 工具选择为什么是 Agent 里最容易做烂的一步

在真实任务里,很多失败并不是模型完全不懂任务,而是选错了动作。

比如明明应该:

  • 先读文件
  • 再定位定义
  • 再做局部修改
  • 最后跑检查

结果系统却:

  • 一上来就直接改
  • 或者先跑了不必要的大命令
  • 或者重复搜索已经知道的内容
  • 或者用高风险工具去做低风险任务

这些问题本质上都不是“不会做”,而是“不会选”。

而工具选择一旦错了,后面整条链都会跟着歪。

所以在 Agent 里,工具选择其实就是一种动作层决策能力。


5.6 工具不是越多越好,动作空间膨胀会把系统拖死

很多人设计 Agent 的第一反应,就是尽可能给它更多工具。

表面上看,这像是在增强系统;实际上常常是在制造混乱。

因为工具越多,就意味着:

  • 候选动作越多
  • 判断成本越高
  • 误用概率越高
  • 重叠能力越多
  • 系统行为越不稳定

最后就会出现典型垃圾味:

  • 明明三步能做完,系统兜了一大圈
  • 同样任务每次走不同路径
  • 一点简单活,调用一堆没必要的能力

所以工具系统的目标不是“覆盖所有可能性”,而是:

为当前任务提供最小但足够的动作集合。

这才叫好品味。


5.7 工具层必须有边界,不然早晚出事故

工具一旦具备真实副作用,边界问题就不能再装死。

至少要区分三类工具:

5.7.1 只读工具

例如:

  • 读文件
  • 搜索内容
  • 查定义
  • 看网页内容

这类工具风险相对低,主要问题是信息质量和范围控制。

5.7.2 可写工具

例如:

  • 改文件
  • 写文件
  • 编辑配置
  • 更新状态记录

这类工具开始改变系统状态,必须控制修改范围。

5.7.3 高风险工具

例如:

  • 执行 shell 命令
  • 推送远端
  • 删除文件
  • 修改外部服务
  • 触发共享基础设施动作

这类工具如果没有确认、审计或权限限制,系统迟早翻车。

所以一个成熟 Agent 的工具层,绝不只是能力列表,而是能力列表加风险分级

再往前走一步,这里其实已经能看到第 12 章要集中展开的那条母线:工具权限、确认策略和可见能力,本质上都属于运行时控制面的一部分;只是这一章先从动作接口层看它。


5.8 为什么工具输出也必须被设计,而不是随便返回一坨文本

工具设计里最容易偷懒的地方,是输出。

很多人觉得只要工具能返回结果就行。错。

如果工具输出:

  • 太乱
  • 太长
  • 没结构
  • 没重点
  • 信号和噪音混在一起

那后续模型判断就会明显变差。

5.8.1 好输出的标准

一个好工具输出,至少应该满足:

  • 结果边界清楚
  • 关键字段稳定
  • 错误信号可识别
  • 便于下一轮决策接入

比如:

  • 成功还是失败要清楚
  • 返回的是路径列表还是全文内容要清楚
  • 错误原因要能被后续判断使用
  • 不能让模型还得先猜“这段输出到底是什么意思”

5.8.2 为什么反馈质量直接受工具输出影响

闭环里,反馈不是凭空产生的。它很多时候就是工具输出的再解释。

如果工具输出质量差,反馈层就会失真;反馈层一失真,后面整轮判断都会变垃圾。

所以工具输出其实是闭环质量的一部分,不是实现细节。


5.10 用 Claude Code 看一个现实中的工具分层样本

Claude Code 这个案例很适合看工具层,因为它没有把动作能力做成一坨“万能执行器”。

当前环境里,Read、Edit、Write、Grep、Glob、Bash、Agent、AskUserQuestion 这些能力是分开的。这个分法不是文档好看,而是在强迫系统承认几件事:

  • 读和写不是一回事
  • 搜索和执行不是一回事
  • 本地改动和外部访问不是一回事
  • 自动继续和请求用户确认不是一回事

再看 examples/settings/settings-strict.json:1,工具层上面还压了一层显式治理:

  • ask: ["Bash"]
  • deny: ["WebSearch", "WebFetch"]
  • allowManagedPermissionRulesOnly: true
  • allowManagedHooksOnly: true

这里能直接抽出几条非常硬的工具设计规则:

  • 工具要先按动作性质分层,再谈开放范围
  • 高风险工具必须比低风险工具更难触发
  • 能力存在,不等于默认放开
  • 工具治理也要可配置、可审查、可复制,不能靠口头纪律

如果再结合 plugins/README.md:48 的插件结构,你会发现工具层还被放进了更大的系统分工里:

  • commands/ 给触发入口
  • agents/ 组织执行角色
  • hooks/ 在生命周期节点拦截
  • .mcp.json 接入外部能力

这说明好工具系统不是“给模型挂更多 API”,而是让动作接口在整套系统里各就各位。真正成熟的设计思路是:

  • 工具回答“能做什么动作”
  • 命令回答“从哪触发”
  • Hook 回答“在哪拦截”
  • Agent 回答“谁来执行”
  • 配置回答“这次运行放到什么边界”

这才叫工具系统。否则所谓工具层,最后通常只会退化成一堆没有风险分级、没有语义边界、没有治理壳层的裸 API。


5.11 本章小结

这一章真正想讲清的是:工具不是外挂,而是 Agent 的动作接口层;但动作接口层要是没分层、没描述、没风险边界,那它就只是在制造事故入口。

你现在应该记住六件事:

  1. 工具让系统第一次真正具备干预外部世界的能力。
  2. “接上工具”很廉价,真正难的是把工具系统设计清楚。
  3. 好工具至少要说清能力、适用时机、调用方式、返回形式和风险。
  4. 工具选择本身就是 Agent 的动作层决策能力。
  5. 成熟工具层一定包含风险分级,而不是只给能力列表。
  6. 工具层不是孤岛,它必须和入口、Hook、Agent、配置一起形成完整动作结构。

下一章我们继续往下走,把另一个最容易被混掉的三件事拆开:记忆、状态与上下文到底分别是什么。