页面顶部
已进入:阅读页
← 返回

个人网站文章2026-04-01lixiaolai.com作者:李笑来原文链接

51.2 万行泄露代码,透露出 AI 工程的未来

报道来得很快。代码有 51.2 万行。下面说的是它里面真正的东西。

报道来得很快。代码有 51.2 万行。下面说的是它里面真正的东西。

2026 年 3 月 31 日凌晨 4 点 23 分,一位名叫 Chaofan Shou 的实习生在 npm registry 上看见了一个 59.8 MB 的文件,明晃晃地摆在那里。它藏在 Anthropic 的 Claude Code 包里。Claude Code 是数十万开发者正在使用的 AI 编程工具,而这个文件是一份 source map,一种调试产物,作用就像编译代码的 X 光片:只要把它举到光下,内部结构就会对任何人一览无余。到中午,整个代码库已经被镜像到 GitHub。到傍晚,这个仓库已经有了 1100 个星。再到第二天早上,已经有人用 Rust 把它重写了一遍。

报道铺天盖地地来了。Fortune 把它称作“第二次重大安全泄露”。Gizmodo 则说,这次泄露来得“偏偏在最糟糕的时候”。Dev.to 和 Hacker News 上的分析,迅速把亮点罗列出来:KAIROS、Buddy、Undercover Mode、44 个 feature flag。

但 51.2 万行代码毕竟不是个小数目。大多数报道都只停留在“它有哪些功能”这一层,很少追问“这些功能究竟怎么工作”,以及“这到底意味着什么”。把全部 1332 个源码文件细读一遍之后,会看到一些最早那波报道根本没有触及的层次。

十八只动物#

在一个叫 types.ts 的文件里,大约四百行的位置,藏着一件奇怪的事。所有动物名字都被写成了十六进制编码:

const c = String.fromCharCode
export const duck = c(0x64,0x75,0x63,0x6b) as 'duck'
export const goose = c(0x67,0x6f,0x6f,0x73,0x65) as 'goose'
export const cat = c(0x63,0x61,0x74) as 'cat'

十八个物种,duck、goose、blob、cat、dragon、octopus、owl、penguin、turtle、snail、ghost、axolotl、robot、rabbit、mushroom、chonk,再加上一个别的。全部都用同一种方式编码。仿佛有人很努力地确保:这些词本身绝不出现在编译后的二进制里。

这些定义上方有一行注释:

"One species name collides with a model-codename canary in excluded-strings.txt."

到底是哪一个?代码没说。至少这里没说。

