从 Claude Code 51 万行源码泄露,看 AI 编程 Agent 的「操作系统级」架构设计

0 阅读10分钟

从 Claude Code 51 万行源码泄露,看 AI 编程 Agent 的「操作系统级」架构设计

2026 年 3 月 31 日,Anthropic 因为一个 npm 打包失误,把 Claude Code 的全部源码暴露给了全世界。51.2 万行 TypeScript、1900 个文件、从 Agent Loop 到权限系统到未发布的自主后台模式——这可能是 AI 行业有史以来最大的一次「意外开源」。本文不讨论泄露本身的安全问题,而是从泄露的架构中提炼出 AI 编程 Agent 的核心设计模式,帮你理解下一代 AI 开发工具到底是怎么构建的。

一、这次泄露到底发生了什么?

3 月 31 日,安全研究员 Chaofan Shou 发现 Claude Code 的 npm 包 @anthropic-ai/claude-code v0.0.88 中包含了一个 59.8MB 的 source map 文件(cli.js.map)。Source map 本来是用于调试的——它把打包压缩后的代码映射回原始源码。但 Anthropic 的构建流程出了问题,把这个文件直接塞进了发布包里。

几个小时内,开发者们就从这个 source map 中还原出了完整的 TypeScript 源码,并上传到了 GitHub。社区迅速开始分析,甚至有人用 OpenAI 的 Codex 在几小时内把整个项目用 Python 重写了一遍。

泄露的内容包括:

  • 完整的 Agent Loop 实现(消息队列、工具调用、结果处理)
  • 权限系统(三级权限模型、工具级别的 allow/deny 规则)
  • 44 个 feature flag,覆盖了大量未发布功能
  • KAIROS:一个未发布的「永远在线」后台自主 Agent 模式
  • ULTRAPLAN:一个 30 分钟远程规划系统
  • 自愈式记忆架构(Self-Healing Memory)
  • 多 Agent 编排系统(SubAgent / Task 工具)

这不是一个简单的 CLI 工具。正如 The Neuron 的分析所说:「Claude Code 不只是'终端里的 Claude',它更像是一个为软件开发工作设计的操作系统。」

二、为什么这次泄露值得每个开发者关注?

你可能会想:「源码泄露关我什么事?」

关系大了。因为 Claude Code 的架构代表了 AI 编程工具的下一个形态——从「聊天式助手」进化到「自主式操作系统」。理解它的设计模式,对于以下几类人都有直接价值:

  1. 正在构建 AI Agent 产品的团队:这是一份价值数亿美元的架构参考
  2. 使用 AI 编程工具的开发者:理解工具的内部机制,才能更高效地使用它
  3. 关注 AI 安全的从业者:这次事件暴露了 AI 工具供应链安全的系统性风险

接下来,我会从泄露的源码中提炼出 5 个核心架构设计模式,每个都附带技术原理和实际应用场景。

三、核心架构模式一:Agent Loop——消息驱动的自主循环

3.1 传统 Chat 模式 vs Agent Loop

传统的 AI 聊天工具是「请求-响应」模式:用户发消息 → 模型回复 → 等待下一条消息。这种模式下,AI 是被动的。

Claude Code 的 Agent Loop 完全不同。它是一个持续运行的消息队列系统:

// 简化的 Agent Loop 核心逻辑
class AgentLoop {
  private messageQueue: Message[] = [];
  
  async run() {
    while (this.messageQueue.length > 0) {
      const message = this.messageQueue.shift();
      
      // 发送给 Claude API
      const response = await this.callAPI(message);
      
      // 如果模型返回了工具调用请求
      if (response.toolCalls) {
        for (const toolCall of response.toolCalls) {
          const result = await this.executeTool(toolCall);
          // 工具执行结果重新入队
          this.messageQueue.push({
            role: 'tool_result',
            content: result
          });
        }
      }
      // 循环继续,直到队列为空
    }
  }
}

关键设计点在于:工具执行的结果会重新进入消息队列,触发模型的下一轮思考。这意味着一次用户输入可能触发多轮「思考 → 行动 → 观察」的循环,直到模型认为任务完成。

3.2 为什么用消息队列而不是简单的递归?

我在自己的项目中踩过这个坑。最初我用递归实现 Agent Loop:

