在这里插入图片描述

本文基于 @anthropic-ai/claude-code@2.1.88 泄露源码进行分析,仅供技术学习交流使用。

前言

2026年3月31日,Anthropic 的 Claude Code 因 npm 发布包中意外包含 sourcemap 文件而导致源代码泄露。这一事件虽然是一次安全事故,却为我们提供了一个难得的机会,得以一窥当前最顶尖的 AI 编程助手是如何架构和实现的。

本文将深入分析泄露源码中的几个核心模块,包括 BUDDY 电子宠物系统、协调器模式、AutoDream 记忆系统以及工具系统架构,希望能为正在开发 AI 应用的开发者提供一些架构设计上的启发。


Claude Code 源码深度解析:从泄露代码看 AI 编程助手的架构设计

一、项目整体架构概览

Claude Code 是一个基于 React + TypeScript 构建的终端 TUI(Text User Interface)应用,使用 Ink 作为终端渲染框架。

1.1 技术栈选型

├── 前端框架:React 18 + Ink(终端 UI)
├── 运行时:Node.js 18+ / Bun(内部构建使用 Bun)
├── 构建工具:Bun Bundler(内部使用)
├── 类型验证:Zod v4
├── 状态管理:自定义 AppState Context
└── AI SDK:@anthropic-ai/sdk

在这里插入图片描述

1.2 核心目录结构

src/
├── entrypoints/
│   └── cli.tsx              # CLI 入口文件
├── components/              # React 组件(TUI 组件)
│   ├── App.tsx             # 应用根组件
│   └── ...                 # 各种 UI 组件
├── tools/                   # 工具系统(40+ 个工具)
│   ├── BashTool/           # 终端命令工具
│   ├── FileEditTool/       # 文件编辑工具
│   ├── AgentTool/          # 多 Agent 协调工具
│   └── ...
├── commands/                # 斜杠命令系统
├── services/                # 核心服务
│   ├── autoDream/          # 自动记忆整理
│   ├── analytics/          # 埋点分析
│   └── mcp/                # MCP 协议实现
├── coordinator/             # 协调器模式
├── buddy/                   # BUDDY 电子宠物系统
├── assistant/               # KAIROS 助手模式
├── skills/                  # 技能系统
└── utils/                   # 工具函数

二、BUDDY 系统:藏在代码里的"电子宠物"

2.1 系统设计概述

BUDDY 是 Claude Code 内置的一个彩蛋功能——一个基于用户 ID 确定性生成的电子宠物系统。它的设计非常精巧,包含 18 种物种、稀有度系统、属性养成等 RPG 元素。

2.2 核心数据模型

// src/buddy/types.ts

// 稀有度等级
export const RARITIES = [
  'common',      // 普通
  'uncommon',    // 稀有
  'rare',        // 罕见
  'epic',        // 史诗
  'legendary'    // 传说
] as const

// 18 种物种(注意:使用了字符编码混淆,避免被字符串扫描工具检测到)
export const SPECIES = [
  duck, goose, blob, cat, dragon, octopus,
  owl, penguin, turtle, snail, ghost, axolotl,
  capybara, cactus, robot, rabbit, mushroom, chonk
] as const

// 6 种眼睛样式
export const EYES = ['·', '✦', '×', '◉', '@', '°'] as const

// 8 种帽子(普通品质无帽子)
export const HATS = [
  'none', 'crown', 'tophat', 'propeller', 
  'halo', 'wizard', 'beanie', 'tinyduck'
] as const

// 5 项属性
export const STAT_NAMES = [
  'DEBUGGING',   // 调试能力
  'PATIENCE',    // 耐心
  'CHAOS',       // 混沌(有趣的设计)
  'WISDOM',      // 智慧
  'SNARK'        // 毒舌
] as const

2.3 确定性生成算法

最精妙的部分在于完全基于用户 ID 的确定性生成,这意味着同一个用户永远会得到同一个伙伴,而且无法通过修改配置来"作弊"。

