耐点
博客

Claude Code 内部源码分析:Agent Loop、四级压缩管道

30 分钟4 次阅读

以 Claude Code 源码(~512,664 行 TypeScript)逆向工程为线索,拆解一个生产级 harness 的全部关键机制。

原始资料:https://wanlanglin.github.io/-awesome-cc-harness/zh/(该指南基于 2025 年中源码;Claude Code 每周发版,2026 年已迭代至 v2.1.x,文末附 2026 年新机制补录)

核心命题

“The model is the agent. The code is the harness. Build great harnesses. The agent will do the rest.”

一、架构全景

技术栈

选择
Runtime Bun(TypeScript 原生)
UI React + Ink(终端组件)
CLI Commander.js
Schema Zod v4
搜索 ripgrep
状态 自定义 Zustand-like Store + React Context

规模

  • ~1,884 文件
  • 512,664 行代码(分析时版本)
  • 43+ 工具
  • 100+ Slash 命令
  • 80+ React Hooks
  • 144+ UI 组件
  • 25+ Hook 事件(持续增长中)

六层 Harness 基础设施

  1. 工具系统(43+)
  2. 权限模型(5 基础模式 + auto + bubble)
  3. Hooks 系统(25+ 事件 × 4 类型)
  4. 沙盒(文件 + 网络 + 进程隔离)
  5. 上下文工程(CLAUDE.md + 记忆 + 四级压缩)
  6. 设置与配置(7 级层级)

二、Harness骨架Agent Loop

位置:src/query.tsqueryLoop() 函数

基本架构

  • Async Generator:yield 每一个中间事件,支持流式渲染
  • 无限循环 + 显式退出:仅 return Terminal 时退出
  • 单一 State 对象:伪不可变语义,每次迭代解构后整体重赋值

State 类型(10 个字段)

type State = {
  messages: Message[]
  toolUseContext: ToolUseContext
  autoCompactTracking: AutoCompactTrackingState | undefined
  maxOutputTokensRecoveryCount: number
  hasAttemptedReactiveCompact: boolean
  maxOutputTokensOverride: number | undefined
  pendingToolUseSummary: Promise<ToolUseSummaryMessage | null> | undefined
  stopHookActive: boolean | undefined
  turnCount: number
  transition: Continue | undefined
}

7 个 Continue 站点(错误恢复)

站点 触发条件 恢复动作
1 token 超阈值 autocompact → 新消息 → continue
2 API 返回 prompt-too-long(413) context-collapse → reactive compact
3 模型输出截断(max_output_tokens) 升级 8k→64k → 多轮重试(最多 3 次)
4 FallbackTriggeredError 切换模型 → 重试请求
5 Stop Hook blocking 注入 Hook 消息 → continue
6 ImageSizeError/ImageResizeError 反应式压缩(移除图片)→ continue
7 正常工具执行完成 收集结果 → 更新状态 → continue

10 种终止原因

completed | blocking_limit | stop_hook_prevented | aborted_streaming | aborted_tools | hook_stopped | max_turns | prompt_too_long | image_error | model_error

复杂度对比

实现 行数
最小实现 ~30 行
生产实现 ~1,800+ 行
倍数 60 倍

仅关注代码密度


三、四级压缩管道

级别 名称 机制 成本 延迟
Level 1 Snip Compact 历史截断,追踪释放 token 数 极低 ~0ms
Level 2 Microcompact 3 轮前工具结果替换为 [Previous: used {tool}],缓存结果 ~1ms
Level 3 Context-Collapse 读时投射(不修改消息数组),按粒度排空可折叠上下文 ~5ms
Level 4 Autocompact 超过默认阈值(约 50k tokens,可配置,随模型上下文窗口变大而调整)时触发,LLM 全对话摘要,保存完整转录到磁盘 ~2s

执行顺序

snip → micro → context-collapse → auto

各级互不排斥,可组合运行。

关键设计细节

  • Snip 释放的 token 数必须传递给 Autocompact 的阈值检查
  • Context-Collapse 不修改任何数据结构,完全可逆
  • Microcompact 的边界消息延迟到 API 响应后(此时才知道 cache 命中情况)
  • taskBudgetRemaining 跨压缩边界追踪预算

四、工具系统

位置:src/Tool.ts(接口)、src/tools.ts(注册表)

Tool 接口(核心字段)

