乐于分享
好东西不私藏

Claude Code源码泄露事件:一场意外的开源,让我们看到了顶级AI工程的内功

本文最后更新于2026-03-31,某些文章具有时效性,若有错误或已失效,请在下方留言或联系老夜

Claude Code源码泄露事件:一场意外的开源,让我们看到了顶级AI工程的内功

Claude Code源码泄露事件:一场意外的开源,让我们看到了顶级AI工程的内功

2026年3月31日,程序员圈子里炸了——Anthropic的Claude Code完整源码意外流出。

事情的起因堪称教科书级别的”低级失误”:有人发现npm上@anthropic-ai/claude-code这个包的发布产物里,竟然混进了.map文件(source map文件,用于调试时将编译后的代码映射回源代码)。

更离谱的是,这个.map文件里藏了一个指向R2存储桶的链接,点开一看——好家伙,未混淆的TypeScript源码,整整齐齐打包在zip文件里,下载即用。

1903个文件,51万行代码。就像你走进一家米其林餐厅,后厨的门突然开了,所有秘方配方都摊在台面上。

这次泄露暴露的东西,远不止”几个隐藏功能”那么简单。它让我们看到了顶级AI团队是如何打磨一个产品的


第一反应:他们居然用React写终端?

当我看到技术栈时,第一反应是”什么鬼”:

Bun + TypeScript + React + Ink

React写网页我见多了,但用React写终端工具?这脑洞有点大。

原来他们用了一个叫Ink的框架,能把React组件渲染到终端。你看到的那些进度条、权限弹窗、工具调用动画,本质上都是JSX。

为什么这么做?

我一开始觉得是”过度设计”,但细想后发现这其实很聪明。

终端UI的复杂度被严重低估了:

  • Agent A在执行任务,Agent B在等待,Agent C被用户中断了——这状态怎么管?
  • 流式输出一边吐一边渲染,还要支持用户随时Ctrl+C——这监听器怎么加?
  • 10个工具并行调用,结果按顺序返回——这队列怎么调度?

如果用传统方式手搓,代码会变得极其复杂。而用React的状态管理范式,这些都有现成的方案。

这给我的启示是:不要被”终端CLI”这个标签限制住思维。现代前端生态的积累,完全可以迁移到命令行工具里。


核心引擎:1729行的while循环

整个Claude Code的大脑,就在src/query.ts这个文件里——1729行

我花了半天时间读懂它。核心逻辑其实很简单:

