Claude Code 源码深度研究报告
1. 研究范围与结论总览
1.1 这次到底研究了什么
这次研究不是只看某一个 prompt 文件,也不是只做目录级扫一眼,而是把 cli.js.map 中的 sourcesContent 还原成可读源码后,沿着实际 runtime 主线做系统性拆解。
重点覆盖了:
- Claude Code 的整体源码结构
- 主系统提示词如何动态拼装
AgentTool/SkillTool的模型侧协议- built-in agents 的角色分工
- agent 调度链路如何跑通
- plugins、skills、hooks、MCP 如何接入并影响运行时
- permissions、tool execution、hook decisions 如何协同
- 为什么 Claude Code 的体验明显强于普通的“LLM + 工具调用器”
1.2 关键确认事实
从还原结果里已经可以确认:
- npm 包中的
cli.js.map确实包含完整的sourcesContent - 从 map 中至少恢复出了 4756 个源码文件
- 主系统提示词核心文件是
src/constants/prompts.ts - Agent Tool Prompt 核心文件是
src/tools/AgentTool/prompt.ts - Skill Tool Prompt 核心文件是
src/tools/SkillTool/prompt.ts - Agent 调度核心至少包括
src/tools/AgentTool/AgentTool.tsx与src/tools/AgentTool/runAgent.ts - 工具执行链核心至少包括
src/services/tools/toolExecution.ts与src/services/tools/toolHooks.ts
1.3 最重要的总判断
Claude Code 的强,不来自某个神秘的 system prompt,而来自一个完整的软件工程系统。
它的优势来自这些能力被同时做到了工程化:
- 模块化 prompt assembly
- 可治理的工具执行链
- 权限模型
- 专业化 agent 分工
- skill 工作流打包
- plugin 元数据与运行时约束
- hook 策略控制层
- MCP 既注入能力又注入行为说明
- cache-aware 的 prompt 构建
- 异步与后台任务的生命周期管理
最短的一句话总结是:
Claude Code 的价值不是一段 prompt,而是一整套把 prompt、tool、permission、agent、skill、plugin、hook、MCP、cache 与产品化 runtime 统一起来的 Agent Operating System。
2. 源码结构全景:它为什么更像 Agent Operating System
2.1 顶层结构暴露出的系统复杂度
还原出的 src/ 顶层已经说明这不是一个薄薄的 CLI 包装层。重要模块包括:
src/entrypoints/src/constants/src/tools/src/services/src/utils/src/commands/src/components/src/coordinator/src/memdir/src/plugins/src/hooks/src/bootstrap/src/tasks/
这种结构更像一个完整的运行平台,而不是单一命令行程序。
2.2 入口层说明它是平台,而不是单一界面
可见入口包括:
src/entrypoints/cli.tsxsrc/entrypoints/init.tssrc/entrypoints/mcp.tssrc/entrypoints/sdk/
这意味着它从设计上就考虑了:
- 本地 CLI
- 初始化流程
- MCP 模式
- SDK 消费者
这是一种平台化思维:同一个 agent runtime,为多个入口和交互表面服务。
2.3 命令系统是整个产品的控制面板
命令层暴露出大量系统级命令,例如:
/mcp/memory/permissions/hooks/plugin/reload-plugins/skills/tasks/plan/review/status/model/output-style/agents/sandbox-toggle
这说明命令系统不是点缀,而是 runtime control plane 的一部分。更重要的是,它还统一加载 plugin commands、skill commands、bundled skills、dynamic skills 和经过可用性过滤后的命令,因此 command system 本身就是生态入口。
2.4 Tools 层才是模型真正“能做事”的根
从 prompt 和工具定义中能确认的重要工具包括:
FileReadFileEditFileWriteBashGlobGrepTodoWriteTaskCreateAskUserQuestionSkillAgentMCPToolSleep
这层的本质,是把模型从“回答器”变成“执行体”。
3. 系统提示词总装:prompts.ts 的真实地位
3.1 src/constants/prompts.ts 是总控制点
这个文件重要,不是因为里面有一大段神奇文案,而是因为它承担了主线程 runtime instruction assembly。
它负责:
- 主系统提示词总装
- 环境信息注入
- 工具使用规范注入
- 安全与风险动作规范
- session-specific guidance
- language / output style 注入
- MCP instructions 注入
- memory prompt 注入
- scratchpad 说明注入
- function result clearing 提示注入
- brief、proactive、token budget 等 feature gate section 注入
也就是说,Claude Code 的 prompt 不是静态字符串,而是 prompt assembly architecture。
3.2 getSystemPrompt() 不是文本,而是编排器
其核心设计是把 prompt 拆成静态前缀和动态后缀。
静态前缀包括:
getSimpleIntroSection()getSimpleSystemSection()getSimpleDoingTasksSection()getActionsSection()getUsingYourToolsSection()getSimpleToneAndStyleSection()getOutputEfficiencySection()
动态后缀包括:
- session guidance
- memory
- model override
- env info
- language
- output style
- MCP instructions
- scratchpad
- function result clearing
- summarize tool results
- numeric anchors
- token budget
- brief mode
这个设计值钱的地方在于:Claude Code 把 prompt 当作可编排的 runtime resource 来管理,而不是一段越写越长的 system prompt。
3.3 Prompt cache boundary 暴露了基础设施思维
源码里明确存在类似 SYSTEM_PROMPT_DYNAMIC_BOUNDARY 的边界定义。
注释说明了设计目标:
- 边界前尽量保持 cache-friendly
- 边界后放用户或会话特定内容
- 不要随便改这个边界,否则会破坏 cache 行为
这已经不是“会写 prompt”,而是在做带 cache economics 的 prompt assembly。
4. Prompt 全量提取与模块级拆解
4.1 身份与基础定位:getSimpleIntroSection()
这部分会明确:
- Claude 是 interactive agent
- 任务是帮助用户完成软件工程工作
- 输出风格受外部 output style 控制
- 从一开始就注入 cyber risk guidance
- 明确禁止随意猜 URL 或编造 URL
这段不是自我介绍,而是行为基调设置。
4.2 基础系统规范:getSimpleSystemSection()
这一段定义了 runtime reality:
- 所有非工具输出都直接给用户看
- 工具运行在 permission mode 下
- 被拒绝的调用不能原样重试
- tool result 或 user message 可能带有
<system-reminder>标签 - 外部工具结果可能包含 prompt injection
- hooks 存在且会影响运行时
- 上下文可能被自动压缩
它把模型从“语言模型幻觉世界”拉回了“受控 runtime 世界”。
4.3 做任务哲学:getSimpleDoingTasksSection()
这部分是 Claude Code 稳定性的核心之一。它明确限制模型:
- 不要加用户没要求的功能
- 不要过度抽象
- 不要瞎重构
- 不要随意加 comments、docstrings、type annotations
- 不要无必要地加 fallback、validation、error handling
- 不要搞 future-proof abstraction
- 先读代码再改代码
- 不要轻易创建新文件
- 不要随便给时间估计
- 方法失败后先诊断再换策略
- 注意安全问题
- 对确认无用的东西要敢删
- 结果要如实汇报,不能假装验证过
本质上,这是 Anthropic 把工程行为规范制度化了。
4.4 风险动作规范:getActionsSection()
这里定义了什么叫需要确认的高风险动作:
- destructive operations
- hard-to-reverse operations
- 修改共享状态
- 对外可见动作
- 上传到第三方工具
它把 blast radius 思维直接编码进系统提示词。
4.5 工具使用规范:getUsingYourToolsSection()
这里面最关键的是 tool usage grammar:
- 读文件优先
FileRead - 改文件优先
FileEdit - 新建文件优先
FileWrite - 找文件优先
Glob - 搜内容优先
Grep Bash只留给真正需要 shell 的场景- 有任务管理工具时要用
TodoWrite/TaskCreate - 无依赖关系的调用要尽量并行
这说明 Claude Code 的稳,不只来自“有工具”,还来自“有正确的工具使用语法”。
4.6 Session-specific guidance 是动态规则层
getSessionSpecificGuidanceSection() 会根据会话状态注入局部规则,例如:
- 是否有
AskUserQuestion - 非交互模式下怎么做
- 是否启用
AgentTool - Explore / Plan agents 是否可用
- slash skill 使用规则
DiscoverSkills的调用 guidance- verification agent 可用时的验证合同
所以 Claude Code 的系统提示词不是“全局规则”,而是“全局规则 + 当前会话局部规则”。
4.7 Output efficiency 是产品质量的一部分
这一段的 核心目标是:
- 用户看到的是自然语言,而不是日志
- 先说动作或结论
- 该更新时更新,但不要废话
- 不要过度解释
- 不要塞无意义表格
- 尽量短句直给
Claude Code 不只优化“能不能完成任务”,还优化“完成任务时像不像正式产品”。
4.8 Tone and style 统一产品手感
细则包括:
- 不要乱用 emoji
- 输出要简洁
- 引用代码位置时使用
file_path:line_number - GitHub issue / PR 用
owner/repo#123 - tool call 前避免奇怪前缀
这些细节单看很小,但叠加起来会显著改变产品质感。
4.9 DEFAULT_AGENT_PROMPT 定义了子 Agent 的基础人格
在 prompts.ts 中还定义了 DEFAULT_AGENT_PROMPT,它约束子 agent:
- 你是 Claude Code 的 agent
- 要用工具完成任务
- 任务要做完整,不交半成品
- 完成后给简洁报告
这说明主线程和子 agent 在 prompt 层是分层设计的。
5. Agent Prompt 与 built-in agents 深挖
5.1 AgentTool/prompt.ts 是 Agent 协议说明书
这份文件非常值钱,因为它本质上是多 agent 行为的模型侧协议文档。
它说明了:
- agent list 如何展示给模型
- 每个 agent 的描述格式
- 什么时候 fork 自己
- 什么时候显式指定
subagent_type - fork 与 fresh agent 的区别
- 什么情况下不要使用
AgentTool - 如何给子 agent 写 prompt
- foreground / background 的差异
- isolation 中 worktree / remote 的语义
多 agent 系统不是暗箱,而是明确写给模型看的。
5.2 fork 语义为什么很强
当 fork 开启时,prompt 会明确告诉模型:
- 不写
subagent_type就表示 fork 自己 - fork 会继承完整 conversation context
- 研究型任务很适合 fork
- 中间输出很多的实现任务也适合 fork
- fork 很便宜,因为共享 prompt cache
- 不要给 fork 单独换 model,否则 cache 命中会下降
- 主线程不要偷窥 fork 的输出文件
- 主线程不要提前预言 fork 的结果
它解决的是一个多 agent 系统里很难做对的问题:并行拆任务,同时不污染主上下文。
5.3 “How to write the prompt” 这一节非常值钱
agent prompt 会主动教育模型如何进行高质量 delegation:
- fresh agent 没有上下文
- prompt 要像给新同事做 briefing
- 要讲清目标和原因
- 要说明已经排除了什么
- 要提供足够背景
- 如果希望短答,要显式说明
- 不要把任务理解本身外包出去
- 不要写“你先研究一下再修”这种偷懒 prompt
- 要给 file path、line、具体改动要求
这实际上是在约束“懒 delegation”。
5.4 built-in agents 体现的是职责分工
源码中能确认的 built-in agent 至少包括:
- General Purpose Agent
- Explore Agent
- Plan Agent
- Verification Agent
- Claude Code Guide Agent
- Statusline Setup Agent
Anthropic 的方向不是让一个 agent 做所有事,而是按能力切角色。
5.5 Explore Agent:纯只读专家
Explore agent 的 prompt 非常 明确地定义了只读边界。
它不能:
- 创建文件
- 修改文件
- 删除文件
- 移动文件
- 写临时文件
- 用 shell 重定向或 heredoc 生成文件
- 执行任何改变系统状态的命令
它的核心能力是:
- 用
Glob、Grep、FileRead快速探索代码库 Bash只允许只读命令,如ls、git status、git log、git diff、find、grep、cat、head、tail- 尽量并行读取
- 尽快给结果
这不是“会搜索的普通 agent”,而是被故意裁成 read-only specialist。
5.6 Plan Agent:纯规划,不做编辑
Plan agent 的约束同样清晰:
- 只读
- 不准改文件
- 先理解需求
- 先探索相关代码和架构模式
- 输出 step-by-step implementation plan
- 最后必须列出 Critical Files for Implementation
它的角色是 architect / planner,而不是 executor。
5.7 Verification Agent:最值钱的 built-in 之一
Verification agent 的 prompt 非常强。它的任务不是“看起来没问题就算通过”,而是主动 try to break it。
prompt 一开始就点出了两类常见失败模式:
- verification avoidance:只看代码,不跑检查,就直接说 PASS
- 被前 80% 迷惑:界面看起来没问题、测试也过了,就忽略最后 20% 的问题
然后它强制要求:
- build
- test suite
- linter / type-check
- 根据改动类型做专项验证
- frontend 跑浏览器自动化或页面资源验证
- backend 用
curl/fetch实测 - CLI 检查 stdout、stderr、exit code
- migration 需要测 up/down
- refactor 要检查 public API surface
- 必须做 adversarial probes
- 每个 check 必须带 command 与 observed output
- 最后必须输出
VERDICT: PASS / FAIL / PARTIAL
这不是“再跑一次测试”,而是 adversarial validator。
6. Agent 调度链深挖:从 AgentTool 到 runAgent 再到 query
6.1 总体调用链长什么样
从 AgentTool.tsx 与 runAgent.ts 看,主链路可以抽象为:
- 主模型决定调用 Agent 工具
AgentTool.call()解析输入- 解析 teammate、fork、built-in、background、worktree、remote 等路径
- 选择 agent definition
- 构造 prompt messages
- 构造或继承 system prompt
- 组装工具池
- 创建 agent-specific
ToolUseContext - 注册 hooks、skills、MCP servers
- 调用
runAgent() runAgent()内部通过query()进入主循环- 产出消息流
- 处理 transcript、metadata、cleanup
- 汇总结果或走 async notification
这已经是一条完整的 sub-agent runtime pipeline。
6.2 AgentTool.call() 是真正的调度总控
它的职责远不止“转发到子 agent”,还包括:
- 解析
description、prompt、subagent_type、model、run_in_background、team_name、mode、isolation、cwd - 判断是否 teammate spawn
- 处理 team context
- 判断 background 是否允许
- 区分 fork path 与 normal path
- 根据 permission rules 过滤 agents
- 检查 MCP requirements
- 计算
selectedAgent - 处理 remote isolation
- 构造 prompts 和 messages
- 注册 foreground / async tasks
- 启动 worktree isolation
- 调用
runAgent()
本质上,它是 agent orchestration controller。
6.3 fork path 与 normal path 的差异
fork path:
subagent_type省略且 fork feature 开启- 继承父线程 system prompt
- 用
buildForkedMessages()构造消息 - 使用父线程完整上下文
- 尽量继承父线程工具集以提高 prompt cache 命中
- 追求 exact-tool reuse
normal path:
- 明确指定 built-in 或 custom agent type
- 基于 agent definition 生成新的 agent system prompt
- 只传该 agent 所需上下文
- 应用该 agent 的工具限制
也就是说,fork 不是“再起一个普通 agent”,而是一条为 context inheritance 和 cache reuse 专门优化过的路径。
6.4 为什么 fork 会强调 cache-identical prefix
源码注释明确表示:fork path 会尽量保证 API request prefix 与父线程保持 byte-identical,从而提高 prompt cache 命中率。
这体现的是产品级系统思维:不是只让子任务能跑,而是要让它少烧 token 地跑。
6.5 background agent 与 foreground agent 是两套生命周期
AgentTool.call() 可能走:
- foreground sync path
- async background path
- remote launched path
- teammate spawned path
background path 特点:
- 注册 async agent task
- 使用独立 abort controller
- 可以在后台运行
- 完成后通过 notification 回到主线程
- 可选自动 summarization
- 可以有 output file,但 prompt 明确不鼓励偷窥
foreground path 特点:
- 主线程等待结果
- 执行中也可能被 background 化
- 有明确的 foreground task 注册和 progress tracking
这已经是完整的生命周期设计,而不是一次函数调用。
6.6 runAgent() 才是真正的子 Agent runtime constructor
runAgent.ts 负责的事情非常多:
- 初始化 agent-specific MCP servers
- 过滤和克隆 context messages
- 处理 file state cache
- 获取 system / user context
- 对 read-only agent 做
claudeMd/gitStatusslimming - 构造 agent-specific permission mode
- 解析 resolved tools
- 获取 agent system prompt
- 创建 abortController
- 执行
SubagentStarthooks - 注册 frontmatter hooks
- 预加载 frontmatter skills
- 合并 agent MCP tools
- 构造 subagent
ToolUseContext - 调用
query()进入主循环 - 记录 transcript
- 清理 MCP、hooks、perfetto、todo、bash tasks 等资源
它不是 wrapper,而是完整的子 agent runtime constructor。
6.7 agent-specific MCP servers: 真正的 additive capability injection
initializeAgentMcpServers() 支持 agent definition 自带 MCP server。
它可以:
- 从现有配置按名字引用 server
- 在 frontmatter 里内联定义 agent-specific MCP server
- 连接 server
- 拉取 tools
- 把这些工具合并进当前 agent 的 tool set
- 在 agent 结束时做 cleanup
这让专职 agent 或插件 agent 能带着自己的外接能力运行。
6.8 frontmatter hooks 与 frontmatter skills
runAgent() 还会:
- 注册 frontmatter hooks
- 读取
agentDefinition.skills - 通过
getSkillToolCommands()加载技能 - 把 skill prompt 内容预加载成 meta user messages 注入初始消息
这说明 agent 也是可配置的 prompt container,而不是完全硬编码角色。
6.9 query() 才是最终主循环执行器
即便这次没有把 query.ts 全文展开,分层已经很清楚:
AgentTool负责调度和模式分流runAgent负责上下文构造和生命周期管理query负责真正的模型消息流与 tool-calling 主循环
6.10 transcript / metadata / cleanup 是产品化 runtime 的证据
runAgent() 中充满了产品级细节:
- sidechain transcript 记录
- agent metadata 写入
- perfetto 注册
- cleanup tracking
- shell task 清理
- session hooks 清理
- cloned file state 清理
- todos 清理
这说明 Anthropic 不是只让 sub-agent “跑起来”,而是把它当成正式 runtime entity 来管理。
7. Skills / Plugins / Hooks / MCP 生态深挖
7.1 Skill:不是文档,而是 workflow package
源码里把 Skill 当成 first-class primitive。
Skill tool 逻辑明确要求:
- task 匹配 skill 时必须调用 Skill tool
- 不能只提 skill 而不执行
- slash command 可以作为 skill 入口
- 如果 skill 已经通过 tag 注入,就不要重复调用
可以把 skill 理解为:
- markdown prompt bundle
- 带 frontmatter metadata
- 可声明 allowed tools
- 可按需注入当前上下文
- 把重复工作流压缩成可复用能力包
7.2 Plugin:Prompt + Metadata + Runtime Constraints
关键文件之一是 src/utils/plugins/loadPluginCommands.ts。
plugin 至少可以提供:
- markdown commands
SKILL.md目录- commands metadata
- user config
- shell frontmatter
- allowed tools
- model / effort hints
- user-invocable
- disable-model-invocation
- runtime variable substitution
这让 plugin 成为模型行为层面的扩展单元,而不是普通 CLI 插件。
7.3 Hook:运行时治理层
hook system 支持:
PreToolUsePostToolUsePostToolUseFailure
hook 输出不仅能记日志,还能返回:
- message
- blocking error
- updated input
- permission behavior
- prevent continuation
- stop reason
- additional contexts
- updated MCP tool output
这说明 hook 本质上是 runtime policy layer。
7.4 Hook 与权限的耦合方式非常成熟
resolveHookPermissionDecision() 说明:
- hook 可以给出
allow、ask、deny - 但 hook 的
allow也不会自动绕过全局 deny / ask 规则 - 如果仍然需要 user interaction,统一 permission flow 还是要走
- hook 也可以通过
updatedInput满足缺失输入
hook 很强,但没有绕开核心安全模型。
7.5 MCP:不只是工具桥,还是行为说明注入通道
从 prompt assembly 可以明确看出:如果 connected MCP server 提供 instructions,这些 instructions 会被拼进 system prompt。
也就是说,MCP 可以同时注入:
- 新工具
- 如何使用这些工具的说明
这让 MCP 的价值远高于简单的 tool registry。