1. 项目概述:当AI开始审视自己的灵魂
最近在AI工程社区里,一个项目引起了我的注意,它叫“Claude Reviews Claude”。初看标题,你可能会觉得这是个玩笑或者某种行为艺术——一个AI模型(Claude)去阅读和分析自己(Claude Code)的源代码。但当我深入这个仓库,翻阅了那17个章节、近5000行的深度架构分析文档后,我意识到这远不止是一个“元”(Meta)玩笑。这是一个极其罕见的、由被分析对象自身完成的、对一套复杂AI代理(Agent)系统的完整工程解构。它为我们这些常年在一线构建AI应用的人,提供了一个前所未有的、从“第一人称”视角窥探顶级AI代理系统设计哲学和实现细节的机会。
这个项目基于Claude Code v2.1.88版本,一个包含1902个文件、超过47.7万行TypeScript代码的庞大工程。Claude Code本身是Anthropic公司推出的一个AI编码助手,但它的本质是一个运行在Bun环境上,集成了42+个工具、多智能体协作、复杂权限流水线和终端UI的生产级AI代理框架。而“Claude Reviews Claude”这个项目,就是让Claude这个AI模型,去逐行阅读、理解并撰写关于这个框架的架构分析报告。这感觉就像让一位建筑师走进一栋完全由他潜意识设计的大楼,并为我们绘制出详细的建筑蓝图和结构力学分析。
对于我们这些开发者而言,这个项目的价值是双重的。首先,它是一份关于Claude Code的、极其详尽的反向工程文档。市面上关于如何构建复杂、可靠、安全的AI代理系统的公开资料并不多,尤其是达到Anthropic这种级别工程水准的实战经验。这份分析几乎涵盖了从核心查询循环、工具编排、安全沙箱到UI状态管理的每一个子系统。其次,更重要的是,它展示了一个强大的AI如何理解和拆解一个复杂的软件系统。这本身就是一种高级的“代码理解”和“技术写作”能力的示范,对于从事AI辅助编程、自动化文档生成或智能代码分析领域的同行,具有直接的参考意义。
在接下来的内容里,我不会简单复述那17章的内容,而是会结合我过去在构建企业级AI代理平台时踩过的坑、做过的权衡,来重点解读我从这份“自述”中看到的几个关键设计范式、工程抉择以及那些隐藏在代码行间的“智慧”。我们会聊到那个驱动一切的“笨循环”,聊到如何像搭积木一样构建一个可扩展的工具生态系统,更会深入那个令人印象深刻的七层纵深防御安全模型。无论你是想深入理解Claude Code的设计,还是正在设计自己的AI代理系统,抑或单纯对大型AI工程项目的架构感到好奇,我相信这些来自“当局者”的洞察,都能给你带来实实在在的启发。
2. 核心架构哲学:智能在云端,脚手架在本地
通读整个分析报告,最核心、也最颠覆我以往认知的一个设计哲学跃然纸上:Claude Code将所有的“智能”严格归属于远端的Claude模型(LLM),而本地运行的这47万行代码,本质上是一个极其复杂、健壮的“脚手架”(Harness)或“执行环境”。这个认知是理解其所有子系统设计的钥匙。
2.1 一切始于一个“While True”循环
整个系统最核心的驱动引擎,在QueryEngine中,其核心逻辑可以简化为一个高度优化的状态机循环。这个循环的伪代码精髓如下:
async function queryEngineLoop(userInput, sessionState) { while (true) { // 1. 组装上下文:将对话历史、工具结果、系统提示等打包 const context = assembleContext(sessionState); // 2. 调用Claude API(流式):将上下文发送给“大脑” const streamResponse = await callClaudeAPIStreaming(context); // 3. 解析流式响应 for await (const chunk of streamResponse) { // 实时输出文本给用户 ui.streamText(chunk.text); // 关键判断:模型是否想使用工具? if (chunk.stop_reason === ‘tool_use’) { // 4. 提取工具调用请求 const toolCall = extractToolCall(chunk); // 5. 权限检查流水线(7层防御!) if (!permissionPipeline.check(toolCall)) { throw new PermissionDeniedError(); } // 6. 执行工具 const toolResult = await toolSystem.execute(toolCall); // 7. 将工具结果作为新一轮的“用户输入”注入循环 sessionState.appendToolResult(toolResult); // 跳回步骤1,继续循环 continue; } if (chunk.stop_reason === ‘end_turn’) { // 模型本轮思考结束,循环跳出 break; } } } }这个循环看似简单,但却是整个系统的“心脏”。它清晰地划分了职责边界:LLM负责思考、规划和决定何时调用何种工具;本地代码则负责提供工具、保障安全、管理状态、处理流式响应和维持会话。这种“云端智能,本地控制”的架构,带来了几个显著优势:
- 安全性可控:所有工具执行、文件访问、命令运行都发生在本地沙箱环境中,由本地代码严格管控。LLM只产生意图(Intent),不直接执行任何有风险的操作。
- 状态管理本地化:复杂的会话状态、工具调用历史、上下文压缩等都在本地维护,避免了将大量中间状态频繁发送至云端,既节省了成本(Token),也降低了延迟。
- 可靠性高:网络波动、API暂时不可用等问题,可以在本地循环中通过重试、回退等机制处理,而不需要LLM理解这些底层故障。
我的实操心得:在早期设计AI代理时,我们常犯的一个错误是试图让LLM“管理一切”,包括执行状态和错误处理逻辑。这会导致提示词(Prompt)异常复杂,且系统行为不稳定。Claude Code的设计明确告诉我们:让LLM做它最擅长的事(推理和规划),让传统的、确定性的程序代码做它最擅长的事(流程控制、状态管理和安全执行)。这是一种经典的“关注点分离”(Separation of Concerns)思想在AI时代的完美应用。
2.2 工具即插件:契约驱动的模块化设计
Claude Code集成了42个以上的工具(如文件读写、Bash执行、代码搜索、Git操作等)。分析报告揭示,其工具系统的设计非常优雅,采用了基于Schema(模式)的契约驱动和依赖注入模式。
每个工具都是一个独立的模块,必须导出一个符合ToolDefinition接口的对象。这个接口定义了工具的名称、描述、输入参数的模式(使用Zod或类似的Schema库进行声明)以及执行函数。
// 工具定义示例(基于分析推测) interface ToolDefinition { name: string; description: string; inputSchema: z.ZodSchema; // 使用Zod定义输入结构 execute: (args: any, context: ToolContext) => Promise<ToolResult>; } // 工具上下文,包含会话、权限、环境等信息 interface ToolContext { sessionId: string; workingDirectory: string; permissionLevel: PermissionLevel; // ... 其他依赖 }工具注册中心(ToolRegistry) 在系统启动时,会动态发现并加载所有符合契约的工具模块。当QueryEngine循环中需要执行工具时,它会通过注册中心按名称查找工具,首先用inputSchema验证传入的参数是否合法,然后注入ToolContext,最后调用execute方法。
这种设计的好处是极高的可扩展性和可测试性:
- 添加新工具:开发者只需创建一个新的符合接口的模块,系统启动时会自动发现并注册。无需修改核心引擎代码。
- 工具隔离:每个工具的错误不会直接影响其他工具或核心循环。工具执行在权限沙箱内进行。
- 便于Mock和测试:由于依赖通过
ToolContext注入,在单元测试中可以轻松替换为模拟对象。
报告特别提到,像BashTool这样的复杂工具,本身就有超过1.1万行代码,负责安全地执行Shell命令、管理子进程、处理流式输出和错误。但它对QueryEngine来说,只是一个提供了execute方法的黑盒,充分体现了模块化设计的威力。
3. 深入安全核心:七层纵深防御体系
如果说工具系统体现了灵活性,那么权限与安全子系统则体现了Claude Code面对“让AI在用户电脑上执行代码”这一高风险场景的极致审慎。报告将其概括为“七层纵深防御”,这绝非营销术语,而是一个环环相扣、逐级过滤的安全流水线。
3.1 权限检查流水线详解
当一个工具调用请求从LLM生成并进入本地循环后,它必须穿过以下七层检查,任何一层拒绝,整个请求都会被驳回:
- 配置规则匹配(Config Rules):最外层,基于用户或管理员的配置文件。例如,可以全局禁用
BashTool,或只允许在特定目录下执行文件操作。这是粗粒度的策略控制。 - 工具级权限声明(Tool-level Permissions):每个工具在定义时,可以声明自己所需的权限级别(如
READ_FILE,EXECUTE_SHELL,NETWORK_ACCESS)。系统会检查当前会话的权限令牌是否包含这些权限。 - 参数模式验证(Schema Validation):利用Zod Schema对工具输入参数进行严格的类型和格式校验。防止LLM构造畸形或越界的参数进行注入攻击。
- 静态AST分析(Static AST Analysis):针对
BashTool或代码执行类工具,系统会对传入的命令字符串或代码片段进行抽象语法树分析。它会检测是否存在明显的高风险模式,例如尝试执行rm -rf /、尝试访问敏感路径(如~/.ssh)、或包含可疑的网络下载命令(curl | bash)。 - 动态YOLO分类器(Dynamic YOLO Classifier):这是一个非常有趣的层。对于无法通过静态分析完全判断的命令,系统有一个轻量级的机器学习模型(报告中戏称为“YOLO”,即You Only Look Once),对命令进行实时分类,判断其风险等级。这可能是基于命令n-gram、参数特征等训练的模型。
- 操作系统级沙箱(OS-level Sandbox):这是最后一道,也是最坚固的防线。工具(尤其是Bash工具)的实际执行并非在宿主进程直接进行,而是被抛入一个严格限制的沙箱环境。这个沙箱可能基于:
- 容器技术:如Docker,但为单命令启动容器开销较大。
- 命名空间隔离:在Linux上使用
unshare,cgroups来隔离进程树、网络、文件系统视图。 - 专用沙箱进程:一个长期运行的、权限被极度剥离的守护进程,所有命令都在其中执行。 沙箱会限制文件系统访问范围(
chroot或bind mount到临时工作区)、网络访问(可能完全禁用或只允许白名单)、以及系统调用(通过seccomp过滤)。
- 实时监控与熔断(Runtime Monitoring & Circuit Breaker):即使在沙箱内执行,系统也会监控工具的执行时间、CPU/内存占用。如果某个工具调用消耗资源异常或长时间不返回,会被强制终止,防止拒绝服务攻击或死循环。
3.2 安全设计的启示
这套设计给我的震撼在于它的深度和层次感。它不依赖任何单一“银弹”,而是构建了一个从策略到语法,从静态到动态,从应用到系统层的立体防御网。
踩坑经验分享:在我们自己的项目中,最初只做了简单的关键字过滤和路径白名单,结果很快被“绕过”。例如,LLM可能会构造
/bin/echo ‘恶意内容’ > ~/.bashrc这样的命令,或者使用反引号、变量替换等技巧。Claude Code的AST分析层就是为了应对这种“语义层面的攻击”。而YOLO分类器则是对抗“未知威胁”的尝试,因为总会有静态规则覆盖不到的边缘情况。最后,操作系统沙箱是承认“本地代码总有被绕过的可能”,因此必须假设最坏情况,进行物理隔离。
给实践者的建议:如果你的AI代理也需要执行本地操作,即使做不到七层,也应遵循“纵深防御”原则,至少实现:
- 声明式权限模型:明确每个操作需要的权限。
- 输入验证与净化:严格校验所有来自LLM的输入。
- 资源限制与隔离:使用子进程、资源限制(
ulimit)或轻量级容器(如gVisor,Firecracker)来运行不可信代码。 - 审计日志:详尽记录每一个工具调用及其结果,便于事后追溯和审计。
4. 上下文管理的艺术:在有限窗口内承载无限对话
大型语言模型的上下文窗口是宝贵且有限的资源。Claude Code面对长对话、多工具调用产生的海量历史信息,设计了一套精巧的四层压缩与装配系统,其目标是在不丢失关键信息的前提下,最大化利用有限的Token窗口。
4.1 上下文装配的三层结构
在每一次循环开始,Context Assembler负责组装发送给Claude API的提示信息。这个提示不是简单的对话历史拼接,而是结构化的三层:
- 系统提示词(System Prompt):这是模型的“宪法”和“角色设定”,定义了Claude Code的身份、核心规则、可用工具列表及其详细描述。这部分相对固定,但也会根据会话模式(如“严谨模式”、“创意模式”)进行微调。
- CLAUDE.md记忆系统:这是一个非常巧妙的设计。Claude Code会在用户的工作区维护一个或多个
CLAUDE.md文件。这个文件不是由用户手动编写,而是由AI在对话过程中自动总结和更新的。它记录了项目的关键信息、对话中达成的重要决策、待办事项、以及需要长期记忆的上下文。在每次组装上下文时,系统会智能地提取CLAUDE.md中与当前对话最相关的片段注入,从而实现了一种超越单次会话的、项目级的持久化记忆。 - 本轮会话的即时上下文:包括:
- 最近的对话历史:经过压缩处理后的前几轮问答。
- 上一轮的工具调用及结果:这是驱动循环的关键。
- 当前轮的用户附件:如用户上传的代码文件、图片等,会经过处理(如提取文本、生成描述)后加入上下文。
4.2 四级级联压缩策略
当会话历史过长,超过预设的Token阈值时,压缩系统会启动,按照以下优先级和策略进行压缩:
- 微压缩(Micro-compaction):第一道防线,对历史消息进行“无损修剪”。例如,删除冗长的空白字符、标准化缩进、将长路径替换为缩写。目标是节省少量Token而不改变语义。
- 会话记忆摘要(Conversation Memory Summarization):如果微压缩后仍超限,系统会启动摘要过程。它不会简单地丢弃最早的对话,而是会调用Claude模型(一个更小、更便宜的模型或专用摘要模式),对较早的、非核心的对话轮次进行总结,用一段简短的摘要文本替换掉大段的原始对话。摘要会保留关键决策、事实和行动项。
- LLM驱动的智能截断(LLM-driven Truncation):当历史仍然过长,且摘要也不够时,系统会请求LLM对当前整个上下文(包括历史、工具结果等)进行分析,识别出哪些部分对当前待解决的问题最不相关,然后直接移除这些部分。这是一种有损的、但目标明确的压缩。
- 紧急压缩(Emergency Compaction):作为最后手段,如果上述方法都无效或超时,系统会启动一个激进但快速的压缩策略,可能包括:只保留最近N轮对话、只保留工具调用结果而删除详细过程、或者用极简的标签替换大段代码。
我的体会:这套压缩策略的核心思想是价值保留而非信息保留。它承认无法在有限窗口内保存所有原始信息,因此优先保留对“推动任务前进”最有价值的内容。
CLAUDE.md的设计尤其精妙,它将重要的、结构化的知识外化到文件系统中,减轻了模型上下文记忆的负担,同时提供了一种人机可读、可编辑的持久化协作界面。在实际项目中,我们也可以借鉴这种“外部记忆体”的思路,例如利用向量数据库存储历史对话的嵌入,在需要时进行语义检索和召回,实现更智能的长期记忆管理。
5. 多智能体协作:从单兵作战到团队作业
Claude Code不仅仅是一个与用户对话的单一代理。它的“Swarm”(集群)子系统允许它创建和管理多个并行的“工作线程”,以分治的方式处理复杂任务。这标志着AI代理从“单智能体”向“多智能体系统”的演进。
5.1 协调器模式
报告中的Coordinator模块是集群的大脑。它的工作流程如下:
- 任务分解:当主Claude(我们可以称之为“经理”Agent)判断当前任务可以并行化或需要专门技能时(例如,“同时运行单元测试和代码风格检查”),它会请求
Coordinator创建子任务。 - 工作线程孵化:
Coordinator并不总是创建新的系统进程。它根据任务类型,选择三种后端之一:- iTerm2后端:在macOS上,直接在新的iTerm2窗口或选项卡中启动一个独立的Claude Code进程。隔离性最好,资源独立。
- tmux后端:在支持tmux的环境中,在新的tmux窗格中启动进程。共享终端会话,便于用户观察。
- 进程内后端:最轻量级,在同一Node.js/Bun进程中创建新的
QueryEngine实例和会话状态。开销小,但隔离性差,适合简单、可信的任务。
- 进程间通信(IPC):各工作线程之间并非孤岛。它们通过一个抽象的“邮箱”系统进行通信。工作线程可以将状态更新、部分结果或请求帮助的消息投递到邮箱,
Coordinator或“经理”Agent可以读取并协调。 - 结果汇总:各工作线程完成任务后,将结果返回给
Coordinator。“经理”Agent再对这些结果进行整合、去重和总结,最终呈现给用户。
5.2 多智能体设计的挑战与解决
这种设计带来了显著的效率提升,但也引入了复杂性:
- 状态同步:每个工作线程有自己的会话状态。
Coordinator需要谨慎管理哪些状态(如文件更改)需要在智能体间同步,以避免冲突。 - 权限委托:子工作线程执行操作时,其权限范围不能超过主会话。
Coordinator需要实现安全的权限令牌传递和范围限制机制。 - 资源竞争:多个线程同时读写文件或运行命令可能导致竞态条件。系统可能采用了文件锁或任务队列来管理对共享资源的访问。
- 错误处理:一个工作线程的失败不应导致整个任务崩溃。
Coordinator需要监控线程健康状态,并能重启失败的任务或重新分配工作。
报告指出,Claude Code定义了7种不同的任务类型(如“代码分析”、“测试运行”、“文档生成”等),Coordinator可以根据类型选择最优的后端和资源分配策略。
对于我们的启示:在构建需要处理多步骤、多维度复杂任务的AI应用时,考虑引入多智能体架构是很有价值的。关键设计点在于:
- 明确的角色划分:每个Agent应有清晰的职责边界(如“分析者”、“执行者”、“审查者”)。
- 轻量级通信协议:定义简单的消息格式(如JSON Schema)用于Agent间通信,避免复杂的RPC。
- 集中式协调与去中心化执行:由一个“协调者”负责任务分解和结果汇总,但执行过程尽可能并行和独立。
- 考虑使用现有的多智能体框架:如AutoGen、CrewAI等,它们已经解决了部分通信和协调的通用问题。
6. 工程基础设施与可观测性
一个成熟的生产级系统,离不开强大的基础设施支持。Claude Code在这方面的投入也令人印象深刻,涵盖了配置管理、功能开关、遥测和构建发布。
6.1 配置合并管道
系统的配置来源多样:默认配置、用户级配置(~/.config/claude-code)、项目级配置(项目目录下的.clauderc)、以及命令行参数。报告提到一个“设置合并管道”,它定义了清晰的优先级顺序(通常是:命令行 > 项目配置 > 用户配置 > 默认配置),并负责将这些配置深度合并(deep merge),处理可能存在的冲突。这保证了系统行为的灵活性和可预测性。
6.2 GrowthBook功能开关
系统集成了GrowthBook——一个开源的功能标志(Feature Flag)服务。这意味着Anthropic可以在不发布新版本的情况下,动态地为部分用户开启或关闭某些功能,进行A/B测试,或者快速回滚有问题的特性。例如,“是否启用新的代码压缩算法”、“是否向10%的用户展示实验性UI”等。这是互联网产品开发的成熟实践在AI桌面应用中的体现,极大地提升了交付和迭代的灵活性。
6.3 双通道遥测与隐私
遥测(Telemetry)对于改进产品至关重要,但也必须尊重用户隐私。Claude Code设计了“双通道”遥测:
- 匿名性能遥测:收集匿名的、聚合的性能数据,如工具调用耗时、API延迟、错误类型分布等。这些数据不包含任何代码、文件内容或个人身份信息(PII),用于监控系统健康度和发现性能瓶颈。
- 可选的诊断数据:在用户明确同意并可能手动触发的情况下,可以上传包含更多上下文(如错误堆栈、非敏感的系统信息)的诊断报告,用于调试复杂问题。
报告还提到了“卧底模式”和“远程紧急开关”。前者可能指一种增强的匿名化模式,后者则允许Anthropic在发现严重安全漏洞时,远程禁用某些高危功能,直到用户更新客户端。这体现了对用户安全的高度责任感。
6.4 构建与发布
一个47万行TypeScript的项目,构建过程必然复杂。项目使用Bun作为运行时和包管理器,构建流程可能包括:TypeScript编译、Tree Shaking、代码分割、资源打包、生成不同平台(macOS, Windows, Linux)的安装包等。一个高效的构建系统是保障团队持续交付能力的基础。
7. 总结与展望:从阅读到构建
“Claude Reviews Claude”这个项目,就像一位顶尖工程师的详尽设计文档和代码评审笔记。它不仅仅让我们看懂了Claude Code是怎么工作的,更重要的是,它向我们展示了一套构建复杂、可靠、安全且用户友好的AI代理系统的完整方法论和最佳实践。
回顾其核心设计,我们可以提炼出几个关键原则,这些原则对于任何有志于构建AI代理的团队都极具参考价值:
- 清晰的职责边界:LLM是战略大脑,本地代码是战术执行者。绝不把执行、安全和状态管理的重任交给不稳定的LLM。
- 模块化与契约驱动:通过清晰的接口(如ToolDefinition)定义组件,通过依赖注入管理复杂度,使系统易于扩展、测试和维护。
- 纵深防御安全:安全不是单一功能,而是一个贯穿始终的体系。从配置、验证、静态分析、动态检测到操作系统隔离,层层设防。
- 上下文是稀缺资源:必须精心设计压缩、摘要和外部记忆策略,让有限的Token窗口发挥最大价值。
CLAUDE.md是一个天才的实践。 - 为协作而设计:无论是人机协作(通过清晰的UI和状态管理),还是机机协作(通过多智能体集群),系统都应支持将复杂任务分解并高效完成。
- 工程化与可观测性:采用功能开关、配置管道、遥测等现代软件工程实践,确保系统可管理、可监控、可快速迭代。
最后,这个项目本身也预示着一个趋势:AI正在成为理解和分析复杂系统(包括它自己)的强大工具。未来,我们或许会看到更多由AI生成的架构分析、代码注释、系统文档甚至重构建议。作为开发者,我们的角色可能从“一切的编写者”逐渐转向“复杂系统的设计者和AI协作的引导者”。
如果你对AI代理架构感兴趣,我强烈建议你不仅阅读这份分析报告,更可以去探索报告中提到的那些社区还原的源代码仓库。亲手翻阅那些TypeScript文件,看看QueryEngine的状态机是如何实现的,PermissionPipeline的七层检查是如何串联的。这种从“元分析”到“源码印证”的学习过程,比阅读任何二手教程都来得深刻。毕竟,最好的学习方式,就是站在巨人的肩膀上,而这次,巨人的肩膀上还坐着一个正在画自画像的AI。