工具系统:Agent 的双手
如果把 Agent Loop 比作 Agent 的心跳,那工具系统就是它的双手。没有手,Agent 只能说话;有了手,Agent 才能动手。

Agent 都有哪些手:内置工具一览
以 Claude Code 为例,我们可以看到一个 Coding Agent 需要哪些工具。按职责,它们可以分为几大类:
文件操作:读取文件、写入文件、编辑文件、编辑 Jupyter 笔记本——这是 Agent 读写代码和配置的基础能力。
系统操作:执行 Shell 命令、按文件名模式搜索、按文件内容搜索——这是 Agent 与操作系统交互的通道。
代理与任务管理:创建子代理、创建/查询/更新/停止任务——这是 Agent 把大任务拆解、委派、追踪的能力。
网络:网页搜索、网页内容抓取——这是 Agent 访问互联网信息的能力。
调度与监控:创建/删除定时任务、监控进程事件——这是 Agent 按计划执行和持续观察的能力。
技能与扩展:执行自定义技能、对接 MCP 协议——这是 Agent 通过插件机制无限扩展的能力。
工具是怎么定义的
以 Claude Code 为例,在 Agent 内部,一个完整的工具定义包括:
| 要素 | 作用 | 说明 |
|---|---|---|
| 名称 | 唯一标识符 | 模型用它来调用工具,比如 Bash、FileRead |
| 描述 | 功能说明 | 告诉模型这个工具是干什么的、什么场景该用它 |
| 参数 Schema | 输入结构 | 定义工具接受的参数——名称、类型、是否必填,相当于一份"调用说明书" |
| 是否启用 ⚡ | 环境判断 | 实时检查当前环境是否允许使用。比如网络搜索工具在断网环境下自动不可用;MCP 工具在对应服务器离线时自动禁用 |
| 是否只读 | 安全标记 | 只读取信息还是会产生修改 |
| 是否破坏性 | 安全标记 | 操作是否不可逆(比如删除文件) |
| 是否并发安全 | 安全标记 | 多个子代理同时使用是否安全 |
| 权限检查 ⚡ | 访问控制 | 每次调用时实时验证。检查用户的权限配置、当前运行模式等,决定放行还是拦截 |
| 是否延迟加载 | 性能优化 | 初始时是否加载,等需要时再发现 |
⚡ 标记为动态要素——每次使用时实时计算,同一个工具在不同环境、不同时刻结果可能不同。其余要素为静态,工具注册时就已确定。
工具过滤管线:谁能上场
不是所有工具在任何时候都能用。工具过滤管线是一个多层筛选机制,决策主体分别是系统、用户、工具自身,确保模型只接触到当前环境允许的工具。

| 层 | 决策者 | 做什么 |
|---|---|---|
| 模式过滤 | 系统 | 根据运行模式决定基础工具集。比如简单模式(--bare)只保留执行命令、读文件、编辑文件 |
| 规则过滤 | 用户 | 移除被用户配置拒绝的工具。比如配置"禁止网络搜索",对应工具直接剔除 |
| 状态检查 | 工具 | 每个工具判断自身当前是否可用。比如,网络搜索工具会检查搜索服务是否配置且可用;语言服务工具会检查语言服务器是否已连接 |
任何一层独立拦截即可移除工具。
Fail-Closed:贯穿管线的设计哲学
Claude Code 设计了偏向保守的安全策略,来提升系统可靠性,主要体现在:
并发安全默认 false。 工具的"是否并发安全"标记注册时就确定了,默认 false,工具必须显式声明安全才能享受并发优化。
拒绝优先于允许。 同时存在 allow 和 deny 规则时,deny 总是优先。
安全检查不可绕过。 即使开启"绕过权限"模式,修改
.git/config、.claude/settings.json等核心文件仍会被拦截。
这种设计的背后是 Fail-Closed 安全哲学——来源于电磁门锁:断电时门锁关闭(fail-closed),而不是打开(fail-open)。核心理念:无法确定安全时,默认拒绝。
但还有一个问题:即使经过过滤,把所有工具描述都塞进提示词,既浪费 token,也让模型难以准确选择,这就有了 TooSearch 按需加载这个设计。
ToolSearch:按需加载
如果把所有工具的定义都塞进每次对话的初始提示里,会有两个问题:一是浪费 token——工具描述占用的空间很大;二是模型在太多工具中反而难以准确选择。延迟加载不仅是性能优化,也是一种"最小暴露"的安全策略——模型看不到的工具,就不会被误调用或滥用。
ToolSearch 是 Claude Code 解决这个问题的方案:延迟加载,或者叫按需加载。

系统把工具分为两类:
立即可用工具:核心工具(执行命令、读写文件、子代理等)在对话开始时就加载,模型可以直接调用
延迟工具:使用频率较低的工具以及所有 MCP 工具,在初始时不加载,交给一个叫
ToolSearch的特殊工具来管理(内置工具需要显式标记才会延迟,而 MCP 工具默认全部延迟,除非明确要求立即加载)
ToolSearch 本身是一个立即可用的工具。 当模型意识到需要某个尚未加载的功能时(比如"我需要搜索网络"),它会调用 ToolSearch,传入关键词。
搜索有三种路径,按优先级依次尝试:
精确匹配。 如果查询恰好等于某个延迟工具的名称(不区分大小写),直接返回,不做任何评分。这是最快的路径。
MCP 前缀匹配。 如果查询以 mcp__ 开头(比如 mcp__slack),直接匹配所有以该前缀开头的 MCP 工具。因为前缀本身已经很明确,不需要评分。
关键词搜索。 前两条路都没命中时,走模糊搜索。系统把查询拆成多个词,分"必需词"和"可选词"——必需词(用 + 前缀标记)必须出现在工具的名称或描述中,不满足的直接淘汰;可选词用来在候选工具中计算匹配分数。
评分考虑三个维度,权重从高到低:
| 匹配维度 | 内置工具 | MCP 工具 | 说明 |
|---|---|---|---|
| 工具名精确匹配 | +10 | +12 | 查询词与工具名完全一致 |
| 工具名部分匹配 | +5 | +6 | 查询词出现在工具名的某个片段中 |
| 搜索提示匹配 | +4 | +4 | 工具开发者手动标注的关键词短语 |
| 描述匹配 | +2 | +2 | 查询词出现在工具描述中 |
MCP 工具在每个维度上都比内置工具权重略高(+1~+2),因为 MCP 工具通常代表外部服务集成(比如 Slack、GitHub),搜索意图更明确,匹配应该优先。
最终按总分降序排列,分数为零的不返回,最多返回 5 个结果。
以查询 +playwright click 为例,候选工具包括 mcp__playwright__browser_click 和 mcp__playwright__browser_navigate 等。playwright 带有 + 前缀,是必需词;click 是可选词。评分流程如下:
- 候选筛选:必需词
playwright必须出现在工具名或描述中,不满足的直接淘汰 - 逐词评分:对每个候选工具,用所有词(必需词 + 可选词)分别匹配各维度,累加得分
mcp__playwright__browser_click:playwright精确匹配工具名片段 → +12,click精确匹配工具名片段 → +12,总分 24mcp__playwright__browser_navigate:playwright精确匹配 → +12,click未命中 → +0,总分 12
- 排序返回:按总分降序排列,
browser_click(24) >browser_navigate(12)
工具经过了过滤筛选和按需加载,终于出现在模型面前。但模型真正发起一次工具调用时,还有最后一道防线——权限管线。那正是下一节的内容。