type Tool<Input, Output> = {
  // 核心标识
  name: string;
  aliases?: string[];
  userFacingName(): string;
  
  // Schema & 验证
  inputSchema: ZodType<Input>;
  outputSchema?: ZodType<Output>;
  validateInput(input): Promise<ValidationResult>;
  
  // 执行
  call(args, context, canUseTool, parentMessage, progressCallback?): Promise<ToolResult>;
  
  // 权限 & 安全
  checkPermissions(args, context): Promise<PermissionDecision>;
  isConcurrencySafe(args): boolean;    // 能否并行
  isDestructive(args): boolean;        // 不可逆?
  isReadOnly(): boolean;
  
  // 行为
  isEnabled(): boolean;                // 特性门控
  shouldDefer: boolean;                // 延迟加载
  alwaysLoad: boolean;                 // 永不延迟
  
  // 渲染
  renderToolUseMessage(args): ReactElement;
  renderToolResultMessage(result): ReactElement;
}

工具注册表机制

  • 始终加载:AgentTool, BashTool, FileReadTool, FileEditTool, FileWriteTool, WebFetchTool 等
  • 特性门控:通过 feature() 函数条件加载(如 SleepTool, ScheduleCronTool, TeamCreateTool)
  • Dead Code Elimination:Bun 的 bun:bundle 在编译时评估 feature() 调用,false 的工具被 tree-shake 移除

工具池组装(Cache Stability 设计)

内置工具按名称排序形成稳定的缓存前缀,MCP 工具增减时前缀不变,Anthropic API 的 prompt cache 不失效。

工具执行生命周期(7 步管道)

1. Zod Schema 验证(结构验证)
2. tool.validateInput()(业务逻辑验证)
3. PreToolUse Hook(可批准/阻止/修改输入/注入上下文)
4. 权限解析(deny规则 → ask规则 → 模式检查 → 分类器)
5. Sandbox 包装(仅 BashTool,wrapWithSandbox())
6. tool.call()(实际执行)
7. PostToolUse Hook(审计日志/输出修改)

核心安全不变量:deny > settings rules > hook allow。即使 Hook 批准操作,settings.json 中的 deny 规则仍阻止它。

工具执行编排(两种模式)

模式 1: StreamingToolExecutor(默认)

  • 模型流式生成时,识别到完整 tool_use JSON 块立即排队执行
  • 模型还在生成第二个工具调用时,第一个已在运行
  • 子 AbortController:一个 Bash 工具出错时兄弟子进程立即死亡,但不中止父级查询

模式 2: runTools()(回退)

  • partitionToolCalls() 工具分区算法:
    • isConcurrencySafe=true(只读工具 Read/Glob/Grep)→ 并发执行
    • isConcurrencySafe=false(写入工具 Write/Edit/Bash)→ 串行执行
  • 上下文修改器(contextModifier)被收集并延迟应用,确保并发期间上下文一致性

工具分类示例

类别 工具 特性
核心 I/O BashTool, FileReadTool, FileWriteTool, FileEditTool, GlobTool, GrepTool 始终加载
Agent AgentTool, SendMessageTool, TeamCreate/DeleteTool 子 Agent 管理
工作流 WebFetchTool, WebSearchTool, NotebookEditTool 外部资源
任务 TaskCreate/Update/List/Output/StopTool 任务管理
计划 EnterPlanModeTool, ExitPlanModeTool, TodoWriteTool 计划模式
高级 ScheduleCronTool, SleepTool, MonitorTool, REPLTool 特性门控
MCP MCPTool, ListMcpResourcesTool, ReadMcpResourceTool MCP 协议
搜索 ToolSearchTool 延迟工具发现

工具延迟加载

  • shouldDefer: true 的工具第一轮不加载到模型上下文
  • 通过 ToolSearchTool 按需发现
  • 第一轮只加载核心工具(~15 个),节省 token 预算

FileEditTool 智能引号匹配

三阶段查找算法:

  1. 精确匹配
  2. 引号规范化匹配(智能引号 ↔ 直引号)
  3. 返回文件中的原始字符串(保留原始引号风格)

替换时使用 () => replace 而非直接传字符串,防止 $1/$& 等特殊模式被误解释。


五、权限模型

权限模式:5 基础 + auto + bubble(共 7 种)

