从源码泄露看Claude Code三层架构:CLI、Agent编排与内核执行 先说结论这份泄露出来的 Claude Code 相关源码我第一时间看了但没有停留在“哪个版本、哪个文件泄露”的吃瓜层面而是把它当成一份难得的“AI 编程工具架构教材”来拆。看完之后最强烈的感受是——这类工具框架本质上就是一条三层装配线CLI 交互层负责接单Agent 编排层负责排产内核执行层负责落地。三层各司其职模型、工具、权限、上下文才能被拧成一股绳。这篇博文不追热点只讲架构。我会结合这次泄露源码中暴露出来的实现细节把“三层装配线”每一层负责什么、为什么这么拆、实际落地时怎么设计掰开揉碎讲清楚。同时把安装配置、模型接入、权限报错、上下文丢失这些常见问题也一并串进去。适合三类人看想把 Claude Code 这类工具用明白的人想自己开发 Agent 工具框架的人以及单纯想通过源码学习工程架构的人。1. 一次源码泄露为什么值得从架构角度拆解1.1 这不是一次普通的“吃瓜”网上流传的 Clauaude Code 相关源码片段里最值钱的信息其实不是某个具体功能而是整套代码的组织方式。你可以看到命令行入口怎么处理参数Agent 循环怎么维护状态工具调用怎么注册和分发模型提供商怎么适配安全策略怎么收敛。这些内容平时散落在文档里被各种营销号包装成“黑科技”这次相当于把底牌直接翻给你看。我当时对照自己实现的几个 Agent 项目逐一比对发现很多设计决策都似曾相识工具要声明 JSON Schema权限要分模式会话要落盘存档模型名要映射到具体的 provider。差别在于这个项目把这些做得非常干净每一层边界清晰你几乎可以沿着代码路径一路追下去从用户敲下命令到模型返回结果中间没有任何“坨”在一起的地方。1.2 三层装配线到底指什么用工厂打比方最直观第一层是 CLI 交互层。用户输入指令、配置参数、选择模式这一层全部接住。它负责把“人话”翻译成结构化指令再把结果渲染回终端。第二层是 Agent 编排层。这是整个框架的中枢。它维护对话历史、决定下一步调哪个工具、根据模型输出分发执行、收集结果再喂回模型形成一个循环。第三层是内核执行层。文件读写、命令执行、网络请求、上下文裁剪、模型网关全部在这里完成。它是真正“动手”的地方也是最需要安全管控的地方。这三层之间通过明确定义的接口通信。CLI 层不关心模型怎么选编排层不关心终端怎么渲染内核层不关心用户说了什么话。每一层都可以被替换、被测试、被单独优化。这就是“装配线”的本质流水线每个工位只干一件事但组合起来能完成复杂总装。1.3 我为什么建议你跟着走一遍很多人在用 Claude Code 这类工具时遇到“模型不识别”“工具没生效”“权限一直弹窗”就卡住了根本原因就是脑子里没有这张三层地图。你以为是一个整体其实是三个独立模块在协作。一旦你知道报错发生在哪一层排查范围直接缩小 80%。这篇文章后面的内容我会按照三条装配线逐层拆解每一层都会结合源码片段里的实现逻辑来讲同时给出实操层面的建议。你不需要提前读源码跟着我的思路走就能把整个工具框架的骨架搭出来。2. 第一层装配线CLI 交互层把所有入口拧成一股绳2.1 CLI 层到底管什么CLI 层是用户接触最频繁的部分很多人以为它就是“解析一下命令参数”但实际上它承担的工作量相当大。从泄露源码中可以看到入口启动时会做几件固定的事加载配置文件、初始化日志、检查环境变量、读取 claude_code 目录下的状态文件、然后才进入交互循环。交互循环本身也是一个微型状态机等待用户输入、解析指令、判断是否包含工具调用、执行工具、渲染结果、回到等待状态。这个循环跑得顺不顺直接影响用户体验。我见过不少自建工具把输入解析和工具执行写在同一个函数里结果改一个参数格式就要动到核心逻辑维护成本非常高。而这里 CLI 层只做“翻译”和“展示”执行逻辑全部交给下层这样才能保证入口稳定。还有一点值得注意CLI 层负责权限确认的交互。当编排层判定某个工具需要用户授权时CLI 层要在终端渲染出提示等用户输入 y/n再把结果回传给编排层。这个交互如果做得不好用户会疯狂按回车最终误授权。源码里的做法是把权限决策做成一个独立的“确认器”跑在交互循环里而不是分散在各个工具调用中。这算是一个很典型的分层设计案例。2.2 从泄露片段里看到的实现细节从泄露的源码片段可以确认几个细节配置管理是集中式的所有配置项API Key、模型名、代理地址、权限模式都被读入一个全局配置对象各层通过依赖注入拿配置而不是到处读环境变量。这看起来起眼实则非常重要。错误处理是分类的CLI 层捕获的错误会先判断类型再决定怎么展示。模型不存在的报错和网络超时报错展示策略完全不同。比如网上那张“deepseek-v4-pro is not a model this version of Claude Code recognizes”的截图本质上就是模型名映射失败CLI 层把底层错误包装成了人类可读的提示。输出是流式的模型生成 token 时CLI 层通过回调实时渲染而不是等全部生成完再统一打印。这背后是流式接口和渲染层彻底解耦。这些细节看起来小但决定了工具的“手感”。我自己的项目早期是全部生成完再打印后来发现用户体验天差地别改成流式之后才有“对话感”。2.3 CLI 层设计的三条实操建议第一入口要薄逻辑要下沉。入口文件只做启动、装配、监听信号这三件事其余全部委托给服务对象。不要因为入口文件好改就把所有逻辑堆进去。第二配置要分级。全局配置、项目配置、会话配置必须分开。Claude Code 允许你在项目里放配置文件覆盖全局设置这个设计非常实用。我建议自建框架时至少做两级全局级和项目级项目级配置的优先级要高于全局。第三把终端渲染做成独立的渲染器。命令输出、流式 token、工具执行日志、错误信息各自用独立的渲染方法。这样后续如果想接 TUI、WebSocket 或桌面版只需要替换渲染器核心逻辑一行都不用改。这正好回应当前很多人在折腾“Claude Code 桌面版”的需求——桌面版本质上只是换了一层皮核心还是那条装配线。3. 第二层装配线Agent 编排层工具框架的真正大脑3.1 Agent 循环和三步状态机如果说 CLI 层是门面编排层就是大脑。这里的核心是一个“Agent 循环”模型输出 → 解析结果 → 判断是否有工具调用 → 执行工具 → 把结果拼进上下文 → 再次请求模型 → 直到模型给出最终答复。这个循环本质上是三步状态机观察态读取当前对话历史和最近一次工具执行结果。决策态把上下文交给模型让模型决定是继续调工具还是给出答案。执行态按模型输出调用具体工具并把返回值加入上下文。我在自己项目里实现过这个循环踩过最大的坑是“死循环”。模型可能反复调用同一个工具永远不给出结论。源码里的做法是限制了单轮会话的最大工具调用次数达到上限后强制让模型基于已有信息作答。这个兜底机制非常重要不加的话一个错误指令能让框架空转很久。3.2 工具注册表模型和工具之间的“翻译官”编排层最关键的机制是工具注册表。每个工具在被调用之前都必须先在注册表里声明自己的名称、描述、参数 JSON Schema。这个声明会被拼进系统提示词让模型“知道”有哪些工具可用以及怎么调用。在设计层面这里最容易出现的误区是“注册表只存函数指针”。如果只是把函数存进 Map模型根本不知道这个函数是干什么的、参数怎么填。正确做法是每个注册项至少包含三部分元信息工具名称、一句话描述、详细说明参数 Schema结构化的参数定义包括必填项、类型、枚举值、默认值执行函数真正被调用的逻辑以及超时和重试策略。从泄露源码看这个项目的工具注册表是带描述缓存的。同一份工具声明会被反复拼进系统提示词每次都重新生成会浪费大量 token所以它做了缓存。这一点很值得学我在自建框架时就吃过亏一开始没缓存每次对话都多花不少 token。工具调用返回后返回值要经过“精简”再塞回上下文。因为工具输出可能非常长比如读取整个文件直接把原始内容塞回上下文很快就把上下文窗口撑爆。源码里的做法是截断过长输出并附带截断提示。这个细节就是工程上的“取舍艺术”——宁可牺牲一点信息也要保住对话的连续性。3.3 权限模型三层里最容易翻车的地方权限模型是编排层里最容易让人迷惑的模块。Claude Code 提供多种权限模式比如默认模式每个敏感操作都要确认、自动接受编辑模式文件编辑不再询问、计划模式只读不写。这些模式本质上是同一个权限决策器的不同配置。从源码里可以看到权限决策器有一套完整的判断优先级命中拒绝列表的操作直接拒绝命中允许列表的操作直接放行命中“需要确认”列表的操作弹窗询问用户未匹配任何规则的高风险操作按当前模式决定是询问还是默认拒绝。我见过很多人在自建工具时忽略权限设计让模型可以直接执行任意命令结果一朝被注入攻击就翻车。实际上权限模型应该成为编排层的“质检工位”每一件产品出厂前都得过检。这也是为什么网上很多人遇到“your organization has disabled Claude subscription access for Claude Code”这类报错时一头雾水——他们不知道组织级策略和本地权限策略是两套独立系统报错来自上层策略本地配置文件改再多也没用。3.4 会话快照与上下文管理Agent 循环跑起来之后另一个关键问题是中间状态存在哪里断线了怎么办换终端了怎么恢复源码实现里有一个清晰的会话快照机制每次对话轮次结束后当前会话的完整状态——包括消息历史、上下文文件引用、工具调用记录——会被序列化保存到本地目录。下次启动时CLI 层会读取这个快照恢复会话上下文。这就是为什么你关掉终端再打开还能接上之前的对话。上下文管理还有一个很核心的操作系统提示词与 tool definition 的组装。模型请求之前编排层会把系统提示词、工具声明、会话历史、最近的工具输出拼在一起拼完后还要做 token 预算检查。如果超出模型上下文上限会优先压缩历史消息而不是直接报错。这个“压缩优先于报错”的策略是考虑长期使用的关键设计。4. 第三层装配线内核执行层把能力关进笼子里4.1 模型网关为什么换模型会报错第三层是第一线和“外部世界”打交道的地方最重要的一项是模型网关。Claude Code 的核心模型来自 Anthropic但它并不只支持官方模型。源码里可以看到 Provider 适配层的存在不同模型提供商有独立的客户端实现通过统一接口暴露给上层。配置模型时你需要指定 provider、模型名、API 地址、密钥。这个设计让“接入 DeepSeek”这类需求变得可行——本质上就是新增一个 Provider 适配器。之前很多人在问“Claude Code 接入 DeepSeek 怎么配”对照源码就很好理解了你只需要在配置里填写 DeepSeek 的 provider 信息、模型名和密钥。但这里有个大坑模型名必须严格对应该 provider 支持的名称。如果填了一个不存在的模型名比如把 DeepSeek 的模型名填错或者 Claude Code 版本太老不认识新模型就会报出“deepseek-v4-pro is not a model this version of Claude Code recognizes”这种错误。这种报错的本质是模型网关的“模型解析器”无法在注册表里找到对应模型。排查思路也很简单先确认你用的版本是否支持该模型再看 provider 是否配对最后看模型名是否和官方文档完全一致大小写、连字符都要一致。我建议配置模型时先手动在终端请求一次该模型的接口确认能通再填进配置这样可以避免大部分“配置了但用不了”的问题。模型网关还有一个容易被忽略的功能模型名解析和重定向。源码里存在模型别名机制比如某些缩写会被映射到完整模型名。这个机制的目的很简单——用户不需要记住一串长 ID。我在实际使用中感觉到这个设计非常有价值。4.2 系统提示词与工具定义的动态拼装模型网关准备好之后还有一个关键环节把工具定义和系统提示词拼进请求体。这一步发生在内核执行层但决策信息来自编排层。从源码看系统提示词不是写死的字符串而是通过模板引擎动态生成的。模板里会注入当前日期、可用工具列表、工作目录信息、用户偏好等。工具定义部分则会遍历注册表把每个工具的 name、description、parameters 序列化成 JSON再嵌入系统提示词。这个设计的直接效果是模型每次收到请求时都能看到一份“恰到好处”的工具清单和上下文说明。而不是一股脑把所有工具都塞进去——那样既浪费 token又容易让模型混淆工具职责。很多自建 Agent 框架做不好工具调用就是因为没有做这个动态筛选和拼装。拼装完成后还有一道校验token 预估。如果请求体超过模型上下文限制内核层会采取“分段塞入”策略先保证系统提示词和工具定义完整再尽量多塞历史消息实在放不下就压缩旧消息。这比直接报错友好太多。4.3 执行沙箱与遥测保障“最后一百米”内核层还包括具体工具的执行比如文件读写、命令执行、网络请求。这里最核心的是安全边界。源码里对命令执行做了很严格的控制——不是直接把命令丢给系统 shell而是走了独立的命令运行器设置超时、捕获输出、限制工作目录。文件读写也有路径约束防止模型通过工具读写超出项目目录的敏感文件。这些约束配合编排层的权限决策形成了纵深防御。遥测与日志系统也是内核层的一部分。每次工具调用、每次模型请求、每次错误都会记录结构化日志。这个设计对线上排查极其重要。我之前遇到过一个诡异问题某个工具偶尔失败但复现不出来后来通过日志发现是偶发超时。如果没有遥测这种问题根本无从查起。5. 从事件反推工程实践自己搭工具框架时该抄哪些作业5.1 三层分离带来的四个直接收益看完这套三层装配线最大的收获不是“原来 Claude Code 是这么写的”而是理解了为什么这种拆分方式能长期演进。第一可替换性。模型提供商可以换终端渲染方式可以换工具实现可以换但架构骨架不变。Claude Code 能兼容 DeepSeek、能出桌面版、能加新工具靠的就是这个松耦合。第二可测试性。三层之间接口清晰每一层都可以独立 mock 测试。你可以不启动真实模型纯靠假数据测编排层的循环逻辑也可以不经过 CLI直接测试内核层的工具执行。这种可测试性让回归变得可控。第三权限治理的可审计性。权限决策集中在编排层所有敏感操作都有明确记录。一旦出安全问题可以顺着日志追溯整个决策链。第四上下文管理的可控性。因为状态管理集中在会话快照里所以断点续聊、多终端切换才会变成可能而不是每次都要从头开始。5.2 这次泄露给我们的安全教训既然题目是“从泄露源码看工程架构”工程实践这一节就必须说透泄密事件本身的教训。密钥管理是红线。从项目结构看密钥信息完全来自环境变量和本地配置文件源码库里没有任何硬编码密钥。这是正确的做法。但我见过很多个人项目为了省事把 API Key 直接写进源码一旦代码泄露损失远不止是架构暴露。依赖锁定要严肃对待。工具框架依赖大量第三方库如果依赖版本不做锁定供应链攻击的风险会成倍放大。泄露代码中多次出现第三方工具的调用说明对这个项目来说依赖审计不能松懈。最小化曝光原则。一条装配线不需要把每个零件的图纸都公开。对个人开发者来说至少要做到核心架构文档内部沉淀敏感配置绝不入仓库第三方服务凭据独立管理。这次事件最值得抄的作业不是哪段代码而是“分层之后每一层都可以单独做防护”的思路。5.3 从零开始搭建一个小型工具框架的落地步骤如果你看了前面的拆解想自己动手搭一个最小可用的“三层装配线”这里给一条我实际验证过的落地路径。第一步定义接口边界。先画出三层之间的接口规范CLI 层调用编排层时传入什么结构编排层调用内核层时返回什么结构。建议先用 TypeScript 或 Python 的 dataclass 定义清楚不要急着写逻辑。第二步实现内核层。从最底层的模型网关开始先接一个 provider打通“发请求 → 拿响应 → 解析内容”的最小链路。然后实现两个最基础的工具read_file 和 run_command配上最简单的安全校验。第三步实现工具注册表和 Agent 循环。让模型能够看到工具声明并基于声明输出调用请求。这个阶段你会体会到 JSON Schema 的意义——模型能不能正确填参数全靠 Schema 写得好不好。第四步实现 CLI 交互层。把编排层暴露成可调用的服务CLI 只负责读终端输入、调服务、渲染输出。到这个阶段你已经有一个能跑通的全链路框架了。第五步逐步加权限、会话、遥测。权限先做成“所有工具都询问”的保守模式会话快照做成 JSON 落盘日志先打印到标准输出。等主流程稳定再迭代优化。每一步都要跑通再做下一步否则调试成本会直线上升。我见过太多人一上来就同时写三层结果出现问题根本不知道是哪一层出的错。5.4 高频问题排查速查表结合这次源码分析和大量实际操作经验我把最常见的问题整理成一张表方便你对照排查现象可能所在层排查思路命令能启动但无响应CLI 层检查配置文件是否加载成功日志是否有启动报错提示模型不存在内核层确认模型名与 provider 支持完全一致确认版本支持工具调用后无结果编排层检查工具注册表是否声明了该工具参数 Schema 是否匹配权限弹窗反复出现编排层检查权限模式配置确认允许列表是否覆盖目标操作对话无法恢复上次内容编排层检查会话快照是否落盘成功目录是否可写上下文很快就满了编排层/内核层检查历史压缩策略是否生效工具输出是否做了精简修改配置不生效CLI 层检查配置优先级确认是否被项目级配置覆盖流式输出卡顿内核层/CLI 层检查网络连接确认流式接口是否有超时保护这张表的价值在于它告诉你解决问题的顺序而不是一上来就怀疑“是不是工具坏了”。框架大了以后绝大多数问题都出在层与层的交界处而不是某一层的内部。结尾我个人在实际操作中的体会是看完这份源码后最值钱的不是某个具体函数而是那种“每一层都知道自己该干什么”的克制感。很多项目写着写着就失控根因是层与层的边界模糊工具、权限、上下文、模型全绞在一起。三层装配线的思路本质上就是逼你自己先想清楚“每条生产线上到底有哪些工位”再开机生产。如果你正在用 Claude Code、正在尝试接入其他模型或者正在自己搭 Agent 工具不妨按这套三层框架重新审视手头的东西把每个问题定位到具体层你会发现排查思路突然就明了了。最后再分享一个小心得搭建这类框架时先让最薄的第一层跑通再去丰富第三层最后回头打磨第二层这个顺序能让你的返工率低很多。