// src/buddy/companion.ts

// Mulberry32 - 小型种子化 PRNG
function mulberry32(seed: number): () => number {
  let a = seed >>> 0
  return function () {
    a |= 0
    a = (a + 0x6d2b79f5) | 0
    let t = Math.imul(a ^ (a >>> 15), 1 | a)
    t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t
    return ((t ^ (t >>> 14)) >>> 0) / 4294967296
  }
}

// 字符串哈希函数
function hashString(s: string): number {
  if (typeof Bun !== 'undefined') {
    return Number(BigInt(Bun.hash(s)) & 0xffffffffn)
  }
  let h = 2166136261
  for (let i = 0; i < s.length; i++) {
    h ^= s.charCodeAt(i)
    h = Math.imul(h, 16777619)
  }
  return h >>> 0
}

// 稀有度权重配置
const RARITY_WEIGHTS = {
  common: 50,      // 50%
  uncommon: 30,    // 30%
  rare: 15,        // 15%
  epic: 4,         // 4%
  legendary: 1     // 1%
}

// 稀有度决定属性下限
const RARITY_FLOOR: Record<Rarity, number> = {
  common: 5,
  uncommon: 15,
  rare: 25,
  epic: 35,
  legendary: 50
}

2.4 属性生成策略

属性生成采用了经典的一高一低其余随机策略,增加了趣味性和差异化:

function rollStats(rng: () => number, rarity: Rarity): Record<StatName, number> {
  const floor = RARITY_FLOOR[rarity]
  
  // 随机选择一个峰值属性
  const peak = pick(rng, STAT_NAMES)
  // 随机选择一个废柴属性(不能和峰值相同)
  let dump = pick(rng, STAT_NAMES)
  while (dump === peak) dump = pick(rng, STAT_NAMES)

  const stats = {} as Record<StatName, number>
  for (const name of STAT_NAMES) {
    if (name === peak) {
      // 峰值属性:下限 + 50-80
      stats[name] = Math.min(100, floor + 50 + Math.floor(rng() * 30))
    } else if (name === dump) {
      // 废柴属性:下限 - 10 到 +5
      stats[name] = Math.max(1, floor - 10 + Math.floor(rng() * 15))
    } else {
      // 普通属性:下限 + 0-40
      stats[name] = floor + Math.floor(rng() * 40)
    }
  }
  return stats
}

2.5 闪光机制

还有 1% 概率生成「闪光(Shiny)」版本,致敬了宝可梦系列:

function rollFrom(rng: () => number): Roll {
  const rarity = rollRarity(rng)
  const bones: CompanionBones = {
    rarity,
    species: pick(rng, SPECIES),
    eye: pick(rng, EYES),
    hat: rarity === 'common' ? 'none' : pick(rng, HATS),
    shiny: rng() < 0.01,  // 1% 闪光概率
    stats: rollStats(rng, rarity),
  }
  return { bones, inspirationSeed: Math.floor(rng() * 1e9) }
}

2.6 架构启示

  1. 确定性生成:基于用户 ID 的确定性算法确保了一致性体验
  2. 防作弊设计:骨骼(bones)不持久化,每次从用户 ID 重新生成
  3. 性能优化:使用闭包级别的缓存避免重复计算
  4. 代码混淆:物种名称使用字符编码,避免被静态扫描工具发现

三、协调器模式:多 Agent 协作的指挥家

在这里插入图片描述

3.1 设计背景

Claude Code 的协调器模式(Coordinator Mode)是一种多 Agent 并行协作架构,允许主 Agent 将任务分派给多个 Worker Agent 并行处理,然后汇总结果。

3.2 模式切换机制

// src/coordinator/coordinatorMode.ts

// 通过环境变量控制模式切换
export function isCoordinatorMode(): boolean {
  if (feature('COORDINATOR_MODE')) {
    return isEnvTruthy(process.env.CLAUDE_CODE_COORDINATOR_MODE)
  }
  return false
}

