第 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 一个好工具系统,至少要回答五个问题
设计工具层时,最少要把五件事说清楚:
能做什么
工具能力范围是什么?什么时候用
在什么场景下它是合适的选择?怎么调用
输入参数、调用格式、前置条件是什么?返回什么
输出是文本、结构化数据、状态信号,还是副作用结果?风险是什么
这个工具会不会改外部状态?是否需要确认?
如果这五件事说不清,工具系统大概率会烂。
因为模型不是靠读心术调用工具,它只能根据描述和上下文去猜。
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: trueallowManagedHooksOnly: true
这里能直接抽出几条非常硬的工具设计规则:
- 工具要先按动作性质分层,再谈开放范围
- 高风险工具必须比低风险工具更难触发
- 能力存在,不等于默认放开
- 工具治理也要可配置、可审查、可复制,不能靠口头纪律
如果再结合 plugins/README.md:48 的插件结构,你会发现工具层还被放进了更大的系统分工里:
commands/给触发入口agents/组织执行角色hooks/在生命周期节点拦截.mcp.json接入外部能力
这说明好工具系统不是“给模型挂更多 API”,而是让动作接口在整套系统里各就各位。真正成熟的设计思路是:
- 工具回答“能做什么动作”
- 命令回答“从哪触发”
- Hook 回答“在哪拦截”
- Agent 回答“谁来执行”
- 配置回答“这次运行放到什么边界”
这才叫工具系统。否则所谓工具层,最后通常只会退化成一堆没有风险分级、没有语义边界、没有治理壳层的裸 API。
5.11 本章小结
这一章真正想讲清的是:工具不是外挂,而是 Agent 的动作接口层;但动作接口层要是没分层、没描述、没风险边界,那它就只是在制造事故入口。
你现在应该记住六件事:
- 工具让系统第一次真正具备干预外部世界的能力。
- “接上工具”很廉价,真正难的是把工具系统设计清楚。
- 好工具至少要说清能力、适用时机、调用方式、返回形式和风险。
- 工具选择本身就是 Agent 的动作层决策能力。
- 成熟工具层一定包含风险分级,而不是只给能力列表。
- 工具层不是孤岛,它必须和入口、Hook、Agent、配置一起形成完整动作结构。
下一章我们继续往下走,把另一个最容易被混掉的三件事拆开:记忆、状态与上下文到底分别是什么。