// ❌ 递归实现——看起来简洁,实际上有严重问题
async function agentStep(messages) {
  const response = await callAPI(messages);
  if (response.toolCalls) {
    const results = await executeTools(response.toolCalls);
    return agentStep([...messages, response, ...results]); // 递归
  }
  return response;
}

问题在于:

  • 调用栈溢出:复杂任务可能需要几十轮迭代
  • 不可中断:用户无法在中途插入新指令
  • 优先级无法控制:所有消息平等处理,无法让用户输入抢占工具结果

Claude Code 的消息队列设计解决了所有这些问题。它用 priority 字段区分消息优先级,用户输入永远优先于工具结果和系统消息。

特性递归实现消息队列实现
可中断性❌ 无法中断✅ 用户输入可抢占
栈安全❌ 可能溢出✅ 事件循环驱动
优先级控制❌ 无✅ 支持优先级排序
并发工具执行❌ 困难✅ 天然支持
调试可观测性❌ 调用栈难追踪✅ 队列状态可快照

四、核心架构模式二:KAIROS——永远在线的后台 Agent

4.1 从 Chat 到 Daemon

这是泄露中最令人兴奋的发现。KAIROS 是一个未发布的功能,它把 Claude Code 从一个「你问我答」的工具变成了一个持续运行的后台守护进程(daemon)。

它的核心机制是 Tick Loop(心跳循环):

// KAIROS 的心跳机制
const scheduleProactiveTick = () => {
  setTimeout(() => {
    // 检查是否应该继续运行
    if (!proactiveModule?.isProactiveActive() || inputClosed) {
      return;
    }
    
    // 注入一个 <tick> 消息到队列
    const tickContent = `<tick>${new Date().toLocaleTimeString()}</tick>`;
    enqueue({
      mode: 'prompt',
      value: tickContent,
      priority: 'later',  // 低优先级,不抢占用户输入
      isMeta: true,
    });
    
    void run(); // 触发 Agent Loop
  }, 0); // setTimeout(0) 让出事件循环
};

当消息队列为空时(即模型完成了当前任务),系统不是停下来等用户输入,而是注入一个 <tick> 消息。模型收到 tick 后,会检查是否有待处理的工作——比如正在运行的 CI 任务、未解决的代码审查、或者之前设定的监控目标。

4.2 SleepTool:成本与响应性的博弈

一个永远在线的 Agent 面临一个核心矛盾:每次 tick 都是一次 API 调用,但不 tick 就无法及时响应

Claude Code 的解决方案是 SleepTool——让模型自己决定休眠多久:

// SleepTool 的提示词(从源码中提取)
const SLEEP_TOOL_PROMPT = `
Wait for a specified duration. The user can interrupt the sleep at any time.
Use this when you have nothing to do, or when you're waiting for something.

Each wake-up costs an API call, but the prompt cache expires
after 5 minutes of inactivity — balance accordingly.
`;

注意最后一句:「每次唤醒消耗一次 API 调用,但 prompt cache 在 5 分钟不活动后过期——请自行权衡。」

这是一个非常精妙的设计——把成本优化的决策权交给模型本身。模型需要在两个成本之间找平衡:

  • 睡太短 → API 调用费用高
  • 睡太长 → prompt cache 过期,下次调用需要重建缓存,延迟更高

4.3 15 秒阻塞预算

KAIROS 还有一个关键机制:任何 shell 命令如果运行超过 15 秒,会被自动移到后台:

const ASSISTANT_BLOCKING_BUDGET_MS = 15_000;

// 超时自动后台化
setTimeout(() => {
  if (shellCommand.status === 'running') {
    startBackgrounding(shellCommand);
  }
}, ASSISTANT_BLOCKING_BUDGET_MS).unref();

这保证了 Agent 不会因为一个 npm installmake build 而卡住整个工作流。命令继续在后台运行,Agent 可以去做其他事情,完成后会收到通知。

五、核心架构模式三:自愈式记忆(Self-Healing Memory)

5.1 Context Entropy 问题

长时间运行的 AI Agent 面临一个被称为「上下文熵」(Context Entropy)的问题:随着对话越来越长,模型的注意力被稀释,早期的重要信息被「遗忘」,输出质量逐渐下降。

