Claude Code源码解析(一):为什么Claude Code系统提示词中需要有tools?
Claude Code每次给发给大模型的提示词中,除开有用户提示词,还有就是系统提示词了,这篇文章来分析一下Claude Code中的系统提示词是如何构成的。
在Claude Code源码prompts.ts中,有一个函数叫做getSystemPrompt,就是用来生成系统提示词的,它接收四个参数:
- tools:本轮对话可用的工具列表
- model:本轮对话所使用的模型,比如 “glm-5.2”
- additionalWorkingDirectories:用户用 --add-dir 添加的额外工作目录
- mcpClients:当前连上的 MCP 服务器客户端列表
这四个都是用来生成系统提示词的。
那么,我们先来分析一下,为什么系统提示词中需要有tools呢?
在Claude Code调用大模型时,会在请求中带上所有工具的定义,比如工具的名字、描述、参数定义,那既然请求中已经有了工具信息,也就是说大模型已经能拿到所有工具的定义了,为什么在生成系统提示词时,还需要工具信息呢?
这是因为,Claude Code需要在系统提示词中,设置工具的调用规则,也就是要在系统提示词中教模型怎么调用工具,规则分为两类:
- 一类是通用规则,不管当前工具列表中有什么工具,都需要遵守此类规则,比如"读文件要用专用工具,别用 cat"、“优先用专用工具而不是 Bash”。
- 一类是动态规则,比如当前工具列表中如果存在任务管理工具,比如TaskCreate工具,那么系统提示词中就会多出一条 “用任务工具拆解任务”。
换句话说,模型光收到工具定义还不够,系统提示词还得在提示词层面告诉它用什么、怎么用、什么优先,而其中一部分要根据这次启用了哪些工具来动态调整,所以需要把 tools 工具信息传进来。
上面说的是原理,下面看看实际代码和真实的提示词长什么样。
tools 进来后,第一步是把它变成一个工具名集合,后面判断「有没有某个工具」就靠它:
const enabledTools = new Set(tools.map(_ => _.name))
然后这个集合传给getUsingYourToolsSection函数,这个函数是用来生成系统提示词中「Using your tools」部分的。
function getUsingYourToolsSection(enabledTools: Set<string>): string {
// 看启用了哪个任务管理工具(动态规则的判断条件)
const taskToolName = [TASK_CREATE_TOOL_NAME, TODO_WRITE_TOOL_NAME].find(n =>
enabledTools.has(n),
)
const providedToolSubitems = [
`To read files use ${FILE_READ_TOOL_NAME} instead of cat, head, tail, or sed`, // 通用规则
`To edit files use ${FILE_EDIT_TOOL_NAME} instead of sed or awk`,
// ...Glob、Grep、Bash 等同样是通用规则
]
const items = [
`Do NOT use the ${BASH_TOOL_NAME} to run commands when a relevant dedicated tool is provided...`, // 通用规则
providedToolSubitems,
taskToolName
? `Break down and manage your work with the ${taskToolName} tool...` // 动态规则的判断,只有启用了任务工具才有这条
: null,
`You can call multiple tools in a single response...`, // 通用规则
].filter(item => item !== null)
return [`# Using your tools`, ...prependBullets(items)].join(`\n`)
}
这段代码实际跑出来,在系统提示词里就是这个样子(先没有传任务工具):
# Using your tools
- Do NOT use the Bash to run commands when a relevant dedicated tool is provided. Using dedicated tools allows the user to better understand and review your work. This is CRITICAL to assisting the user:
- To read files use Read instead of cat, head, tail, or sed
- To edit files use Edit instead of sed or awk
- To create files use Write instead of cat with heredoc or echo redirection
- To search for files use Glob instead of find or ls
- To search for the content of files, use Grep instead of grep or rg
- Reserve using the Bash exclusively for system commands and terminal operations that require shell execution...
- You can call multiple tools in a single response. If you intend to call multiple tools and there are no dependencies between them, make all independent tool calls in parallel...
都是通用规则。
然后把TaskCreate 工具加上,整份提示词只多了这一行:
- Break down and manage your work with the TaskCreate tool. These tools are helpful for planning your work and helping you track your progress. Mark each task as completed as soon as you are done with the task. Do not batch up multiple tasks before marking them as completed.
这正是代码里 taskToolName ? 'Break down...' : null 判断出来的,动态的。
到这,大家应该就能理解为什么系统提示词中需要有tools了,因为系统提示词中需要设置工具的调用规则,从而需要动态的判断某些工具在没在工具列表里,如果在就动态生成对应的规则,否则就不需要了。
实操:自己导出系统提示词看看
文章里这些「真实提示词」都是用源码自带的 --dump-system-prompt 导出来的,你也可以自己跑,换个条件就能看到提示词哪里变、哪里不变。
第一步:打开 dump 开关。
这个入口被一个编译期开关门控,默认不开。打开 devkit/build.ts,在 features 那行加上 'DUMP_SYSTEM_PROMPT':
features: ['BUDDY', 'DUMP_SYSTEM_PROMPT'],
第二步:重新打包。
bun devkit/build.ts
第三步:导出来。
把提示词重定向到文件,方便等下对比:
bun dist/cli.js --dump-system-prompt > a.txt
打开 a.txt 就是完整的系统提示词,大约 25 KB、200 行出头。
第四步:给 dump 传上工具列表。
上面默认导出的是空工具版本,所以那条动态规则没出现。
--dump-system-prompt 在源码 src/entrypoints/cli.tsx 里调的是 getSystemPrompt([], model),把那个空数组换成带上 TaskCreate 的工具列表就行:
// 改前
const prompt = await getSystemPrompt([], model);
// 改后
const prompt = await getSystemPrompt([{ name: 'TaskCreate' }], model);
改完重新打包、再导出一次,和空工具版 diff:
bun devkit/build.ts
bun dist/cli.js --dump-system-prompt > b.txt
diff a.txt b.txt
这次 diff 就会多出那一行——正是「用 TaskCreate 拆解任务」那条。这下「动态规则」就亲眼看到了。
看完把
features里的'DUMP_SYSTEM_PROMPT'去掉、把 cli.tsx 里那行改回[],再重新打包,恢复默认构建。
系列教程文章在:IT周瑜,欢迎关注。
更多推荐

所有评论(0)