随笔

Claude Code 深度源码解读 01:Harness 不是工具集合,而是受控运行时

从交叉对象和运行时不变量切入,重建 Claude Code Harness 的工程读法。

2026-04-05 Claude Code源码解读Agent Harness技术深读

简化版入口:Harness 总览

机制图
机制图

我的设问与回应

设问 1:Harness 的边界到底在哪里?

回应答案: 边界不在模型输出处,而在模型输出被转换为可验证协议事件之后。用户输入、Context、Query Loop、Tool Execution、Permissions、Hooks、Memory、Transcript 共同构成边界。

证据与证明路径: 证据来自总览报告对 Message、Tool、ToolUseContext、AppState、Transcript / Session State 的归类,以及 Tools、Permissions、Hooks 的交叉展开。它证明 Claude Code 不是工具列表,而是一组围绕运行时上下文协作的控制层。

还值得继续学习或反思: 进一步要追的是这些交叉对象是否有稳定测试边界,以及新增场景能力时是否会绕过这些对象。

设问 2:主线功能和场景插入应该怎么分?

回应答案: 主线是没有它系统就不能推进的链路;场景插入是 MCP、remote、team、worktree、auto memory 等条件能力。

证据与证明路径: 证据是第 1 期把 Context、Query Loop、Tools、Permissions、Hooks、Memory 分别展开,又在后续路线中要求标注主线与场景特殊处理。

还值得继续学习或反思: 读 agent 源码不能按目录读,要按运行时是否改变主线语义读。

源码调研主体

本组解读基于冻结源码快照进行教育、防御和架构研究。

范围:只分析 Harness 运行机制,不提供复用实现,不改变源码边界。

0. 调研目标

本期目标是建立 Harness 机制的第一版可导航地图,用“总-分”的方式把源码中的关键运行机制画清楚:

  • Tools:模型的手脚,解决行动能力。
  • Context:模型当前思考窗口,解决模型看到什么。
  • Memory:长期存储与召回,解决持久化记忆。
  • Hooks:事件驱动反射,解决自动化介入。
  • Permissions:安全围栏,解决安全底线。

本报告不把所有细节堆在一张图里,而是先给出总览,再对关键节点分别展开。每张展开图都标注其对应的总览节点。

0.1 主线与场景特殊处理标注

后续报告统一使用四类标签,避免把场景增强误写成 Harness 必经路径:

标签含义
主线机制常规任务中直接支撑用户输入、模型调用、工具回灌或会话推进的路径
场景增强只在特定环境、功能开关、输入形态或产品模式下生效的补充能力
安全/治理横切对权限、安全边界、策略约束、风险控制有关键作用的横切机制
可观测/恢复支撑支撑 transcript、审计、恢复、调试和进度展示的机制

本期作为总览报告,先给出一级机制地图;从第 2 期开始,每期都在关键源码入口之后补充分类表。

1. Harness 总览图

对应目标:明确整体协同关系,而不是展开每个节点内部细节。

这部分机制参考前文「Harness 总览 机制图」。

总览解读

Harness 的核心不是某一个目录,而是一个由 QueryEngine.submitMessage()query() 驱动的闭环:

  1. 用户输入先经过 Context Preparation。
  2. Query Loop 将上下文、工具、权限上下文发送给模型。
  3. 模型流式返回文本或 tool_use
  4. tool_use 进入 Tool Execution。
  5. Tool Execution 必须经过 Permissions 与 Hooks。
  6. 工具结果以 tool_result 形式回灌给 Query Loop。
  7. Memory、Hooks、Compaction、Session State 横切整个循环。

2. Context Preparation 展开图

对应总览节点:Context Preparation

目标:解释模型“看到什么”是如何准备出来的。

这部分机制参考前文「Harness 总览 机制图」。

关键源码入口

职责文件
会话生命周期入口src/QueryEngine.ts
system/user/system context 构造src/utils/queryContext.ts
默认 system promptsrc/constants/prompts.ts
用户输入处理src/utils/processUserInput/processUserInput.ts
附件注入src/utils/attachments.ts
消息规范化src/utils/messages.ts

机制说明

QueryEngine.submitMessage() 是 Context Preparation 的中心。它先调用 fetchSystemPromptParts() 获取三类 API 前缀上下文:

  • defaultSystemPrompt
  • userContext
  • systemContext

