Claude Code 架构全景图
📍 导航指南
这是「从零理解 Claude Code 源码」系列的第零篇——整个系列的地图。根据你的背景,选择合适的阅读路径:
- 🗺️ 全景派? → 第一部分:三种运行模式 + 五大核心模块 - 先建立整体印象
- ⚙️ 机制派? → 第二部分:数据流 + 关键架构判断 - 理解系统如何协作
- 🔧 技术派? → 技术栈速览 - 快速了解用了哪些技术
目录
第一部分:系统结构 🗺️
第二部分:运行机制 ⚙️
补充
附录
引言
用户在终端输入一句话:
帮我把这个函数重构一下
Claude Code 是怎么读文件、写代码、执行命令的?
答案不在某一个文件里。它分散在 src/query.ts、src/tools/、src/QueryEngine.ts、src/bootstrap/state.ts 等几十个文件的协作中。
这篇文章是整个系列的地图。读完之后,你会知道这个系统由哪些部分组成、它们怎么协作、以及后续每篇文章在讲哪一块。
第一部分:系统结构 🗺️
三种运行模式
Claude Code 不只是一个终端工具。它有三种运行模式,面向不同场景:
| 模式 | 入口 | 典型场景 |
|---|---|---|
| Interactive REPL | src/replLauncher.tsx → src/screens/REPL.tsx | 开发者日常使用,有终端 UI |
| Headless / SDK | --print flag,stream-json 输出 | CI 流水线、自动化脚本 |
| Remote / CCR | src/bridge/ — WebSocket 远程会话 | claude.ai/code 背后的机制 |
三种模式的关键在于:它们共享同一套 query / tool / message 基础设施。无论你是在终端交互,还是在 CI 里跑自动化,底层走的是同一个 query.ts 主循环。
五大核心模块
整个系统可以拆成五个模块:
1. QUERY LOOP — 对话核心
源码:src/QueryEngine.ts(会话对象)、src/query.ts(主循环)
这是整个系统的心脏。query.ts 是一个 AsyncGenerator,每次调用 Anthropic API,拿到响应后决定:继续调用工具,还是把最终答案 yield 给上层。所有推理和工具调度都发生在这里。
2. TOOL RUNTIME — 40+ 工具
源码:src/tools.ts(注册中心)、src/tools/(实现)、src/services/tools/toolOrchestration.ts(并发调度)
工具是模型和真实系统之间的桥梁。每个工具实现 Tool.ts 定义的统一协议:
// src/Tool.ts
interface Tool {
call(input, context): AsyncGenerator<ToolProgress, ToolResult>
checkPermissions(input, context): PermissionResult
render(result): React.ReactNode
isReadOnly(): boolean
}
3. 四层扩展体系
源码:src/commands/、src/plugins/、src/services/mcp/、src/skills/
- Commands:
/commit、/review等 slash 命令 - Skills:可复用的提示词模板,通过 SkillTool 调用
- Plugins:本地插件,扩展工具和 Hook
- MCP:Model Context Protocol,接入外部工具服务器
4. AGENT/TASK 子系统
源码:src/tools/AgentTool/、src/Task.ts
Agent 在 Claude Code 里是一级概念。每个子 Agent 有独立的 tool pool、model 配置、权限上下文、可选的 worktree 隔离。
5. 持久化与状态层
源码:src/bootstrap/state.ts、src/utils/sessionStorage.ts、src/memdir/、src/history.ts
会话状态、历史消息、记忆文件、文件快照都在这一层管理。bootstrap/state.ts 是全局状态的单一来源,注释里甚至写着:
// DO NOT ADD MORE STATE HERE - BE JUDICIOUS WITH GLOBAL STATE
模块协作关系
graph TD
REPL[REPL Screen] --> Input[processUserInput]
Input --> QE[QueryEngine]
Input --> LocalCmd[Local Commands]
QE --> QL[Query Loop]
QL --> API[Anthropic API]
QL --> TR[Tool Runtime]
QL --> State[State & Persistence]
subgraph Tools
TR --> Builtin[Built-in Tools]
TR --> Agent[AgentTool / Sub-Agent]
TR --> Skill[SkillTool / Prompts]
TR --> MCP[MCP Tools]
end
subgraph Extensions
Plugins[Plugins]
Plugins -.-> LocalCmd
Plugins -.-> Skill
Plugins -.-> MCP
end
第二部分:运行机制 ⚙️
数据流:一条消息的完整旅程
用户输入
↓
REPL 捕获
↓
processUserInput(解析 Slash 命令与附件)
↓
QueryEngine 组装上下文
(含:会话历史 / MEMORY.md 记忆注入 / 文件快照 / 系统提示词构建)
↓
queryLoop 调用 Anthropic API
↓
API 返回 tool_use
↓
权限检查(User / Hook / Classifier / Bridge)
↓
工具执行(并发调度)
↓
tool_result 回填消息列表
↓
再次调用 API(循环直到任务完成)
↓
Ink 渲染到终端
这个循环是 ReAct 模式的直接实现:
推理(Reasoning)→ 行动(Action)→ 观察(Observation)→ 再推理
直到任务完成。
注意 QueryEngine 的上下文组装步骤:它不只是拼接历史消息,还要注入 MEMORY.md、构建系统提示词、加载文件快照。这一步是首次调用 API 有时较慢的原因之一,011 篇会专门讲上下文压缩如何优化这个过程。
调试视角:关键调用栈
| 阶段 | 关键文件/函数 | 作用 |
|---|---|---|
| 输入捕获 | src/screens/REPL.tsx → useInput | Ink 终端输入监听 |
| 预处理 | src/commands.ts + messageAttachments | 解析 /commit、@file 等 |
| 上下文组装 | src/QueryEngine.ts → buildMessages() | 注入记忆、历史、系统提示词 |
| 主循环 | src/query.ts → *queryLoop() | AsyncGenerator,驱动 ReAct 循环 |
| 工具调度 | src/services/tools/toolOrchestration.ts → callTool() | 并发控制与权限拦截 |
| 渲染 | src/components/ → Ink 组件树 | 流式输出到终端 |
关键架构判断
理解这三个设计决策,能帮你更快读懂源码:
消息驱动
用户输入、模型输出、工具进度、系统事件——全部抽象成 Message。src/types/message.ts 定义了所有消息类型。整个系统是一个消息流,而不是函数调用链。→ 这意味着你可以在任意位置插入一个消息监听器,而不需要修改调用链。
Prompt Cache 优先
工具列表按名称稳定排序,系统提示词用 DYNAMIC_BOUNDARY 分界,目的是最大化 Anthropic API 的 prompt cache 命中率。这是一个对成本和延迟都有直接影响的设计。→ 这意味着阅读源码时,你会发现工具定义列表的排序是固定的,系统提示词的变动极其保守——任何破坏 DYNAMIC_BOUNDARY 的改动都会导致 cache miss,成本和延迟双双飙升。
Agent 是一级概念
子 Agent 不是"调用自己的递归",而是一个独立的执行单元,有自己的 tool pool、model、权限、甚至 git worktree。这让多 Agent 协作成为可能,也是 Claude Code 能处理复杂任务的基础。→ 这意味着你可以给子 Agent 配置完全不同的模型和权限策略,主 Agent 和子 Agent 之间只通过输入/输出通信,互不污染上下文。worktree 隔离是可选的,取决于调用时的配置——不是每个子 Agent 都会创建独立的 git worktree。
技术栈速览 🔧
| 技术 | 用途 |
|---|---|
| Bun | 运行时,替代 Node.js,启动更快,内置 bun:bundle feature flag 机制 |
| TypeScript | 全栈类型安全,.tsx 用于终端 UI 组件 |
| React 组件模型 + Ink 终端渲染引擎 | 用 React 组件模型管理 UI 状态,Ink 负责将组件树渲染到终端(src/replLauncher.tsx 里直接写 JSX) |
| @anthropic-ai/sdk | 流式 SSE 调用,AsyncGenerator 消费响应流 |
| Commander.js | CLI 参数解析(@commander-js/extra-typings 提供类型安全) |
| chalk | 终端颜色输出 |
快速上手与调试指南
如果你想在本地跑起 Claude Code 并调试源码:
- 安装依赖:项目使用 Bun 运行时。
bun install - 本地运行:
bun run dev - 关键断点:建议在
src/query.ts的queryLoop生成器起始处,以及src/services/tools/toolOrchestration.ts的callTool处打断点。 - 环境变量:确保
ANTHROPIC_API_KEY已正确配置。
本系列文章导航
建议阅读顺序:
000(本页,全景图)
↓
001(queryLoop 执行引擎)→ 002(工具系统设计)→ 003-009(各类工具)
↓
010(四层扩展体系)→ 013(系统提示词)
↓
011(上下文压缩)→ 012(状态持久化)→ 014(记忆系统)
↓
015(Hook 系统)→ 016(可观测性)
001 是核心必读,理解 queryLoop 之后其他模块才能串起来。010 是横向扩展,可以按需跳读。
| 编号 | 文章 | 对应模块 |
|---|---|---|
| 001 | queryLoop 执行引擎总览 | QUERY LOOP |
| 002 | 工具系统设计总览 + 权限机制 | TOOL RUNTIME |
| 003 | 普通工具实现(Read/Write/Edit/Todo) | TOOL RUNTIME |
| 004 | plan 工具 | TOOL RUNTIME |
| 005 | askUserQuestion 工具 | TOOL RUNTIME |
| 006 | subagent 工具(AgentTool) | AGENT/TASK |
| 007 | MCP 工具 | 扩展体系 |
| 008 | skill 工具 | 扩展体系 |
| 009 | 多 Agent 协作:task/team/worktree | AGENT/TASK |
| 010 | Commands / Skills / Plugins / MCP 的关系 | 扩展体系 |
| 011 | 五层上下文压缩全解析 | QUERY LOOP |
| 012 | 状态管理与会话持久化 | 持久化层 |
| 013 | 系统提示词的构建与注入 | QUERY LOOP |
| 014 | 记忆系统:MEMORY.md 的实现 | 持久化层 |
| 015 | Hook 系统:三类 Hook 的设计 | 扩展体系 |
| 016 | 日志与链路追踪 | 基础设施 |
系列导航:
- 当前:000 - Claude Code 架构全景图
- 下一篇:[001 - queryLoop 执行引擎总览]
常见问题 FAQ
Q: 这篇文章说的"五大核心模块"是官方划分吗?
A: 不是,是我读源码后归纳的。官方没有公开架构文档。这个划分是为了帮助理解,不是源码里的命名。
Q: query.ts 和 QueryEngine.ts 有什么区别?
A: QueryEngine.ts 是会话对象,管理一次完整对话的上下文、历史、配置;query.ts 是主循环函数,负责单次"调用 API → 处理工具 → 再调用"的迭代。QueryEngine 持有并调用 query。
Q: 为什么用 Bun 而不是 Node.js?
A: 主要是启动速度和内置的 bun:bundle API。Claude Code 用 feature('FLAG_NAME') 做运行时 feature flag,这个机制依赖 Bun 的 bundle 能力,在 Node.js 下无法直接运行。
Q: 为什么终端 UI 用 React?CLI 不是直接打印字符串就行了吗?
A: 简单 CLI 确实可以。但 Claude Code 的终端 UI 有实时流式输出、工具执行进度、多行动态更新等需求。React + Ink 让你用组件模型管理这些状态,比手动操控 ANSI 转义码要可维护得多。
Q: 子 Agent 和主 Agent 共享上下文吗?
A: 不共享。子 Agent 有独立的消息历史和 tool pool,通过 AgentTool 的输入/输出与主 Agent 通信。这是有意的隔离设计,防止子任务污染主上下文。