乐于分享
好东西不私藏

泄漏的Claude Code源码是拆解Harness工程的绝佳案例

泄漏的Claude Code源码是拆解Harness工程的绝佳案例

我是@卜寒兮,主要聊点【科技| AI |科研】方面的内容,这是我在公众号发布的第【33】篇原创内容,感兴趣的可以点击下方关注我。

欢迎关注👆

大家都知道,昨天科技圈最大的乌龙是Claude Code源码泄漏了。

简单来说,官方团队在发布Claude Code最新版本v2.1.88的时候,在npm包里包含了source map文件,它指向了Anthropic的公有存储桶,能直接下载src.zip。

下载地址:https://t.co/jBiMoOzt8G

source map 原本应该是用于调试的文件,生产环境不应该打包,最近Claude code基本上每天一更,之前看到有人总结了近一个多月以来Claude code/Claude app的迭代细节,堪称劳模。

这次怕不是Claude code作者更新把自己都更麻了,才不小心弄了个大乌龙。

反正这款目前可以称为地表最强的CLI coding agent 就这么赤裸裸的展现在大家面前了。

然后千千万万程序员过年,因为源码的泄露导致Claude code内部架构变得完全透明。很多人已经跃跃欲试的根据源码自己重新部署了,接下来肯定也会有一大波Claude code分支出现。

我看有网友说,接下来就是期待某个分支能让你Claude code的tokens消耗量减少97%。

不过除了会助力一大波 “xxx code” 的出现(就像openclaw繁衍出无数只“龙虾”一样),我觉得泄漏的源码的另一大用处就是用来学习harness工程,这绝对是harness设计和落地的绝佳案例。

因为harness engineering这个概念很火,而且它也是anthropic提出来的,前几天还专门发了一篇文,讲harness设计。

Claude code本身就是harness工程的最佳工程决策,现在用Claude code的源码去拆解harness工程的设计哲学。

还有比这更般配的么。

以免有人还不熟悉,先简单说一下什么是harness?

什么是Harness?

Harness原意是“马具”,也就是让一匹马进入到工作状态所需的一套装备。

到AI 这里,harness工程就是给模型建立一套规则和框架,约束模型行为,让它按照你制定逻辑和策略去工作,目的是完成那种光靠模型裸跑所完不成的任务。

你可以按照 prompt –> skills –> harness工程 这个演化过程来理解harness。

Claude Code源码拆解

然后再来看 Claude code,表面上看它是一个独立的cli智能体,但是透过源码,可以发现它并不是单agent,比如:

  • • tools/AgentTool/ 目录中有可以 spawn的子 agent
  • • utils/forkedAgent.ts 表明上下文压缩有专门 forked agent 完成
  • • tasks/LocalAgentTask/ 是负责后台任务的agent
  • • 以及utils/swarm/ 下的多 agent 协作工具
  • • 等等

Claude code本身就是一个被深度优化过的 harness,里面有很多工程决策。

以泄漏的源码为准,Claude code完整的结构如下:

核心模块包括:

  • • 查询引擎 (query.ts → QueryEngine.ts) 。处理用户输入、调用 claude api、执行工具调用的主循环
  • • 工具系统 (tools/) 。46 种工具,每个有独立权限检查逻辑
  • • 命令系统 (commands/) 。104 个斜杠命令,我看到了/btw
  • • MCP (services/mcp/) 。MCP的客户端实现
  • • 权限系统 (utils/permissions/) 。逐个的工具使用权限控制
  • • Bridge (bridge/) 。本地与远程 Claude code 会话通信
  • • plugins和skills系统 。可扩展的第三方集成和自定义命令

从harness设计的角度看它具体的工程决策。

### 1、AsyncGenerator 核心

大多数 AI 应用的结构是”发请求,拿响应”。Claude code 的核心函数 query() 不是:

它是一个AsyncGenerator,用 yield 流出每一个事件:token、工具调用结果、错误等。