type PermissionMode =
  | 'default'            // 敏感操作始终询问
  | 'acceptEdits'        // 自动批准文件编辑,其他询问
  | 'bypassPermissions'  // 自动批准一切(危险)
  | 'dontAsk'           // 自动拒绝需要询问的操作
  | 'plan'              // 计划模式(只读+计划文件)
  | 'auto'              // AI 分类器自动审批(2026 年已主流化,不再视为实验特性)
  | 'bubble';           // 冒泡到父 Agent(子 Agent 用)

三级规则系统

type PermissionRule = {
  source: PermissionRuleSource;
  ruleBehavior: 'allow' | 'deny' | 'ask';
  ruleValue: {
    toolName: string;       // "Bash", "Write", "mcp__server"
    ruleContent?: string;   // "git *", "*.ts", "prefix:npm *"
  };
};

规则语法示例:

  • Bash(git *) — 允许所有 git 命令
  • Write(*.ts) — 允许写入 TypeScript 文件
  • mcp__* — 拒绝所有 MCP 服务器工具
  • Bash(rm -rf *) — 拒绝 rm -rf 命令

六层纵深防御模型

层级 类型 机制 绕过率
1 软约束 CLAUDE.md 指导性约束 ~5%
2 中等约束 Permission Rules (settings.json allow/deny/ask) 可配置
3 中等约束 Hooks (PreToolUse 脚本检查) 可配置
4 中等约束 YOLO Classifier (独立 AI 模型审查) 可配置
5 硬约束 Sandbox (操作系统级文件/网络隔离) 极低
6 硬约束 Hardcoded Denials (不可覆盖) 0%

6 层叠加后累积绕过概率:0.05^6 ≈ 1.56×10⁻⁸(约 0.0000016%)。注意:这是量级示意,实际各层并不统计独立,且单层"5% 绕过率"本身无官方出处。

7 级设置优先级(从高到低)

  1. CLI 参数 (cliArg)
  2. 会话命令 (command) — /permissions 命令
  3. Flag 设置 (flagSettings)
  4. 策略设置 (policySettings) — 组织策略
  5. 本地设置 (localSettings) — .claude/settings.json.local
  6. 项目设置 (projectSettings) — .claude/settings.json
  7. 用户设置 (userSettings) — ~/.claude/settings.json

加上企业管理设置(MDM):/managed/managed-settings.json + Drop-in 覆盖 + macOS plutil / Windows Registry

权限决策管道

工具调用请求
    │
├─ 1a. 整个工具被 Deny? → 拒绝
├─ 1b. 整个工具被 Ask? → 沙盒可自动允许? 否 → ask
├─ 1c. tool.checkPermissions()
├─ 1d. 工具实现拒绝? → 拒绝
├─ 1e. 需要用户交互? → ask
├─ 1f. 内容级 ask 规则? → 必须尊重(即使 bypassPermissions)
├─ 1g. 安全检查(.git/.claude/.vscode/shell 配置)? → 必须提示
├─ 2a. bypassPermissions 模式? → 允许
├─ 2b. 整个工具被 Allow? → 允许
└─ 3. passthrough → ask → 模式转换
       ├─ dontAsk → deny
       ├─ auto → YOLO 分类器
       └─ default → 用户提示

关键设计

  • bypassPermissions 模式下,内容级 ask 规则和安全检查仍必须提示
  • hasAttemptedReactiveCompact 标志在 Stop Hook 恢复中被保留而非重置(防止无限循环)
  • YOLO 分类器(auto 模式)分两阶段:fast stage(50-200ms,约 $0.001)和 thinking stage(500ms-2s,约 $0.01)。注:以上成本为源码分析的估算值,非官方公布数据

权限决策分布

路径 占比 延迟
Rule-based Allow ~40% <1ms
Mode-based Allow ~20% <1ms
Safe-tool Allowlist ~15% <1ms
Tool checkPermissions Allow ~10% 1-5ms
YOLO Classifier (fast) ~8% 50-200ms
YOLO Classifier (thinking) ~3% 500ms-2s
User Prompt ~3% 1-30s
Deny ~1% varies

六、Hooks 系统

位置:src/utils/hooks.ts(执行引擎)、src/utils/hooks/(配置管理)

26 个 Hook 事件(注:2025 年中版本的)

覆盖 Agent 生命周期的所有关键节点:UserPromptSubmit、PreToolUse、PostToolUse、Stop、SessionEnd 等。2026 年已持续新增事件(见文末补录)。