随后根据运行条件决定是否调用 loadMemoryPrompt(),把“如何使用记忆系统”的机制说明加入 system prompt。之后 processUserInput() 把用户输入加工成模型可消费的消息,包括普通文本、slash command、附件、图片、IDE 上下文等。

最后构造 ToolUseContext。这个对象非常关键,它不是普通参数包,而是工具执行、权限判断、Hook 执行、状态更新、读文件缓存、记忆去重等机制共享的运行时上下文。

3. Query Loop 展开图

对应总览节点:Query Loop

目标:解释模型请求、工具调用、结果回灌、停止和恢复如何循环。

这部分机制参考前文「Harness 总览 机制图」。

关键源码入口

职责文件
主查询循环src/query.ts
QueryEngine 调用 querysrc/QueryEngine.ts
query 配置快照src/query/config.ts
token budgetsrc/query/tokenBudget.ts
stop hook 接入src/query/stopHooks.ts
compact 机制src/services/compact/

机制说明

query() 是 Harness 的主循环。它不是一次模型请求后结束,而是在以下条件下反复迭代:

  • 模型产生 tool_use,需要执行工具并把结果回灌。
  • Stop Hook 返回阻断信息,需要把阻断反馈加入上下文再问模型。
  • token budget 或 compact 机制要求续跑。
  • prompt too long、max output tokens、fallback model 等恢复路径触发。

在每轮请求前,query() 会处理消息窗口、工具结果预算、compact 状态和相关记忆预取。模型流式返回时,如果检测到 tool_use,则进入工具执行;如果没有工具调用,则进入 stop hook 和完成判断。

4. Tools 展开图

对应总览节点:Tool Execution

目标:解释模型如何从 tool_use 变成实际行动。

这部分机制参考前文「Harness 总览 机制图」。

关键源码入口

职责文件
工具注册src/tools.ts
工具接口src/Tool.ts
批量/串并行编排src/services/tools/toolOrchestration.ts
单个工具执行src/services/tools/toolExecution.ts
流式工具执行器src/services/tools/StreamingToolExecutor.ts
工具 Hook 封装src/services/tools/toolHooks.ts

工具池来源

这部分机制参考前文「Harness 总览 机制图」。

机制说明

工具系统有两层:

  1. 工具注册层:tools.ts 决定哪些工具进入工具池。
  2. 工具执行层:toolExecution.ts 决定单个 tool_use 如何被执行。

StreamingToolExecutor 的作用是让并发安全的工具在模型流式输出时提前执行。它会维护工具队列,区分 concurrency-safe 工具和非 concurrency-safe 工具,并保证结果按工具调用顺序回灌。

runTools() 是非流式或剩余工具执行路径。它将工具调用分成批次:多个连续的并发安全工具可并行执行;非并发安全工具串行执行。

5. Permissions 展开图

对应总览节点:Permissions

目标:解释安全围栏如何拦截、放行或转交用户/宿主决策。

这部分机制参考前文「Harness 总览 机制图」。

关键源码入口

职责文件
权限类型src/types/permissions.ts
权限主决策src/utils/permissions/permissions.ts
React/交互承接src/hooks/useCanUseTool.tsx
权限队列与上下文src/hooks/toolPermission/PermissionContext.ts
交互处理src/hooks/toolPermission/handlers/interactiveHandler.ts
coordinator 处理src/hooks/toolPermission/handlers/coordinatorHandler.ts
swarm worker 处理src/hooks/toolPermission/handlers/swarmWorkerHandler.ts
Bash 权限细化src/tools/BashTool/bashPermissions.ts

机制说明

权限机制分两层:

  • hasPermissionsToUseTool():生成 allow / ask / deny 决策。
  • useCanUseTool():把 ask 决策接到 UI、自动分类器、coordinator、swarm worker 或宿主回调。

决策顺序的安全意义很强:

  1. deny 规则最先命中。
  2. 工具自身的 checkPermissions() 可执行内容级安全判断。
  3. safety check 和内容级 ask 可以抵抗 bypass。
  4. bypass 和 always allow 在这些检查之后才生效。
  5. auto mode 不直接等于放行,而是走 classifier、allowlist、acceptEdits fast path 和 denial tracking。