这个设计直接解决了 long-running agent 里最麻烦的执行时间不确定问题。

一个任务可能需要 3 秒,也可能需要 3 小时。用传统的 async/await,要么阻塞等待,要么手写回调。AsyncGenerator 让调用方随时可以消费下一个事件,或者中途 .return() 终止整个生成器链。

这个模式也方便组合:compaction 是一个 generator,工具执行是一个 generator,主循环用 yield* 把它们串起来。每个环节可以独立测试,也可以拼在一起用。

开头提到Harness design那篇文章提到 agent 系统需要“处理中断和恢复”——AsyncGenerator 在语言层面就提供了这个能力,不需要额外的状态机。

2、Agentic Loop:while(true) 状态管理

核心循环在 queryLoop 里,结构是:

1
2
3
4
5
6
7
8
9

while (true) {  1. 上下文预处理(压缩/截断)  2. 调用 Claude API(流式)  3. 解析响应,收集 tool_use 块  4. 执行工具  5. 把结果追加到消息历史  6. 如果没有工具调用 → return(终止)     如果有 → continue(下一轮)}

这是标准的 ReAct(Reason + Act) 模式,细节在状态管理上。

循环里有一个 State 类型,记录每轮迭代间需要携带的可变状态:

1
2
3
4
5
6
7

