深度拆解Claude Code架构:不靠总管,靠边界搭建编程Agent

文章目录

  • [1 先给整个架构定个性](#1 先给整个架构定个性)
    • [1.1 整条代码库分五层](#1.1 整条代码库分五层)
  • [2 跑起来分两个阶段](#2 跑起来分两个阶段)
    • [2.1 启动阶段:冷启动的全套流程](#2.1 启动阶段:冷启动的全套流程)
    • [2.2 会话阶段:每轮对话的循环](#2.2 会话阶段:每轮对话的循环)
  • [3 入口层:一套核心,三种皮肤](#3 入口层:一套核心,三种皮肤)
    • [3.1 核心设计:同核多形态](#3.1 核心设计:同核多形态)
    • [3.2 为啥不搞单入口+参数?](#3.2 为啥不搞单入口+参数?)
  • [4 装配层:把冷启动开销摁死](#4 装配层:把冷启动开销摁死)
    • [4.1 CLI的天然痛点:次次冷启动](#4.1 CLI的天然痛点:次次冷启动)
    • [4.2 四个省时间的小技巧](#4.2 四个省时间的小技巧)
      • [4.2.1 并行预取](#4.2.1 并行预取)
      • [4.2.2 懒加载](#4.2.2 懒加载)
      • [4.2.3 按环境加载](#4.2.3 按环境加载)
      • [4.2.4 编译期裁剪](#4.2.4 编译期裁剪)
  • [5 引擎层:最稳定的甩手掌柜](#5 引擎层:最稳定的甩手掌柜)
    • [5.1 引擎里没有任何工具实现](#5.1 引擎里没有任何工具实现)
    • [5.2 工具从哪来?装配层注入](#5.2 工具从哪来?装配层注入)
    • [5.3 工具执行也不在引擎里](#5.3 工具执行也不在引擎里)
    • [5.4 引擎只认协议,不认工具](#5.4 引擎只认协议,不认工具)
    • [5.5 为啥不把工具写死在引擎里?](#5.5 为啥不把工具写死在引擎里?)
  • [6 能力层:每个模块都是个体户](#6 能力层:每个模块都是个体户)
    • [6.1 三类能力](#6.1 三类能力)
    • [6.2 设计理念:自包含模块](#6.2 设计理念:自包含模块)
    • [6.3 为啥不按职能拆分?](#6.3 为啥不按职能拆分?)
  • [7 支撑层:外围的隐形保障](#7 支撑层:外围的隐形保障)
    • [7.1 权限系统:独立的一层](#7.1 权限系统:独立的一层)
    • [7.2 多Agent编排](#7.2 多Agent编排)
    • [7.3 扩展点:能力不写死](#7.3 扩展点:能力不写死)
    • [7.4 终端界面](#7.4 终端界面)
  • [8 核心设计哲学:靠边界,不靠总管](#8 核心设计哲学:靠边界,不靠总管)
    • [8.1 没有总管,靠边界维持秩序](#8.1 没有总管,靠边界维持秩序)
  • [9 三个可直接抄的经验](#9 三个可直接抄的经验)
    • [9.1 新增能力放外围,别碰核心](#9.1 新增能力放外围,别碰核心)
    • [9.2 冷启动场景,坚持「用多少装多少」](#9.2 冷启动场景,坚持「用多少装多少」)
    • [9.3 靠边界,不靠总管](#9.3 靠边界,不靠总管)


P.S. 无意间发现了一个巨牛的人工智能教程,非常通俗易懂,对AI感兴趣的朋友强烈推荐去看看, 传送门https://blog.csdn.net/HHX_01

1 先给整个架构定个性

Claude Code本质上就是个「大模型拍板,代码干活」的编程Agent。模型负责想想要干啥,那几十万行TypeScript负责把想法落地------调工具、管上下文、守权限。

它有三种交付形态:命令行CLI、可嵌入SDK、MCP服务。别以为是三套代码,人家全是同一条代码库编译出来的。

1.1 整条代码库分五层

从外到内一共五层,每层职责边界划得清清楚楚,就像个分工明确的小公司。

分层 核心职责 对应代码位置
入口层 每种运行形态一个启动入口 src/entrypoints/
装配层 启动初始化、按需组装能力 src/main.tsx
引擎层 会话主循环、工具协议定义 src/query.ts
能力层 工具、命令、服务的具体实现 src/tools/src/commands/src/services/
支撑层 权限、编排、扩展、终端界面 src/utils/permissions/

2 跑起来分两个阶段

一次完整的运行分两大阶段,启动阶段每次运行走一遍,会话阶段每轮对话走一遍。

2.1 启动阶段:冷启动的全套流程

你在终端敲下claude回车,程序就开始启动流程。

先是入口层cli.tsx补环境、判断运行模式;然后把控制权交给装配层main.tsx

装配层并行拉配置、读密钥、解析参数,按当前环境拼出可用的命令集,最后挂载终端界面,等你输入。

整个过程就像你早上到公司:先打卡开机,后台偷偷登微信钉钉,等你接杯水坐下来,电脑刚好能用。

2.2 会话阶段:每轮对话的循环

你输入一句话,就进入会话主循环。

消息先到引擎层主循环,主循环把你的消息+工具清单一起发给大模型。

模型说要调用工具,就交给能力层执行------先过权限校验、钩子逻辑,再真正跑工具。

执行结果包装成消息塞回对话里,再发给模型,直到模型说不用工具了,给出最终回答流式渲染到终端。

重点:主循环自己啥工具都不执行,全程只和相邻层的接口打交道,没有啥都管的「大总管」。

3 入口层:一套核心,三种皮肤

入口层就是每种运行形态自己的启动文件,全放在src/entrypoints/目录下。

cli.tsx起命令行,mcp.ts起MCP服务,sdk/给第三方嵌入用。

3.1 核心设计:同核多形态

三种形态用的是完全同一套核心模块。入口文件把形态差异全部拦截在外,核心代码一行都不用改。

比如MCP的入口文件里,工具清单、命令注册表、权限检查,引的全是命令行模式下的同一批模块。

typescript 复制代码
// src/entrypoints/mcp.ts
import review from '../commands/review.js'
import type { Command } from '../commands.js'
import { getTools } from '../tools.js'
import { hasPermissionsToUseTool } from '../utils/permissions/permissions.js'

说白了就是:新增加一种运行形态,只需要加一个新的入口文件就行,核心代码碰都不用碰。

3.2 为啥不搞单入口+参数?

肯定有人会说,搞这么多入口干嘛,整个单入口加个参数区分形态不行吗?

还真不行。首先参数表会随着形态越来越多越变越长,最后变成屎山。

更关键的是,每加一种新形态都要改核心代码,改核心就意味着所有老形态都要回归测试。

就像你家玄关,本来只是进门的地方,你非要让它兼顾快递柜、鞋柜、杂物间,最后加个新功能,整个玄关都得砸了重装。

4 装配层:把冷启动开销摁死

装配层的核心就是main.tsx,负责把程序从0组装到「可以对话」的状态。

4.1 CLI的天然痛点:次次冷启动

CLI工具和服务端不一样。服务端启动一次跑半年,初始化开销摊薄到可以忽略。

CLI每次运行都从零开始,你每敲一次命令,启动开销就重复付一次。那每多100毫秒,都是在消耗用户的耐心。

所以装配层的核心原则就是:用多少,装多少。

4.2 四个省时间的小技巧

为了把冷启动时间压到最低,人家用了四个机制,全是干货。

4.2.1 并行预取

文件头先把启动路径上最慢的两件事给启动了------读系统配置的子进程、钥匙串读取,不等结果,后台先跑着。

后面的import继续加载,两件事并行,省下来的都是真金白银的时间。

typescript 复制代码
// src/main.tsx(文件头)
// These side-effects must run before all other imports:
// 2. startMdmRawRead fires MDM subprocesses (plutil/reg query) so they run in
//    parallel with the remaining ~135ms of imports below
// 3. startKeychainPrefetch fires both macOS keychain reads (OAuth + legacy API
//    key) in parallel --- ... (~65ms on every macOS startup)
import { startMdmRawRead } from './utils/settings/mdm/rawRead.js';
startMdmRawRead();
import { ensureKeychainPrefetchCompleted, startKeychainPrefetch } from './utils/secureStorage/keychainPrefetch.js';
startKeychainPrefetch();
import { feature } from 'bun:bundle';
// ...往下还有160多行import

4.2.2 懒加载

遥测、分析这些体量大、启动又用不上的模块,先不加载。

什么时候第一次真正用到,什么时候才加载进内存。用不上的,永远不占资源。

4.2.3 按环境加载

交互终端、CI流水线、无头环境,能用的命令本来就不一样。

启动的时候按当前环境查一遍,该加载啥加载啥,没用的一律不装。

4.2.4 编译期裁剪

main.tsx一个文件里就有61处编译期开关,构建的时候就决定哪些能力打进包里。

关闭的功能直接不进产物,连死代码都没有,比运行时判断高效多了。

四个机制合起来就是一句话:能推迟的推迟,能排除的排除,都不行的就并行跑。

5 引擎层:最稳定的甩手掌柜

引擎是整个会话的核心驱动,一共三个文件,全放在src根目录,地位一目了然。

文件 核心职责
query.ts 主循环:问模型、执行工具、结果回灌
Tool.ts 工具协议:规定一个工具长啥样
QueryEngine.ts 会话封装:管统计、中断、给SDK用

5.1 引擎里没有任何工具实现

你去query.ts里搜BashTool、FileReadTool这些工具名,一个都搜不到。

引擎对工具目录的引用全是类型定义和常量,连一个工具实现都没import。

说白了,引擎根本不知道具体有啥工具,它只知道「有一批工具」。

5.2 工具从哪来?装配层注入

主循环请求模型的时候,工具清单是从上下文里拿的,是启动的时候装配层注进来的。

typescript 复制代码
// src/query.ts
for await (const message of deps.callModel({
  messages: ...,
  systemPrompt: fullSystemPrompt,
  tools: toolUseContext.options.tools,
  ...

引擎面对的永远是「这批工具」,至于这批工具具体是啥,引擎不关心。

5.3 工具执行也不在引擎里

不光工具定义不在引擎里,连执行都不在。主循环把工具执行整体委托给了服务层。

typescript 复制代码
// src/query.ts
import { runTools } from './services/tools/toolOrchestration.js'

5.4 引擎只认协议,不认工具

Tool.ts定义了工具的契约:名字、说明、参数 schema、执行函数、权限模型,一个工具必须满足这些。

typescript 复制代码
// src/Tool.ts
export type Tool<
  Input extends AnyObject = AnyObject,
  Output = unknown,
  ...
> = {
  ...
  call(...): Promise>
  description(...): Promise
  readonly inputSchema: Input
  ...

只要你按契约实现,你塞啥工具进来引擎都能用。你换掉一半工具,引擎一行代码都不用改。

5.5 为啥不把工具写死在引擎里?

工具清单两大特点:一是多,二是经常变。

写死在引擎里,每次加工具改工具都要动主循环------全库最不能出错的地方。出一个bug,所有能力全崩。

放在外面只认协议,加工具引擎不用动,换工具引擎也不用动。核心稳了,外围才能随便折腾。

6 能力层:每个模块都是个体户

能力层就是模型和用户能用的所有功能,分三类:工具、命令、服务。

6.1 三类能力

工具 :给模型执行动作用的,在src/tools/下,一个工具一个目录,目录名就是能力名。参数、权限、执行逻辑全在自己目录里。

命令 :用户用斜杠调用的,在src/commands/下。注册表不是静态表,每次调用按当前环境现拼。

服务 :支撑性子系统,在src/services/下,一个服务一个目录。

6.2 设计理念:自包含模块

新增一个能力,就是新增一个目录,再加一行注册,不碰引擎,也不碰其他能力模块。

删除一个能力,直接删目录就行,不会留下一堆没人用的遗留引用。

6.3 为啥不按职能拆分?

肯定有人觉得,把所有参数定义放一个目录,所有执行函数放一个目录,看起来更整齐。

整齐是整齐了,加一个新能力,你得横跨好几个目录改好几处。查一个bug,得翻几十个文件。

就像传统工厂,做个产品要跑好几个车间,加个新产品,每个车间都得改工艺。出了问题,冲压说焊接的锅,焊接说装配的问题。

自包含就像个体户,从谈单到交付全自己扛,加新业务直接开个新店,互不干扰。

7 支撑层:外围的隐形保障

剩下的代码都归支撑层,负责让上面四层安全运行、承接大任务、对外连接。

7.1 权限系统:独立的一层

模型的每个动作都要过三态检查:直接放行、直接拒绝、必须问用户。

权限是独立的一层,不是散落在每个工具里的判断。同一套规则全局一致,可配置,可审计。

总比每个工具自己写权限判断强,不然哪天哪个工具漏写了,直接把你系统删了都有可能。

7.2 多Agent编排

一个上下文装不下的大任务,就派给多个Agent去做。通信、分发、隔离,全靠编排模块管。

7.3 扩展点:能力不写死

skills、plugins、MCP,都是扩展点。能力边界不写死在代码里,工作流可以渐进加载,外部工具按协议动态接入。

毕竟能力永远列不全,留好口子比啥都强。

7.4 终端界面

用React+Ink渲染终端界面。React负责逻辑,渲染器负责画到哪。react-dom画浏览器,Ink画终端,同一套组件模型。

支撑层同样没有总管,这些目录各干各的,没有谁凌驾于谁之上做统一调度。

8 核心设计哲学:靠边界,不靠总管

把五层的设计理念收一下,就是一张表。

层级 核心理念 底层原因
入口 同核多形态 新形态=新入口文件,核心零改动
装配 用多少装多少 CLI次次冷启动,启动开销重复付
引擎 只认协议,不认工具 核心不动,能力常新
能力 自包含模块 新增不影响其他,删除不留遗留
支撑 权限成层,边界留扩展 规则全局一致;能力永远列不全

8.1 没有总管,靠边界维持秩序

整个系统里,没有一个啥都管的中央模块。每层只依赖下一层的接口,谁都不越界。

入口把控制权交装配,装配把工具清单注引擎,引擎只认协议,能力按协议实现,支撑在外围兜底。

改一个工具,碰不到引擎;加一种形态,碰不到核心;换掉大半能力,主循环该咋跑咋跑。

这种秩序,从来不是靠一个大权在握的总管管出来的,是靠大家都守边界守出来的。

9 三个可直接抄的经验

看完人家的设计,别光觉得厉害,这三条直接能用到你自己的项目里。

9.1 新增能力放外围,别碰核心

这就是开闭原则的极致体现。多一种运行形态,加个入口文件;多一个工具,加个目录。

反过来,如果你加个新功能,必须改核心代码,那就是架构出问题了。

核心每被碰一次,所有既有能力都得跟着回归测试,成本高到离谱。

9.2 冷启动场景,坚持「用多少装多少」

只要是每次都要从零启动的场景,启动路径上的每一毫秒都值得抠。

能推迟的推迟(懒加载),能排除的排除(编译期裁剪),都做不到的就并行执行(预取)。

别上来就把所有东西都加载进来,用户可能只用一个功能,你把全家桶都装上,纯纯浪费。

9.3 靠边界,不靠总管

分层架构、松耦合,说起来都是老生常谈,做起来难。

检验标准就一条:你的系统敢不敢让人替换掉大半能力?敢,说明边界立住了;不敢,那就是得了「总管病」。

今晚就能验证:打开你手头最大的项目,数数核心流程文件直接import了多少个功能模块。

超过一只手的数?恭喜你,总管病确诊了。

P.S. 无意间发现了一个巨牛的人工智能教程,非常通俗易懂,对AI感兴趣的朋友强烈推荐去看看,传送门https://blog.csdn.net/HHX_01

相关推荐
Sophnet云平台1 小时前
从 IT 自嗨到业务可用,制造企业 AI 平台的落地实践
大数据·人工智能·llm·制造·token·云平台
ting94520001 小时前
Hey Noah 主动式 AI 执行助理全栈技术深度剖析 —— 从被动对话 LLM 到 FSD 级自主 Agent 工程实现
人工智能·架构
TechEdu2026061 小时前
[人工智能]Granite(IBM):企业级开放模型、治理与工程实践
人工智能·ai
Mr数据杨1 小时前
莫斯科公寓价格预测实战 从 Kaggle 房价回归题理解结构化建模
人工智能·数据分析·kaggle竞赛
huashengzsj1 小时前
钛合金真空钎焊工艺方法 钛合金真空钎焊厂家推荐
人工智能
龙兵科技小程序1 小时前
Codex与ChatGPT区别详解:AI智能体时代,如何用十分之一成本提升工作效率
人工智能·ai·chatgpt·自动化
史一试1 小时前
深度解析 OpenAI API:Chat Completions API 与 Responses API
人工智能
PhotonixBay1 小时前
3D共聚焦显微镜与探针式粗糙度仪:谁更适合亚表面损伤检测?
图像处理·人工智能·测试工具·3d
科技拓维者1 小时前
2026年看论文AI工具怎么选?Tabbit浏览器具体应用
人工智能