ClaudeCode 源码深度剖析:从零读懂 Agent 架构与 MVP 最小骨架实现

ClaudeCode 源码深度剖析:从零读懂 Agent 架构与 MVP 最小骨架实现

很多开发者学习 Agent 框架源码时,都会陷入一个误区:直接逐文件啃代码、逐行读逻辑,最终只会陷入零散的函数调用中,看不懂整体架构、摸不清模块依赖,更无法落地复用。

Claude Code 作为 Anthropic 官方的代码智能 Agent,其架构设计简洁、分层清晰、低耦合高可扩展,是入门 Agent 框架开发的绝佳范本。

本文将采用**「架构先行 → 链路贯通 → 源码落地」的实战思路,带大家系统拆解 ClaudeCode 源码:先梳理整体分层架构与核心设计约束,搞懂框架的底层设计思想;再通过真实业务场景贯通完整运行链路,理解模块协作逻辑;最后落地官方 examples/mvp 6 个核心文件,手写最简可运行骨架,真正实现读懂、吃透、可复用**。

一、阅读前置与受众定位

1.1 适合人群

本文面向所有想要入门 Agent 框架底层开发的工程师,尤其适合:

  • 想要从源码层面理解 Claude Code、通用 LLM Agent 架构设计的开发者
  • 自研 Agent 时,频繁在消息流转、多轮循环、工具调用、状态管理环节踩坑,需要标准架构参考的从业者
  • 具备基础 Node.js/TS 能力,想要从零实现轻量化 Agent 骨架的技术学习者

1.2 前置技术储备

阅读本文无需熟悉 Claude Code 底层源码与 Anthropic 接口,仅需掌握基础技术栈:

  • TypeScript 基础语法、联合类型、async/await、异步生成器 async generator
  • Node.js 原生模块:fs/promises 文件读写、readline/promises 终端交互
  • 基础异步编程思想,了解 LLM 对话多轮交互逻辑即可

二、核心架构拆解:六层分层设计与全局约束

优秀框架的核心一定是分层架构与单向依赖 。ClaudeCode 摒弃了混乱的模块耦合设计,将整套系统拆分为六大层级,各层级职责单一、边界清晰,仅与相邻层级通信,彻底规避跨层依赖、循环依赖问题,这也是其可扩展、易维护的核心原因。

2.1 六层架构层级全解析

从用户交互到底层数据持久化,自上而下完整层级职责如下,所有业务逻辑、工具调用、模型交互均围绕该架构运转:

  • 入口层(UI 交互层) :核心文件 index.ts,系统唯一交互入口。负责接收用户终端输入、实时回显运行事件、维护全局会话消息队列,不处理任何业务逻辑与模型调度。
  • 编排层(核心调度层) :核心文件query.ts,全局唯一状态机,是整个 Agent 的大脑。管控多轮对话循环、模型调用调度、工具执行触发、异常容错、事件产出,所有核心流转逻辑均收敛于此。
  • 模型层(LLM 适配层) :核心文件 modelClient.tsfakeModel.tsconfig.ts。封装统一的模型调用入口,通过配置文件实现模拟模型/真实 Anthropic LLM API 无缝切换,彻底解耦业务与具体模型实现。
  • 工具层(能力执行层) :核心文件 tools.ts。统一所有工具的执行入口,内置权限校验、本地 IO 操作、执行结果封装、异常错误兜底,所有工具调用均通过该层统一调度。
  • 消息协议层(数据地基层) :核心文件 messages.ts。定义系统全量消息类型、提供标准化消息工厂函数,是所有模块的数据依赖基础,全局统一消息格式,保证流转一致性。
  • 持久化层(扩展能力层) :核心文件 compress.tssession.ts,可选扩展层级。实现对话历史压缩、会话数据保存与恢复,MVP 最小骨架可舍弃,不影响核心运行。

2.2 四条不可突破的架构硬约束

分层只是表象,真正支撑框架稳定性和扩展性的是依赖约束规则。这 4 条核心原则是 ClaudeCode 架构设计的精髓,所有源码开发、二次迭代都必须严格遵守,一旦打破架构直接失效:

  1. 编排层与模型实现完全解耦query.ts 仅通过通用模型响应类型做逻辑判断,不依赖任何具体模型(模拟/真实 API),切换 LLM 服务商无需修改调度核心代码。
  2. 模型层统一路由枢纽modelClient.ts 是唯一的模型切换入口,所有模型配置、接口路由、环境切换均收敛于此,改动成本极低。
  3. 工具层独立可复用:工具执行逻辑不依赖编排层,上层通过统一方法间接调用工具,工具能力可被任意业务场景复用,不绑定对话调度逻辑。
  4. 消息协议全局唯一地基messages.ts 被所有模块依赖,自身不依赖任何业务代码,全局消息格式统一,修改该文件会影响全系统,需谨慎迭代。

2.3 架构避坑准则

