<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <author>
    <name>Yufeng He</name>
  </author>
  <generator uri="https://hexo.io/">Hexo</generator>
  <id>https://he-yufeng.github.io/</id>
  <link href="https://he-yufeng.github.io/" rel="alternate"/>
  <link href="https://he-yufeng.github.io/atom.xml" rel="self"/>
  <rights>All rights reserved 2026, Yufeng He</rights>
  <subtitle>Ad Astra Per Aspera</subtitle>
  <title>Yufeng He | 何宇峰</title>
  <updated>2026-03-31T13:28:39.102Z</updated>
  <entry>
    <author>
      <name>Yufeng He</name>
    </author>
    <category term="技术" scheme="https://he-yufeng.github.io/categories/%E6%8A%80%E6%9C%AF/"/>
    <category term="Claude Code" scheme="https://he-yufeng.github.io/tags/Claude-Code/"/>
    <category term="AI Agent" scheme="https://he-yufeng.github.io/tags/AI-Agent/"/>
    <category term="Anthropic" scheme="https://he-yufeng.github.io/tags/Anthropic/"/>
    <category term="源码分析" scheme="https://he-yufeng.github.io/tags/%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90/"/>
    <category term="TypeScript" scheme="https://he-yufeng.github.io/tags/TypeScript/"/>
    <content>
      <![CDATA[<p>今天（2026.3.31）发生了一件大事：Anthropic 的 Claude Code 完整源码泄露了。</p><p>起因很离谱——有人发现 npm 上 <code>@anthropic-ai/claude-code</code> 的包里残留了 <code>.map</code> 文件，map 里有个指向 Anthropic R2 存储桶的下载链接，点进去是完整的未混淆 TypeScript 源码。zip 一解压，1903 个文件，51 万行代码，全部摊在眼前。</p><p>作为一个日常使用 Claude Code 的重度用户（也是 AI Agent 方向的开发者），我立刻 clone 下来通读了一遍。下面是我觉得最有价值的发现，按”有意思的程度”排序，不是按目录结构排。</p><h2 id="先说技术栈，刷新了我的认知"><a href="#先说技术栈，刷新了我的认知" class="headerlink" title="先说技术栈，刷新了我的认知"></a>先说技术栈，刷新了我的认知</h2><p>Bun + TypeScript + React + Ink。没错，一个终端 CLI 工具的 UI 层用的是 React。</p><p>Ink 是一个把 React 组件渲染到终端的框架，Claude Code 用它做了整个 TUI 界面——你看到的那些权限弹窗、进度条、工具调用的实时展示，底下全是 JSX。</p><p>说实话一开始挺意外的，但想想也合理：终端 UI 的状态管理确实比想象中复杂（多个 Agent 并行、流式输出、用户中断…），用 React 的状态模型来管比手搓要靠谱得多。</p><h2 id="Agentic-Loop：一个-while-true-撑起整个-Agent"><a href="#Agentic-Loop：一个-while-true-撑起整个-Agent" class="headerlink" title="Agentic Loop：一个 while(true) 撑起整个 Agent"></a>Agentic Loop：一个 while(true) 撑起整个 Agent</h2><p>整个 Claude Code 最核心的文件是 <code>src/query.ts</code>，1729 行，实现了完整的 agentic 循环。注意不是 <code>QueryEngine.ts</code>——那个是外层的会话管理。真正的”大脑”在 <code>query.ts</code> 里。</p><p>简化后的核心逻辑：</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">async</span> <span class="keyword">function</span>* <span class="title function_">queryLoop</span>(<span class="params">params</span>) &#123;</span><br><span class="line">  <span class="keyword">let</span> state = &#123; messages, toolUseContext, <span class="attr">turnCount</span>: <span class="number">1</span>, ... &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">while</span> (<span class="literal">true</span>) &#123;</span><br><span class="line">    <span class="comment">// 1) 一堆预处理：裁历史、压缩上下文、预取 memory 和 skills</span></span><br><span class="line">    <span class="comment">// 2) 调 Claude API（流式）</span></span><br><span class="line">    <span class="comment">// 3) 一边收流一边看有没有 tool_use block</span></span><br><span class="line">    <span class="comment">// 4) 有的话 → 检查权限 → 执行 → 结果塞回 messages → 回到 while</span></span><br><span class="line">    <span class="comment">// 5) 没有工具调用 → 退出</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>看着简单，但魔鬼在细节里。我挑几个最有意思的说。</p><h3 id="上下文管理：不是一刀切，是四把手术刀"><a href="#上下文管理：不是一刀切，是四把手术刀" class="headerlink" title="上下文管理：不是一刀切，是四把手术刀"></a>上下文管理：不是一刀切，是四把手术刀</h3><p>用过 Claude Code 的人都知道，长对话到后面它会自动”压缩”。我之前以为就是简单地把早期对话摘要一下。读了源码才知道，它其实有四种不同粒度的压缩机制在同时工作：</p><ol><li><p><strong>HISTORY_SNIP</strong>：最精细，直接把某些特定消息范围删掉，不做任何摘要。适合清理掉已经没用的中间工具调用结果。</p></li><li><p><strong>Microcompact</strong>：利用 API 层的 <code>cache_deleted_input_tokens</code> 能力，在缓存层面做编辑。这个比较黑科技——它不改消息内容，而是告诉 API”这些 token 你缓存里有但别用了”。</p></li><li><p><strong>CONTEXT_COLLAPSE</strong>：把旧的对话轮次”归档”成摘要，维护一个类似 git 提交日志的结构。每次新查询时重放这个日志。和 autocompact 的区别是它保留了结构化的归档，不是一坨摘要。</p></li><li><p><strong>Autocompact</strong>：最粗暴的一种，把整个历史压缩成一段摘要。最后的兜底手段。</p></li></ol><p>这四种机制不互斥，按顺序依次执行。如果 snip 和 microcompact 已经把上下文压到阈值以下了，autocompact 就不触发。</p><p>这给我的启发挺大的——做 Agent 的上下文管理不能只有一种策略。不同场景下信息的”过期速度”不一样，需要分层处理。</p><h3 id="流式工具并行：模型还在说话就开始干活了"><a href="#流式工具并行：模型还在说话就开始干活了" class="headerlink" title="流式工具并行：模型还在说话就开始干活了"></a>流式工具并行：模型还在说话就开始干活了</h3><p>这是我觉得最精巧的设计。普通的实现是：等模型说完 → 看有没有工具调用 → 有的话执行 → 结果返回 → 下一轮。Claude Code 不等。</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// StreamingToolExecutor.ts</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">class</span> <span class="title class_">StreamingToolExecutor</span> &#123;</span><br><span class="line">  <span class="comment">// 模型流式吐出一个 tool_use block，立刻开始执行</span></span><br><span class="line">  <span class="title function_">addTool</span>(<span class="attr">block</span>: <span class="title class_">ToolUseBlock</span>, <span class="attr">message</span>: <span class="title class_">AssistantMessage</span>): <span class="built_in">void</span> &#123; ... &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 并发安全的工具可以同时跑，写操作独占</span></span><br><span class="line">  <span class="comment">// 结果按接收顺序缓冲、按序输出</span></span><br><span class="line">  <span class="keyword">async</span> *<span class="title function_">getRemainingResults</span>(): <span class="title class_">AsyncGenerator</span>&lt;<span class="title class_">MessageUpdate</span>&gt; &#123; ... &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>模型还在流式输出后面的内容，前面的工具就已经在跑了。每个工具有个 <code>isConcurrencySafe</code> 标记——读文件、grep 这种只读操作可以并行，写文件、bash 这种需要独占。结果按接收顺序缓冲，不会乱序。</p><p>实测下来 Claude Code 的响应速度明显比 Cursor 快，有一部分原因应该就在这里。</p><h3 id="max-output-tokens-自动恢复"><a href="#max-output-tokens-自动恢复" class="headerlink" title="max_output_tokens 自动恢复"></a>max_output_tokens 自动恢复</h3><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> <span class="variable constant_">MAX_OUTPUT_TOKENS_RECOVERY_LIMIT</span> = <span class="number">3</span></span><br></pre></td></tr></table></figure><p>模型输出撞到 token 上限？循环不报错，”扣留”这个错误消息，悄悄重试，最多 3 次。对用户来说是无感的。</p><p>这段代码上面有个很有意思的注释，是一段模仿中世纪巫师口吻的话：</p><blockquote><p><em>Heed these rules well, young wizard. For they are the rules of thinking, and the rules of thinking are the rules of the universe. If ye does not heed these rules, ye will be punished with an entire day of debugging and hair pulling.</em></p></blockquote><p>翻译过来就是：”好好记住这些规则，年轻的巫师。如果你不听，你就等着花一整天调试和薅头发吧。”</p><p>看得出来这块代码的维护者被坑过不少次。</p><h2 id="工具系统：不用-class-继承，全是工厂函数"><a href="#工具系统：不用-class-继承，全是工厂函数" class="headerlink" title="工具系统：不用 class 继承，全是工厂函数"></a>工具系统：不用 class 继承，全是工厂函数</h2><p>我之前做 Agent 框架的工具系统，下意识会写一个 <code>BaseTool</code> 基类然后继承。Claude Code 完全没有继承，全是纯函数式的 <code>buildTool()</code>：</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">type</span> <span class="title class_">ToolDef</span>&lt;T&gt; = &#123;</span><br><span class="line">  <span class="attr">name</span>: <span class="built_in">string</span></span><br><span class="line">  <span class="attr">description</span>: <span class="built_in">string</span></span><br><span class="line">  <span class="attr">inputSchema</span>: <span class="title class_">ZodSchema</span>&lt;T&gt;           <span class="comment">// Zod v4 做校验 + 自动生成 JSON Schema</span></span><br><span class="line">  <span class="title function_">call</span>(<span class="attr">input</span>: T, <span class="attr">ctx</span>: <span class="title class_">ToolUseContext</span>): <span class="title class_">AsyncGenerator</span>&lt;...&gt;</span><br><span class="line">  <span class="title function_">isReadOnly</span>(): <span class="built_in">boolean</span></span><br><span class="line">  <span class="title function_">getPermissions</span>(): <span class="title class_">ToolPermission</span>[]</span><br><span class="line">  renderToolUse?(<span class="attr">input</span>: T): <span class="title class_">ReactNode</span>  <span class="comment">// 直接渲染到终端</span></span><br><span class="line">  getToolUseSummary?(input, result): <span class="built_in">string</span>  <span class="comment">// 压缩上下文时的摘要</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>每个工具完全自包含：schema、权限、执行逻辑、UI 渲染、压缩摘要，全在一个文件里。没有全局注册表——每个 session 动态组装工具池，可以混合静态工具、MCP 工具、Agent 定义的工具。</p><p>40 多个工具里最复杂的是 BashTool。它不是简单 <code>exec(command)</code>：自动分类命令类型（search&#x2F;read&#x2F;write），macOS 上走 sandbox-exec 沙箱，超过 15 秒的阻塞命令自动转后台，大输出存磁盘只给模型一个文件路径引用，还内置了一个 sed 命令专用解析器。</p><h2 id="Feature-Flag：我见过最优雅的功能门控"><a href="#Feature-Flag：我见过最优雅的功能门控" class="headerlink" title="Feature Flag：我见过最优雅的功能门控"></a>Feature Flag：我见过最优雅的功能门控</h2><p>这块设计我觉得是全项目最值得偷的。Claude Code 用编译时 + 运行时两层 flag：</p><h3 id="编译时：字符串级别的死代码消除"><a href="#编译时：字符串级别的死代码消除" class="headerlink" title="编译时：字符串级别的死代码消除"></a>编译时：字符串级别的死代码消除</h3><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; feature &#125; <span class="keyword">from</span> <span class="string">&#x27;bun:bundle&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> voiceModule = <span class="title function_">feature</span>(<span class="string">&#x27;VOICE_MODE&#x27;</span>)</span><br><span class="line">  ? <span class="built_in">require</span>(<span class="string">&#x27;./voice/index.js&#x27;</span>)</span><br><span class="line">  : <span class="literal">null</span></span><br></pre></td></tr></table></figure><p><code>feature()</code> 是 Bun 的编译时宏。构建时会被替换成 <code>true</code> 或 <code>false</code>，<code>false</code> 的分支直接被删除——不是”不执行”，是从二进制文件里物理消失，连字符串字面量都不剩。</p><p>为什么要这么做？因为安全研究员会反编译你的二进制去找隐藏功能（比如今天这次泄露…）。运行时 flag 再怎么关，字符串还在那。编译时 DCE 才是真的”不存在”。</p><p>讽刺的是，源码泄露之后这层保护就不管用了。但设计思路还是很值得学的。</p><p>我在源码里搜到十几个编译时 flag：<code>VOICE_MODE</code>、<code>BRIDGE_MODE</code>、<code>DAEMON</code>、<code>KAIROS</code>、<code>COORDINATOR_MODE</code>、<code>PROACTIVE</code>、<code>ABLATION_BASELINE</code>、<code>HISTORY_SNIP</code>、<code>REACTIVE_COMPACT</code>、<code>CONTEXT_COLLAPSE</code>、<code>CACHED_MICROCOMPACT</code>、<code>CHICAGO_MCP</code>……每一个都对应一个未发布或正在灰度的功能。</p><h3 id="运行时：GrowthBook-A-B-测试"><a href="#运行时：GrowthBook-A-B-测试" class="headerlink" title="运行时：GrowthBook A&#x2F;B 测试"></a>运行时：GrowthBook A&#x2F;B 测试</h3><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> enabled = <span class="title function_">checkStatsigFeatureGate_CACHED_MAY_BE_STALE</span>(</span><br><span class="line">  <span class="string">&#x27;tengu_streaming_tool_execution2&#x27;</span></span><br><span class="line">)</span><br></pre></td></tr></table></figure><p>用于灰度发布和 kill switch。所有 gate 名称都是 <code>tengu_</code> 前缀——tengu（天狗）大概是 Claude Code 的内部代号。从磁盘缓存读取，接受脏读，不阻塞启动。</p><h3 id="意外发现：消融实验基础设施"><a href="#意外发现：消融实验基础设施" class="headerlink" title="意外发现：消融实验基础设施"></a>意外发现：消融实验基础设施</h3><p>有个 flag 叫 <code>ABLATION_BASELINE</code>，启用后会一次性关掉思考模式、上下文压缩、自动记忆、后台任务：</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (<span class="title function_">feature</span>(<span class="string">&#x27;ABLATION_BASELINE&#x27;</span>) &amp;&amp; process.<span class="property">env</span>.<span class="property">CLAUDE_CODE_ABLATION_BASELINE</span>) &#123;</span><br><span class="line">  <span class="keyword">for</span> (<span class="keyword">const</span> k <span class="keyword">of</span> [</span><br><span class="line">    <span class="string">&#x27;CLAUDE_CODE_DISABLE_THINKING&#x27;</span>,</span><br><span class="line">    <span class="string">&#x27;DISABLE_COMPACT&#x27;</span>,</span><br><span class="line">    <span class="string">&#x27;DISABLE_AUTO_COMPACT&#x27;</span>,</span><br><span class="line">    <span class="string">&#x27;CLAUDE_CODE_DISABLE_AUTO_MEMORY&#x27;</span>,</span><br><span class="line">    <span class="string">&#x27;CLAUDE_CODE_DISABLE_BACKGROUND_TASKS&#x27;</span>,</span><br><span class="line">  ]) &#123;</span><br><span class="line">    process.<span class="property">env</span>[k] ??= <span class="string">&#x27;1&#x27;</span>;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>做过 ML 的都知道消融实验是什么——逐个关掉组件看性能影响。但把这套方法论搬到产品工程上，这是我第一次在工业代码里见到。说明 Anthropic 在认真量化每个功能特性到底值不值。</p><h2 id="隐藏功能：还没发布的东西"><a href="#隐藏功能：还没发布的东西" class="headerlink" title="隐藏功能：还没发布的东西"></a>隐藏功能：还没发布的东西</h2><p>这部分是全文最有噱头的内容。</p><h3 id="Voice-Mode（代号-Amber-Quartz）"><a href="#Voice-Mode（代号-Amber-Quartz）" class="headerlink" title="Voice Mode（代号 Amber Quartz）"></a>Voice Mode（代号 Amber Quartz）</h3><p><code>src/voice/</code> 目录确认了语音模式的存在。从 <code>voiceModeEnabled.ts</code> 看：</p><ul><li>只支持 Claude.ai OAuth 认证（API key、Bedrock、Vertex 都不行）</li><li>走的是一个专门的 <code>voice_stream</code> 端点</li><li>有个紧急 kill switch：GrowthBook flag <code>tengu_amber_quartz_disabled</code></li><li>从注释看已经开发到可以使用的程度了</li></ul><h3 id="Bridge-Mode：把你的电脑变成-Claude-的远程终端"><a href="#Bridge-Mode：把你的电脑变成-Claude-的远程终端" class="headerlink" title="Bridge Mode：把你的电脑变成 Claude 的远程终端"></a>Bridge Mode：把你的电脑变成 Claude 的远程终端</h3><p><code>src/bridge/</code> 有 31 个文件，是一个完整的远程控制系统。运行 <code>claude remote-control</code> 之后，你的本地环境就变成一个被 claude.ai 远程操控的”桥接环境”。</p><p>最多支持 32 个并发会话，有 JWT 认证 + 可信设备机制，企业管理员可以通过策略禁用。这应该是为了让 claude.ai 网页版能直接操作用户本地的开发环境。</p><h3 id="Buddy：终端里的电子宠物"><a href="#Buddy：终端里的电子宠物" class="headerlink" title="Buddy：终端里的电子宠物"></a>Buddy：终端里的电子宠物</h3><p>这个是最让我没想到的。</p><p>Claude Code 内置了一个完整的虚拟宠物系统，而且没有用 feature flag 门控——已经在公开版本的二进制里了（只是可能还没暴露入口）：</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 18 种宠物</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> <span class="variable constant_">SPECIES</span> = [</span><br><span class="line">  duck, goose, blob, cat, dragon, octopus, owl, penguin,</span><br><span class="line">  turtle, snail, ghost, axolotl, capybara, cactus, robot,</span><br><span class="line">  rabbit, mushroom, chonk</span><br><span class="line">] <span class="keyword">as</span> <span class="keyword">const</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 5 级稀有度：普通(60%) / 罕见(25%) / 稀有(10%) / 史诗(4%) / 传说(1%)</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> <span class="variable constant_">RARITY_WEIGHTS</span> = &#123;</span><br><span class="line">  <span class="attr">common</span>: <span class="number">60</span>, <span class="attr">uncommon</span>: <span class="number">25</span>, <span class="attr">rare</span>: <span class="number">10</span>, <span class="attr">epic</span>: <span class="number">4</span>, <span class="attr">legendary</span>: <span class="number">1</span>,</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// RPG 式属性</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> <span class="variable constant_">STAT_NAMES</span> = [<span class="string">&#x27;DEBUGGING&#x27;</span>,<span class="string">&#x27;PATIENCE&#x27;</span>,<span class="string">&#x27;CHAOS&#x27;</span>,<span class="string">&#x27;WISDOM&#x27;</span>,<span class="string">&#x27;SNARK&#x27;</span>] <span class="keyword">as</span> <span class="keyword">const</span></span><br></pre></td></tr></table></figure><p>还有帽子（皇冠、礼帽、螺旋桨帽、光环、巫师帽、豆豆帽、头顶小鸭子）、眼睛样式、1% 概率的闪光变体。宠物属性用 Mulberry32 伪随机数生成器从你的用户 ID 确定性计算——所以每个用户的宠物是固定的，不能刷。</p><p>但最有意思的是物种名的编码方式：</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 所有物种名用 hex 编码，因为有一个名字和内部模型代号撞了</span></span><br><span class="line"><span class="keyword">const</span> c = <span class="title class_">String</span>.<span class="property">fromCharCode</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> duck = <span class="title function_">c</span>(<span class="number">0x64</span>,<span class="number">0x75</span>,<span class="number">0x63</span>,<span class="number">0x6b</span>) <span class="keyword">as</span> <span class="string">&#x27;duck&#x27;</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> goose = <span class="title function_">c</span>(<span class="number">0x67</span>,<span class="number">0x6f</span>,<span class="number">0x6f</span>,<span class="number">0x73</span>,<span class="number">0x65</span>) <span class="keyword">as</span> <span class="string">&#x27;goose&#x27;</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> capybara = <span class="title function_">c</span>(<span class="number">0x63</span>,<span class="number">0x61</span>,<span class="number">0x70</span>,<span class="number">0x79</span>,<span class="number">0x62</span>,<span class="number">0x61</span>,<span class="number">0x72</span>,<span class="number">0x61</span>) <span class="keyword">as</span> <span class="string">&#x27;capybara&#x27;</span></span><br><span class="line"><span class="comment">// ... 全部 18 个都这样</span></span><br></pre></td></tr></table></figure><p>注释原文：”One species name collides with a model-codename canary in excluded-strings.txt.”</p><p>构建系统会 grep 输出文件里有没有被排除的字符串。也就是说，这 18 个物种名中有一个恰好是 Anthropic 某个未公开模型的代号。把所有名字都 hex 编码是为了防止误触发检测。</p><p>到底是哪个名字？duck、goose、blob、cat、dragon、octopus、owl、penguin、turtle、snail、ghost、axolotl、capybara、cactus、robot、rabbit、mushroom、chonk——其中一个是 Anthropic 下一个模型的代号。随便猜猜？</p><h2 id="泄露根因：npm-source-map"><a href="#泄露根因：npm-source-map" class="headerlink" title="泄露根因：npm source map"></a>泄露根因：npm source map</h2><p>这次泄露的技术原因很简单：npm 发布时 <code>.map</code> 文件没有移除。Source map 中包含指向 R2 存储桶的 zip 下载链接，而该链接无需认证即可访问。</p><p>对所有 npm 包发布者的教训：</p><ol><li><code>.npmignore</code> 或 <code>package.json</code> 的 <code>files</code> 字段要显式排除 <code>.map</code> 文件</li><li>构建流水线应该有 CI 检查，验证发布产物不包含 source map</li><li>如果必须保留 source map，确保其中不包含源码引用 URL</li><li>存储桶的访问控制要独立于代码的发布流程</li></ol><h2 id="写在最后"><a href="#写在最后" class="headerlink" title="写在最后"></a>写在最后</h2><p>读完 51 万行代码，我最大的感受不是某个具体技术多牛，而是这个团队在用做研究的方法做工程。</p><p>消融实验基础设施、双层 feature flag、四种粒度的上下文管理、流式工具并行——每一个都不是拍脑袋加的，背后大概率有数据支撑。这种”每个功能都有量化验证”的工程文化，比任何单点技术都更值得学。</p><p>当然，宠物系统除外。那个纯粹是因为好玩。</p><hr><p><em>声明：本文基于公开泄露的源码进行技术分析，所有代码版权归 Anthropic 所有。</em></p>]]>
    </content>
    <id>https://he-yufeng.github.io/2026/03/31/claude-code-source-leak/</id>
    <link href="https://he-yufeng.github.io/2026/03/31/claude-code-source-leak/"/>
    <published>2026-03-31T12:00:00.000Z</published>
    <summary>1903个文件，51万行TypeScript。npm的source map没删干净，Anthropic最核心的AI Agent产品源码就这么泄露了。</summary>
    <title>我读完了Claude Code泄露的51万行源码</title>
    <updated>2026-03-31T13:28:39.102Z</updated>
  </entry>
  <entry>
    <author>
      <name>Yufeng He</name>
    </author>
    <category term="漫谈" scheme="https://he-yufeng.github.io/categories/%E6%BC%AB%E8%B0%88/"/>
    <category term="动漫" scheme="https://he-yufeng.github.io/tags/%E5%8A%A8%E6%BC%AB/"/>
    <category term="Vtuber" scheme="https://he-yufeng.github.io/tags/Vtuber/"/>
    <category term="文化" scheme="https://he-yufeng.github.io/tags/%E6%96%87%E5%8C%96/"/>
    <category term="进化论" scheme="https://he-yufeng.github.io/tags/%E8%BF%9B%E5%8C%96%E8%AE%BA/"/>
    <content>
      <![CDATA[<h2 id="为什么用生物学类比"><a href="#为什么用生物学类比" class="headerlink" title="为什么用生物学类比"></a>为什么用生物学类比</h2><p>Vtuber 圈子在过去几年里发生了太多变化：事务所扩张、个人势崛起、主播毕业、AI Vtuber 登场、观众的消费习惯迁移。用”娱乐行业竞争”或者”内容创作者市场”这类框架来分析，其实缺少一个视角——这个圈子里的各个角色之间，存在非常完整的生态位关系，用生物学的进化论框架反而能说清楚很多东西。</p><p>当然，类比终究是类比。下面的内容是一个思维框架，不是严格的生物学论证。</p><hr><h2 id="大型事务所：顶级捕食者还是大型植食者？"><a href="#大型事务所：顶级捕食者还是大型植食者？" class="headerlink" title="大型事务所：顶级捕食者还是大型植食者？"></a>大型事务所：顶级捕食者还是大型植食者？</h2><p>Hololive 和 Nijisanji 是 Vtuber 生态里的顶级生物，毫无疑问。但它们更接近顶级捕食者（猛虎）还是大型植食者（大象）？</p><p>我倾向于大型植食者。它们不靠”捕食”其他 Vtuber 为生，而是靠大量消耗资源（运营成本、制作成本、IP 维护成本）来维持规模优势。它们的竞争力来源于体量：更多的成员、更完善的运营体系、更广泛的品牌认知。</p><p>大型植食者的特点是生态位宽，占据大量资源，对周围的小型物种产生巨大的生态压力。对个人势来说，Hololive 的存在就是这种压力：观众的注意力是有限的，大型事务所的高产出内容占据了大量时间，个人势只能在大型事务所覆盖不到的缝隙里生存。</p><p>两家事务所之间的竞争，更接近两个大型草食者在同一片草原上竞争——不是你死我活的捕食，而是资源竞争。Hololive 偏向更封闭的运营（强调成员协作和整体品牌感），Nijisanji 更开放（接近”给主播提供工具和平台”的模式，个人风格空间更大），这本质上是两种不同的生态位策略，各自吸引不同偏好的观众群体。</p><hr><h2 id="个人势：特化物种"><a href="#个人势：特化物种" class="headerlink" title="个人势：特化物种"></a>个人势：特化物种</h2><p>个人势（没有事务所背书的独立 Vtuber）在这个生态里扮演高度特化物种的角色。</p><p>特化物种的生存策略是：不和通用型物种竞争，而是找到一个足够小、足够深的生态位，把这个生态位占满。在一个大型事务所根本不会去的细分领域，个人势可以活得很好：极硬核的某类小众游戏受众、某个特定的语言&#x2F;地区社群、某个极其细分的内容风格（比如纯音乐向、编程教学向、ASMR 极度细分的某个方向）。</p><p>这类特化物种的脆弱性在于：一旦这个细分生态位的资源减少（受众流失、流量平台算法变化），它们缺少大型物种的缓冲能力，生存风险更高。个人势的毕业率远高于大型事务所的主播，这在生态学上是预期内的——特化物种的局部灭绝概率更高。</p><p>但特化物种也有一个优势：适应速度更快。个人势不需要经过事务所的多重审批流程，可以在几天内试验新的内容方向，对平台规则变化和受众需求变化的响应速度比大型事务所快得多。这种灵活性在环境快速变化时是生存优势。</p><hr><h2 id="观众的角色：寄生还是共生？"><a href="#观众的角色：寄生还是共生？" class="headerlink" title="观众的角色：寄生还是共生？"></a>观众的角色：寄生还是共生？</h2><p>观众在这个生态里的角色比看起来更复杂。</p><p>从经济关系上看，观众是共生的：他们提供的打赏、订阅、周边消费是整个生态的能量来源，没有观众的输入，整个生态系统的能量供给断绝。Vtuber 和观众之间是真正的互惠共生，不是寄生。</p><p>但从内容消费模式来看，有一类观众更接近寄生关系：他们消费内容但几乎不参与经济贡献（只看免费直播，不打赏不订阅），同时对主播的情绪状态产生较大影响（发弹幕、参与互动、形成舆论压力）。这类”寄生”本身不一定是坏的，它提供了内容的传播和人气的基础，但对主播来说它带来的成本有时不小。</p><p>还有一种分类是”养成系”和”消费系”。养成系观众把和主播的关系理解为一种长期陪伴，关注成长轨迹，愿意投入大量情感；消费系观众把 Vtuber 当作内容服务消费，主播对他们来说是可替换的娱乐产品。前者的粘性更高、经济贡献更大，但对主播的期待也更强，处理不好容易造成心理压力。大型事务所通常会试图培养养成系受众，因为这是更稳定的生态能量来源。</p><hr><h2 id="毕业：物种灭绝的三种原因"><a href="#毕业：物种灭绝的三种原因" class="headerlink" title="毕业：物种灭绝的三种原因"></a>毕业：物种灭绝的三种原因</h2><p>Vtuber “毕业”（退出）的原因，在生态学框架里对应三类物种灭绝：</p><p>环境变化导致的灭绝。平台规则变化（Youtube 算法调整、直播限制收紧）、粉丝群体的代际更替、游戏内容的版权问题，这类外部环境变化对所有类型的 Vtuber 都构成压力，但抗压能力不同。大型事务所有资源缓冲，个人势没有。</p><p>竞争失败导致的灭绝。生态位被更有竞争力的个体占据。有时候不是因为自己变差了，而是因为来了一个更强的竞争者。这类情况在 Nijisanji 旗下极多成员的情况下特别明显——数百个频道同时运营，内部竞争激烈，长期低人气的频道更新频率下降最终到停播，这就是竞争压力下的局部灭绝。</p><p>自然死亡。主播本人到了想做别的事的时候，选择毕业。这是最正常的原因，但往往被过度解读——观众有时候会把”自然死亡”解读成某种外部压力造成的，其实未必。</p><hr><h2 id="AI-Vtuber：外来入侵物种"><a href="#AI-Vtuber：外来入侵物种" class="headerlink" title="AI Vtuber：外来入侵物种"></a>AI Vtuber：外来入侵物种</h2><p>这是当前最有趣的生态扰动。</p><p>AI Vtuber（使用语言模型驱动角色，不依赖固定的人类主播）的出现，在生态学上相当于外来入侵物种：它们消耗同样的资源（观众注意力、平台流量），但有生物学上不存在的优势——不需要休息、可以同时出现在多个平台、心理状态高度稳定、可以根据观众反馈快速迭代风格。</p><p>外来入侵物种的标准剧本是：最开始被本地物种当作新奇事物，随后因为不受”原生限制”而快速扩张，最终改变整个生态的能量流动。</p><p>AI Vtuber 目前还处于早期阶段，很多方向还不成熟（交互深度不够、个性化程度有限、情感连接的真实感不足），但轨迹已经很清晰了。它不会消灭人类主播驱动的 Vtuber，但会占据相当一部分对”内容本身”的需求，迫使人类主播向”不可被 AI 替代的维度”转移——真实的情感互动、不可预测的人格魅力、现场性和在场感。</p><hr><h2 id="初音未来：初级生产者"><a href="#初音未来：初级生产者" class="headerlink" title="初音未来：初级生产者"></a>初音未来：初级生产者</h2><p>这是整个框架里最特殊的位置。</p><p>初音未来不是 Vtuber。她没有直播，没有实时互动，没有事务所运营，没有毕业的可能。但她对整个 Vtuber 文化生态的贡献比任何一个具体的 Vtuber 都重要——她创造的是这个生态得以存在的底层文化土壤。</p><p>生态学里，初级生产者（植物）是通过光合作用把太阳能转化成有机物，为整个食物网提供能量基础的生物。它们不是任何东西的猎物，也不捕食任何东西，但没有它们整个生态系统就不存在。</p><p>初音未来是这个意义上的初级生产者：她验证了一件事——虚拟的、没有真实人类面孔的二次元角色，可以成为大量人类情感投射和文化创作的对象，这件事在文化上是可能的。没有她奠定的这个文化可能性，虚拟主播在观众心理层面的接受度不会有今天这么高。</p><p>所以在用进化论分析 Vtuber 生态时，初音未来的位置不在这个生态内，她是这个生态存在的前提条件之一。</p><hr><p>最后的观点：这个生态还在快速演化，AI Vtuber 带来的扰动才刚刚开始，大型事务所的体量优势和个人势的灵活性之间的张力还没有到达新的均衡点。值得持续观察，不值得过早下定论。</p><hr><p><em>本文是思维框架性的讨论，不构成对任何主播或机构的商业评价。</em></p>]]>
    </content>
    <id>https://he-yufeng.github.io/2026/03/09/vtuber-evolution/</id>
    <link href="https://he-yufeng.github.io/2026/03/09/vtuber-evolution/"/>
    <published>2026-03-09T12:00:00.000Z</published>
    <summary>Hololive、Nijisanji、个人势——这是一个完整的生态位竞争模型</summary>
    <title>用进化论理解Vtuber生态</title>
    <updated>2026-03-05T04:46:23.922Z</updated>
  </entry>
  <entry>
    <author>
      <name>Yufeng He</name>
    </author>
    <category term="漫谈" scheme="https://he-yufeng.github.io/categories/%E6%BC%AB%E8%B0%88/"/>
    <category term="BanG Dream" scheme="https://he-yufeng.github.io/tags/BanG-Dream/"/>
    <category term="MyGO" scheme="https://he-yufeng.github.io/tags/MyGO/"/>
    <category term="千早爱音" scheme="https://he-yufeng.github.io/tags/%E5%8D%83%E6%97%A9%E7%88%B1%E9%9F%B3/"/>
    <category term="动漫" scheme="https://he-yufeng.github.io/tags/%E5%8A%A8%E6%BC%AB/"/>
    <category term="社会观察" scheme="https://he-yufeng.github.io/tags/%E7%A4%BE%E4%BC%9A%E8%A7%82%E5%AF%9F/"/>
    <content>
      <![CDATA[<h2 id="爱音和奶龙都很唐"><a href="#爱音和奶龙都很唐" class="headerlink" title="爱音和奶龙都很唐"></a>爱音和奶龙都很唐</h2><p>先说一个离谱的联想。</p><p>爱音和奶龙都很唐。就是那种你看着会脱口而出”你在干嘛啊”的角色。</p><p>但区别是：奶龙的唐就是纯粹的互联网无厘头，刷到了笑一下划走了，没人真的在意它。爱音的唐呢？你笑完之后过了两天还在想她。明明吉他技术菜得一批，却张口就要当主唱兼吉他手；明明是乐队里最边缘的那个人，却天天在那里运营SNS账号搞服装设计ANON TOKYO搞得好像自己是灵魂人物一样。</p><p>奶龙让人觉得无聊，爱音让人心疼。同样是唐，为什么差这么多？</p><p>因为爱音所有的”唐”背后都有一个理由：一个从英国灰溜溜逃回来的小孩在拼命证明”我还行”。</p><h2 id="英国留学失败这件事比表面上严重一百倍"><a href="#英国留学失败这件事比表面上严重一百倍" class="headerlink" title="英国留学失败这件事比表面上严重一百倍"></a>英国留学失败这件事比表面上严重一百倍</h2><p>很多人看MyGO的时候一笔带过了爱音的留学经历，觉得就是”不适应所以回来了”。但你仔细想想这个逻辑链：</p><p>爱音初中的时候是学生会长，人气爆棚，成绩优秀——标准的”别人家的孩子”。然后她去英国读高中，去之前大概率是全校瞩目的”哇你好厉害要出国了”。结果呢？在英国完全适应不了，连名字都被叫错（老师同学都叫她Ann而不是Anon），最后退学回国。</p><p>退学回国。</p><p>你知道这四个字对一个之前一帆风顺的优等生意味着什么吗？就相当于你raid打到final boss然后灭团了，不是那种”下次再来”的灭团，是直接退出副本删号重练的那种。</p><p>而且回国之后她不是回了原来的学校——她去了羽丘。一个她谁都不认识的、在一个奇怪的时间点（黄金周前）转入的学校。所有人都已经有了自己的圈子，所有人都已经组了乐队。她就像一个游戏开服三个月才注册的新玩家，看着满屏幕的公会招募，焦虑地想：”我得赶紧找个组织。”</p><h2 id="“请问……那我呢？”"><a href="#“请问……那我呢？”" class="headerlink" title="“请问……那我呢？”"></a>“请问……那我呢？”</h2><p>MyGO里最让我破防的一句台词。</p><p>爱音好不容易凑齐了人搞了个乐队，结果发现灯、爽世、立希这三个人之前在CRYCHIC一起混过。三个老战友+两个新人（爱音和乐奈），乐奈吉他水平又高到离谱，爱音就成了团队里最尴尬的存在——既不是元老，也不是大佬。</p><p>然后那个桌会场景来了。三个CRYCHIC的人在那里交心、和解、冰释前嫌，场面感人至极。爱音坐在旁边。三个人决定重组乐队。爱音还是坐在旁边。</p><p>“请问……那我呢？”</p><p>被忘了。组乐队最积极的人，在关键时刻被忘了。</p><p>这不是欺负，甚至不是故意的。就是很自然地，三个有共同记忆的人聊嗨了，忘了旁边还有一个人。这种”不是被排挤而是被忽略”的感觉，比被针对还难受。</p><p>被针对至少说明你存在。被忽略说明你根本不在别人的世界里。</p><h2 id="所以阶级在哪里"><a href="#所以阶级在哪里" class="headerlink" title="所以阶级在哪里"></a>所以阶级在哪里</h2><p>你可能要问了：你说了半天，阶级分析在哪呢？</p><p>在这里：爱音的所有困境，本质上都是一个”没有归属资本的人试图购买归属感”的故事。</p><p>什么叫归属资本？就是那些让你在一个群体里自然而然属于”自己人”的东西——共同的经历、共同的记忆、共同的”你还记得那次吗”。CRYCHIC三人组有这个资本，爱音没有。乐奈有强到不需要归属感的技术资本，爱音也没有。</p><p>爱音有什么？她有一腔热情和一堆讨好策略。她运营SNS、设计服装、照顾灯的情绪、忍受立希的压力、被素世疏远也不说什么。她在用”劳动”换取”归属”。</p><p>这不就是无产阶级的核心处境吗——你没有资本（关系网&#x2F;技术&#x2F;出身），你只能出卖劳动力来换取一个位置。而且这个位置随时可能因为”原股东们要开会”而把你晾在一边。</p><p>“请问……那我呢？”——这句话翻译成社会学就是：”我已经在这里付出了这么多劳动，你们能不能给我一个正式工位？”</p><h2 id="爱音的虚荣心是一种生存策略"><a href="#爱音的虚荣心是一种生存策略" class="headerlink" title="爱音的虚荣心是一种生存策略"></a>爱音的虚荣心是一种生存策略</h2><p>很多人说爱音虚荣、爱出风头、动机不纯。Real Sound的评论员说她是”最棒的俗人”——她玩乐队不是因为热爱音乐，单纯是因为”羽丘大家都在搞乐队，我也得搞一个”。</p><p>但你换个角度想：一个留学失败、谁都不认识、在奇怪时间点转学进来的女高中生，她不”虚荣”一点怎么办？安安静静坐在角落里等别人来主动认识她？等到毕业都不会有人搭理她。</p><p>爱音的虚荣不是”我想当明星”，而是”我必须让自己看起来有价值，否则没人会要我”。这和在就业市场上疯狂包装简历的逻辑一模一样——不是你想吹牛，是你不吹牛连面试机会都没有。</p><p>奶龙不需要虚荣因为奶龙是网红，天生有流量。爱音没有这个buff，她得自己卷出来。</p><h2 id="灯给了爱音什么"><a href="#灯给了爱音什么" class="headerlink" title="灯给了爱音什么"></a>灯给了爱音什么</h2><p>灯在水族馆那场戏里给爱音的东西，不是什么”我理解你”的鸡汤。</p><p>当时爱音在街上碰到初中同学，同学一看她穿着羽丘校服就猜到她留学失败了。爱音当场社死。然后灯拉着她跑了，跑到水族馆，爱音交代了自己的失败经历。</p><p>灯的回应不是安慰，不是”你已经很棒了”，而是跑去拿了一张访客问卷在上面涂鸦，画了一堆乱七八糟的东西来肯定爱音——用一种完全跳脱正常社交逻辑的方式。</p><p>灯本身就是一个不在乎社交规则的人。她不理解什么”面子”、什么”形象管理”、什么”你留学失败好丢人”。她就是单纯地觉得：小爱在身边很好，小爱不在就不好。</p><p>这种”我不是因为你有价值才要你，我就是要你”的态度，对爱音来说比任何肯定都重要。因为爱音活了十几年，所有关系都是建立在”交换价值”上的。灯是第一个完全不讲交换逻辑的人。</p><p>所以后来灯说”小爱是MyGO一辈子的成员”的时候爱音开怀大笑——那一刻她终于不需要计算自己的价值了。</p><h2 id="写在最后"><a href="#写在最后" class="headerlink" title="写在最后"></a>写在最后</h2><p>奶龙的唐是流水线上的无厘头，刷过就忘。</p><p>爱音的唐是一个留学失败、无圈可入的小孩在横冲直撞，满身伤痕还要笑着说”我没事我很好我可以的”。</p><p>前者没人会记住。后者让整个B站为她流泪。</p><p>同样是唐，有的唐是消费品，有的唐是勇敢。</p><hr><p><em>BGM：春日影 —— MyGO!!!!!</em></p><p><em>本文参考了<a href="https://zh.moegirl.org.cn/%E5%8D%83%E6%97%A9%E7%88%B1%E9%9F%B3">萌娘百科</a>、<a href="https://www.zhihu.com/question/664256442">知乎相关讨论</a>和<a href="https://zh.wikipedia.org/zh-hans/%E5%8D%83%E6%97%A9%E6%84%9B%E9%9F%B3">维基百科</a>的角色资料。</em></p>]]>
    </content>
    <id>https://he-yufeng.github.io/2026/03/02/anon-proletariat/</id>
    <link href="https://he-yufeng.github.io/2026/03/02/anon-proletariat/"/>
    <published>2026-03-01T20:00:00.000Z</published>
    <summary>从英国逃回来的留学失败者、乐队里的透明人、以及为什么我觉得这一切的底层逻辑是阶级</summary>
    <title>千早爱音和奶龙都很唐，但爱音唐得让人心疼</title>
    <updated>2026-03-01T19:45:18.724Z</updated>
  </entry>
</feed>
