SOURCE CODE DEEP DIVE

Claude Code 的
Harness 工程

一个 AI 编程助手背后的「调度中枢」是怎么设计的?用工程师能理解的方式讲清楚。

目录
  1. 01什么是 Harness
  2. 02整体架构
  3. 03核心循环 — queryLoop
  4. 04消息管道
  5. 05工具执行
  6. 06Hook 生命周期
  7. 07权限系统
  8. 08Feature Gate
  9. 09错误恢复
  10. 10设计哲学
01

什么是 Harness?

在 Claude Code 中,Harness(直译「线束」)是指包裹在 AI 模型外面的整个编排层。它不负责「思考」,而是负责「调度」——接收你的输入、调用模型、执行工具、检查权限、管理上下文窗口、处理错误。

类比理解

如果 Claude 大模型是一位飞行员,那 Harness 就是整个驾驶舱——仪表盘、操纵杆、通信系统、安全系统。飞行员做决策,但驾驶舱确保这些决策能安全正确地执行。

为什么重要

大模型本身只是一个「输入文本 → 输出文本」的函数。让它变成一个能读文件、跑命令、管理 Git 的编程助手,靠的全是 Harness 这一层工程。

02

整体架构

Harness 分为四个层次,从外到内依次是:

入口层
main.tsxCLI 启动
QueryEngineSDK 入口
编排层
query()外壳函数
queryLoop()核心循环
能力层
Tools工具系统
Hooks钩子系统
Perms权限
基础层
Settings配置
State状态
Compact压缩

有两个入口:CLI 模式通过 main.tsx 启动终端交互,SDK 模式通过 QueryEngine 类提供 headless 调用。两者最终都进入同一个 queryLoop

03

核心循环 — queryLoop

整个 Harness 的心脏是 query.ts 中的 queryLoop() 函数——一个 async function*(异步生成器),用 while(true) 驱动每一轮对话。

📝
1. 准备消息
压缩、裁剪、注入上下文
🤖
2. 调用 API
流式接收模型响应
🔧
3. 执行工具
Read / Edit / Bash / ...
4. 检查结果
还有工具调用?继续循环
🎣
5. Stop Hook
后处理、修正、追加操作
↩ 如果模型请求了工具调用,回到第 1 步继续
为什么用异步生成器?

async function*yield 既能向外传递流式事件(让 UI 实时更新),又能在等待工具结果时暂停循环。这比回调或 Promise 链更自然地表达了「流式 + 多轮」的控制流。

循环维护一个 State 对象,每轮迭代在顶部解构读取,在 continue 点整体替换:

let state: State = {
  messages,            // 消息历史
  toolUseContext,      // 工具执行上下文
  autoCompactTracking, // 压缩追踪
  turnCount,           // 轮次计数
  transition,          // 跳转控制
  ...
}
04

消息管道

消息在送到 Claude API 之前,要经过一条 6 级处理管道。这是 Harness 最精巧的部分——因为上下文窗口有限,必须在有限空间里装下最有价值的信息。

📏
Tool Result Budget
控制工具输出大小
✂️
Snip Compact
裁剪历史片段
🗜️
Micro Compact
细粒度压缩
📦
Context Collapse
折叠冗余上下文
📚
Auto Compact
接近限制时全面压缩
💉
Inject Context
注入用户/系统上下文
类比理解

想象你的书桌只能放 10 本书。每次工作前,你会:扔掉用完的草稿纸(Snip)→ 把笔记压缩成卡片(Micro Compact)→ 合并重复资料(Collapse)→ 如果还是放不下,把所有内容总结成一页纸(Auto Compact)。

05

工具执行

Claude 模型输出 tool_use 块时,Harness 负责实际执行这些工具(读文件、写文件、跑命令等)。有两种执行模式:

⚡ 流式执行

模型还在输出时就开始执行工具。不等完整响应。用 StreamingToolExecutor 实现。

// 模型流式返回 tool_use block 时
// 立即提交给执行器
for (const toolBlock of msgToolUseBlocks) {
  streamingToolExecutor.addTool(
    toolBlock, message
  )
}

📦 批量执行

等模型完整输出后再依次执行所有工具。通过 runTools 函数。更保守但更安全。