基于以上约束,可快速校验代码合理性,规避经典架构问题:

  • 禁止在编排层直接引入具体模型实现 → 破坏解耦设计,无法快速切换 LLM
  • 禁止在工具层反向调用编排层方法 → 形成循环依赖,导致系统调度混乱
  • 禁止拆分全局消息类型定义 → 破坏数据统一性,全模块类型适配失效

2.4 全局架构依赖全景图

为直观展现六大层级之间的依赖关系,下面给出整套系统的分层架构图,所有箭头方向严格遵循上述 4 条核心约束:
#mermaid-svg-YLKKXoQY4JQmT3ve{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-YLKKXoQY4JQmT3ve .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-YLKKXoQY4JQmT3ve .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-YLKKXoQY4JQmT3ve .error-icon{fill:#552222;}#mermaid-svg-YLKKXoQY4JQmT3ve .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-YLKKXoQY4JQmT3ve .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-YLKKXoQY4JQmT3ve .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-YLKKXoQY4JQmT3ve .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-YLKKXoQY4JQmT3ve .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-YLKKXoQY4JQmT3ve .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-YLKKXoQY4JQmT3ve .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-YLKKXoQY4JQmT3ve .marker{fill:#333333;stroke:#333333;}#mermaid-svg-YLKKXoQY4JQmT3ve .marker.cross{stroke:#333333;}#mermaid-svg-YLKKXoQY4JQmT3ve svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-YLKKXoQY4JQmT3ve p{margin:0;}#mermaid-svg-YLKKXoQY4JQmT3ve .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-YLKKXoQY4JQmT3ve .cluster-label text{fill:#333;}#mermaid-svg-YLKKXoQY4JQmT3ve .cluster-label span{color:#333;}#mermaid-svg-YLKKXoQY4JQmT3ve .cluster-label span p{background-color:transparent;}#mermaid-svg-YLKKXoQY4JQmT3ve .label text,#mermaid-svg-YLKKXoQY4JQmT3ve span{fill:#333;color:#333;}#mermaid-svg-YLKKXoQY4JQmT3ve .node rect,#mermaid-svg-YLKKXoQY4JQmT3ve .node circle,#mermaid-svg-YLKKXoQY4JQmT3ve .node ellipse,#mermaid-svg-YLKKXoQY4JQmT3ve .node polygon,#mermaid-svg-YLKKXoQY4JQmT3ve .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-YLKKXoQY4JQmT3ve .rough-node .label text,#mermaid-svg-YLKKXoQY4JQmT3ve .node .label text,#mermaid-svg-YLKKXoQY4JQmT3ve .image-shape .label,#mermaid-svg-YLKKXoQY4JQmT3ve .icon-shape .label{text-anchor:middle;}#mermaid-svg-YLKKXoQY4JQmT3ve .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-YLKKXoQY4JQmT3ve .rough-node .label,#mermaid-svg-YLKKXoQY4JQmT3ve .node .label,#mermaid-svg-YLKKXoQY4JQmT3ve .image-shape .label,#mermaid-svg-YLKKXoQY4JQmT3ve .icon-shape .label{text-align:center;}#mermaid-svg-YLKKXoQY4JQmT3ve .node.clickable{cursor:pointer;}#mermaid-svg-YLKKXoQY4JQmT3ve .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-YLKKXoQY4JQmT3ve .arrowheadPath{fill:#333333;}#mermaid-svg-YLKKXoQY4JQmT3ve .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-YLKKXoQY4JQmT3ve .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-YLKKXoQY4JQmT3ve .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-YLKKXoQY4JQmT3ve .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-YLKKXoQY4JQmT3ve .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-YLKKXoQY4JQmT3ve .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-YLKKXoQY4JQmT3ve .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-YLKKXoQY4JQmT3ve .cluster text{fill:#333;}#mermaid-svg-YLKKXoQY4JQmT3ve .cluster span{color:#333;}#mermaid-svg-YLKKXoQY4JQmT3ve div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-YLKKXoQY4JQmT3ve .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-YLKKXoQY4JQmT3ve rect.text{fill:none;stroke-width:0;}#mermaid-svg-YLKKXoQY4JQmT3ve .icon-shape,#mermaid-svg-YLKKXoQY4JQmT3ve .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-YLKKXoQY4JQmT3ve .icon-shape p,#mermaid-svg-YLKKXoQY4JQmT3ve .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-YLKKXoQY4JQmT3ve .icon-shape .label rect,#mermaid-svg-YLKKXoQY4JQmT3ve .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-YLKKXoQY4JQmT3ve .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-YLKKXoQY4JQmT3ve .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-YLKKXoQY4JQmT3ve :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-YLKKXoQY4JQmT3ve .ui>*{fill:#e3f2fd!important;stroke:#1565c0!important;}#mermaid-svg-YLKKXoQY4JQmT3ve .ui span{fill:#e3f2fd!important;stroke:#1565c0!important;}#mermaid-svg-YLKKXoQY4JQmT3ve .loop>*{fill:#fff3e0!important;stroke:#e65100!important;}#mermaid-svg-YLKKXoQY4JQmT3ve .loop span{fill:#fff3e0!important;stroke:#e65100!important;}#mermaid-svg-YLKKXoQY4JQmT3ve .model>*{fill:#e8f5e9!important;stroke:#2e7d32!important;}#mermaid-svg-YLKKXoQY4JQmT3ve .model span{fill:#e8f5e9!important;stroke:#2e7d32!important;}#mermaid-svg-YLKKXoQY4JQmT3ve .tool>*{fill:#e0f7fa!important;stroke:#00695c!important;}#mermaid-svg-YLKKXoQY4JQmT3ve .tool span{fill:#e0f7fa!important;stroke:#00695c!important;}#mermaid-svg-YLKKXoQY4JQmT3ve .proto>*{fill:#f3e5f5!important;stroke:#6a1b9a!important;}#mermaid-svg-YLKKXoQY4JQmT3ve .proto span{fill:#f3e5f5!important;stroke:#6a1b9a!important;}#mermaid-svg-YLKKXoQY4JQmT3ve .persist>*{fill:#fce4ec!important;stroke:#ad1457!important;}#mermaid-svg-YLKKXoQY4JQmT3ve .persist span{fill:#fce4ec!important;stroke:#ad1457!important;} 持久化层 Persistence(可选)
消息协议层 Protocol
工具层 Tool
模型层 Model
编排层 Orchestration
入口层 UI
for-await 消费
provider === 'fake'
provider === 'anthropic'
可选
加载
index.ts