四种 Hook 类型

  • 同步 Hook:阻塞执行,返回前等待完成
  • 异步 Hook:不阻塞,后台执行
  • 条件 Hook:通过 if 字段条件匹配
  • 内部回调 Hook:快速路径(~1.8µs/call,比外部 Hook 快 70%)

Hook 输入/输出协议

  • Hook 可修改工具输入(hookUpdatedInput)
  • Hook 可注入上下文
  • Hook 可修改 MCP 工具输出(updatedMCPToolOutput)
  • PreToolUse Hook 的 allow 绕过 deny 规则

核心安全不变量

deny > settings rules > hook allow

即使第三方 MCP 服务器提供了返回 allow 的 PreToolUse Hook,settings.json 中的 deny 规则仍然阻止操作。


七、上下文工程

核心原则

“Agent 无法在上下文中访问的信息不存在”

上下文预算分配

上下文窗口分为:系统提示 + 工具定义 + CLAUDE.md + MCP 工具 + 记忆 + 对话历史 + 工具结果

CLAUDE.md — 项目级持久上下文

  • 静态上下文:仓库文档、设计文档
  • 精心设计的 CLAUDE.md 文件只需 30 分钟,可将 Agent 在特定项目上表现提升 20-40%(数据出自原逆向指南,建议以官方实测为准)

系统提示构建管道

base + tools + CLAUDE.md + MCP + memory

消息规范化管道(normalizeMessagesForAPI)

  • 连续用户消息合并(Bedrock 不支持多个连续 user 消息)
  • PDF/图片错误内容剥离(防止重复发送)
  • 工具名称规范化(别名 → 正式名)
  • Tool Reference 处理(ToolSearch 启用时保留引用块)
  • 虚拟消息过滤(REPL 内部工具调用的显示消息不发送给 API)

记忆系统

  • 位置:src/memdir/
  • 持久记忆目录系统
  • 预取机制:using pendingMemoryPrefetch = startRelevantMemoryPrefetch(...)
  • 使用 TC39 的 using 关键字确保生成器退出时自动清理

四级压缩策略

(见上面第三节)


八、子代理系统

位置:src/tools/AgentTool/src/coordinator/

Agent Tool

  • 子 Agent 生成工具,作为主 Agent 的代理
  • 每个子 Agent 有独立的上下文和工具池

Agent 类型

  • 探索型 Agent(Explore)
  • 通用 Agent
  • 自定义 Agent(用户定义)

子 Agent 生成流程

  • 独立的 ToolUseContext
  • 独立的 AbortController
  • 权限模式可 bubble(冒泡到父 Agent)

Coordinator / Swarm 系统

  • 多 Agent 编排(Agent Teams,2026 年已正式发布:支持 tmux/iTerm2 分屏 teammate、跨会话 mailbox 消息、teammateMode 设置,不再视为实验特性)
  • TeamCreate/TeamDelete 工具
  • 早期版本通过 feature('COORDINATOR_MODE') 特性门控

任务系统

TaskCreate/Update/List/Output/Stop 工具,支持任务的创建、更新、监控和终止。

Worktree 隔离

  • EnterWorktreeTool / ExitWorktreeTool
  • Git worktree 隔离,每个子 Agent 在独立的 Git 工作树中操作
  • 防止并发文件修改冲突

子 Agent 隔离开销与收益

子 Agent 的独立上下文避免污染主 Agent,但有一定的初始化开销和上下文传递成本。


九、MCP 集成

位置:src/services/mcp/

  • 6 种传输协议:stdio、SSE、HTTP、WebSocket 等(注:官方 MCP 规范的标准传输为 stdio 与 HTTP 两种,"6 种"为源码中可见的传输封装口径,引用时建议保守表述)
  • MCP 配置:支持多服务器配置
  • MCP 工具执行:通过 MCPTool 调用,与内置工具统一管理
  • 连接生命周期:包含重连、超时、健康检查
  • 配置去重策略:避免重复加载相同 MCP 服务器
  • MCP Skills 发现:自动发现 MCP 服务器提供的 Skills
  • 工具池集成:MCP 工具与内置工具合并,按名称排序,内置优先去重

十、沙盒与安全

位置:src/utils/sandbox/

