Claude Code 源码拆解:它其实是一套 Agent Runtime
副标题:从 CLI 外壳、工具系统到 QueryEngine 内核,一口气看懂它怎么跑起来
仓库:https://github.com/freekatz/claude-code-src/tree/main
一开始我也以为,Claude Code 无非就是“把 Claude 塞进终端里”,做成一个能聊天、能执行命令的 CLI。
但把源码真正啃下来之后,我发现这个判断明显低估它了。它的核心根本不是一个聊天壳,而是一套正在成形的 Agent Runtime:
里面有会话内核,有工具协议,有消息流,有长会话治理,甚至已经长出了多代理协作的雏形。所以这篇文章不准备停留在“它能做什么”这种表层介绍,而是想直接把它的源码骨架拆开:
从cli.tsx、main.tsx到QueryEngine.ts、query.ts、tools.ts,看清楚 Claude Code 到底是怎么跑起来的。
先说结论:它的本质不是聊天 UI,而是终端里的 Agent Runtime
如果只看表面,你会觉得这像是一个“命令行聊天工具”:
- 能输入自然语言
- 能显示回复
- 能执行一些工具
- 还能开子代理
但源码真正表达出来的,不是“聊天程序”思路,而是另一件事:
把用户输入,变成一个可持续推进的 Agent 回合(turn);让模型在回合中安全地调用工具、更新状态、写回消息流,并在必要时做压缩、恢复与继续执行。
换句话说,这套系统真正的主线是:
CLI 入口
↓
启动初始化(配置 / 环境 / 鉴权 / 网络 / 遥测)
↓
装配运行时(commands + tools + agents + settings + session)
↓
进入 REPL 或非交互 QueryEngine
↓
query loop
↓
模型输出 assistant / tool_use
↓
工具调度执行
↓
tool_result 回灌消息流
↓
继续下一轮,直到任务完成
图 1:先看全局总图

图注: 这套源码的中心不是 UI,而是 QueryEngine + tool runtime。入口层负责分流和装配,命令层负责用户控制,工具层负责模型行动,基础设施层负责托底。
一、这套源码最重要的设计目标是什么?
我看下来,它主要在解决 5 件事。
1)同一套内核,同时支持 CLI、REPL、SDK、远程/桥接模式
从目录和入口就能看出,这不是只为一个终端聊天壳写的。
它同时要支持:
- 普通 CLI 启动
- 交互式终端 REPL
- 非交互 print / stream-json
- daemon / bridge / remote-control
- direct connect / ssh remote
- MCP server / 资源读取
- 子代理 / 多代理 / worktree / remote agent
所以它必须把“会话执行内核”独立出来,而不是把逻辑写死在 UI 里。
2)工具调用必须是一等公民,而不是外挂
它更像:
- 模型输出是消息流的一部分
tool_use/tool_result是消息流中的正式节点- 工具有 schema、权限、UI、结果格式、并发规则
- 工具调用会反向影响上下文和会话状态
也就是说:
工具系统不是补丁,而是运行时的一部分。
3)权限与安全控制必须嵌进执行路径
比如 BashTool / PowerShellTool / FileEditTool 这些能力,并不是“能执行就行”,而是从设计上就包含:
- schema 校验
- destructive command 检查
- sandbox 决策
- 只读约束
- 自动/手动权限模式
- 是否允许后台运行
4)长会话必须可压缩、可恢复、可持久化
Agent 系统真正麻烦的,从来不是跑一轮,而是:
- 上下文会越积越大
- tool result 会越来越重
- token 会爆
- 中途中断后要恢复
- 子代理和主代理之间还要共享一部分状态
5)启动性能被认真优化过
main.tsx 和 cli.tsx 里有很明显的“启动性能工程”痕迹:
- 提前做 profile checkpoint
- 提前触发 MDM raw read
- 提前做 keychain prefetch
- 大量 feature-gated lazy import
- 能并行的初始化尽量并行
- fast path 尽量不加载重模块
二、别被目录吓到:真正主干其实只有 6 层
第一次看这个仓库,很容易被目录数量劝退。但按职责压缩,其实可以归纳成 6 层:
- Entrypoints
- Runtime Assembly
- Conversation Core
- Command Layer
- Tool Layer
- Infra Layer
这也是为什么你会觉得它目录很多,但真正的运行骨架其实很稳定。
三、Entrypoint 层:决定从哪条启动路径进入
核心文件:
entrypoints/cli.tsxmain.tsxentrypoints/init.ts
这一层负责回答:
- 现在是不是
--version这种 fast path? - 是不是 daemon / bridge / remote-control?
- 是不是非交互 print 模式?
- 是不是要进入 REPL?
- 初始化要做哪些最早期动作?
这一层最值得学的三个点
1. cli.tsx 很薄,但特别重视“分流”
它不是一启动就把整个程序都加载进来,而是:
- 先读参数
- 对 fast path 走最小化导入
- 只有必要时才动态 import 大模块
2. init.ts 只做 runtime bring-up,不做业务逻辑
它主要负责:
- enable configs
- 应用安全环境变量
- 配置 CA / mTLS / proxy
- 预连接 API
- 远程设置 promise 初始化
- scratchpad 初始化
- 清理钩子注册
- Windows shell 处理
3. main.tsx 本质是“总装厂”
它不是简单入口,而是在做 runtime assembly:
- 解析 CLI options
- 准备 settings / model / permissions
- 装配 tools
- 装配 commands
- 装配 agent definitions
- setup cwd / worktree / session
- 决定进入 REPL、resume、ssh、headless query 等不同路径
图 2:启动之后,一次请求到底怎么跑