while (true) {// 1. 看看上下文太长了没,太长了就压缩一下// 2. 调Claude API,让它生成下一个动作// 3. 如果要调工具,执行工具,把结果塞回消息// 4. 如果不需要调工具,退出循环}

但这个简单的外表下,藏着一堆精心设计的细节。


上下文压缩:四层防御,而不是一刀切

Claude Code用久了会发现,对话很长的时候它不会直接报错,而是”自动压缩”。

我原以为就是简单地把旧对话合成一段摘要。

看了源码才知道,人家上了四层压缩机制,每层处理不同类型的信息冗余:

第一层:精准切除(HISTORY_SNIP)

直接把某些没用的中间结果删掉,连摘要都不留。

举个例子:你用grep搜了个日志,结果10万行,然后又说”只要前100行”。那10万行的结果就可以直接删了,反正你也不看。

第二层:缓存层编辑(Microcompact)

这个很黑科技。它不删消息,而是告诉API:”这些token你虽然缓存了,但以后别算进context了”。

就像你下载了一个10GB的文件,然后告诉系统”这个文件占的空间别算磁盘配额”。

第三层:结构化归档(CONTEXT_COLLAPSE)

把旧的对话轮次打包成”提交记录”,类似git的commit历史。需要的时候可以展开重放,不用的时候就是一条摘要。

第四层:暴力压缩(Autocompact)

前面三层都不够了,才动用这个终极大招——把整个历史压缩成一段自然语言摘要。

为什么这么复杂?

因为不同类型的信息,”保质期”不一样:

  • 工具调用的错误信息 → 几轮对话后就没用 → 直接删
  • 代码文件的上下文 → 整个对话期间都有用 → 归档保留
  • 用户的原始需求 → 可能需要多次回顾 → 压缩但保留要点

一刀切的压缩策略,要么浪费token,要么丢失关键信息。分层处理才是正解。

这个设计对我的启发很大:做系统优化时,不要指望”一个算法解决所有问题”。不同场景用不同策略,组合起来才能达到最优效果。


性能优化:不等模型说完,工具就开跑

这个设计是我觉得最精巧的,也是Claude Code响应快的关键。

传统做法是这样的:

1. 等模型把整个回复生成完2. 解析出要调哪些工具3. 依次执行工具4. 把结果返回给模型5. 开始下一轮

时间开销:模型生成2秒 + 工具执行3秒 = 总共5秒

Claude Code的做法:

1. 模型一边流式输出,一边解析2. 解析出一个工具调用,立刻执行3. 模型继续生成下一个工具调用4. 多个工具并行执行(如果是只读操作)5. 按接收顺序返回结果

时间开销:模型生成2秒 + 工具执行3秒(并行)= 总共3秒

并发安全性

每个工具都有一个标记:isConcurrencySafe(并发安全)。

  • 只读操作(read、grep、ls)→ 可以并行跑
  • 写操作(edit、bash、write)→ 必须独占执行

这样既提升了速度,又避免了竞态条件。

这就像餐厅上菜:汤和沙拉可以一起准备,但主厨炒菜的时候别人不能动同一个锅。


工具系统:没有继承,全是组合

我见过很多Agent框架的工具系统,都长这样:

abstractclassBaseTool {abstractnamestringabstractexecute(paramsany): Promise<Result>}classGrepToolextendsBaseTool { ... }classEditToolextendsBaseTool { ... }

Claude Code完全没这么干。它用工厂函数:

function buildTool<T>(config: {namestringschemaZodSchema<T>execute(input: T) =>AsyncGenerator<Result>permissionsPermission[]isReadOnlyboolean// ... 还有UI渲染、摘要生成等}) { ... }

为什么不用继承?

因为工具之间的差异太大了:

  • BashTool需要权限提示、超时控制、输出截断
  • ReadTool需要文件路径校验、大文件流式读取
  • EditTool需要diff预览、冲突处理

如果用继承,要么基类臃肿不堪,要么每个工具都要override一堆方法。

用组合模式,每个工具只声明自己需要的能力。简单、灵活、易扩展。


那些未发布的功能

源码里最吸引人的,当然是那些还没公开的功能。

Voice Mode(代号:Amber Quartz)

src/voice/目录下有一整套语音模式的实现。从代码看:

  • ✅ 功能基本完成,可以正常使用
  • ✅ 有专门的voice_stream API端点
  • ⚠️ 只支持Claude.ai OAuth登录(API key不行)
  • ⚠️ 有个紧急开关:tengu_amber_quartz_disabled

猜测:这个功能可能会作为Claude.ai的付费增值服务发布。

Bridge Mode(桥接模式)

这个有点意思。运行claude remote-control后,你的本地电脑就变成了一个”远程终端”,可以被claude.ai网页版操控。

场景是这样的:

  • 你在Claude.ai网页版聊天
  • Claude说”我来帮你改代码”
  • 网页版直接操作你本地的文件和终端
  • 不需要安装CLI,浏览器里就能完成

技术上:

  • 支持最多32个并发会话
  • 有JWT认证 + 可信设备机制
  • 企业管理员可以禁用这个功能

这个功能野心很大,相当于把Claude.ai变成了一个远程开发环境的入口。

Buddy(电子宠物)

这个是我没想到的——Claude Code内置了个虚拟宠物系统

而且代码已经写在发布版本里了,只是没开放入口:

// 18种宠物,包括鸭子、鹅、猫、龙、章鱼...constSPECIES = [  duck, goose, blob, cat, dragon, octopus, owl, penguin,  turtle, snail, ghost, axolotl, capybara, cactus, robot,  rabbit, mushroom, chonk]// 5个稀有度等级constRARITY = {common60,      // 60%概率uncommon25,    // 25%概率rare10,        // 10%概率epic4,         // 4%概率legendary1// 1%概率}// RPG式的属性constSTATS = ['DEBUGGING''PATIENCE''CHAOS''WISDOM''SNARK']

还有帽子、眼睛、闪光变体(Shiny,1%概率)。

最骚的是,18个宠物名字全部用hex编码

const duck = String.fromCharCode(0x64,0x75,0x63,0x6b// 'duck'

注释说:”有一个宠物名和模型代号撞了,所以全部hex编码避免构建检测。”

也就是说,这18个名字里有一个是Anthropic未发布的模型代号。

是哪个?我猜是”ghost”或者”dragon”——听着像模型名。不过也可能是”capybara”(水豚),这个梗在AI圈挺火的。


消融实验:用科研思维做工程

源码里有个flag叫ABLATION_BASELINE,打开后会一次性关闭所有高级功能:

if (feature('ABLATION_BASELINE')) {// 关闭思考模式  process.env.CLAUDE_CODE_DISABLE_THINKING = '1'// 关闭上下文压缩  process.env.DISABLE_COMPACT = '1'// 关闭自动记忆  process.env.CLAUDE_CODE_DISABLE_AUTO_MEMORY = '1'// 关闭后台任务  process.env.CLAUDE_CODE_DISABLE_BACKGROUND_TASKS = '1'}

这是机器学习里的消融实验(Ablation Study)——逐个关掉组件,看整体性能下降多少,从而评估每个组件的贡献。

把科研方法用到工程上,这还是第一次见。

说明Anthropic不是拍脑袋加功能,而是每个功能都有量化数据支撑。他们能回答:思考模式让准确率提升了多少?上下文压缩减少了多少token成本?自动记忆节省了多少重复劳动?

这种用数据驱动决策的文化,比任何技术细节都值得学习。


编译时Feature Flag:死代码消除

源码里有一堆这样的代码:

import { feature } from'bun:bundle'const voiceModule = feature('VOICE_MODE')  ? require('./voice/index.js')  : null

feature()是Bun的编译时宏。如果VOICE_MODE是false,那整个voice目录的代码在编译时就被物理删除了——不是不执行,是从二进制文件里彻底消失。

为什么要这么激进?

因为安全研究员会反编译二进制,挖掘隐藏功能。

运行时flag(if判断)只是不执行,但代码和字符串都在。编译时DCE才是真正的”不存在”。

讽刺的是,源码一泄露,这层保护就没用了。但设计思路还是很精妙的。


泄露原因:一个低级失误

技术上没什么复杂的:

发布npm包时,.map文件被打进去了  ↓.map文件里有个链接指向R2存储桶  ↓那个链接没有访问控制  ↓任何人下载.map文件,就能拿到源码

教训

1. package.json的files字段要白名单制

{"files":["dist/*.js","README.md"]// 不要写 "files": []  这样会发布所有文件}

2. CI里加检查脚本

# 检查打包产物里有没有.map文件npm packtar -tzf *.tgz | grep '\.map$' && echo"发现.map文件!" && exit 1

3. 源码存储要加鉴权

别把源码zip裸挂在CDN上,至少加个token验证或者IP白名单。


我的收获:从”炫技”到”务实”

读完这51万行代码,我最大的感触不是” Anthropic技术多牛”,而是他们真的很克制

你看:

  • 上下文压缩:不是一刀切,是四层分层处理
  • 工具系统:不用花哨的继承,用简单的组合
  • 性能优化:不等模型说完,工具就开始跑
  • 功能开发:每个功能都有消融实验数据支撑

没有为了炫技而炫技。每一行代码,都在解决真实问题。

这才是顶级团队的标志:技术是手段,不是目的。


结语

这次泄露对Anthropic来说是个意外,但对整个AI工程社区来说,是份意外之礼。

我们看到了一个顶级AI团队是如何打磨产品的:

  • 技术选型务实:React写终端,只要好用,为什么不用?
  • 架构设计分层:上下文压缩分四层,每层解决不同问题
  • 性能优化精细:流式工具并行,省下的每一秒都是用户体验
  • 功能开发科学:消融实验,用数据说话

这些,比知道”他们有语音模式”或者”有个电子宠物”要有价值得多。

当然,电子宠物确实挺好玩的。我已经在猜我会抽到哪只了。