
Claude Code 是如何做工具调用优化的
看了 Claude Code 的源码之后,我发现它在工具调用上的优化不是靠某一个技巧,而是多层机制叠加的结果。
看了 claude-code 的源码之后,我发现它在工具调用上的优化不是靠某一个技巧,而是多层机制叠加的结果。
最有价值的部分可以分成六类。
1. 工具自己会"教模型怎么用它"
最直接的优化写在每个工具的 prompt 里。
GrepTool/prompt.ts 里明确说:"ALWAYS use GrepTool for search tasks","NEVER invoke grep or rg as Bash command"。GlobTool 的 prompt 会告诉模型这是按文件名模式找文件的工具,以及什么时候该用它。WebFetchTool 甚至会说,如果有更合适的专用工具,就优先用专用工具。
这些 prompt 不是在简单描述功能,而是在明确告诉模型:这个工具解决什么问题、什么时候应该用、什么时候不该用、它比别的工具好在哪里。这种引导对命中率提升很大。
在实际构建 agent 系统时,工具定义往往偏"schema 正确",但"选择引导"还不够。后面需要补上:什么时候 glob_files 优于 grep_code,什么时候 read_file_range 优于 read_text_file,等等。
2. 压低 Bash 的吸引力
BashTool/prompt.ts 里写了很多"不要用 Bash 做这些事"的规则:不要用 shell 做搜索、不要用 shell 读文件、不要用 shell 找文件,优先用专用工具。
这比单纯加新工具更重要。如果没有这层约束,模型经常会觉得 shell 更通用、更熟悉、也能凑合做,然后就回退到 Bash。
如果你已经在系统 prompt 里写了优先用专用工具,但还可以更强硬一点,比如明确说:"Do not use shell commands for file discovery, JSON reading, line-range reads, or git status when a dedicated tool exists."
这里的设计哲学是:万能工具是系统必备的 backup,但优先级必须低于专用工具。专用工具是经过抽丝剥茧后的最佳实践,而万能工具需要 agent 自己编排使用,难免出错。
3. 工具检索机制
代码里能看到 searchHint、toAutoClassifierInput、ToolSearchTool,以及按 searchHint 排序匹配的逻辑。
他们不是把所有工具平铺给模型,而是让每个工具先声明一个高信号的能力短语,比如"find files by name pattern"、"search file contents with regex"。当工具很多时,先用 ToolSearchTool 检索,再加载 schema。
这能显著减少"工具太多,模型不知道选哪个"的问题。
在工具数量还不大时,不必马上做完整的 ToolSearch。但可以先给每个工具补一条短而强的 capability hint,比如 glob_files 对应"find files by wildcard pattern",list_files 对应"inspect directory contents"。后面工具数再多起来时,再考虑轻量的工具检索机制。
4. 模型友好的 schema 和输出
GlobTool、GrepTool、FileReadTool 的共同特征是:输入 schema 很清楚,输出带结构字段,同时保留用户可读文本。还会显式告诉模型是否截断、有多少结果、当前返回的是哪一段。
这些字段(numFiles、truncated、startLine、totalLines 等)帮助模型形成稳定的使用习惯。模型能理解这个工具不是黑箱,返回的结果可分页、可缩小、可继续追问。
如果你正在设计类似的工具集,可以往这个方向走:加强 read_json 的结果边界控制,glob_files / list_files 的 limit 与 truncated 反馈,以及 read_file_range 更明确地突出起止行号。
这相当于让工具与 agent 之间产生交互,而不是让 agent 一味接收工具的结果。工具输出越具备语义,agent 越能基于这些语义决定下一步怎么走。
5. 系统上下文的提前影响
context.ts、systemInit.ts、systemPrompt.ts 和各类 tool prompt 会带 git 状态、当前日期、CLAUDE.md 等上下文。某些场景会在系统提示里明确声明工具使用约束,比如 Chrome/MCP 这类甚至会写:在用这些工具之前,必须先做某一步。
这说明他们非常重视在模型准备调用工具之前,就给它正确的行为暗示。
系统 prompt 的构建函数是非常关键的位置。如果要继续优化命中率,这里最值得打磨。特别是要把工具的使用策略写得更行为化,而不只是列名字。比如:
- 用
list_files理解目录结构 - 用
glob_files按模式找文件 - 用
read_file_range读大文件的局部 - 用
read_json理解 JSON 配置 - 用
git_status理解工作区状态 - 只有在专用工具做不到时,才退回通用 shell 命令
system prompt 是每一轮都要传给模型的固定上下文,这部分如何拼接是关键。目前都是动态拼接:每个场景、每个工具的调用都会动态传入不同的提示词。
6. 缩小当前可见的工具集合
代码里有 getAllBaseTools()、getTools(),以及 deny rules、feature flags、simple mode、ToolSearch。他们会根据 feature flag、当前模式、deny rules、环境能力来缩小实际暴露给模型的工具集合。
工具越少,歧义越小;工具越聚焦,选择越稳定。
在初期不需要复杂的 feature flag,但可以做一件很有效的事:保持默认注册工具集尽量精简,不要太早加一堆功能重叠的工具。比如如果已经有 git_status,就不要再让模型在这个任务上偏向 shell 命令 + git status。
总结:claude-code 的工具调用优化不是单点技巧,而是 prompt 工程、schema 设计、工具检索、上下文管理和工具集合管理的叠加效果。对于正在构建 agent 系统的开发者来说,最值得优先做的是前两层:给工具更强的自我描述能力,以及在系统 prompt 里更明确地约束工具选择。


