Claude Code Agent 工程深度教学手册
基于 Claude Code 泄露源码(2026-03-31),约 1,915 个 TypeScript 文件、51.2 万行代码的逐模块拆解。 本文档面向想要理解「一个工业级 AI Agent 系统到底是怎么设计的」的工程师和研究者。
第一章 全局架构:一个 Agent 系统的骨架
1.1 技术选型
| 层面 | 选择 | 为什么这么选 |
|---|---|---|
| 运行时 | Bun | 比 Node.js 快 3-5 倍的启动速度,内置 bundler,支持编译时 feature flag |
| 语言 | TypeScript (strict) | 类型安全在大型 Agent 系统中至关重要——工具参数、权限、消息类型都需要精确约束 |
| 终端 UI | React + Ink | 把 CLI 当成 SPA 来写,组件化、状态驱动、声明式渲染 |
| CLI 解析 | Commander.js | 成熟的 CLI 框架,额外类型支持 |
| Schema 验证 | Zod v4 | 工具输入验证、配置验证,TypeScript 类型推断零成本 |
| 协议 | MCP SDK + LSP | 标准协议,可扩展的工具和语言服务集成 |
| API | Anthropic SDK | 官方 SDK,流式响应、工具调用原生支持 |
启发性:选择 Bun 而非 Node.js,不仅是速度考量——Bun 的 feature() 宏允许在编译时剥离整个代码分支,这对一个需要在多种环境(内部/外部、Pro/Free)部署的 Agent 系统来说是关键能力。
1.2 启动流程:毫秒级优化
main.tsx 入口
│
├── [Side Effect] startMdmRawRead() // 启动 plutil 子进程读取 MDM 配置
├── [Side Effect] startKeychainPrefetch() // 并行读取 macOS 钥匙串
├── [Side Effect] profileCheckpoint() // 打桩计时
│
├── Commander.js 解析 CLI 参数
│
├── init() ─── 核心初始化(memoized,只跑一次)
│ ├── configureGlobalAgents() // HTTP 代理
│ ├── configureGlobalMTLS() // mTLS 证书
│ ├── applyExtraCACertsFromConfig() // CA 证书
│ ├── setupGracefulShutdown() // SIGTERM/SIGINT 处理
│ ├── detectCurrentRepository() // Git 仓库检测
│ ├── ensureScratchpadDir() // 临时目录
│ └── initializeTelemetry() // OpenTelemetry(延迟加载 ~400KB)
│
├── initializeGrowthBook() // 特性开关
├── loadPolicyLimits() // 策略限制
├── loadRemoteManagedSettings() // 远程设置
│
└── React/Ink render() ──── 进入 REPL 界面
核心设计思想:启动时间 = 用户感受到的「Agent 是否好用」的第一印象。Claude Code 做了三件关键的事:
- 并行预取:MDM 读取、Keychain 读取、API 预连接都在
import语句的副作用中启动,与后续 135ms 的模块加载并行 - 延迟加载:OpenTelemetry(~400KB)和 gRPC(~700KB)通过
dynamic import()推迟到实际需要时 - 编译时剔除:
feature('VOICE_MODE')等标志在 Bun bundle 时直接变成false,整个分支被 tree-shake 掉
对比其他 Agent 框架:LangChain、AutoGPT 等框架通常不关注启动性能。对于 CLI 工具而言,每次命令调用都是一次冷启动——如果启动要 2 秒,用户会放弃。Claude Code 把启动优化到了 200ms 以内。
第二章 工具系统:Agent 的手和脚
2.1 工具的统一抽象
每个工具通过 buildTool() 工厂函数创建,必须实现以下接口:
type ToolDef<InputSchema, Output> = {
name: string // 工具名称
inputSchema: ZodSchema // 输入 Schema(Zod 定义)
outputSchema: ZodSchema // 输出 Schema
description(): Promise<string> // 给 LLM 看的描述
prompt(): Promise<string> // 给 LLM 看的使用提示
call(input, context): Promise<Output> // 实际执行逻辑
checkPermissions(input): Promise<PermissionResult> // 权限检查
renderToolUseMessage() // 终端渲染:调用时显示
renderToolResultMessage() // 终端渲染:结果显示
renderToolUseProgressMessage() // 终端渲染:进度显示
isResultTruncated(output): boolean // 结果是否被截断
userFacingName(): string // 用户看到的名称
}
设计亮点:
- 描述和提示分离:
description()是简短的功能说明,prompt()是详细的使用指南。前者出现在工具列表中,后者只在 LLM 选中该工具后才注入。这节省了大量 token。 - 权限前置:
checkPermissions()在call()之前执行。不是「执行了再问」,而是「问了才执行」。 - 渲染解耦:每个工具自己负责终端展示。BashTool 显示命令和输出,FileEditTool 显示 diff,GrepTool 显示匹配行——这不是框架统一处理的,而是每个工具根据自己的语义定制的。
2.2 工具详解
2.2.1 BashTool — Shell 命令执行
文件:src/tools/BashTool/BashTool.tsx(~600 行)
BashTool 是最复杂的工具之一。它不是简单地 exec(command),而是:
- 安全解析:通过
parseForSecurity()对命令进行 AST 解析,检测危险操作 - 命令分类:区分搜索命令(grep/find/rg)、读取命令(cat/head/tail)、列目录命令(ls/tree)和写入命令,用于生成不同的摘要和折叠显示
- 沙箱支持:通过
SandboxManager可选地在隔离环境中执行 - 超时管理:默认 2 分钟超时,最大 10 分钟,可配置
- 进度显示:超过 2 秒的命令会显示进度指示
- 自动后台化:在 assistant 模式下,主 Agent 的阻塞命令超过 15 秒会自动转为后台任务
- 输出截断:输出过大时会截断并生成预览,完整结果存到磁盘
- Git 跟踪:自动检测 git 操作并记录变更
- 编码保持:检测文件编码和行尾(CRLF/LF),确保写回时一致
// 命令语义分类——不同命令给 LLM 不同的摘要
const BASH_SEARCH_COMMANDS = new Set(['find', 'grep', 'rg', 'ag', 'ack', 'locate'])
const BASH_READ_COMMANDS = new Set(['cat', 'head', 'tail', 'less', 'wc', 'jq', 'awk'])
const BASH_LIST_COMMANDS = new Set(['ls', 'tree', 'du'])
const BASH_SEMANTIC_NEUTRAL_COMMANDS = new Set(['echo', 'printf', 'true', 'false', ':'])
启发性:大多数 Agent 框架的 Shell 工具就是一个 subprocess.run()。Claude Code 对命令语义的理解深度——安全检查、分类摘要、自动后台化——体现了工业级 Agent 对可靠性的要求。
2.2.2 FileEditTool — 精确文件编辑
文件:src/tools/FileEditTool/FileEditTool.ts
不同于 FileWriteTool(全文覆写),FileEditTool 做的是字符串替换:
- 读取文件并检测编码、行尾符
- 查找
old_string,确保唯一(不唯一则报错要求提供更多上下文) - 替换为
new_string - 写回文件,保持原编码和行尾符
- 触发 LSP 诊断清除(因为文件变了,旧诊断不再有效)
- 通知 VS Code 文件已更新(通过 MCP 通道)
- 记录 file history(用于回退)
- 发现
.claude/skills等目录中的技能文件时,自动激活相关技能
核心设计:「精确替换」而非「diff/patch」。这对 LLM 来说更容易——它只需要写出要替换的原文和替换后的文本,不需要理解 unified diff 格式。代价是需要确保 old_string 的唯一性。
2.2.3 FileReadTool — 智能文件读取
支持读取:
- 普通文本文件(带行号输出)
- 图片(转为 base64 给多模态 LLM)
- PDF(分页读取,最大 20 页/次)
- Jupyter Notebook(解析所有 cell 和输出)
设计选择:不让 LLM 用 cat 读文件,而是提供专用工具。好处是可以添加行号、限制读取范围、支持非文本格式,并且跟踪哪些文件被读过(用于 FileEditTool 的前置校验——编辑前必须先读过)。
2.2.4 GlobTool 和 GrepTool — 搜索双雄
- GlobTool:基于文件名模式匹配,返回按修改时间排序的文件路径列表
- GrepTool:基于 ripgrep 的内容搜索,支持正则、文件类型过滤、上下文行、多行匹配
设计选择:为什么不直接用 BashTool 执行 find 和 grep?因为:
- 专用工具的权限模型更简单(只是读操作,不需要额外许可)
- 输出格式可以针对 LLM 优化(比如 GrepTool 的
files_with_matches模式比grep -l的输出更结构化) - 有
head_limit和offset参数,避免 LLM 被海量搜索结果淹没
2.2.5 WebFetchTool 和 WebSearchTool
- WebFetchTool:获取 URL 内容,有域名白名单限制
- WebSearchTool:网络搜索
这两个工具是 Agent 获取外部知识的窗口。
2.2.6 MCPTool — 协议桥梁
文件:src/tools/MCPTool/MCPTool.ts
MCPTool 是一个「元工具」——它本身不做什么,而是作为 MCP 服务器工具的代理。关键设计:
export const MCPTool = buildTool({
isMcp: true,
name: 'mcp', // 会被 mcpClient.ts 覆盖为真实 MCP 工具名
inputSchema: z.object({}).passthrough(), // 接受任意输入,因为 MCP 工具定义自己的 schema
async call() { return { data: '' } }, // 也会被覆盖
// ...
})
MCPTool 的 name、description、prompt、call 全部在 mcpClient.ts 中被覆盖。这是一个巧妙的「模板模式」——统一了 MCP 工具在 Claude Code 工具系统中的接入方式。
2.2.7 AgentTool — 子 Agent 生成器(核心中的核心)
文件:src/tools/AgentTool/AgentTool.tsx(~800 行)+ runAgent.ts(~400 行)
AgentTool 是整个多 Agent 架构的入口。当主 Agent 调用 AgentTool 时:
- 选择 Agent 类型:根据
subagent_type参数选择内置或用户定义的 Agent - 构建工具池:根据 Agent 定义的
tools字段筛选可用工具 - 创建上下文:
createSubagentContext()为子 Agent 创建独立的运行上下文 - 执行查询循环:调用
query()函数——与主 Agent 使用完全相同的查询引擎 - 结果回收:子 Agent 完成后,提取结果返回给父 Agent
// runAgent.ts 核心流程
export async function runAgent(options) {
// 1. 创建子 Agent 上下文(独立的消息历史、工具集、权限)
const subagentContext = createSubagentContext(toolUseContext, ...)
// 2. 注册到 perfetto tracing(可选)
if (isPerfettoTracingEnabled()) registerPerfettoAgent(agentId)
// 3. 执行 query() —— 和主 Agent 用同一个引擎
const result = await query(messages, tools, systemPrompt, ...)
// 4. 记录 transcript
recordSidechainTranscript(agentId, messages)
// 5. 清理
killShellTasksForAgent(agentId)
cleanupAgentTracking(agentId)
return result
}
六种内置 Agent:
| Agent 类型 | 用途 | 工具集 | 关键特点 |
|---|---|---|---|
general-purpose |
通用任务 | 所有工具 | 默认 Agent,能搜能写 |
Explore |
代码探索 | 只读工具 | 严格只读——不能创建、修改、删除任何文件 |
Plan |
架构规划 | 只读工具 | 设计实现方案,不执行 |
claude-code-guide |
使用指南 | Glob, Grep, Read, WebFetch, WebSearch | 回答关于 Claude Code 本身的问题 |
verification |
验证检查 | 只读 + Bash | 运行测试、检查构建 |
statusline-setup |
状态栏配置 | Read, Edit | 配置用户的状态栏设置 |
Fork Subagent 机制(实验性):当省略 subagent_type 时,触发隐式 fork——子 Agent 继承父 Agent 的完整对话上下文和系统提示。这使得「分支思考」成为可能:主 Agent 可以同时在多个方向探索,而不丢失上下文。
关键设计约束:
- Fork 子 Agent 不能再 fork(通过检测对话历史中的
FORK_BOILERPLATE_TAG防止递归) - Fork 和 Coordinator 模式互斥——协调器有自己的委派模型
- Fork 子 Agent 使用父 Agent 的渲染后系统提示(字节精确),避免重新渲染导致 prompt cache 失效
2.2.8 SendMessageTool — Agent 间通信
允许向正在运行的 Agent 发送后续消息。支持多种消息类型:
- 普通文本消息
shutdown_request/shutdown_response— 请求/确认关闭plan_approval_response— 计划审批
通过解析目标地址,可以发送给:
- 本地 Agent(
isLocalAgentTask) - 团队 Teammate(
findTeammateTaskByAgentId) - 主会话(
isMainSessionTask) - REPL Bridge 中的远程会话
2.2.9 TaskCreateTool / TaskUpdateTool / TaskGetTool / TaskListTool / TaskStopTool / TaskOutputTool — 任务生命周期
完整的任务管理 API:
- Create:创建异步任务,可以后台运行
- Update:更新任务状态和进度
- Get:查询任务当前状态
- List:列出所有任务
- Stop:终止运行中的任务
- Output:获取任务的输出内容
2.2.10 TeamCreateTool / TeamDeleteTool — 团队管理
TeamCreateTool 创建一个 Agent 团队:
- 生成团队文件(JSON 格式,存储团队成员信息)
- 分配 team lead
- 注册颜色(每个 teammate 在终端中有不同颜色)
- 初始化任务目录
2.2.11 其他工具
- SkillTool:执行注册的 Skill(详见技能系统章节)
- ToolSearchTool:延迟工具发现——当工具列表太长时,部分工具会延迟加载,LLM 通过 ToolSearch 按需获取
- EnterPlanModeTool / ExitPlanModeTool:切换计划模式——在此模式下 Agent 只能思考和搜索,不能修改文件
- EnterWorktreeTool / ExitWorktreeTool:Git worktree 隔离——子 Agent 在独立的 git 分支中工作,完成后可以合并或丢弃
- TodoWriteTool:管理用户可见的 TODO 列表
- SleepTool:主动模式下的等待——Agent 可以「睡眠」等待外部事件
- ScheduleCronTool:创建定时任务
- RemoteTriggerTool:远程触发
- SyntheticOutputTool:生成结构化输出(JSON 等)
- ConfigTool:读写配置
- BriefTool:切换简洁输出模式
- LSPTool:调用 Language Server Protocol 功能
- PowerShellTool:Windows 环境下的 PowerShell 执行
- REPLTool:交互式 REPL 环境
- AskUserQuestionTool:向用户提问并等待回答
2.3 工具系统的设计启发
与 LangChain Tools 对比:
- LangChain 的工具是扁平的函数签名 + 描述字符串。Claude Code 的工具有完整的权限模型、进度报告、渲染逻辑和错误处理。
- LangChain 的工具互相独立。Claude Code 的工具之间有协作关系(FileEditTool 要求先 FileReadTool,BashTool 追踪 git 操作)。
与 OpenAI Function Calling 对比:
- OpenAI 的 function calling 只定义了输入 schema。Claude Code 还定义了输出 schema、权限模型、渲染逻辑和截断策略。
核心设计原则:
- 工具是一等公民——不是简单的函数包装,而是有生命周期的实体
- 安全优先——每个工具都有独立的权限检查
- 对 LLM 友好——描述、提示、结果格式都为 LLM 优化
- 对用户友好——每个工具都有定制的终端渲染
第三章 服务层:Agent 的神经系统
3.1 MCP 服务(src/services/mcp/)—— 最复杂的服务
Model Context Protocol 是 Claude Code 扩展能力的核心通道。22 个文件,涵盖:
客户端连接(client.ts):
- 支持三种传输方式:Stdio(进程管道)、SSE(Server-Sent Events)、Streamable HTTP
- 每个 MCP 服务器连接是一个
Client实例 - 连接后自动发现工具(
fetchToolsForClient),将 MCP 工具包装为 Claude Code 的MCPTool
配置管理(config.ts):
- MCP 服务器可以在多个层级配置:全局、项目、用户
- 支持环境变量扩展(
envExpansion.ts)
认证(auth.ts + xaa.ts):
- OAuth 2.0 Token 管理
- Claude.ai 集成认证(
claudeai.ts) - IDP 登录(
xaaIdpLogin.ts)
通道权限(channelPermissions.ts + channelAllowlist.ts):
- 每个 MCP 通道有独立的权限策略
- 白名单机制控制哪些工具可以被调用
VS Code 集成(vscodeSdkMcp.ts):
- 通过 MCP 通道与 VS Code 扩展通信
- 文件变更通知、编辑器状态同步
3.2 API 服务(src/services/api/)
与 Anthropic API 的通信层:
- bootstrap.ts:启动时获取引导数据(模型列表、费率限制等)
- filesApi.ts:文件上传/下载
- dumpPrompts.ts:调试用——转储发送给 API 的完整 prompt
- promptCacheBreakDetection.ts:检测 prompt cache 是否被破坏(MCP 异步连接、插件重载等会改变工具列表,导致 cache 失效)
Prompt Cache 优化是 Claude Code 的核心性能策略之一。Agent 列表曾占全量 cache_creation tokens 的 10.2%——MCP 异步连接、/reload-plugins、权限模式变化都会改变列表内容导致 cache 失效。解决方案是把 Agent 列表从工具描述中移到消息附件中(shouldInjectAgentListInMessages()),这样工具 schema 保持稳定。
3.3 Compact 服务(src/services/compact/)—— 上下文压缩
当对话变长、Token 接近限制时,Compact 服务会:
- 运行
preCompactHooks - 使用 forked agent(共享父 Agent 的 prompt cache)对对话历史进行总结压缩
- 插入
SystemCompactBoundaryMessage标记压缩边界 - 重新注入关键的附件信息(Agent 列表、延迟工具列表、MCP 指令)
- 运行
postCompactHooks
Forked Agent 模式:Compact 不是调用一个独立的 API,而是 fork 当前 Agent 的对话。这意味着压缩 Agent 可以共享父 Agent 的 prompt cache,大幅减少 token 消耗。
3.4 记忆提取服务(src/services/extractMemories/)
在每次完整查询循环结束后(模型产生无工具调用的最终响应)自动运行:
- 检查是否启用自动记忆(
isAutoMemoryEnabled()) - 使用 forked agent 分析当前对话
- 提取值得持久化的记忆
- 写入
~/.claude/projects/<path>/memory/目录
同样使用 forked agent 模式——共享 prompt cache,几乎零额外 token 成本。
3.5 其他服务
| 服务 | 职责 | 设计要点 |
|---|---|---|
oauth/ |
OAuth 2.0 认证 | 支持 PKCE 流程、Token 刷新 |
lsp/ |
Language Server Protocol | 管理多个 LSP 服务器实例,提供诊断、补全 |
analytics/ |
GrowthBook 特性开关 + 事件日志 | 支持缓存和阻塞两种查询模式 |
policyLimits/ |
组织策略限制 | Enterprise 版限制特定功能 |
remoteManagedSettings/ |
远程托管设置 | IT 管理员远程推送配置 |
teamMemorySync/ |
团队记忆同步 | 在团队成员间同步共享记忆 |
AgentSummary/ |
Agent 总结 | 生成 Agent 执行摘要 |
PromptSuggestion/ |
提示建议 | 基于上下文推荐下一步操作 |
autoDream/ |
自动后台推理 | 在空闲时进行预测性推理 |
tips/ |
使用提示 | 适时显示使用技巧 |
plugins/ |
插件加载 | 管理插件生命周期 |
第四章 Query 引擎:Agent 的大脑
4.1 QueryEngine 的核心循环
QueryEngine.ts 约 46,000 行,是整个系统最大的单文件。它实现了 Agent 的「思考-行动-观察」循环:
用户消息 → query()
│
├── 构建 QueryConfig(快照 runtime gates)
├── 创建 BudgetTracker(Token 预算追踪)
│
└── 循环:
├── 调用 Anthropic API(流式)
├── 解析响应
│ ├── 文本内容 → 累积到结果
│ ├── 工具调用 → 执行工具 → 工具结果加入消息
│ └── 结束标记 → 检查 stopHooks
│
├── stopHooks 检查:
│ ├── extractMemories(提取记忆)
│ ├── autoDream(自动推理)
│ ├── promptSuggestion(提示建议)
│ ├── taskCompletedHooks(任务完成钩子)
│ └── tokenBudget(Token 预算检查)
│
├── Token 预算检查:
│ ├── 消耗 < 90% → 继续
│ ├── 消耗 >= 90% → 发送 nudge 继续
│ └── 回报递减(delta < 500 且连续 3 次)→ 停止
│
└── 继续/停止决策
4.2 QueryConfig — 查询快照
type QueryConfig = {
sessionId: SessionId
gates: {
streamingToolExecution: boolean // 是否流式执行工具
emitToolUseSummaries: boolean // 是否发射工具使用摘要
isAnt: boolean // 是否 Anthropic 内部用户
fastModeEnabled: boolean // 快速模式
}
}
QueryConfig 在每次 query() 入口快照一次。这是一个重要的设计决策——查询过程中的配置是不可变的,避免了中途配置变化导致的不一致。
4.3 Token 预算管理
type BudgetTracker = {
continuationCount: number // 已继续次数
lastDeltaTokens: number // 上次增量
lastGlobalTurnTokens: number // 上次全局 turn token 数
startedAt: number // 开始时间
}
预算决策逻辑:
- 如果是子 Agent(
agentId存在)或无预算——停止 - 消耗 < 90% → 停止(还没到需要继续的程度)
- 消耗 >= 90% → 发送 nudge 消息让模型继续
- 连续 3 次以上且每次增量 < 500 token → 回报递减,停止
4.4 Stop Hooks — 查询结束后的连锁反应
当一次查询循环结束(模型产生无工具调用的响应),会触发一系列 hook:
- extractMemories:提取并保存记忆
- autoDream:后台推理
- promptSuggestion:生成下一步建议
- taskCompletedHooks:通知任务管理系统
- teammateIdleHooks:通知团队系统(如果在 swarm 模式中)
第五章 多 Agent 协调:从独行侠到军团
5.1 协调器模式(Coordinator Mode)
通过 CLAUDE_CODE_COORDINATOR_MODE=1 环境变量 + COORDINATOR_MODE feature flag 启用。
在协调器模式下,主 Agent 变成一个纯粹的指挥官:
- 不直接操作文件
- 不执行 shell 命令
- 只使用三种工具:
AgentTool(生成 Worker)、SendMessageTool(向 Worker 发消息)、TaskStopTool(终止 Worker)
协调器的系统提示(直接来自源码):
You are Claude Code, an AI assistant that orchestrates software engineering tasks across multiple workers.
Your Role: You are a coordinator. Your job is to help the user achieve their goal, direct workers to research/implement/verify code changes, synthesize results and communicate with the user, and answer questions directly when possible — don't delegate work that you can handle without tools.
Worker 的结果通知机制:Worker 完成后,其结果以 <task-notification> XML 格式作为用户消息注入协调器的对话:
<task-notification>
<task-id>{agentId}</task-id>
<status>completed|failed|killed</status>
<summary>{human-readable status summary}</summary>
<result>{agent's final text response}</result>
<usage>
<total_tokens>N</total_tokens>
<tool_uses>N</tool_uses>
<duration_ms>N</duration_ms>
</usage>
</task-notification>
Scratchpad 目录:协调器模式下有一个共享临时目录,Worker 可以在这里读写文件而无需权限提示。这是跨 Worker 知识传递的关键通道。
5.2 Agent Swarms(Team 模式)
更高级的多 Agent 架构。TeamCreateTool 创建一个由多个 teammate 组成的团队:
- Team Lead:团队领导,协调团队内部工作
- Teammates:团队成员,执行具体任务
- Team File:JSON 文件,记录团队成员信息、状态、角色
- Mailbox:每个 teammate 有一个信箱,通过文件系统实现异步消息传递
TeamCreateTool
│
├── 生成 team_name
├── 创建 team file (JSON)
├── 分配 lead agent
├── 注册颜色(每个 teammate 终端颜色不同)
├── 初始化任务目录
└── 注册 session 清理回调
团队通信协议:
SendMessageTool支持向指定to地址发送消息- 支持
shutdown_request/shutdown_response协议——teammate 可以请求关闭,lead 审批 - 支持
plan_approval_response——teammate 提出计划,等待审批
5.3 Fork Subagent(实验性)
当 FORK_SUBAGENT feature flag 启用时,省略 subagent_type 参数会触发隐式 fork:
export const FORK_AGENT = {
agentType: 'fork',
tools: ['*'], // 继承父 Agent 所有工具
maxTurns: 200,
model: 'inherit', // 使用父 Agent 的模型
permissionMode: 'bubble', // 权限冒泡到父终端
// ...
}
Fork 子 Agent 的核心创新:
- 上下文继承:子 Agent 拿到父 Agent 的完整对话历史
- Prompt Cache 保持:使用父 Agent 渲染后的系统提示字节流,确保 cache 命中
- 防递归:通过检测
FORK_BOILERPLATE_TAG防止 fork 嵌套
5.4 与其他多 Agent 框架的对比
| 维度 | Claude Code | AutoGPT | CrewAI | MetaGPT |
|---|---|---|---|---|
| Agent 间通信 | XML 通知 + Mailbox | 共享内存 | 委派链 | 消息队列 |
| 任务管理 | 内置 Task 工具 + 文件系统 | 简单目标链 | 角色基础 | SOPWorkflow |
| 资源隔离 | Git Worktree + 独立上下文 | 无 | 无 | 无 |
| Prompt Cache | 精心优化 | 不关注 | 不关注 | 不关注 |
| 权限模型 | 冒泡/独立/自动多种模式 | 无 | 无 | 无 |
Claude Code 的独特优势:
- Prompt Cache 意识:这是其他框架完全忽略的维度。Claude Code 在多 Agent 场景下仍然关注 cache 命中率
- Git Worktree 隔离:子 Agent 在独立的 git 分支中工作,互不干扰
- 任务通知机制:异步通知而非轮询,效率更高
- 权限冒泡:子 Agent 的权限请求可以冒泡到用户终端
不足之处:
- 协调器模式和 Team 模式并行存在,概念上有重叠
- Fork 机制和 Coordinator 互斥,需要在启动时选择
- 缺乏正式的 Agent 通信协议——
<task-notification>是硬编码的 XML 格式
第六章 技能系统:可复用的工作流
6.1 Skill 的三层结构
技能来源
├── Bundled Skills(内置技能)—— 编译进 CLI
├── Disk Skills(磁盘技能)—— .claude/skills/ 目录下的 Markdown 文件
└── MCP Skills(MCP 技能)—— 通过 MCP 协议暴露的 prompts
6.2 Bundled Skills(内置技能)
定义格式:
type BundledSkillDefinition = {
name: string
description: string
aliases?: string[]
whenToUse?: string
argumentHint?: string
allowedTools?: string[] // 限制可用工具
model?: string // 指定模型
disableModelInvocation?: boolean
userInvocable?: boolean
isEnabled?: () => boolean // 动态启用/禁用
hooks?: HooksSettings
context?: 'inline' | 'fork' // 内联执行还是 fork 新 Agent
agent?: string
files?: Record<string, string> // 附加的参考文件
getPromptForCommand(args, context): Promise<ContentBlockParam[]>
}
17 个内置技能:
| 技能 | 用途 | 设计要点 |
|---|---|---|
batch |
批量处理多个任务 | 循环调用工具 |
claudeApi / claudeApiContent |
Claude API 使用指南 | 参考文档类 |
claudeInChrome |
Chrome 浏览器自动化 | 集成浏览器 MCP |
debug |
调试辅助 | 诊断问题 |
keybindings |
快捷键配置 | 读写 keybindings.json |
loop |
循环执行命令 | 反复执行直到条件满足 |
loremIpsum |
生成占位文本 | 简单工具 |
remember |
存储记忆 | 写入 MEMORY.md |
scheduleRemoteAgents |
远程 Agent 调度 | 配合 ScheduleCronTool |
simplify |
简化代码/文本 | 重构辅助 |
skillify |
将工作流封装为技能 | 元技能——创建新技能 |
stuck |
卡住时求助 | 诊断并建议下一步 |
updateConfig |
更新配置 | 修改 settings |
verify / verifyContent |
验证执行结果 | 质量保证 |
6.3 Disk Skills(磁盘技能)
加载流程(loadSkillsDir.ts):
- 扫描
.claude/skills/目录下的 Markdown 文件 - 解析 YAML frontmatter(提取 name、description、allowed_tools、model 等)
- 注册为
Command(与斜杠命令统一) - 支持参数替换(frontmatter 中定义的参数在 prompt 中用
$1等占位符)
Frontmatter 示例:
---
name: review-pr
description: Review a pull request
allowed_tools: [Read, Grep, Glob, Bash]
model: sonnet
---
# Your instructions here...
6.4 SkillTool 的执行流程
用户/LLM 调用 SkillTool({ skill: "commit" })
│
├── findCommand("commit") —— 查找注册的命令/技能
│
├── 检查权限(根据技能来源——official marketplace 免检)
│
├── 解析 frontmatter hooks
│
├── 决定执行模式:
│ ├── inline(context: 'inline')→ 注入消息到当前对话
│ └── fork(context: 'fork')→ 调用 runAgent() 创建子 Agent
│
├── 如果 fork 模式:
│ ├── 筛选工具集(allowedTools)
│ ├── 解析模型覆盖
│ ├── 构建消息和系统提示
│ └── 执行 query()
│
└── 返回结果
6.5 启发性
技能 vs 工具的边界:工具是原子操作(读文件、执行命令),技能是工作流编排(代码审查 = 读文件 + 分析 + 生成报告)。Claude Code 把这个区分做得很清晰。
用户可扩展性:通过 Markdown 文件定义技能,降低了扩展门槛——不需要写 TypeScript,不需要理解工具系统,只需要写好 prompt。
第七章 Memory 系统:Agent 的长期记忆
7.1 记忆的层级结构
记忆系统
├── MEMORY.md(入口文件)—— 每次对话都会加载,最大 200 行/25KB
├── memory/ 目录 —— 自动提取的记忆文件
│ ├── *.md 文件(每个记忆一个文件)
│ └── 带 frontmatter(type, description, date)
└── Team Memory(团队记忆)—— 通过 TEAMMEM feature flag
7.2 记忆类型分类学
Claude Code 将记忆分为四种类型,每种有明确的定义和使用场景:
| 类型 | 含义 | 存储范围 | 示例 |
|---|---|---|---|
user |
用户角色、偏好、知识背景 | 仅私有 | 「用户是数据科学家,目前关注可观测性」 |
feedback |
用户对 Agent 行为的指导 | 默认私有,项目级可团队 | 「测试不要 mock 数据库——上次因此出了事故」 |
project |
项目进展、目标、在进行的工作 | 偏向团队 | 「Alice 在做认证重构,预计周四完成」 |
reference |
外部参考信息 | 看内容 | 「API 文档链接:...」 |
关键设计原则(直接来自源码注释):
Memories are constrained to four types capturing context NOT derivable from the current project state. Code patterns, architecture, git history, and file structure are derivable (via grep/git/CLAUDE.md) and should NOT be saved as memories.
翻译:记忆只存储不能从项目当前状态推导出的信息。代码模式、架构、Git 历史、文件结构都可以通过搜索获得,不应该存为记忆。
7.3 记忆检索机制(findRelevantMemories.ts)
用户查询 → findRelevantMemories()
│
├── scanMemoryFiles() —— 扫描 memory/ 目录
│ └── 读取每个文件的 frontmatter(filename, description)
│
├── 过滤已展示的记忆(alreadySurfaced)
│
├── selectRelevantMemories() —— 调用 Sonnet 模型
│ ├── 系统提示:「你是记忆选择器,从列表中选择对当前查询有用的记忆」
│ ├── 输入:用户查询 + 记忆清单(filename + description)
│ ├── 约束:最多选 5 个
│ └── 额外规则:如果提供了 recentTools 列表,不要选那些工具的使用文档
│ (但仍然选相关的 warnings/gotchas——因为正在用的工具更需要注意事项)
│
└── 返回选中的记忆文件路径 + mtime
为什么用 LLM 做记忆检索而不是向量搜索?
这是一个有意思的设计选择。向量搜索(如 RAG)适合大规模文档检索,但记忆文件通常不多(几十个),用 LLM 直接看清单判断相关性更准确。而且 LLM 能理解「工具使用文档 vs 工具注意事项」这种语义细节。
7.4 自动记忆提取(extractMemories.ts)
触发时机:每次查询循环结束(模型无工具调用的最终响应)
query() 完成
│
└── handleStopHooks()
└── extractMemories()
│
├── 检查 isAutoMemoryEnabled()
├── 扫描已有记忆(避免重复)
│
└── runForkedAgent() —— Fork 当前对话
├── 共享父 Agent 的 prompt cache
├── 系统提示:指导提取哪些信息值得记忆
├── 有权使用:FileRead, FileWrite, FileEdit, Glob, Grep, Bash
└── 输出:新的记忆文件(带 frontmatter)
Forked Agent 的巧妙之处:记忆提取 Agent 不需要重新理解整个对话——它 fork 了父 Agent 的上下文,prompt cache 直接命中,额外 token 成本极低。
7.5 团队记忆同步
在 Team 模式下,记忆分为 private(个人)和 team(团队共享)两个目录。teamMemorySync 服务负责:
- 检测团队记忆文件变更
- 同步到团队成员
- 检查机密泄露(
teamMemSecretGuard.ts)
7.6 与其他 Agent 记忆方案的对比
| 维度 | Claude Code | MemGPT | LangChain Memory |
|---|---|---|---|
| 存储格式 | Markdown 文件 | 数据库 | Python 对象 |
| 检索方式 | LLM 选择 | 向量搜索 | 向量搜索 |
| 记忆分类 | 4 种明确类型 | 自由格式 | 自由格式 |
| 持久化 | 文件系统 | 数据库 | 可插拔 |
| 团队共享 | 原生支持 | 不支持 | 不支持 |
| 自动提取 | 对话结束自动触发 | 手动/规则 | 无 |
Claude Code 的优势:
- 记忆类型的分类学设计非常用心——不是笼统的「记住一切」,而是精确定义了什么值得记
- 文件系统作为存储——简单、可检查、可版本控制
- Forked agent 做提取——零额外成本
不足之处:
- 无向量搜索——当记忆文件增多到数百个时,LLM 选择可能效率下降
- 200 行/25KB 的 MEMORY.md 限制较为刚性
第八章 权限系统:信任的边界
8.1 权限模型
每个工具调用都经过权限检查。权限模式(PermissionMode)有多种:
| 模式 | 含义 |
|---|---|
default |
每次都询问用户 |
plan |
只允许只读操作,写操作需要确认 |
bypassPermissions |
跳过所有权限(危险模式) |
auto |
基于规则自动判断 |
bubble |
冒泡到父 Agent 的终端询问 |
8.2 权限检查流程(PermissionContext.ts)
工具调用 → checkPermissions()
│
├── 1. 工具自身的权限检查
│ └── 返回 PermissionResult: allow | deny | passthrough | ask
│
├── 2. Hook 检查(executePermissionRequestHooks)
│ └── 用户定义的 hooks 可以自动批准/拒绝
│
├── 3. Classifier 检查(awaitClassifierAutoApproval)
│ └── BashTool 特有——AI 分类器判断命令是否安全
│
├── 4. 用户确认(如果前面没有自动决策)
│ └── 终端弹出确认对话框
│
└── 5. 记录决策 + 更新规则
├── 如果用户选择「永久允许」→ 写入权限规则
└── 日志记录
竞态处理:createResolveOnce 确保一个权限请求只被解决一次——hook 回调、分类器、用户操作可能并发到达,通过 claim() 原子操作防止重复解决。
8.3 BashTool 的特殊权限
BashTool 是权限最复杂的工具:
- AST 解析:
parseForSecurity()解析 shell 命令的 AST - 命令分割:
splitCommandWithOperators()理解管道、&&、|| - 通配符匹配:
matchWildcardPattern()支持基于模式的权限规则 - 只读验证:
checkReadOnlyConstraints()在 plan 模式下确保命令是只读的 - Sed 解析:
parseSedEditCommand()专门解析 sed 编辑命令判断是否修改文件
第九章 Bridge 系统:IDE 的神经传导
9.1 架构概览
VS Code / JetBrains
│
│ (HTTP API + WebSocket)
│
▼
Bridge Main Loop (bridgeMain.ts)
│
├── bridgeApi.ts ────── API 客户端
├── jwtUtils.ts ─────── JWT Token 管理与刷新
├── sessionRunner.ts ── 会话执行(spawn CLI 子进程)
├── pollConfig.ts ───── 轮询配置(指数退避)
└── capacityWake.ts ─── 容量唤醒
│
▼
REPL Bridge (replBridge.ts)
│
├── replBridgeTransport.ts ── 传输层
├── replBridgeHandle.ts ──── 句柄管理
└── inboundMessages.ts ───── 入站消息处理
9.2 关键设计
31 个文件,比想象中复杂。核心挑战是:IDE 扩展和 CLI 进程是两个独立进程,需要可靠的双向通信。
JWT 认证:IDE 和 CLI 之间通过 JWT Token 互相验证身份,有自动刷新机制。
容量唤醒(capacityWake.ts):当 CLI 进程空闲时降低轮询频率,有新消息时立即唤醒。
可信设备(trustedDevice.ts):设备级别的信任标记,避免每次连接都要认证。
第十章 状态管理:React 在终端中的实践
10.1 Store 模式
// store.ts
function createStore(initialState: AppState, onChange?): AppStateStore {
let state = initialState
const listeners = new Set<() => void>()
return {
getState: () => state,
setState: (updater) => {
const oldState = state
state = updater(state)
if (onChange) onChange({ newState: state, oldState })
listeners.forEach(l => l())
},
subscribe: (listener) => {
listeners.add(listener)
return () => listeners.delete(listener)
},
}
}
与 React 的集成:通过 useSyncExternalStore hook——React 18 的 API,确保外部 store 与 React 渲染同步。
10.2 Context 体系
AppStateProvider
├── MailboxProvider // Agent 间消息
├── VoiceProvider // 语音输入(feature flag 控制)
├── KeybindingProvider // 快捷键
├── ModalContext // 模态对话框
├── OverlayContext // 覆盖层
├── NotificationsContext // 通知
└── QueuedMessageContext // 排队消息
第十一章 Vim 模式:状态机的艺术
11.1 状态机设计
type VimState =
| { mode: 'INSERT'; insertedText: string }
| { mode: 'NORMAL'; command: CommandState }
type CommandState =
| { type: 'idle' }
| { type: 'count'; digits: string }
| { type: 'operator'; op: Operator }
| { type: 'operatorCount'; op: Operator; digits: string }
| { type: 'find'; findType: FindType }
| { type: 'operatorFind'; op: Operator; count?: number; findType: FindType }
| { type: 'operatorTextObj'; op: Operator; count?: number; scope: TextObjScope }
| { type: 'replace' }
| { type: 'g' }
| { type: 'indent'; direction: '>' | '<' }
状态转换图:
NORMAL/idle ──┬── [d/c/y] ──► operator
├── [1-9] ──► count
├── [fFtT] ──► find
├── [g] ──► g
├── [r] ──► replace
└── [><] ──► indent
operator ──┬── [motion] ──► execute
├── [0-9] ──► operatorCount
├── [ia] ──► operatorTextObj
└── [fFtT] ──► operatorFind
每个状态精确地知道它在等待什么输入。TypeScript 的 discriminated union 确保 switch 语句的穷举性。
启发性:Vim 的输入处理本质上就是一个状态机。Claude Code 把这个状态机的类型定义做到了极致——注释里说「The types ARE the documentation」,类型本身就是文档。这是 TypeScript 在复杂交互逻辑中的最佳实践。
第十二章 Keybinding 系统:可配置的交互
12.1 三层合并
默认绑定(defaultBindings.ts)
↓ 被用户绑定覆盖
用户绑定(~/.claude/keybindings.json)
↓ 某些键不可覆盖
保留快捷键(reservedShortcuts.ts):ctrl+c, ctrl+d
12.2 平台适配
// 图片粘贴快捷键
const IMAGE_PASTE_KEY = getPlatform() === 'windows' ? 'alt+v' : 'ctrl+v'
// 模式切换快捷键
const MODE_CYCLE_KEY = SUPPORTS_TERMINAL_VT_MODE ? 'shift+tab' : 'meta+m'
Windows Terminal 不支持 VT 模式时,shift+tab 不可靠,回退到 meta+m。
第十三章 插件系统:可扩展的边界
13.1 两类插件
内置插件(builtinPlugins.ts):
- ID 格式:
{name}@builtin - 通过
/pluginUI 启用/禁用 - 可提供:skills、hooks、MCP servers
- 状态持久化到用户设置
第三方插件:
- ID 格式:
{name}@{marketplace} - 从 marketplace 安装
- 有版本管理和自动更新
13.2 插件加载流程
启动 → initBuiltinPlugins()
│
├── 注册内置插件定义
├── 检查 isAvailable()(动态判断插件是否在当前环境可用)
├── 读取用户设置(哪些插件被启用/禁用)
├── defaultEnabled 作为回退
│
└── 返回 { enabled: LoadedPlugin[], disabled: LoadedPlugin[] }
第十四章 Query Pipeline 的完整图景
把前面各章串起来,一次完整的用户查询经历以下管道:
用户输入 "帮我修复 src/auth.ts 中的 bug"
│
├── 1. 系统提示构建
│ ├── 基础系统提示
│ ├── + 用户上下文(OS、shell、cwd、git 状态)
│ ├── + 记忆(MEMORY.md + 相关记忆文件)
│ ├── + MCP 指令
│ ├── + 活跃 Agent 列表
│ └── + 延迟工具列表
│
├── 2. 工具池组装
│ ├── 内置工具(BashTool, FileReadTool, ...)
│ ├── + MCP 工具(从连接的 MCP 服务器)
│ ├── + 插件工具
│ ├── - 延迟工具(放入 deferred 列表)
│ └── = 最终工具集
│
├── 3. Query 循环
│ ├── API 调用(流式)
│ ├── 模型思考...
│ ├── 模型选择工具:FileReadTool({ file_path: "src/auth.ts" })
│ │ ├── 权限检查 → 通过(只读)
│ │ ├── 执行
│ │ └── 结果返回给模型
│ ├── 模型分析代码...
│ ├── 模型选择工具:FileEditTool({ file_path: "src/auth.ts", old_string: "...", new_string: "..." })
│ │ ├── 权限检查 → 询问用户
│ │ ├── 用户确认
│ │ ├── 执行
│ │ ├── 通知 VS Code 文件已更新
│ │ ├── 记录 file history
│ │ └── 结果返回给模型
│ ├── 模型选择工具:BashTool({ command: "npm test" })
│ │ ├── 安全解析
│ │ ├── 权限检查
│ │ ├── 沙箱执行
│ │ └── 结果返回给模型
│ └── 模型生成最终回复(无工具调用)
│
├── 4. Stop Hooks
│ ├── extractMemories → 提取「用户关注 auth.ts 的 bug」记忆
│ ├── promptSuggestion → 建议「要不要运行完整测试套件?」
│ └── taskCompleted → 通知任务系统
│
└── 5. 渲染
└── React/Ink 渲染最终回复到终端
第十五章 设计模式总结与反思
15.1 值得学习的设计模式
- 并行预取 + 延迟加载:启动性能的黄金组合
- 编译时特性开关:Bun 的
feature()宏实现零成本条件编译 - Forked Agent:共享 prompt cache 的子 Agent,几乎零额外成本
- 工具作为一等公民:完整的生命周期(定义、权限、执行、渲染、截断)
- 记忆分类学:不是「记住一切」,而是精确定义什么值得记
- 状态机驱动:用 TypeScript 类型系统保证状态转换的完备性
- Agent 列表外置:从工具描述移到消息附件,保护 prompt cache
- 权限冒泡:子 Agent 的权限请求透传到用户终端
15.2 工程质量的体现
- 类型安全:几乎所有核心数据结构都有精确的 TypeScript 类型
- 错误处理:
TelemetrySafeError确保错误信息不泄露代码或文件路径到遥测 - 优雅退出:
setupGracefulShutdown()+registerCleanup()确保资源释放 - 调试支持:
logForDebugging()、logForDiagnosticsNoPII()分级日志 - 性能意识:startup profiler、prompt cache 优化、lazy loading
- 安全意识:BashTool AST 解析、文件路径验证、JWT 认证
15.3 不足与改进空间
- QueryEngine 过大:46,000 行的单文件违反了单一职责原则。源码中已有注释暗示未来会拆分为「纯函数 reducer」模式。
- 状态管理碎片化:AppState、bootstrap/state、各种 module-level 闭包状态共存,没有统一的状态层。
- 测试覆盖未知:源码中有
testing/目录和测试工具,但泄露的代码不包含测试文件。 - 概念重叠:Coordinator Mode、Team Mode、Fork Subagent 三种多 Agent 模式并存,用户需要理解它们的区别才能有效使用。
- 记忆系统扩展性:纯文件系统 + LLM 选择的方案在记忆量大时可能成为瓶颈。
- MCP 耦合:22 个 MCP 相关文件说明协议集成的复杂度不低,新协议的接入成本高。
15.4 对 Agent 工程的启示
如果你要构建自己的 Agent 系统,Claude Code 源码告诉我们:
- 性能是功能——用户不会等一个慢 Agent
- 安全是底线——每个工具调用都需要权限检查
- 记忆要有策略——不是什么都记,而是记「不能从当前状态推导的信息」
- 多 Agent 需要通信协议——不只是 spawn,还要有通知、消息、权限冒泡
- Prompt Cache 是金矿——在大规模 Agent 系统中,cache 命中率直接决定成本
- 工具不是函数包装——需要完整的生命周期管理
- 编译时优化很重要——feature flag + tree shaking 可以显著减小 bundle 体积
- 状态机是好工具——复杂交互逻辑用状态机 + 类型系统比 if-else 可靠得多
本文档基于 Claude Code 泄露源码(2026-03-31)的深度阅读,力求「每个结论都有源码支撑」。如有疑问,请直接查阅对应的源文件。