readline + for-await
query.ts

query() async generator

executeToolUse()

权限/错误处理
modelClient.ts

callModel() 路由
config.ts

provider / apiKey / baseUrl
fakeModel.ts

关键词 + 路径提取
真实 LLM API

(Anthropic Messages API)
tools.ts

ReadFile / ListDir

permission 校验

执行 + 错误返回
messages.ts

Message 联合类型

create* 工厂

lastUserMessage
compress.ts

history 压缩
session.ts

保存/恢复

三、业务链路贯通:一次文件读取的完整 Agent 运行流程

看懂静态架构后,需要通过真实业务链路贯通所有模块,理解模块如何协作、数据如何流转、状态如何更新 。我们以最经典的 read package.json(读取项目配置文件)场景,拆解完整的两轮模型调度+工具执行链路,还原 Agent 核心运行心跳。

3.0 完整链路时序图

renderEvent (REPL 端) messages.ts tools.ts ReadFile executeToolUse fakeModel.ts (或 API) modelClient.ts query.ts index.ts readline 用户 renderEvent (REPL 端) messages.ts tools.ts ReadFile executeToolUse fakeModel.ts (或 API) modelClient.ts query.ts index.ts readline 用户 #mermaid-svg-aWRK5lBU0LklAOru{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-aWRK5lBU0LklAOru .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-aWRK5lBU0LklAOru .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-aWRK5lBU0LklAOru .error-icon{fill:#552222;}#mermaid-svg-aWRK5lBU0LklAOru .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-aWRK5lBU0LklAOru .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-aWRK5lBU0LklAOru .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-aWRK5lBU0LklAOru .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-aWRK5lBU0LklAOru .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-aWRK5lBU0LklAOru .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-aWRK5lBU0LklAOru .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-aWRK5lBU0LklAOru .marker{fill:#333333;stroke:#333333;}#mermaid-svg-aWRK5lBU0LklAOru .marker.cross{stroke:#333333;}#mermaid-svg-aWRK5lBU0LklAOru svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-aWRK5lBU0LklAOru p{margin:0;}#mermaid-svg-aWRK5lBU0LklAOru .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-aWRK5lBU0LklAOru text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-aWRK5lBU0LklAOru .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-aWRK5lBU0LklAOru .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-aWRK5lBU0LklAOru .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-aWRK5lBU0LklAOru .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-aWRK5lBU0LklAOru #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-aWRK5lBU0LklAOru .sequenceNumber{fill:white;}#mermaid-svg-aWRK5lBU0LklAOru #sequencenumber{fill:#333;}#mermaid-svg-aWRK5lBU0LklAOru #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-aWRK5lBU0LklAOru .messageText{fill:#333;stroke:none;}#mermaid-svg-aWRK5lBU0LklAOru .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-aWRK5lBU0LklAOru .labelText,#mermaid-svg-aWRK5lBU0LklAOru .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-aWRK5lBU0LklAOru .loopText,#mermaid-svg-aWRK5lBU0LklAOru .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-aWRK5lBU0LklAOru .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-aWRK5lBU0LklAOru .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-aWRK5lBU0LklAOru .noteText,#mermaid-svg-aWRK5lBU0LklAOru .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-aWRK5lBU0LklAOru .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-aWRK5lBU0LklAOru .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-aWRK5lBU0LklAOru .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-aWRK5lBU0LklAOru .actorPopupMenu{position:absolute;}#mermaid-svg-aWRK5lBU0LklAOru .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-aWRK5lBU0LklAOru .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-aWRK5lBU0LklAOru .actor-man circle,#mermaid-svg-aWRK5lBU0LklAOru line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-aWRK5lBU0LklAOru :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} ── 第 1 轮 ── ── 工具执行 ── ── 第 2 轮 ── latest.role === 'tool_result' → summarizeFile 分支 输入 "read package.json" 1 createUserMessage("read package.json") 2 query(userMsg, runtime) 3 callModel(messages) 4 fakeModel(messages) 5 { type:'tool_use', toolName:'ReadFile', input:{path:'package.json'} } 6 ModelResponse 7 createToolUseMessage('ReadFile', input) 8 messages.push(toolUse) 9 yield { type:'tool_use', message:toolUse } 10 console.log("assistant_tool_use: ReadFile ...") 11 executeToolUse(toolUse, ctx, askUser) 12 tool.execute({path:'package.json'}) permission check ok 13 文件内容 "{...}" 14 createToolResultMessage(toolUse, content, false) 15 messages.push(toolResult) 16 yield { type:'tool_result', message:toolResult } 17 console.log("tool_result(ok): ReadFile ...") 18 callModel(messages) 19 fakeModel(messages) 20 { type:'assistant', content:"package.json 内容是..." } 21 ModelResponse 22 createAssistantMessage(content) 23 messages.push(assistant) 24 yield { type:'assistant', message } 25 console.log("assistant: ...") 26 return(结束循环) 27