type State = {  messages: Message[]  maxOutputTokensRecoveryCount: number  hasAttemptedReactiveCompact: boolean  transition: Continue | undefined  // 上一轮为何 continue  ...}

transition 字段记录”为什么进入下一轮”,可选值有 reactive_compact_retrycollapse_drain_retrymax_output_tokens_recovery 等。这不只是调试信息——它是防止死循环的断路器:如果上一轮已经因 X 重试过一次,就不再重试 X,而是进入下一个恢复策略。

Harness Design 里有一个类似的观点:long-running task 的错误恢复需要分层次,而不是”catch 一下再试一次”。

3、五层压缩策略的上下文管理

上下文焦虑是 long-running task 的头号问题,anthropic在文章中提到,context 快满时,模型就会开始草草收尾,质量急剧下降。

Claude Code 对此有五层策略,从轻到重依次触发:

层级
机制
触发条件
代价
1
microcompact
每轮开始,无条件
极低,仅压缩重复工具结果
2
snip
历史过长时
低,截断中间段落
3
autocompact
接近阈值(context 窗口 – 13k buffer)
中,调用 Haiku 生成摘要
4
reactiveCompact
API 返回 413 后
高,主动压缩后重试
5
contextCollapse
413 后先于 reactiveCompact
中,先消耗已暂存的折叠
1
2
3
4

// autoCompact.tsexport const AUTOCOMPACT_BUFFER_TOKENS = 13_000  // 主动压缩的安全余量export const MAX_OUTPUT_TOKENS_FOR_SUMMARY = 20_000  // 压缩摘要的 token 预算// 实测 p99.99 的压缩摘要输出为 17,387 tokens,因此取 20k

20_000 这个数字不是估算,注释写明来自生产数据的 p99.99。

autocompact 本身也是一个 forked agent——fork 出子 agent,共享父对话的系统提示和工具定义(保证 prompt cache 命中),再用 Claude Haiku 生成摘要。CacheSafeParams 类型描述了哪些参数必须与父 agent 完全一致才能命中缓存:

1
2
3

// forkedAgent.ts// CacheSafeParams carries system prompt, tools, model, messages (prefix), thinking config.// Thinking config is derived from inherited toolUseContext.options.thinkingConfig

4、流式并发执行,工具不用模型说完就开始跑

通常的实现是:等模型完整输出所有 tool_use 块 → 全部执行完 → 把结果返回模型。

Claude Code 有一个 StreamingToolExecutor,模型还在流式输出时,已经完成的 tool_use 块就立刻开始执行:

1
2
3
4
5
6
7
8
9
10

// 在 for await 流式循环内if (streamingToolExecutor) {  for (const toolBlock of msgToolUseBlocks) {    streamingToolExecutor.addTool(toolBlock, message)  // 立即入队并尝试执行  }}// 同时,已经完成的工具结果也在同一循环里被 yield 出来for (const result of streamingToolExecutor.getCompletedResults()) {  yield result.message}

模型输出和工具执行是并行的。对于需要读取 10 个文件的任务,这意味着在模型还没说完”我需要读哪些文件”的时候,前几个文件已经读完了。

并发引入了新问题:哪些工具可以并行?每个工具实现一个 isConcurrencySafe() 接口,自己声明:

1
2
3
4
5
6
7
8
9
10
11

// GrepTool - 只读,天然并发安全isConcurrencySafe() { return true }// BashTool - 视命令内容而定isConcurrencySafe(input) {  return this.isReadOnly?.(input) ?? false  // isReadOnly 会分析命令的 AST,判断是否包含写操作}// McpAuthTool - 涉及认证状态,强制串行isConcurrencySafe: () => false

调度规则很简单:只有当所有正在执行的工具都是并发安全的,才能启动新的并发安全工具;非安全工具必须独占执行

还有一个细节:并发执行时引入了 siblingAbortController——当某个并行工具报错,同批次的其他工具立即收到取消信号,而不是等它们跑完再处理错误。

5、Hook 架构:在正确的时机插入逻辑

Harness 文章里提到的 generator-evaluator 分离,在 Claude code 里是以 hook 的形式实现的:evaluation 逻辑(stop hooks 检查任务是否完成)与 generation 逻辑完全解耦。

Claude Code 提供了 PreToolUsePostToolUsePostSamplingPreCompactPostCompact 几个 hook 点。

这对应了 Harness Design 的一个思路:agent 的行为需要在不改动核心循环的情况下被调整

PostSampling Hook 在每次模型生成完成后触发,让外部逻辑可以观察主循环而不干扰它。注意它的错误处理:

1
2
3
4
5
6
7
8

// postSamplingHooks.tsfor (const hook of postSamplingHooks) {  try {    await hook(context)  } catch (error) {    logError(toError(error))  // 只记录,不 rethrow  }}

Hook 的错误被吞掉,不会破坏主循环。监控、日志、评估逻辑出问题了,不应该把主任务也带崩。

6、运营数据驱动的工程决策

Harness 文章的一个重要观点:harness 编码了对”模型不能独立完成什么”的假设,这些假设值得随着模型能力的提升不断重新审视。也许模型有一天变更强了,这些假设就不成立了。

Claude Code 源码里有几处注释,直接体现了这种做法:

1
2
3

// 2026-03-10: 1,279 sessions had 50+ consecutive failures (up to 3,272)// in a single session, wasting ~250K API calls/day globally.// Based on p99.99 of compact summary output being 17,387 tokens.

每一个数字背后都是实际经验或统计分析。把这些数据直接写进注释而不是只留常量,意味着下一个来改代码的人知道这个数字是怎么来的、改了会有什么后果。

这说明 Claude code 团队把 harness 当做一个需要持续迭代的运营系统,而不是一次设计完就不动的基础设施。从这个角度看,这些注释其实是最重要的文档。

总结

回到上面那句话:harness 编码了对”模型不能独立完成什么”的假设(“the harness itself encodes assumptions about what models cannot do independently”。)

Claude code的harness 告诉你:当前模型需要被辅助管理上下文、需要并发调度、需要权限沙箱、需要多层错误恢复。这些都是现阶段的假设,不是永久的结论。随着模型能力的变化,哪些假设还成立、哪些可以去掉,值得持续观察。

既然已经看到这里,不如来个“点赞、在看、转发”三连再走,非常感谢。也欢迎你关注我

感谢关注👆

推荐阅读

点击“阅读原文”了解更多信息↓