这说明 Permissions 是 Harness 的安全边界,不只是 UI 弹窗。

6. Hooks 展开图

对应总览节点:Lifecycle HooksTool Hooks

目标:解释事件驱动机制在哪里介入主循环。

这部分机制参考前文「Harness 总览 机制图」。

关键源码入口

职责文件
Hook 聚合执行src/utils/hooks.ts
Hook 事件广播src/utils/hooks/hookEvents.ts
会话临时 Hooksrc/utils/hooks/sessionHooks.ts
prompt Hook 执行src/utils/hooks/execPromptHook.ts
agent Hook 执行src/utils/hooks/execAgentHook.ts
HTTP Hook 执行src/utils/hooks/execHttpHook.ts
工具 Hook 封装src/services/tools/toolHooks.ts
Stop Hook 接入src/query/stopHooks.ts

机制说明

Hooks 是 Harness 的事件驱动反射层。它可以在多个关键节点介入:

  • 用户提交 prompt 后、模型请求前。
  • 工具执行前。
  • 权限请求时。
  • 工具执行后或失败后。
  • 模型停止时。
  • compact 前后。
  • session start / session end 等生命周期点。

Hook 的输出不只是日志。它可以补充上下文、阻止继续、修改权限决策、产生阻断反馈,甚至通过异步 Hook 在后续回合唤醒模型。

7. Memory 展开图

对应总览节点:Memory

目标:解释长期记忆如何进入当前上下文窗口。

这部分机制参考前文「Harness 总览 机制图」。

关键源码入口

职责文件
memory promptsrc/memdir/memdir.ts
relevant memory 选择src/memdir/findRelevantMemories.ts
memory 文件扫描src/memdir/memoryScan.ts
memory 类型src/memdir/memoryTypes.ts
memory 新鲜度src/memdir/memoryAge.ts
memory 附件注入src/utils/attachments.ts
session memorysrc/services/SessionMemory/sessionMemory.ts
session memory compactsrc/services/compact/sessionMemoryCompact.ts
team memory syncsrc/services/teamMemorySync/
auto memory consolidationsrc/services/autoDream/autoDream.ts

机制说明

Memory 至少有三种进入模型上下文的路径:

  1. 机制提示路径loadMemoryPrompt() 把“如何使用记忆系统”的说明加入 system prompt。
  2. 相关记忆召回路径startRelevantMemoryPrefetch() 根据当前用户输入异步搜索相关 memory 文件,形成 relevant_memories 附件。
  3. 嵌套指令路径:文件路径触发 nested memory,沿路径查找 CLAUDE.md 和条件规则,形成 nested memory 附件。

这个设计说明 Memory 不是单纯的数据库读写,而是和 Context Preparation、Query Loop、附件系统、agent 隔离、team memory 同时耦合。

8. Tools、Permissions、Hooks 的交叉展开

对应总览节点:Tool Execution + Permissions + Tool Hooks

目标:解释一次工具调用的完整安全链路。

这部分机制参考前文「Harness 总览 机制图」。

机制说明

模型不能直接执行工具。模型只产生 tool_use,真正执行前必须穿过:

  1. 工具查找与 schema 校验。
  2. 权限决策。
  3. PermissionRequest Hook。
  4. PreToolUse Hook。
  5. 工具自己的 call()
  6. PostToolUse / failure Hook。
  7. 结果映射与回灌。

因此 Harness 的行动能力不是“模型调用函数”这么简单,而是一个受状态、权限、Hook、并发、安全检查共同约束的执行器。

9. 第一版源码归类索引

Tools

模块作用
src/tools.ts内置工具集合、MCP 工具合并、deny rule 过滤
src/Tool.tsTool 接口、ToolUseContext、ToolPermissionContext
src/services/tools/toolExecution.ts单个工具调用生命周期
src/services/tools/toolOrchestration.ts串并行工具编排
src/services/tools/StreamingToolExecutor.ts流式工具执行
src/tools/BashTool/Bash 执行、命令安全、沙箱、权限
src/tools/FileReadTool/文件读取与附件上下文
src/tools/FileEditTool/文件编辑与 diff
src/tools/AgentTool/子代理与 agent memory
src/tools/MCPTool/MCP 工具适配