不过代码里倒是把它们的肖像留了下来。这些手工做的 ASCII 精灵图,就放在 sprites.ts 里,紧挨着那些十六进制编码。下面这十八只,完全照源码抄录(· 代表眼睛):

 duck           goose         blob
   __             (·>        .----.
 <(· )___         ||        ( ·  · )
  (  ._>       _(__)_       (      )
   `--´         ^^^^         `----´

 cat            dragon       octopus
  /_/\        /^\  /^\      .----.
 ( ·  ·)      <  ·  ·  >    ( ·  · )
 (  ω  )      (   ~~   )    (______)
 (")_(")       `-vvvv-´     /\/\/\/\

 owl           penguin       turtle
  /\  /\       .---.         _,--._
 ((·)(·))      (·>·)        ( ·  · )
 (  ><  )     /(   )\      /[______]\
  `----´       `---´        ``    ``

 snail          ghost          axolotl
 ·    .--.      .----.      }~(______)~{
  \  ( @ )     / ·  · \     }~(· .. ·)~{
    _`--´      |      |       ( .--. )
  ~~~~~~~      ~`~``~`~       (_/  _)

 ???????? cactus robot
 n______n n ____ n .[||].
( · · ) | |· ·| | [ · · ]
( oo ) |_| |_| [====]
 `------´       |    |      `------´

 rabbit mushroom chonk
  (__/) .-o-OO-o-. /\ /\
 ( · · ) (__________) ( · · )=( .. )=|· ·| ( .. )
 (")__(") |____| `------´

少了一个名字。在源码里,它对应的字节是:0x63, 0x61, 0x70, 0x79, 0x62, 0x61, 0x72, 0x61。

这些动物属于 /buddy,一个 Tamagotchi 式的电子宠物系统,在 4 月 1 日上线,也就是泄露发生后的第二天。每个用户会根据自己的账户 ID,通过带 seed 的伪随机数生成器,得到一只确定性的宠物。系统里有稀有度(从 common 到 legendary,legendary 的概率是 1%),有属性(DEBUGGING、PATIENCE、CHAOS、WISDOM、SNARK),还有各种帽子(crown、tophat、propeller、halo、wizard、tinyduck)。

这东西当然很可爱。但十六进制编码告诉你的是另一回事:在这十八个物种里,有一个名字敏感到 Anthropic 专门写了一个编译期扫描器 excluded-strings.txt,只要那个字符串的明文出现在产物里,整个构建就会直接失败。而他们选择把全部十八个物种名都统一编码,而不是只编码那个敏感词。这就像证人保护计划不是只转移一个人,而是把整条街一起搬走,好让真正受保护的身份不那么显眼。

这已经不只是工程了。这是谍报术。

理清代号#

模型代号是最先被传播出去的一批细节。早期分析找出了 Tengu、Fennec 之类的名字;后来的报道则试图把这些名字映射到模型层级上。但真正把源码仔细读一遍后,故事会微妙得多。

比如 Tengu。它根本不是某个模型的代号。它是 Claude Code 整个产品本身的项目代号​,指的是这个 CLI 工具,而不是任何模型。证据很直接:966 个分析事件使用 tengu_* 前缀,880 个 feature flag 以 tengu_ 开头。它们是产品遥测标记,不是模型标识。

Fennec 也不是 Opus 4.6。源码里有个迁移文件,叫 migrateFennecToOpus.ts,专门把旧的 fennec-latest 别名迁移到 Opus 产品线。这意味着 Fennec 是后来变成 Claude Sonnet 5 的内部代号,而 Sonnet 5 已经在 2026 年 2 月发布了。

把这些线索汇总起来,就能得到一张更准确的对应表:

代号 实际指代 证据来源
Tengu Claude Code 这个产品本身 966 个 logEvent('tengu_*') 调用,880 个 tengu_* feature flag
Fennec Claude Sonnet 5(2026 年 2 月发布) migrateFennecToOpus.ts 迁移文件
Capybara 位于 Opus 之上的全新第四层级 prompts.ts 注释里的 "Capybara v8"、"capy v8 counterweight"
Mythos 第一个 Capybara 层级产品(3 月 26 日因 CMS 泄露暴露) Fortune 报道,Anthropic 博客草稿
Numbat 尚未发布的下一代模型 prompts.ts:402:"Remove this section when we launch numbat"

Capybara 不是一个模型版本号,而是一个全新的层级​。这是 Anthropic 自 2024 年建立 Haiku / Sonnet / Opus 体系之后,第一次增加新的层级。它比 Opus 更大、更强,也更贵。命名风格也从诗性词汇转向了动物。水豚,世界上最大的啮齿动物,体型巨大却性格温和,正是那个藏在十六进制里的名字。

而这条时间线本身,也已经是一篇故事。3 月 26 日,一次 CMS 配置错误暴露了关于 "Claude Mythos" 的博客草稿,文案把它描述为能力上的"阶跃式跃迁",并称它"在网络安全能力上目前远远领先于任何其他 AI 模型"。代号先泄了。3 月 31 日,npm 上那份 source map 又把整个 Claude Code 代码库掀了出来,其中包括七套专门为隐藏这个代号而设计的安全系统,以及一条精确记录模型说谎频率的注释。源码也泄了。4 月 1 日,/buddy 上线,用户现在可以领养一只虚拟水豚,给它取名、摸摸它,而 Anthropic 原本花了几个月、用十六进制死死藏住的那个词,转眼就变成了一个玩具。

代号泄了。隐藏代号的代码也泄了。然后,代号成了玩具。

而在那堆代码里,还有一条任何新闻稿都不会写进去的注释。

虚假声明率#

prompts.ts 第 237 行写着:

// @[MODEL LAUNCH]: False-claims mitigation for Capybara v8
// (29-30% FC rate vs v4's 16.7%)

FC 指的是 false claims,也就是模型错误地汇报自己工作结果的频率。比如测试明明没过,却说过了;代码明明跑不通,却说已经运行成功;工作明明没完成,却说完成了。

Capybara v8 的数字是 29% 到 30%。几乎每三次,就会有一次。

上一代表现最好的模型,也就是注释里说的 v4,是 16.7%,大约每六次一次。

能力更强的模型,说谎频率却几乎翻了一倍。

v4 和 v8 的虚假声明对比图:v4 是 5 个绿色对勾配 1 个破裂红叉,v8 是 4 个绿色对勾配 2 个破裂红叉。

这条注释不是什么埋在角落里的冷门细节。它就在主 system prompt 构建器里,也就是 Claude 每次对话前都会收到的那套核心指令拼装逻辑里。紧跟在它后面的,还有一段只有 Anthropic 内部工程师才看得到的文字:

process.env.USER_TYPE==='ant'
  ? [`Report outcomes faithfully: if tests fail, say so with the
      relevant output; if you did not run a verification step,
      say that rather than implying it succeeded. Never claim
      "all tests pass" when output shows failures...`]
  : []

这里的 ==='ant' 检查,意味着这段指令只会编译进 Anthropic 的内部构建。外部用户,也就是每天都在用 Claude Code 的那几十万开发者,是拿不到这段指令的。这并不一定意味着 Anthropic 不在乎外部用户,而更像是它把系统运作成了一场药物试验:一条新指令先在内部人群上实验,测量效果,等数据支持之后,再决定是否给普通用户"开药"。

最早一波讨论曾顺手提到过这些数字,但它真正重要的地方,远远超出单个模型。

如果 Anthropic 迄今能力最强的模型,比前一代更频繁地产生 false claims,那么这可能指向一种随规模扩展而出现的系统性性质​。OpenAI 在 2025 年 9 月的研究也得出过类似结论:标准训练过程会奖励"猜测",而不是承认"不确定";一个"知道一点东西"的模型,反而比一个"什么都不知道"的模型更难说出"我不知道"。能力越强,自信越高,编造也越多。模型并不知道自己错了,它只是更擅长构造听起来极有道理的报告。

这里有一个值得记住的历史平行。Enigma 密码机在数学上几乎牢不可破,它的理论复杂度非常夸张。布莱切利园最终把它破掉,并不单靠数学天才,而是抓住了操作员层面的错误​:偷懒、重复按键、固定的报文开头、程序性抄近路。机器本身的能力没有问题,脆弱的地方在行为层。

Capybara v8 似乎也是同样的模式。它的 benchmark 分数,是 Anthropic 目前做出过最高的一档。它的问题不在计算层,而在行为层。Anthropic 也正像布莱切利园会做的那样,用明确的程序性指令去压制这种弱点:内部构建会反复告诉模型,先验证再汇报;确认了的结果就别含糊;没有证据就不要宣称成功。代码里甚至还有一个专门的 "verification agent",一个被 tengu_hive_evidence flag 卡住的对抗性子 agent,会在模型宣称自己完成实现之前,独立去检查那份实现工作。它专门用于识别并抵抗所谓的 "verification avoidance patterns",也就是模型倾向于"读代码代替跑代码"、跳过测试却自信汇报成功的那套习惯。

系统越强,弱点就越从技术问题转移成行为问题。1940 年的 Enigma 是这样,2026 年的大语言模型看起来也正在走向同样的方向。

Prompt 工厂#

最初的报道提到过,Claude Code 使用了一套模块化、具备 cache-aware boundary 的 system prompt。真正读源码之后,你会发现这种模块化远比表面看到的更深。

Claude Code 会把 system prompt 分六个阶段拼起来,而整个设计几乎都围绕着一个工程约束展开:​prompt cache 的经济学​。

每一次对 Claude 的 API 调用都要花钱。其中很大一部分成本,来自处理 system prompt,也就是模型在正式进入对话前必须先读完的那套行为指令。如果这套 system prompt 在调用之间完全一致,Anthropic 的基础设施就可以把它缓存起来,跳过重复处理。对于一份所有用户共用的 prompt 来说,只要有一次 cache miss,就意味着数百万次调用都在白白浪费算力。

所以 Anthropic 在 prompt 里划出了一条水线:

export const SYSTEM_PROMPT_DYNAMIC_BOUNDARY='__SYSTEM_PROMPT_DYNAMIC_BOUNDARY__'

水线以上的部分,对全球每一个 Claude Code 用户都完全相同:身份指令、编码规范、工具描述、权限规则。它会被打上 cacheScope: 'global' 标签,通过 Blake2b hash 共享。算一次,所有人一起用。

水线以下的部分,则是按会话变化的:你的环境信息、MCP 服务器说明、语言偏好、记忆内容。它会被打上 cacheScope: 'org' 标签,在组织内部共享。船体一样,装的货不一样。

那你的 CLAUDE.md 文件去哪了?它根本不进 system prompt。它被作为 user context 注入,走的是另一条通道,不会碰到那份已经缓存好的 system prompt。这就是为什么 Claude Code 会显得很快:行为指令被预先做成全球缓存,而项目上下文则像搭载在旁边的附加货物一样带进来,不会把缓存打碎。

整个 prompt 还有一套六层记忆层级:Managed(企业政策)→ User(~/.claude/CLAUDE.md)→ Project(随仓库提交)→ Local(私有、gitignore)→ AutoMem(自动抽取的记忆)→ TeamMem(组织共享记忆,通过 API 同步,并有 40 多条 secret scanning 规则做保护)。

后加载的文件,优先级更高。模型会更重视自己最后读到的东西。这个效果几乎像法域层级:地方法覆盖州法,州法覆盖联邦法。你的私有指令压过项目规则,项目规则压过企业政策。

对于那些真正自己在 LLM API 上做产品的人来说,这种围绕缓存组织起来的 prompt 架构,可能是整份泄露代码里最直接可复用的工程模式。

竞争维度#

源码深处,有一套被 feature('KAIROS') 保护起来的逻辑。最初报道把它描述成"一个持续运行的后台 agent"。但真正读进去之后,你会发现它远不止如此。

KAIROS 是一个完整的平台​,目的是把 Claude Code 变成一个常驻在线的 AI 同事。它里面包括:

  • 一个借助 Agent SDK 持续运行的 daemon process
  • Channels​,也就是和 Slack、Discord 以及其他消息平台双向通信的 MCP 集成层
  • Proactive mode​,通过周期性 <tick> prompt 把 agent 唤醒,让它主动找活干
  • terminal focus detection​,当你切出终端时,agent 会知道“用户现在没盯着看”,从而提高自主性
  • 直接面向用户的 push notifications 和 file sharing
  • 用于监控 PR 的 GitHub webhook subscriptions
  • 一个叫 SendUserMessage 的结构化沟通工具,真正给用户看的答复会走专门通道,而普通 stdout 会被藏进详情视图

这些东西,源码里都已经写完了。而在外部构建里,它们全部会编译成 false。所有逻辑都蜷在那里,藏在 GrowthBook 的 feature flags 后面,形态完整,只等某个布尔值从 false 翻成 true。

现在再看 OpenClaw,这个过去一年慢慢开始获得关注的开源个人 AI 助手。它的架构包括:

  • 一个持续运行的 gateway daemon​(openclaw gateway)
  • 18+ channel adapters​,覆盖 WhatsApp、Telegram、Slack、Discord、Signal、iMessage、Teams、Matrix 等
  • 通过 cron jobs 和 webhooks 实现的主动唤醒机制
  • 按渠道原生组织的 channel-native messaging
  • 借助平台原生 API 实现的 push notifications
  • 在 macOS / iOS / Android 上的 Voice Wake and Talk Mode

OpenClaw 与 KAIROS 的“平面图”:一个是开源虚线墙,一个是 Claude 专属实线墙,房间几乎一样,只是准入方式不同。

把两套架构并排放着看,会发现它们的蓝图几乎是同构的:持续运行的 daemon、channel 抽象层、外部推送入口、主动调度、结构化用户沟通。它们在解决的是同一个问题:怎样把语言模型变成一个常驻、连通各渠道、能够主动行动的助手。而面对同一条河,两位桥梁工程师独立得出了差不多同样的悬索桥设计。

差别只在于:OpenClaw 是开源的、模型无关的,而且今天就能跑;KAIROS 是闭源的、Claude 专属的,还躲在一个 flag 后面。

这里面的竞争动态很眼熟。2007 年 Apple 推出 iPhone 时,做的是一套纵向一体化堆栈:硬件、操作系统、应用商店、服务,全都由一家公司控制。Google 的回应是 Android:开源、谁都能拿去用、谁都能改。今天 Android 靠开放性和设备多样性,拿走了全球 72% 的智能手机市场;Apple 则靠生态锁定和高端定位,拿走了 68% 的应用收入。两边都赢了,只是赢在不同维度。

KAIROS 是 iPhone 路线:Claude-only、深度整合、端到端控制。OpenClaw 是 Android 路线:模型无关、社区驱动、运行在你自己的硬件上,让你自己选 provider。假如这个常驻 AI 助手最终原生长进 Claude Code,那么对 Claude 用户而言,OpenClaw 的必要性就会显著下降,Anthropic 将直接拿下整条堆栈。OpenClaw 的反制策略也很清楚:它本来就能 with Claude 一起工作(README 甚至直接推荐 Anthropic 模型),同时支持别的模型,让数据仍然掌握在用户手里。

最初 那一波报道把 KAIROS 当成“一个功能”来看。把它和 OpenClaw 并排之后,它看起来更像是一步战略动作。

七套系统与一个 dotfile#

Anthropic 的工程文化,有一件事通过这份源码被证明得毫无悬念:他们在安全上真的很强。

Undercover Mode 会根据 git remote 是否命中一个含 14 个仓库的 allowlist,自动判断你当前是在内部仓库还是公开仓库。如果是公开仓库,它会把提交信息中的模型代号洗掉,把 system prompt 里模型自己的身份信息压掉,同时去掉内部 Slack 频道引用。

excluded-strings canary 会在编译产物里扫描禁用字符串。只要扫到,构建就直接失败。这也是为什么那些宠物物种名要写成十六进制编码,因为二进制里一旦出现 capybara 明文,就会触发扫描器。

build-time dead code elimination 借助 Bun 的 feature() 宏工作。内部路径在外部构建里会被常量折叠成 false,然后在结构上直接删掉。不是说代码“存在但不会执行”,而是它压根不会出现在外部产物里。

GrowthBook feature flags​(880 多个)在运行时做服务器端控制,值会缓存到磁盘上,进程重启后还在,同时还会记录曝光数据,用于 A/B 实验。

codename masking function 会连内部显示也做部分遮蔽:比如 capybara-v2-fast 会被显示成 cap*****-v2-fast。工程师能看到足够的调试信息,却看不到足够清晰、足以在截图里暴露真正内容的信息。

anti-distillation defenses 会向 API 请求里注入假的工具定义。Alex Kim 最先记录了这个机制。它像银行金库里的染色包:如果竞争对手试图录下 API 流量去训练自己的模型,这些诱饵工具就会把偷来的数据污染掉。代码里还存在一个客户端哈希验证系统:HTTP 头里的 cch=00000 占位符,会在请求真正发出去之前,被 Bun 那套用 Zig 写的 HTTP 栈替换成计算好的哈希值。服务器收到后再验证哈希,确认请求确实来自真实的 Claude Code 二进制。

把十八个物种名统一写成 ​String.fromCharCode 的方式,也就是我们开头看到的那套编码,是第七套系统。统一编码,避免模式本身暴露“到底是哪一个物种敏感到必须隐藏”。

七套系统。每一套都真有点东西。

随便挑两套看都够说明问题。Undercover Mode 会检查每一个 git remote URL 是否命中内部 14 仓 allowlist,一旦发现是公开仓库,就会自动启动,在 commit message 还没签名前拦截它,把代号、Slack 频道和内部短链都洗掉。String.fromCharCode 那一层则更绝:它把整整十八个物种全部迁移进十六进制,让一个本来可以被 build scanner grep 到的禁词完全消失,而且就连编码方式本身,也不会泄露到底是哪一个名字触发了这层防护。

这不是打勾式的安全措施,而是一整套持续、创造性、带着对抗思维的工程实践。

军事史上,这种架构有个很贴切的名字。马奇诺防线曾是那个时代最复杂的防御工程:混凝土碉堡、反坦克障碍、炮台、地下铁路,应有尽有。它从未在正面战场上被攻破。德国人直接绕过去了,从法国规划者以为“不可能通行”的阿登森林穿了过去。

到 3 月 31 日,Anthropic 这七套系统遭遇了和马奇诺防线相同的命运。没有一套被攻破。也没有一套真正派上用场。

因为 Bun 默认会生成 source map,而且一则 3 月 11 日就有人报告过的 Bun bug显示,就算在不该暴露的生产模式下,source map 也仍然会被带出去。而与此同时,没有人把 *.map 写进 .npmignore。

canary 保护的是构建产物本身​,不保护和产物一起被打包出去的东西。Undercover Mode 保护的是 git commit​,不保护 npm package。feature flags 保护的是运行时行为​,不保护 tarball。

1999 年,NASA 因为一个团队用 pound-force 计算推力,另一个团队却默认单位是 newton,损失了 1.25 亿美元的 Mars Climate Orbiter。至少有两位工程师注意到了导航误差,但他们的警告被无视了,因为他们没有按规定流程提交文档。任务里的科学内容本身没有错,错的是两个系统交界处的假设不一致。

Anthropic 这里也一样。每一套系统都把自己那一亩三分地守得很好。但没有任何一套系统在守“系统和系统之间的边界”。

一行。六个字符,再加一个文件扩展名。

*.map

真正能阻止整件事发生的,就是这一行。不是 feature flag,不是 canary scanner,不是 String.fromCharCode 编码方案,也不是 anti-distillation 防御。

只是 dotfile 里缺掉的一行。

七套保护代码的安全系统。一个名字温顺得足以拿来当宠物的模型层级。一个随着能力增长而不断升高的虚假声明率。一场与 Android 和 iPhone 那场战争相互映照的平台竞争。一个已经写完、只等开关翻转的常驻助手。

所有这些,都装在一个 59.8 MB 的文件里,被放在公开 registry 上,任人阅读。所有这些,也都因为某个周一清晨、某个 dotfile 里缺掉的一行而暴露无遗。

主要来源:Claude Code CLI v2.1.88,从 claude-code-2.1.88.tgz 解包得到。文中所有文件路径与代码摘录,均对照原始源码树核验。