每个工具执行前后,都会经过 Hook 拦截和权限检查(下面两节详述)。

06

Hook 生命周期

Hook 是 Harness 的扩展机制——在关键节点插入自定义逻辑,不修改核心代码。一共有 7 个生命周期钩子:

SessionStart
会话初始化时触发。适合加载环境变量、检查前置条件。
UserPromptSubmit
用户提交 prompt 后、发送给模型前。可以验证、修改、拦截输入。
PreToolUse
工具执行前。可以修改参数、阻止执行。比如禁止删除某些文件。
PostToolUse
工具执行后。可以修改输出、记录日志、触发后续动作。
Stop
助手消息生成完毕后。可以追加检查、触发自动格式化等后处理。
类比理解

Hook 就像生产线上的质检站。产品(消息/工具调用)在流水线上移动时,经过每个质检站都可能被检查、修改甚至退回。

07

权限系统

每次工具调用前,Harness 通过 canUseTool() 回调检查权限。权限模式决定了检查的严格程度:

Auto
自动判断安全性,大部分操作直接放行
Default
敏感操作弹窗询问用户确认
Plan
只读模式,禁止任何写操作

Auto 模式下有一个 yoloClassifier(分类器),根据工具类型和参数判断操作是否安全。比如读文件几乎总是安全的,但 rm -rf / 就需要拦截。

// 权限检查的调用链
canUseTool(toolName, toolInput)
  → checkPermissionMode(mode)
  → yoloClassifier(tool)  // auto 模式showPromptUI()       // 需要确认时
08

Feature Gate

Claude Code 用 Bun 打包器的 feature() 函数做编译时功能开关。这不是运行时 if/else,而是在打包时直接删除代码(死代码消除)。

// 编译时决定:保留还是删除这段代码
const reactiveCompact =
  feature('REACTIVE_COMPACT')
    ? require('./reactiveCompact.js')
    : null

主要的 Feature Gate:

🔄
REACTIVE_COMPACT
响应式压缩
📦
CONTEXT_COLLAPSE
上下文折叠
✂️
HISTORY_SNIP
历史裁剪
💰
TOKEN_BUDGET
Token 预算
CACHED_MICRO
缓存微压缩
🔧
STREAMING_TOOL
流式工具执行
为什么不用运行时开关?

编译时消除意味着外部发布版本完全不包含实验性代码——不是隐藏,是物理删除。零运行时开销,零泄露风险。

09

错误恢复

Harness 内建多重恢复策略,确保对话不会因为一次错误就中断:

1
模型降级
主模型高负载时,自动切换到 fallback model 重试
2
输出截断恢复
max_output_tokens 被截断时,自动重试(最多 3 次)
3
上下文溢出恢复
prompt 过长时,触发 reactive compact 或 context collapse 后重试
4
图片过大恢复
附件超限时移除图片、调整大小后重新发送
5
Tombstone 清理
降级/重试时通过 tombstone 事件清理 UI 中的孤立消息
类比理解

就像飞机有多套冗余系统——液压失灵用电动,主发动机停了还有辅助动力。Harness 的恢复策略确保单点故障不会让整个对话崩溃。

10

设计哲学

从源码中可以提炼出 Claude Code Harness 的 5 个核心设计原则:

🌊 异步生成器驱动

async function* 的 yield 既传数据又让出控制权,天然适配「流式输出 + 多轮工具调用」的场景。比回调地狱和 Promise 链都更优雅。

🔁 单循环 + 状态机

所有复杂性都收敛在一个 while(true) 中。通过 State 对象和 continue 跳转管理状态转换,避免了分散在多处的控制流。

🧱 编译时 Feature Gate

用 Bun bundler 的死代码消除做功能开关。不是运行时判断,是构建时物理删除。零开销,零泄露。

💉 依赖注入

核心循环通过 QueryDeps 接口注入 API 调用、压缩等依赖。测试时可以替换为 mock,不需要真实的网络请求。

🎣 Hook 扩展点

7 个生命周期钩子覆盖完整的 Agent 循环。用户可以在不修改核心代码的情况下,通过 settings.json 配置自定义行为。

以上分析基于 Claude Code 开源代码
generated by Claude Code