图注: Claude Code 的核心不是“问一次答一次”,而是一个 可循环的 Agent 回合系统:模型思考 → 发起工具调用 → 工具结果回灌 → 再继续思考,直到任务完成。
四、Command 层:用户显式命令入口
核心文件:
commands.tscommands/*
Command 层不是模型工具,而是面向用户的控制面。
你可以把它理解成:
- Commands:用户直达系统功能的入口
- Tools:模型在推理过程中可调用的能力
这两层必须分开,不然系统很快会混乱。
五、Tool 层:模型可调用能力总线
核心文件:
Tool.tstools.tstools/*
这是整套源码里最关键的一层之一。
Claude Code 的工具系统,不是“命令集合”,而是一层完整的工具协议系统。
每个工具大体都具备这些元素:
- 名称
- 输入 schema
- 输出 schema
- prompt 描述
- 权限检查
- UI 渲染
- 执行逻辑
- 并发规则
- 是否会修改上下文
Tool.ts 更像工具层的接口定义;tools.ts 则负责统一注册、启停裁剪和组合最终 tool pool。
图 3:工具系统与调度图

图注: 工具层不是“很多工具摆在一起”,而是一个典型的 协议 + 注册表 + 实现 三段式设计。而且它已经有了成熟的调度策略:读型工具并发,写型工具串行。
六、为什么 Tool 层是全系统最值钱的地方?
因为 Agent 的关键不在“会聊天”,而在:
模型能不能在正确边界里稳定地行动。
这套代码为此做了几个关键设计:
1)每个工具都是结构化对象,不是裸函数
这带来的好处是:
- 输入输出可校验
- 可自动生成模型可见 prompt
- 可做统一权限处理
- 可做统一 UI 展示
- 可做统一 telemetry
- 可做统一并发调度
2)ToolUseContext 很重,但这是故意的
工具不是一个“独立函数”,而是运行在会话运行时内部的状态节点。
3)不是一律串行,读型工具可以并发
这说明它不是“能跑就行”,而是已经非常在意效率与一致性。
4)工具不仅返回结果,还会影响后续上下文
比如:
- 改 cwd
- 更新 app state
- 注册后台任务
- 追加消息
- 影响后续权限状态
- 修改文件历史 / 快照
所以工具层不是配角,而是 Agent Runtime 的行动协议层。
七、QueryEngine:这才是 Claude Code 的会话内核
核心文件:
QueryEngine.tsquery.tsservices/tools/toolOrchestration.ts
源码里有一句很准确的话:
QueryEngine owns the query lifecycle and session state for a conversation.
翻译成人话就是:
一段会话,不管 UI 怎么变化,真正管理“这一轮 Agent 怎么跑”的,就是 QueryEngine。
它管的事情非常多:cwd、tools、commands、mcpClients、agents、read file cache、system prompt、model、budget、partial messages、abortController、usage、discovered skills、nested memory paths……
这不是“发个请求”的类,而是一个会话回合控制器。
八、真正的主循环在 query.ts
如果要我选“全仓库最重要的一个逻辑文件”,我大概率会选 query.ts。
因为它定义了 Agent 最核心的循环:
- 组装系统提示词 + 用户上下文 + 消息历史
- 发起模型请求
- 流式接收 assistant 输出
- 如有
tool_use,则调度工具执行并生成tool_result - 把结果回填消息流
- 必要时做 compact / snip / recovery
- 满足终止条件后结束本轮
这就是一个标准但工程细节很重的 Agent loop。
九、BashTool 为什么值得单独拿出来讲
核心文件:
tools/BashTool/BashTool.tsx
表面上它只是“执行命令”,但你一读就会发现,它实际上做了很多事情:
- Zod schema 校验
- 背景任务控制
- command parsing
- destructive / read-only / sandbox 规则
- 结果裁剪与存盘
- UI 折叠显示
- 文件历史跟踪
- git 操作跟踪
- 输出预览生成
- image output 适配
- 任务输出路径管理
也就是说:
BashTool 不是 shell wrapper,而是一个安全可控的命令执行子系统。
十、AgentTool:说明这个项目已经不是单代理玩具了
核心文件:
tools/AgentTool/AgentTool.tsx
这个工具的存在本身就说明很多问题。
它意味着:
- 主代理可以派子代理干活
- 子代理可以有自己的 prompt、cwd、mode
- 可以后台运行
- 可以 worktree 隔离
- 可以 remote 运行
- 可以 team / teammate 化
这会带来完全不同层级的系统复杂度:
- 上下文边界
- 子任务生命周期
- 进度上报
- 远程 / worktree 隔离
- 结果回传
- 代理身份与 UI 区分
图 4:AgentTool 多代理结构图

图注: AgentTool 的存在说明 Claude Code 已经不是单代理模型,而是在做子任务拆分、后台执行、worktree 隔离、远程代理和多代理协作。
十一、为什么说它本质上是“消息驱动”
整套系统里,很多看起来复杂的问题,最后都被统一落回到 Message 流:
- user message
- assistant message
- system message
- attachment message
- tool_result
- compact boundary
- interruption message
这意味着它不是“函数拼接型架构”,而是典型的 消息驱动运行时。
这会带来几个很大的好处:
- 持久化更统一
- 恢复更容易
- replay 更自然
- debug 更容易做 transcript
- 各种模式下更容易复用
十二、长会话治理:这才是 Agent 工程真正烧人的地方
真正做过 Agent 产品的人都知道:
- prompt 不难
- 调工具不难
- 难的是跑久了别崩
Claude Code 在这块显然下了很多工夫,所以仓库里有大量和以下问题相关的代码:
- token warning state
- auto compact
- reactive compact
- snip
- context collapse
- tool summary
- transcript 恢复
- session 持久化
这不是“代码臃肿”,而是产品已经进入真实复杂场景。
十三、这套架构为什么看起来大,但其实不乱?
因为它的复杂性主要来自三种“必要复杂度”:
1)产品形态本身就复杂
它不是单一 CLI,而是同时支持:
- 本地交互
- 非交互脚本
- 远程连接
- 后台会话
- 子代理
- MCP
- 技能 / 插件
- 多种 feature gate
2)安全与权限是硬需求,不是点缀
任何能:
- 读文件
- 改文件
- 跑 shell
- 上网
- 开子代理
- 接远程桥接
的系统,安全逻辑都不可能短。
3)长会话恢复和上下文治理非常难
这类代码看起来多,但很多其实都是为了“跑久了仍然可控”。
十四、如果我来给这套设计下定义,我会这么说
claude-code-src的本质,是一个以消息流为中心、以工具协议为行动接口、以 QueryEngine 为回合控制器的终端 Agent Runtime。
拆开说就是:
- 不是聊天 UI 项目,而是 Agent Runtime 项目
- 不是单次 prompt 执行器,而是多回合状态机
- 不是几个工具拼装,而是结构化工具协议系统
- 不是单代理助手,而是已经具备多代理 / 远程 / 任务化能力的平台雏形
十五、如果你准备继续深挖源码,建议按这条路线读
不要一上来就试图把 main.tsx 一口气啃完,太痛苦。
建议顺序:
entrypoints/cli.tsxentrypoints/init.tsmain.tsx(先只看装配主线)QueryEngine.tsquery.tsservices/tools/toolOrchestration.tsTool.tstools.tstools/BashTool/BashTool.tsxtools/AgentTool/AgentTool.tsx
结尾
如果只看表面,claude-code-src 像一个“大而杂的终端项目”。
但看清骨架后,你会发现它其实非常统一:
- 入口层负责分流与装配
- QueryEngine 负责会话生命周期
- query loop 负责 Agent 回合推进
- Tool 层负责行动能力
- Command 层负责用户控制面
- services / utils 负责把这一切撑住
所以最准确的理解不是:
“这是一个命令行版 Claude。”
而是:
这是一个终端里的 Agent Runtime,而 Claude 只是它的智能核心之一。
夜雨聆风