3.1 完整链路流程拆解

整体流程分为模型决策工具调用、工具执行处理、模型汇总返回三个核心阶段,闭环完成一次用户请求:

  1. 用户输入与消息初始化 :用户在终端输入 read package.json,入口层接收输入,通过消息工厂生成标准化用户消息,推入全局会话消息队列,触发编排层调度。
  2. 第一轮模型调度(工具决策) :编排层调用模型路由入口,模型识别用户「读取文件」意图,不直接返回文本,而是输出结构化 tool_use 工具调用指令,指定调用文件读取工具、传入文件路径参数。
  3. 工具权限校验与执行 :编排层接收模型指令,触发工具执行函数,先进行用户权限询问,校验通过后通过 Node.js 原生 API 读取本地 package.json 文件内容。
  4. 工具结果封装回传 :文件读取完成后,工具层将执行结果(成功内容/失败信息)统一封装为 tool_result 消息,更新至全局消息队列,回传给编排层。
  5. 第二轮模型调度(结果汇总):模型接收包含工具执行结果的完整消息队列,解析文件内容,生成自然语言总结回复。
  6. 会话结束与结果回显:编排层接收最终模型回复,终止多轮循环,入口层将结果渲染至终端,完成一次完整对话。

3.2 链路核心设计亮点

  • 结构化交互:模型不输出自由文本,通过结构化指令驱动工具执行,可控性、可扩展性极强
  • 统一异常链路:工具执行成功、权限拒绝、读取失败、未知工具,全部走统一结果封装逻辑,无散乱异常
  • 消息驱动状态:整个 Agent 无全局状态变量,所有决策、状态流转完全依赖消息队列,解耦彻底

四、MVP 最小骨架落地:6 个核心文件源码逐析

完整的 ClaudeCode 源码包含会话持久化、历史压缩、多工具池、路径沙箱、API 限流容错等冗余扩展能力。而官方 examples/mvp 剔除了所有非核心能力,仅保留能跑通完整对话+工具调用链路的 6 个核心文件,完美复刻原版架构设计,是入门落地的最佳模板。

6 个文件严格遵循六层架构与四条核心约束,依赖关系清晰、无冗余代码,下面逐文件拆解源码、核心逻辑与设计思想。

4.0 6 个核心文件的实现层依赖图