Context

模块作用
src/QueryEngine.ts会话生命周期和上下文准备
src/query.ts主查询循环
src/context.ts用户/系统上下文收集
src/utils/queryContext.tssystem/user/system context 组装
src/utils/processUserInput/processUserInput.ts用户输入加工
src/utils/attachments.ts附件、文件、memory、任务上下文注入
src/utils/messages.ts消息规范化、tool_use/tool_result 配对
src/components/ContextVisualization.tsx上下文可视化入口

Memory

模块作用
src/memdir/memdir.tsmemory prompt 与目录保证
src/memdir/findRelevantMemories.ts相关记忆选择
src/memdir/memoryScan.tsmemory 文件扫描
src/memdir/memoryTypes.tsmemory 类型说明
src/memdir/paths.tsmemory 路径
src/memdir/teamMemPrompts.tsteam memory prompt
src/services/SessionMemory/session memory
src/services/extractMemories/memory 提取
src/services/autoDream/memory consolidation
src/services/teamMemorySync/team memory 同步

Hooks

模块作用
src/utils/hooks.tsHook 主执行器
src/utils/hooks/hookEvents.tsHook 事件广播
src/utils/hooks/sessionHooks.ts会话内临时 Hook
src/utils/hooks/execPromptHook.tsprompt Hook
src/utils/hooks/execAgentHook.tsagent Hook
src/utils/hooks/execHttpHook.tsHTTP Hook
src/utils/hooks/hooksConfigManager.tsHook 配置说明
src/components/hooks/Hook 配置 UI
src/commands/hooks//hooks 命令

Permissions

模块作用
src/types/permissions.ts权限模式、规则、更新、决策类型
src/utils/permissions/permissions.ts权限主决策链
src/hooks/useCanUseTool.tsx交互环境下的权限承接
src/hooks/toolPermission/PermissionContext.ts权限请求上下文与队列
src/hooks/toolPermission/handlers/interactiveHandler.ts用户交互审批
src/hooks/toolPermission/handlers/coordinatorHandler.tscoordinator 审批路径
src/hooks/toolPermission/handlers/swarmWorkerHandler.tsswarm worker 审批路径
src/utils/permissions/permissionRuleParser.ts权限规则解析
src/utils/permissions/permissionsLoader.ts权限配置加载
src/utils/permissions/yoloClassifier.tsauto mode 分类器

10. 初步结论

10.1 Harness 的核心抽象

本项目的 Harness 可以概括为:

一个以 Query Loop 为中心,把模型流式输出、工具执行、权限决策、Hook 事件、记忆召回、上下文压缩和会话持久化绑定在一起的运行时外壳。

10.2 五个一级维度不是并列目录,而是运行时角色

  • Tools 是行动执行层。
  • Context 是模型输入层。
  • Memory 是跨回合信息层。
  • Hooks 是事件干预层。
  • Permissions 是安全约束层。

它们都通过 ToolUseContextAppStateMessagequery()Tool 接口发生交汇。

10.3 当前最关键的交汇对象

对象重要性
ToolUseContext几乎所有工具、权限、Hook、状态更新都依赖它
Message模型上下文、工具结果、附件、系统提醒的统一载体
Tool行动能力的统一接口
ToolPermissionContext权限模式、规则、工作目录边界的统一载体
AppStateUI、MCP、权限、任务、Hook、agent 状态的运行时存储

机制主线收束

站在架构师视角,我会把这组源码机制收束为三个问题:为什么需要它,什么时候必须引入它,以及真正要设计的是什么。

  • why:Harness 要解决的是模型推理和真实副作用之间缺少工程缓冲层的问题。
  • when:当 agent 需要读写文件、调用 shell、连接外部服务或长期执行任务时,必须把模型输出纳入受控运行时。
  • what:先定义 Message、Tool、ToolUseContext、State、Transcript 这类交叉对象,再让工具、权限、Hook、Memory 围绕它们扩展。

Harness 总览 这组源码最值得带走的,不是某个函数或某个配置项,而是它如何把模型能力放进一组可验证、可拒绝、可恢复、可审计的工程边界里。读源码时,如果只记住名词,会很快散;如果抓住 why、when、what,就能把这组机制迁移到自己的 agent 架构判断中。