三大限制维度

  1. 文件隔离:限制可访问的路径
  2. 网络隔离:限制网络访问
  3. 进程隔离:限制可创建的子进程

路径解析(Claude Code 特有约定)

Claude Code 内部的路径解析规则,处理相对路径、符号链接等。

权限规则到沙盒配置的转换

settings.json 中的权限规则自动转换为沙盒限制配置。

dangerouslyDisableSandbox

  • 特定命令可绕过沙盒(如需要完整系统访问的工具)
  • 即使 bypassPermissions 模式下,绕过沙盒的命令仍需遵守 ask 规则

十一、设置与配置

位置:src/utils/settings/

settings.json 结构

支持权限规则、Hook 配置、MCP 配置、沙盒设置、特性门控等。

层级化加载(7 级 + 企业管理)

(见权限模型部分的 7 级优先级)

Schema 验证

使用 Zod v4 进行严格的 Schema 验证。

设置合并算法

深度合并策略,高优先级覆盖低优先级,数组按规则合并。

Managed Settings 的 Drop-in 模式

/managed/managed-settings.d/*.json 支持多个 Drop-in 文件,按字母序加载覆盖。

防御性缓存克隆

设置读取时进行防御性克隆,防止运行时修改影响缓存。


十二、Agent Loop 设计哲学总结

  1. 弹性优于刚性:7+ 个 continue 站点允许从几乎任何错误中恢复
  2. 渐进式降级:每种错误先尝试最轻量恢复,逐步升级
  3. 流式优先:Async Generator 使每个中间状态都可观察
  4. 状态显式化:单一 State 对象,无隐式全局状态
  5. 可观测性内建:每个恢复点都有 analytics 和 profiling

核心洞察:生产级 Agent Loop 的复杂性不在于"循环本身",而在于"循环失败时如何优雅恢复"。30 行实现基本循环,1800+ 行处理所有边界情况——这中间的差距就是 Harness Engineering 的全部价值。


十三、Harness Engineering 三大支柱(Claude Code)

┌────────────────────────────────────────────┐
│  Context Engineering (45%)                  │
│  静态上下文 + 动态上下文 + 上下文压缩         │
│  ├─ CLAUDE.md / AGENTS.md                  │
│  ├─ 日志 / Git 状态 / CI 状态              │
│  └─ 四级压缩管道 + 按需加载 + 记忆系统      │
├────────────────────────────────────────────┤
│  Architectural Constraints (35%)           │
│  权限模型 + 工具约束 + 安全边界              │
│  ├─ 5 种模式,7 级规则层级,AI 分类器        │
│  ├─ Schema 验证,并发安全标记,延迟加载      │
│  └─ 沙盒隔离,硬编码拒绝,纵深防御          │
├────────────────────────────────────────────┤
│  Entropy Management (20%)                  │
│  定期清理 + 约束验证 + 性能监控              │
│  ├─ 死代码检测,文档一致性                  │
│  ├─ 依赖审计,模式强制                     │
│  └─ 覆盖率守卫,回归检测                   │
└────────────────────────────────────────────┘

ROI 定量证据

优化方式 Terminal Bench 提升
仅模型优化 +3-5%
仅 Harness 优化 +14%(LangChain 案例 52.8%→66.5%)
两者结合 +18-20%

附:2026 年新机制补录

  • Dynamic Workflows(动态工作流):2026 年最大机制变革——让 Claude 创建一个 workflow,在后台跨数十到数百个 agent 编排工作;配套 /workflows 视图、Workflow 工具与 agent({schema}) 结构化输出。
  • Hook 事件持续新增:2026 年已新增 DirectoryAdded(2.1.219)、PreCompactPostToolUseFailureConfigChangeTaskCreated 等事件,总数已显著超过 26。
  • 子代理配额:嵌套深度默认放开到 3(曾为 1),每会话子代理上限 200、并发上限 20;Task 工具的 mode 参数已废弃(2.1.212),子代理默认继承父会话权限模式。
  • Sandbox 细分:新增 sandbox.network.strictAllowlist(白名单外主机直接拒绝)与 sandbox.filesystem.disabled(跳过文件隔离、只保留网络出口控制)。
  • Auto mode 正式化:分类器默认 Sonnet 5(2.1.210),Bedrock/Vertex/Foundry 无需 opt-in(2.1.207),并支持 settings.autoMode.hard_deny 硬拒绝规则。

参考来源