将前面那张全景图"剪裁"到 examples/mvp,依赖箭头方向与 4 条硬约束保持完全一致:
#mermaid-svg-t2Byimf0GRH1M9M2{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-t2Byimf0GRH1M9M2 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-t2Byimf0GRH1M9M2 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-t2Byimf0GRH1M9M2 .error-icon{fill:#552222;}#mermaid-svg-t2Byimf0GRH1M9M2 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-t2Byimf0GRH1M9M2 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-t2Byimf0GRH1M9M2 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-t2Byimf0GRH1M9M2 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-t2Byimf0GRH1M9M2 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-t2Byimf0GRH1M9M2 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-t2Byimf0GRH1M9M2 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-t2Byimf0GRH1M9M2 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-t2Byimf0GRH1M9M2 .marker.cross{stroke:#333333;}#mermaid-svg-t2Byimf0GRH1M9M2 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-t2Byimf0GRH1M9M2 p{margin:0;}#mermaid-svg-t2Byimf0GRH1M9M2 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-t2Byimf0GRH1M9M2 .cluster-label text{fill:#333;}#mermaid-svg-t2Byimf0GRH1M9M2 .cluster-label span{color:#333;}#mermaid-svg-t2Byimf0GRH1M9M2 .cluster-label span p{background-color:transparent;}#mermaid-svg-t2Byimf0GRH1M9M2 .label text,#mermaid-svg-t2Byimf0GRH1M9M2 span{fill:#333;color:#333;}#mermaid-svg-t2Byimf0GRH1M9M2 .node rect,#mermaid-svg-t2Byimf0GRH1M9M2 .node circle,#mermaid-svg-t2Byimf0GRH1M9M2 .node ellipse,#mermaid-svg-t2Byimf0GRH1M9M2 .node polygon,#mermaid-svg-t2Byimf0GRH1M9M2 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-t2Byimf0GRH1M9M2 .rough-node .label text,#mermaid-svg-t2Byimf0GRH1M9M2 .node .label text,#mermaid-svg-t2Byimf0GRH1M9M2 .image-shape .label,#mermaid-svg-t2Byimf0GRH1M9M2 .icon-shape .label{text-anchor:middle;}#mermaid-svg-t2Byimf0GRH1M9M2 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-t2Byimf0GRH1M9M2 .rough-node .label,#mermaid-svg-t2Byimf0GRH1M9M2 .node .label,#mermaid-svg-t2Byimf0GRH1M9M2 .image-shape .label,#mermaid-svg-t2Byimf0GRH1M9M2 .icon-shape .label{text-align:center;}#mermaid-svg-t2Byimf0GRH1M9M2 .node.clickable{cursor:pointer;}#mermaid-svg-t2Byimf0GRH1M9M2 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-t2Byimf0GRH1M9M2 .arrowheadPath{fill:#333333;}#mermaid-svg-t2Byimf0GRH1M9M2 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-t2Byimf0GRH1M9M2 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-t2Byimf0GRH1M9M2 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-t2Byimf0GRH1M9M2 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-t2Byimf0GRH1M9M2 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-t2Byimf0GRH1M9M2 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-t2Byimf0GRH1M9M2 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-t2Byimf0GRH1M9M2 .cluster text{fill:#333;}#mermaid-svg-t2Byimf0GRH1M9M2 .cluster span{color:#333;}#mermaid-svg-t2Byimf0GRH1M9M2 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-t2Byimf0GRH1M9M2 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-t2Byimf0GRH1M9M2 rect.text{fill:none;stroke-width:0;}#mermaid-svg-t2Byimf0GRH1M9M2 .icon-shape,#mermaid-svg-t2Byimf0GRH1M9M2 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-t2Byimf0GRH1M9M2 .icon-shape p,#mermaid-svg-t2Byimf0GRH1M9M2 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-t2Byimf0GRH1M9M2 .icon-shape .label rect,#mermaid-svg-t2Byimf0GRH1M9M2 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-t2Byimf0GRH1M9M2 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-t2Byimf0GRH1M9M2 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-t2Byimf0GRH1M9M2 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} index.ts

REPL 入口
src/query.ts

query() async generator
src/modelClient.ts

callModel() 路由
src/fakeModel.ts

关键词模拟模型
src/tools.ts

executeToolUse
src/message.ts

四类消息 + 工厂

对照规则:当你在 6 个文件里看到任何不符合这张图的依赖关系,都是反例。

4.1 消息协议层:message.ts(全局数据地基)

该文件是整个项目的类型与数据核心,定义了 Agent 对话的所有消息类型,提供标准化创建函数,所有模块均依赖该文件,自身无任何业务依赖,实现「消息即状态」的核心设计。

复制代码
export type UserMessage = {
    role: 'user'
    content: string
}

export type AssistantMessage = {
    role: 'assistant'
    content: string
}

export type ToolUseMessage = {
    role: 'tool_use'
    id: string
    toolName: string
    input: Record<string, unknown>
}

export type ToolResultMessage = {
    role: 'tool_result'
    toolUseId: string
    toolName: string
    content: string
    isError?: boolean
}

// 全局消息联合类型,统一所有对话数据格式
export type Message =
    | UserMessage
    | AssistantMessage
    | ToolUseMessage
    | ToolResultMessage

let nextToolUseNumber = 1

// 各类消息标准化工厂函数
export function createUserMessage(content: string): UserMessage {
    return { role: 'user', content }
}

export function createAssistantMessage(content: string): AssistantMessage {
    return { role: 'assistant', content }
}

export function createToolUseMessage(
    toolName: string,
<string, unknown>,
): ToolUseMessage {
    return {
        role: 'tool_use',
        id: `toolu_${nextToolUseNumber++}`,
        toolName,
        input,
    }
}