传统的解决方案是维护一个 MEMORY.md 文件,每次对话时读取和更新。但对于 KAIROS 这种可能运行数天的 Agent,反复重写同一个文件既低效又容易丢失信息。

5.2 Append-Only Daily Log + Nightly Distillation

Claude Code 的方案是一个两层记忆架构:

第一层:追加写入的每日日志

// 记忆写入路径:logs/YYYY/MM/YYYY-MM-DD.md
const buildAssistantDailyLogPrompt = () => {
  return [
    'This session is long-lived. Record anything worth remembering',
    'by appending to today\'s daily log file:',
    `${logPathPattern}`,  // logs/2026/03/2026-03-31.md
    'Do not rewrite or reorganize the log — it is append-only.',
    'A separate nightly process distills these logs into',
    'MEMORY.md and topic files.'
  ].join('\n');
};

第二层:夜间蒸馏(/dream 命令)

每天夜间,一个独立的进程会读取当天的原始日志,提取关键信息,更新结构化的 MEMORY.md 索引和主题文件。

这个设计借鉴了数据库领域的 WAL(Write-Ahead Log) 模式:

概念数据库 WALClaude Code 记忆
写入层WAL 日志文件每日追加日志
持久层数据页MEMORY.md + 主题文件
合并过程Checkpoint/dream 夜间蒸馏
恢复能力从 WAL 重放从日志重建记忆

为什么不直接更新 MEMORY.md?因为追加写入有三个关键优势:

  1. 不会丢失信息:即使蒸馏过程出错,原始日志还在
  2. 写入性能高:追加比随机更新快得多
  3. 天然的时间线:按日期组织,方便回溯

bitnet-architecture.png

六、核心架构模式四:分层权限系统

6.1 三级权限模型

AI Agent 能读文件、写文件、执行命令——这意味着它有能力搞砸你的整个项目。Claude Code 的权限系统设计了三个层级:

  1. 只读模式:只能读取文件和搜索代码
  2. 监督执行模式:可以修改文件和执行命令,但每次都需要用户确认
  3. 自主执行模式:在预定义的规则范围内自主操作

权限规则可以精确到工具级别:

{
  "permissions": {
    "allow": [
      "Read(*)",
      "Bash(npm test)",
      "Bash(npm run lint)",
      "Write(src/**/*.ts)"
    ],
    "deny": [
      "Bash(rm -rf *)",
      "Write(.env*)",
      "Bash(git push*)"
    ]
  }
}

6.2 为什么权限设计如此重要?

我在项目中见过太多 AI Agent 的权限设计是「全有或全无」——要么完全信任,要么完全不信任。Claude Code 的设计告诉我们,细粒度权限是 Agent 从玩具走向生产的关键

一个好的 Agent 权限系统应该满足:

  • 最小权限原则:默认只读,需要时逐步提权
  • 可审计:每次工具调用都有记录
  • 可配置:团队可以通过配置文件统一管理
  • 可版本控制:权限规则可以提交到 Git

七、核心架构模式五:SendUserMessage——结构化输出通道

7.1 为什么需要专门的消息通道?

在普通的 CLI 工具中,模型的输出直接打印到终端。但对于一个后台运行的 Agent,这种方式完全不可行——没人盯着终端看。

Claude Code 设计了一个叫 SendUserMessage(内部名 BriefTool)的专用工具:

const SEND_USER_MESSAGE_PROMPT = `
SendUserMessage is where your replies go. Text outside it is visible
if the user expands the detail view, but most won't — assume unread.

Anything you want them to actually see goes through SendUserMessage.

The failure mode: the real answer lives in plain text while
SendUserMessage just says "done!" — they see "done!" and miss everything.
`;

消息还带有 status 字段:'normal' 表示回复用户的问题,'proactive' 表示主动通知。下游的 UI 层根据这个字段决定是弹通知还是静默记录。

7.2 三层过滤渲染

UI 层实现了三层过滤:

// 三层过滤逻辑
// 1. Transcript 模式(Ctrl+O):完全不过滤,显示所有内容
// 2. Brief-only 模式:只显示 SendUserMessage + 用户输入
// 3. 默认模式:在有 SendUserMessage 的轮次中隐藏冗余文本