// 会话恢复时自动匹配模式
export function matchSessionMode(
  sessionMode: 'coordinator' | 'normal' | undefined,
): string | undefined {
  if (!sessionMode) return undefined

  const currentIsCoordinator = isCoordinatorMode()
  const sessionIsCoordinator = sessionMode === 'coordinator'

  if (currentIsCoordinator === sessionIsCoordinator) {
    return undefined
  }

  // 自动切换环境变量
  if (sessionIsCoordinator) {
    process.env.CLAUDE_CODE_COORDINATOR_MODE = '1'
  } else {
    delete process.env.CLAUDE_CODE_COORDINATOR_MODE
  }

  return sessionIsCoordinator
    ? 'Entered coordinator mode to match resumed session.'
    : 'Exited coordinator mode to match resumed session.'
}

3.3 系统提示词设计

协调器模式有专门的系统提示词,定义了主 Agent 的角色:

export function getCoordinatorSystemPrompt(): string {
  return `You are Claude Code, an AI assistant that orchestrates software engineering tasks across multiple workers.

## 1. Your Role

You are a **coordinator**. Your job is to:
- Help the user achieve their goal
- Direct workers to research, implement and verify code changes
- Synthesize results and communicate with the user
- Answer questions directly when possible — don't delegate work that you can handle without tools

Every message you send is to the user. Worker results and system notifications are internal signals, not conversation partners — never thank or acknowledge them. Summarize new information for the user as it arrives.

## 2. Your Tools

- **Agent** - Spawn a new worker
- **SendMessage** - Continue an existing worker
- **TaskStop** - Stop a running worker
- **subscribe_pr_activity** - Subscribe to GitHub PR events

When calling Agent:
- Do not use one worker to check on another
- Do not use workers to trivially report file contents
- Do not set the model parameter
- Continue workers whose work is complete via SendMessage
- After launching agents, briefly tell the user what you launched

### Agent Results

Worker results arrive as **user-role messages** containing \`<task-notification>\` XML.

Format:
\`\`\`xml
<task-notification>
<task-id>{agentId}</task-id>
<status>success | error | interrupted</status>
<title>Task Title</title>
<result>Task result summary</result>
</task-notification>
\`\`\``
}

3.4 Worker 工具上下文

协调器会根据配置动态生成 Worker 可用工具的上下文信息:
在这里插入图片描述

export function getCoordinatorUserContext(
  mcpClients: ReadonlyArray<{ name: string }>,
  scratchpadDir?: string,
): { [k: string]: string } {
  if (!isCoordinatorMode()) return {}

  // Worker 可用工具列表
  const workerTools = isEnvTruthy(process.env.CLAUDE_CODE_SIMPLE)
    ? ['Bash', 'Read', 'Edit']
    : Array.from(ASYNC_AGENT_ALLOWED_TOOLS)
        .filter(name => !INTERNAL_WORKER_TOOLS.has(name))

  let content = `Workers spawned via the Agent tool have access to these tools: ${workerTools.join(', ')}`

  // MCP 工具
  if (mcpClients.length > 0) {
    const serverNames = mcpClients.map(c => c.name).join(', ')
    content += `\n\nWorkers also have access to MCP tools from connected MCP servers: ${serverNames}`
  }

  // Scratchpad 目录(免权限检查的工作区)
  if (scratchpadDir && isScratchpadGateEnabled()) {
    content += `\n\nScratchpad directory: ${scratchpadDir}\nWorkers can read and write here without permission prompts.`
  }

  return { workerToolsContext: content }
}

3.5 架构启示

  1. 明确的角色分离:协调器负责决策和汇总,Worker 负责执行
  2. 环境变量控制:通过 CLAUDE_CODE_COORDINATOR_MODE 切换模式
  3. 动态上下文注入:根据配置动态生成系统提示词
  4. 任务通知机制:使用 XML 格式的系统消息传递 Worker 结果

四、AutoDream:AI 的"做梦"记忆系统

在这里插入图片描述

4.1 设计背景

AutoDream 是 Claude Code 的后台记忆整合系统,灵感来源于人类睡眠时的记忆巩固过程。它会在后台定期运行,整理用户的会话历史,提取重要信息并更新记忆文件。

4.2 触发条件(三重门控)

// src/services/autoDream/autoDream.ts

/**
 * Gate order (cheapest first):
 *   1. Time: hours since lastConsolidatedAt >= minHours
 *   2. Sessions: transcript count with mtime > lastConsolidatedAt >= minSessions
 *   3. Lock: no other process mid-consolidation
 */

const DEFAULTS: AutoDreamConfig = {
  minHours: 24,       // 至少 24 小时
  minSessions: 5,     // 至少 5 个新会话
}

// 扫描节流:避免频繁扫描
const SESSION_SCAN_INTERVAL_MS = 10 * 60 * 1000  // 10 分钟

4.3 核心流程

async function runAutoDream(context, appendSystemMessage) {
  const cfg = getConfig()
  
  // Gate 1: 功能开关检查
  if (!isGateOpen()) return
  
  // Gate 2: 时间门控
  const lastAt = await readLastConsolidatedAt()
  const hoursSince = (Date.now() - lastAt) / 3_600_000
  if (hoursSince < cfg.minHours) return
  
  // Gate 3: 扫描节流
  const sinceScanMs = Date.now() - lastSessionScanAt
  if (sinceScanMs < SESSION_SCAN_INTERVAL_MS) return
  
  // Gate 4: 会话数量检查
  const sessions = await listSessionsTouchedSince(lastAt)
  if (sessions.length < cfg.minSessions) return
  
  // Gate 5: 获取分布式锁
  const lock = await tryAcquireConsolidationLock()
  if (!lock.acquired) return
  
  try {
    // 注册 Dream 任务
    registerDreamTask()
    
    // 运行 Forked Agent 执行记忆整理
    await runForkedAgent({
      messages: buildConsolidationPrompt(sessions),
      // ... 其他参数
    })
    
    completeDreamTask()
  } catch (e) {
    failDreamTask(e)
    await rollbackConsolidationLock()
  }
}

4.4 记忆整理提示词

function buildConsolidationPrompt(sessions: Session[]): Message[] {
  return [
    createUserMessage(`You are consolidating memories from ${sessions.length} sessions.`),
    createUserMessage(`Review the sessions and extract important information to update the memory files.`),
    // ... 更详细的指示
  ]
}

4.5 架构启示

  1. 多级门控设计:从便宜到昂贵的检查顺序,避免不必要的资源消耗
  2. 分布式锁:使用文件锁确保多进程环境下不会重复执行
  3. Forked Agent:在独立进程中运行,避免阻塞主循环
  4. 失败回滚:失败时回滚锁状态,确保系统一致性

五、工具系统:插件化架构设计

在这里插入图片描述

5.1 工具基类设计

Claude Code 的工具系统采用了声明式 + 函数式的混合架构:

// src/Tool.ts (简化版)

export interface ToolDef<TParams, TResult> {
  name: string                    // 工具名称
  description: string             // 工具描述(用于 LLM)
  paramsSchema: z.ZodSchema<TParams>  // 参数校验模式
  async run(params: TParams, context: ToolUseContext): Promise<TResult>
}

export function buildTool<TParams, TResult>(
  def: ToolDef<TParams, TResult>
): Tool {
  return {
    ...def,
    // 包装校验逻辑
    async run(params, context) {
      const validated = def.paramsSchema.parse(params)
      return def.run(validated, context)
    }
  }
}

5.2 Bash 工具实现示例

以最常用的 BashTool 为例,展示完整的工具实现:

// src/tools/BashTool/BashTool.tsx (简化版)

export const BashTool = buildTool({
  name: BASH_TOOL_NAME,
  description: `Execute a bash command in the terminal.`,
  
  paramsSchema: z.object({
    command: z.string().describe('The bash command to execute'),
    timeout: z.number().optional(),
    description: z.string().optional(),
  }),
  
  async run(params, context) {
    const { command, timeout, description } = params
    
    // 1. 权限检查
    const permission = await bashToolHasPermission(command, context)
    if (!permission.granted) {
      throw new PermissionDeniedError(permission.reason)
    }
    
    // 2. 安全检查
    const securityCheck = parseForSecurity(command)
    if (securityCheck.isDangerous) {
      await showDestructiveWarning(context, securityCheck)
    }
    
    // 3. 执行命令
    const result = await exec(command, { timeout })
    
    // 4. 结果处理
    return {
      stdout: result.stdout,
      stderr: result.stderr,
      exitCode: result.exitCode,
    }
  },
})

5.3 工具分类

Claude Code 将 40+ 个工具分为几类:

// 文件操作工具
const FILE_TOOLS = [
  'FileRead', 'FileWrite', 'FileEdit', 'Glob', 'Grep'
]

// 系统工具
const SYSTEM_TOOLS = [
  'Bash', 'PowerShell', 'REPL', 'Sleep'
]

// Agent 工具
const AGENT_TOOLS = [
  'Agent', 'SendMessage', 'TaskCreate', 'TaskStop'
]

// 配置工具
const CONFIG_TOOLS = [
  'Config', 'McpAuth', 'ScheduleCron'
]

5.4 权限系统

工具权限采用规则引擎 + 用户确认的双层设计:

// src/utils/permissions/PermissionResult.ts

export type PermissionResult = 
  | { granted: true }
  | { granted: false; reason: string }

// 权限规则示例
const PERMISSION_RULES = [
  {
    pattern: /^git (add|commit|push)/,
    level: 'auto',  // 自动批准
  },
  {
    pattern: /^rm -rf \//,
    level: 'deny',  // 永远拒绝
  },
  {
    pattern: /.*/,
    level: 'ask',   // 默认询问
  }
]

六、架构设计总结

6.1 模块化设计原则

  1. 单一职责:每个模块只做一件事(如 buddy、coordinator、autoDream 各自独立)
  2. 依赖注入:通过 context 和参数传递依赖,避免硬编码
  3. 功能开关:大量使用 feature flag 控制功能发布

6.2 性能优化策略

  1. 懒加载:动态导入减少启动时间
  2. 缓存策略:多级缓存(用户级、进程级、闭包级)
  3. 后台任务:耗时操作放入 forked agent 异步执行

6.3 安全设计

  1. 输入校验:所有工具参数使用 Zod 严格校验
  2. 权限分级:自动/询问/拒绝三级权限体系
  3. 沙箱执行:危险命令在沙箱环境中运行

6.4 可扩展性

  1. MCP 协议:支持外部工具通过 MCP 协议接入
  2. 技能系统:用户可自定义斜杠命令
  3. 插件系统:预留插件扩展点

七、结语

通过这次源码泄露事件,我们得以深入了解 Claude Code 的架构设计。从中可以学习到:

  1. TUI 应用的最佳实践:使用 React + Ink 构建终端界面
  2. 多 Agent 协作模式:协调器模式为复杂任务分解提供了思路
  3. 记忆系统设计:AutoDream 的"做梦"机制为长程记忆管理提供了灵感
  4. 工具系统设计:声明式 + 函数式的工具定义方式

这些设计思想不仅适用于 AI 编程助手,对于构建任何复杂的 AI 应用都具有参考价值。


参考资料

  • 泄露源码版本:@anthropic-ai/claude-code@2.1.88
  • GitHub 备份仓库:claude-code-sourcemap
  • Ink 终端 UI 框架:https://github.com/vadimdemedes/ink
  • MCP 协议规范:https://modelcontextprotocol.io

本文仅供技术学习交流,源码版权归 Anthropic 所有。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