export function createToolResultMessage(
    toolUse: ToolUseMessage,
    content: string,
    isError = false,
): ToolResultMessage {
    return {
        role: 'tool_result',
        toolUseId: toolUse.id,
        toolName: toolUse.toolName,
        content,
        ...(isError ? { isError } : {}),
    }
}

// 工具方法:获取最新消息、最新用户消息
export function lastMessage(messages: Message[]): Message | undefined {
    return messages.at(-1)
}

export function lastUserMessage(messages: Message[]): UserMessage | undefined {
    for (let i = messages.length - 1; i >= 0; i--) {
        const message = messages[i]
        if (message?.role === 'user') return message
    }
    return undefined
}

核心设计要点 :通过联合类型实现 TypeScript 自动类型收窄,无需类型断言;自增 ID 实现工具调用与结果精准配对;按需挂载 isError 字段,精简消息结构,让模型语义更清晰。

4.2 模型路由层:modelClient.ts(统一调用入口)

作为模型层的统一枢纽,该文件彻底解耦业务与具体模型实现,对外暴露唯一的 callModel 方法,为后续切换真实 LLM API 预留完整扩展能力。

复制代码
import { fakeModel } from "./fakeModel.ts";
import { Message, ToolResultMessage, UserMessage } from "./message.ts";

// 模型统一响应类型,仅两种输出结果
export type ModelResponse =
    | { type: 'assistant'; content: string }
    | { type: 'tool_use'; toolName: string<string, unknown> }

// 允许传入模型的消息类型
export type ModelMessage = UserMessage | ToolResultMessage