这种设计的核心思想是:Agent 的内部思考过程和面向用户的输出应该分离。内部思考是「草稿纸」,SendUserMessage 是「正式报告」。

八、从泄露中学到的工程经验

8.1 Feature Flag 驱动开发

泄露的代码中有 44 个 feature flag,覆盖了从 KAIROS 到实验性 UI 的各种功能。这些功能已经完全实现,只是在外部构建中被编译为 false

这告诉我们:成熟的 AI 产品团队会大量使用 feature flag 来管理功能发布节奏。一个功能从开发完成到正式发布,中间可能经历数月的内部测试。

8.2 Source Map 是一个被低估的安全风险

这次泄露的根本原因是 source map 文件被包含在了 npm 发布包中。这不是 Anthropic 独有的问题——我检查过很多 npm 包,不少都包含了不该发布的 source map。

如果你在发布 npm 包,请检查你的 .npmignorepackage.jsonfiles 字段

{
  "files": [
    "dist/index.js",
    "dist/index.d.ts"
  ]
}

明确列出要发布的文件,比用 .npmignore 排除不想发布的文件更安全。

8.3 AI Agent 的架构正在趋同

对比 Claude Code、OpenAI Codex、Cursor 等工具的架构,你会发现它们正在趋同于一个共同的模式:

组件Claude CodeOpenAI Codex通用模式
核心循环Agent Loop + 消息队列Agent Loop消息驱动的自主循环
工具系统Tool Registry + 权限Function Calling声明式工具注册 + 权限控制
记忆MEMORY.md + Daily LogSession Memory短期 + 长期记忆分层
多 AgentSubAgent / TaskMulti-agent主 Agent + 专业子 Agent
安全三级权限 + HooksSandbox最小权限 + 沙箱隔离

九、实战:用这些模式构建你自己的 Agent

理解了这些模式后,你可以用任何语言实现一个简化版的 Agent Loop。以下是一个 Python 版本的核心框架:

import asyncio
from dataclasses import dataclass, field
from enum import Enum
from typing import Any

class Priority(Enum):
    HIGH = 0    # 用户输入
    NORMAL = 1  # 工具结果
    LOW = 2     # tick 心跳

@dataclass(order=True)
class Message:
    priority: int
    content: Any = field(compare=False)
    is_meta: bool = field(default=False, compare=False)

class AgentLoop:
    def __init__(self, llm_client, tools: dict):
        self.queue = asyncio.PriorityQueue()
        self.llm = llm_client
        self.tools = tools
    
    async def enqueue(self, content, priority=Priority.NORMAL, is_meta=False):
        msg = Message(priority.value, content, is_meta)
        await self.queue.put(msg)
    
    async def run(self):
        while not self.queue.empty():
            msg = await self.queue.get()
            
            response = await self.llm.chat(msg.content)
            
            if response.tool_calls:
                for call in response.tool_calls:
                    tool = self.tools.get(call.name)
                    if not tool:
                        continue
                    result = await tool.execute(call.params)
                    await self.enqueue(
                        {"role": "tool_result", "name": call.name, "result": result},
                        priority=Priority.NORMAL
                    )

这只是骨架,但它包含了最核心的设计:优先级队列、工具执行结果回注、异步驱动。

十、总结

Claude Code 的源码泄露,意外地给整个 AI 开发社区上了一堂架构课。从中我们可以提炼出 5 个关键设计模式:

  1. 消息驱动的 Agent Loop:用优先级队列替代简单的请求-响应
  2. Tick + Sleep 的后台自主模式:让 Agent 从被动工具变成主动助手
  3. WAL 式的分层记忆:追加写入 + 定期蒸馏,解决长期记忆问题
  4. 细粒度权限系统:最小权限原则,工具级别的 allow/deny
  5. 结构化输出通道:内部思考和用户可见输出分离

这些模式不是 Anthropic 的专利——它们是 AI Agent 架构演进的必然方向。无论你用什么框架、什么模型,这些设计思想都值得借鉴。

AI 编程工具正在从「聊天机器人」进化为「开发者操作系统」。理解这个趋势,比学会用任何一个具体工具都重要。


如果这篇文章对你有帮助,欢迎点赞收藏,也欢迎在评论区分享你对 AI Agent 架构的看法。

参考来源: