从 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 编程工具的下一个形态——从「聊天式助手」进化到「自主式操作系统」。理解它的设计模式,对于以下几类人都有直接价值:
- 正在构建 AI Agent 产品的团队:这是一份价值数亿美元的架构参考
- 使用 AI 编程工具的开发者:理解工具的内部机制,才能更高效地使用它
- 关注 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 install 或 make 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) 模式:
| 概念 | 数据库 WAL | Claude Code 记忆 |
|---|---|---|
| 写入层 | WAL 日志文件 | 每日追加日志 |
| 持久层 | 数据页 | MEMORY.md + 主题文件 |
| 合并过程 | Checkpoint | /dream 夜间蒸馏 |
| 恢复能力 | 从 WAL 重放 | 从日志重建记忆 |
为什么不直接更新 MEMORY.md?因为追加写入有三个关键优势:
- 不会丢失信息:即使蒸馏过程出错,原始日志还在
- 写入性能高:追加比随机更新快得多
- 天然的时间线:按日期组织,方便回溯
六、核心架构模式四:分层权限系统
6.1 三级权限模型
AI Agent 能读文件、写文件、执行命令——这意味着它有能力搞砸你的整个项目。Claude Code 的权限系统设计了三个层级:
- 只读模式:只能读取文件和搜索代码
- 监督执行模式:可以修改文件和执行命令,但每次都需要用户确认
- 自主执行模式:在预定义的规则范围内自主操作
权限规则可以精确到工具级别:
{
"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 包,请检查你的 .npmignore 或 package.json 的 files 字段:
{
"files": [
"dist/index.js",
"dist/index.d.ts"
]
}
明确列出要发布的文件,比用 .npmignore 排除不想发布的文件更安全。
8.3 AI Agent 的架构正在趋同
对比 Claude Code、OpenAI Codex、Cursor 等工具的架构,你会发现它们正在趋同于一个共同的模式:
| 组件 | Claude Code | OpenAI Codex | 通用模式 |
|---|---|---|---|
| 核心循环 | Agent Loop + 消息队列 | Agent Loop | 消息驱动的自主循环 |
| 工具系统 | Tool Registry + 权限 | Function Calling | 声明式工具注册 + 权限控制 |
| 记忆 | MEMORY.md + Daily Log | Session Memory | 短期 + 长期记忆分层 |
| 多 Agent | SubAgent / Task | Multi-agent | 主 Agent + 专业子 Agent |
| 安全 | 三级权限 + Hooks | Sandbox | 最小权限 + 沙箱隔离 |
九、实战:用这些模式构建你自己的 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 个关键设计模式:
- 消息驱动的 Agent Loop:用优先级队列替代简单的请求-响应
- Tick + Sleep 的后台自主模式:让 Agent 从被动工具变成主动助手
- WAL 式的分层记忆:追加写入 + 定期蒸馏,解决长期记忆问题
- 细粒度权限系统:最小权限原则,工具级别的 allow/deny
- 结构化输出通道:内部思考和用户可见输出分离
这些模式不是 Anthropic 的专利——它们是 AI Agent 架构演进的必然方向。无论你用什么框架、什么模型,这些设计思想都值得借鉴。
AI 编程工具正在从「聊天机器人」进化为「开发者操作系统」。理解这个趋势,比学会用任何一个具体工具都重要。
如果这篇文章对你有帮助,欢迎点赞收藏,也欢迎在评论区分享你对 AI Agent 架构的看法。
参考来源:
- Ars Technica: Entire Claude Code CLI source code leaks thanks to exposed map file
- The Hacker News: Claude Code Source Leaked via npm Packaging Error
- Code Pointer: Architecture of KAIROS, the Unreleased Always-on Background Agent
- The Neuron: Anthropic Leaks Claude Code, a Blueprint for AI Coding Agents
- Beam.ai: What the Claude Code Leak Means for Enterprise AI
- Claude Code Official Docs: How Claude Code Works