// 全局唯一模型调用入口
export async function callModel(messages: Message<ModelResponse> {
    return await fakeModel(messages)
}

核心设计要点:固定模型输出契约,仅支持「文本回复/工具调用」两种结果;极简路由逻辑,后续切换 Anthropic 真实 API 仅需修改当前文件,业务层完全无感。

4.3 模拟模型层:fakeModel.ts(无状态规则模拟)

MVP 骨架无需依赖真实 LLM,通过规则模拟模型决策逻辑,复刻 LLM「意图识别-工具调用-结果汇总」的完整能力,无状态设计,完全依赖消息队列判断对话轮次。

复制代码
import { lastMessage, Message, ToolResultMessage, UserMessage } from "./message.ts";
import { ModelResponse } from "./modelClient.ts";

export async function fakeModel(messages:<ModelResponse> {
    const latest = lastMessage(messages)
    // 处理用户最新输入:识别工具调用意图
    if (latest?.role === 'user') {
        const lowerText = latest.content.toLocaleLowerCase()
        if (shouldReadFile(latest.content, lowerText)) {
            return {
                type: 'tool_use',
                toolName: `Read`,
                input: {
                    filename: extractPath(latest.content)
                }
            }
        } else {
            return {
                type: 'assistant',
                content: `普通回答:${latest?.content}`
            }
        }
    }
    // 处理工具执行结果:汇总生成最终回复
    else if (latest?.role === 'tool_result') {
        if (latest.isError) {
            return {
                type: 'assistant',
                content: `工具 ${latest.toolName} 执行失败:${latest.content}`
            }
        }
        return {
            type: 'assistant',
            content: `Read package.json:${latest?.content}`
        }
    }

    return {
        type: 'assistant',
        content: `无法处理消息:${JSON.stringify(latest)}`
    }
}

// 匹配文件读取意图(中英文关键词兼容)
function shouldReadFile(text: string, lowerText: string): boolean {
    return (
        text.includes('读') ||
        text.includes('打开') ||
        lowerText.includes('read ') ||
        lowerText.includes('show file')
    )
}

// 启发式提取文件路径
function extractPath(text: string): string | undefined {
    const quoted = text.match(/["'`](.+?)["'`])?.[1]
    if (quoted) return quoted

    const tokens = text.split(/\s+/).filter(Boolean)
    const pathLikeToken = tokens.find(token =>
        /[./\\]|\.json$|\.md$|\.ts$|\.txt$/i.test(token),
    )
    if (pathLikeToken) return pathLikeToken

    const commandWords = new Set([
        '读', '读取', '打开', '列出', '目录',
        'read', 'show', 'file', 'list', 'ls',
    ])
    return tokens.find(token => !commandWords.has(token.toLowerCase()))
}

核心设计要点:无状态设计,仅通过最新消息角色判断对话轮次;中英文意图识别+路径智能提取;统一处理工具成功/异常场景,保证循环不卡死。

4.4 工具执行层:tools.ts(权限+执行+异常统一封装)

统一工具执行入口,整合路径解析、权限校验、IO 执行、结果封装能力,所有工具执行逻辑收敛于此,上层无需关注底层实现。

复制代码
import { readFile } from 'fs/promises'
import { createToolResultMessage, ToolResultMessage, ToolUseMessage } from "./message.ts";
import path from 'node:path';
import { RuntimeOption } from './query.ts';

// 统一工具执行入口
export async function executeToolUse(tool_use: ToolUseMessage, runtime:<ToolResultMessage> {
    if (tool_use.toolName === 'Read') {
        // 解析绝对路径
        const readPath = path.resolve(runtime.toolContext.rootDir, tool_use.input.filename as string)
        // 交互式权限校验
        const hasAccess = await runtime.askUser(`申请访问:${readPath}`)
        if (!hasAccess) return createToolResultMessage(tool_use, "访问被拒绝", false)
        // 读取文件并返回标准化结果
        const content = await readFile(readPath, 'utf-8')
        return createToolResultMessage(tool_use, content)
    }
    // 未知工具统一异常返回
    return createToolResultMessage(tool_use, "未知工具调用失败", false)
}

核心设计要点:路径根目录可控,为后续沙箱白名单扩展预留能力;权限询问与业务逻辑解耦;所有执行结果走统一消息通道,无散落异常。

4.5 编排调度层:query.ts(全局状态机核心)

整个 Agent 的核心调度中枢,基于异步生成器实现多轮循环,管控模型调用、工具执行、事件产出、轮次兜底,是唯一掌控「对话轮次」的模块。

复制代码
import { createAssistantMessage, createToolUseMessage, Message, ToolResultMessage, ToolUseMessage } from "./message.ts"
import { callModel } from "./modelClient.ts";
import { executeToolUse } from "./tools.ts";

// 对外事件类型:统一UI渲染数据源
export type QueryEvent =
    | { type: 'assistant'; message: Message }
    | { type: 'tool_use'; message: Message }
    | { type: 'tool_result'; message: Message }

// 运行时配置:根目录+用户权限询问方法
export type RuntimeOption = {
    toolContext: { rootDir: string },
    askUser: (p: any) => any
}

// 核心调度异步生成器
export async function* query(messages: Message[], runtime: RuntimeOption): AsyncIterable<QueryEvent, void> {
    // 最大轮次兜底,防止死循环
    const maxToolRounds = 5

    for (let round =< maxToolRounds; round++) {
        // 调用模型获取决策
        const response = await callModel(messages)

        // 模型直接返回文本:结束对话
        if (response.type === 'assistant') {
            yield { type: 'assistant', message: createAssistantMessage(response.content) }
            return
        }

        // 模型触发工具调用:执行工具流程
        const toolUse = createToolUseMessage(response.toolName, response.input)
        messages.push(toolUse);
        yield { type: 'tool_use', message: toolUse }

        // 执行工具并获取结果
        const toolResult = await executeToolUse(toolUse, runtime)
        messages.push(toolResult);
        yield { type: 'tool_result', message: toolResult }
    }

    // 超出最大轮次,强制终止
    const failed = createAssistantMessage(
        `工具循环超过 ${maxToolRounds} 轮,已停止。`,
    )
    messages.push(failed)
    yield { type: 'assistant', message: failed }
}

核心设计要点:异步生成器实现流式事件产出,UI 可实时渲染中间状态;最大轮次防呆兜底;纯消息驱动循环,无硬编码终止条件;异常无需 try/catch,全部由工具层封装。

4.6 入口交互层:index.ts(可替换 REPL 终端)

系统交互入口,负责终端输入输出、会话消息维护、事件渲染,与核心调度逻辑完全解耦,可直接替换为 HTTP、WebSocket、GUI 交互。

复制代码
import { cwd, stdin, stdout } from "node:process"
import { createInterface } from "node:readline/promises"
import type { Message } from "./src/message.ts";
import { createUserMessage } from "./src/message.ts";
import { query, QueryEvent } from "./src/query.ts";

const rl = createInterface({
    input: stdin,
    output: stdout
})
const lineIterator = rl[Symbol.asyncIterator]()

// 全局会话消息状态
const messages: Message[] = []

// 持续监听用户输入
while (true) {
    const answer = await ask('user:\n')
    if (answer == null) break;
    const input = answer.trim();
    if (input === "") continue;
    messages.push(createUserMessage(input))

    // 消费调度事件,实时渲染结果
    for await (const event of query(messages, {
        toolContext: { rootDir: cwd() },
        askUser: async question => {
            const answer = await ask(`${question} [y/N] `);
            if (answer === null) return false;
            return answer.trim().toLowerCase() === "y";
        }
    })) {
        renderEvent(event)
    }
}

rl.close();

// 终端提问工具方法
async function ask(prompt: string) {
    stdout.write(prompt);
    const next = await lineIterator.next()
    return next.done ? null : next.value
}

// 统一事件渲染方法
function renderEvent(event: QueryEvent) {
    const message = event.message
    if (message.role === 'assistant') {
        console.log(`assistant:\n ${message.content}`)
        return
    }
    if (message.role === 'tool_use') {
        console.log(`tool_use:\n ${JSON.stringify(message.input)}`)
        return
    }
    if (message.role === 'tool_result') {
        const status = message.isError ? 'error' : 'ok'
        const preview =
            message.content.length > 500
                ? `${message.content.slice(0, 500)}\n...`
                : message.content
        console.log(`tool_result(${status}): ${message.toolName}\n${preview}`)
    }
}

核心设计要点:会话状态由入口层维护,调度层仅更新状态;UI 与核心业务完全解耦,支持无缝替换交互场景;事件流式渲染,实时展示工具调用、执行结果、最终回复。

4.7 架构图与源码端到端对照

第三部分的时序图描述了"完整图被点亮一次"的全过程,本节给出"完整图被落地为 6 个文件"的对照清单。把时序图中的每一拍和源码核到一起,可以验证"骨架"和"实现"是完全一致的:

阶段 调用方 被调函数 在哪个文件
用户输入 index.ts ask('user:\n') index.ts
消息入栈 index.ts createUserMessage(input) message.ts
编排开启 index.ts query(messages, runtime) query.ts
模型决策 query.ts callModel(messages) modelClient.ts
模拟模型 modelClient.ts fakeModel(messages) fakeModel.ts
工具调用包装 query.ts createToolUseMessage(...) message.ts
工具执行 query.ts executeToolUse(toolUse, runtime) tools.ts
真实读取 tools.ts readFile(readPath, 'utf-8') Node.js fs
结果包装 tools.ts createToolResultMessage(...) message.ts
收尾 query.ts yield ... + return query.ts
渲染 index.ts renderEvent(event) index.ts

核心设计要点 :消息流是双向的,但所有数据都是同一套类型。模型不返回字符串,返回结构化 ModelResponse;工具不抛异常,返回结构化 ToolResultMessage;UI 不直接传字符串,传 QueryEvent。三层之间的接口都是"结构 + 类型",没有 any 漏到主路径。

五、MVP 骨架能力复盘与扩展方向

本文拆解的 6 文件最简骨架,已经完整跑通「用户输入→模型决策→工具执行→结果汇总」的标准 Agent 核心链路,完全复刻 ClaudeCode 原版架构设计思想。同时该骨架预留了完整扩展接口,可基于现有架构快速迭代完善能力。

5.1 当前骨架缺失的高阶能力(扩展方向)

  • 真实 LLM API 对接:在 modelClient.ts 新增 Anthropic 真实接口路由
  • 会话持久化:新增历史压缩、会话保存恢复能力
  • 安全沙箱:新增路径白名单、越权访问拦截机制
  • 异常容错:补充模型超时、网络异常、API 限流捕获逻辑
  • 多工具扩展:基于统一工具入口,新增文件夹读取、代码修改、命令执行等工具
  • 权限精细化:完善 allow/ask/deny 三态权限策略

六、总结

读懂 ClaudeCode 源码的核心,不在于熟记每一行代码,而在于吃透其分层架构、单向依赖、消息驱动的设计思想:

  • 六层分层架构实现职责单一、边界清晰,从根源规避耦合问题;
  • 四条核心依赖原则,保障框架的可扩展性与可维护性;
  • 消息驱动的无状态设计,是 LLM Agent 多轮交互、工具调度的最优范式;
  • MVP 最简骨架以最小成本复刻了官方核心能力,可直接作为自研 Agent 的基础模板。

掌握这套架构逻辑后,不仅能彻底理解 Claude Code 的运行原理,更能快速迁移到任意通用 Agent 框架的开发与二次迭代中。

延伸阅读

相关推荐
抱抱宝5 小时前
Agent-study项目教程(03):手写 Mini-ReAct Agent(不依赖框架)
javascript·人工智能·gpt·react.js·prompt·agent
抱抱宝5 小时前
大模型应用开发教程08 | 构建完整 RAG 应用(Chroma/FAISS 实战)
人工智能·gpt·prompt·agent
纯爱掌门人8 小时前
DeepSeek Harness 上手实录:从启动 Web UI 到组装自己的 Agent
agent·deepseek
纯爱掌门人8 小时前
给 DeepSeek Harness 开发功能:别急着写 Tool,先找对扩展层
agent·deepseek
JaydenAI8 小时前
[基于OpenEvals的自动化评估-10]针对Agent对话的评估[上篇]
ai·langchain·agent·evaluation·openevals
纯爱掌门人8 小时前
我把 DeepSeek Harness 源码跑了一遍,终于看懂了它的“一切皆插件”
agent·deepseek
张彦峰ZYF8 小时前
LangGraph 深入理解 ReAct:让 AI Agent 真正学会「边想边做」
人工智能·llm·agent·react·langgroup
梦想很大很大9 小时前
如果有一个本地优先的 Workflow 工具,你们团队会愿意用吗?
python·agent·workflow
阿图灵9 小时前
Agentic AI 架构入门(九):Agent 通信协议全景——ACP/A2A/AG-UI/MCP
人工智能·ui·架构·ai agent·智能体·mcp·agentic ai