10416 字
52 分钟
Claude Code 中的 Harness:从受控工具循环到生产级 Agent 运行时

Claude Code 中的 Harness:从受控工具循环到生产级 Agent 运行时#

模型能力决定 Agent 的能力上限,Harness 决定系统能否安全、稳定、低成本地触达这个上限。

Claude Code 是什么:Harness 总体架构

摘要#

Claude Code 经常被简单理解为“一个可以读写代码、调用 Shell 的 Claude 客户端”,但这种描述忽略了它最有价值的部分:包裹在模型之外的 Agent Harness

Anthropic 官方将 Claude Code 描述为运行在终端、IDE、桌面端和浏览器中的 Agentic Coding Tool。它能够读取代码库、编辑文件、运行命令、调用外部工具,并通过“收集上下文—执行动作—验证结果”的循环持续工作。官方文档进一步明确指出:Claude Code 是包裹 Claude 模型的 Agentic Harness,负责提供工具、上下文管理和执行环境。1

而社区项目 how-claude-code-works 则基于源码快照、二进制字符串、抓包与黑盒测试,对这一 Harness 做了更细粒度的工程分析,包括双层生成器运行时、工具流式预执行、渐进式上下文压缩、权限状态机、长期记忆、子 Agent 隔离与可观测性等。2

本文不讨论 Claude 模型内部的 Transformer、训练方法或推理机制,而是聚焦以下问题:

  1. Claude Code 的 Harness 由哪些层组成?
  2. 模型决策与确定性运行时如何分工?
  3. 为什么它不是一个简单的 ReAct Demo?
  4. 上下文、工具、权限、记忆与多 Agent 如何形成统一系统?
  5. 哪些结论得到官方文档确认,哪些来自社区源码分析或黑盒推断?
  6. 这些设计对企业级 Agent Harness 有什么可复用价值?

1. 证据边界:先区分官方事实、源码分析与工程推断#

在深入架构之前,必须明确材料的可信边界。

1.1 三类证据#

证据等级来源适合支撑的结论
A:官方确认Claude Code 官方文档、Claude Platform 官方文档产品行为、权限模式、Sandbox、Memory、Skills、Subagents、MCP、OTel 等公开能力
B:社区源码分析how-claude-code-works 对源码快照的走读类、函数、状态字段、循环结构、工具调度和部分内部机制
C:黑盒逆向与架构推断二进制字符串、网络抓包、运行行为测试新功能可能的实现方式、内部编排和未公开机制

社区仓库本身明确声明:它是独立教育性分析,不代表 Anthropic 官方设计,也不保证与 Claude Code 当前版本的真实内部实现完全一致。仓库前半部分主要基于一个历史源码快照,后续新能力则更多依赖黑盒实测和公开情报。2

因此,本文采用以下写法:

  • “官方文档确认”:可以视为当前产品公开契约;
  • “社区源码分析显示”:可以作为高价值工程参考,但不等同于官方承诺;
  • “可推断”或“可能”:只作为架构解释,不写成确定事实。

1.2 为什么社区分析仍然有价值#

官方文档通常回答“用户如何使用”,而源码分析更适合回答:

  • 为什么要把会话管理和 Agent Loop 分成两层?
  • 工具为什么需要并发安全语义?
  • 为什么旧工具输出要先裁剪,再做全局摘要?
  • 为什么 Permission 不能只是一个确认弹窗?
  • 为什么多 Agent 必须优先做上下文和文件系统隔离?
  • 为什么错误恢复应该是状态转移,而不是统一重试?

这类问题决定了一个 Agent 能否从 Demo 演进为生产系统。


2. Harness 到底是什么#

在 Agent 语境中,Harness 不是某个单一模块,也不是一个 Prompt 模板。

可以把它定义为:

Harness 是包裹在模型之外、负责上下文、能力、执行、状态、权限、恢复、预算和观测的确定性工程系统。

模型擅长:

  • 理解模糊目标;
  • 阅读代码和错误信息;
  • 推断下一步行动;
  • 在新观察出现后调整方案;
  • 生成代码、计划和解释。

Harness 负责:

  • 模型能看到哪些信息;
  • 模型能调用哪些工具;
  • 工具参数是否有效;
  • 动作是否被授权;
  • 哪些工具可以并行;
  • 失败后应该重试、压缩、降级还是停止;
  • 会话如何恢复;
  • 费用、轮数和 Token 如何限制;
  • 整个过程如何回放和审计。

二者的边界可以概括为:

模型:下一步“应该做什么”
Harness:这一步“能不能做、怎样做、失败后怎么办”

Anthropic 官方在 “How Claude Code works” 中将其核心循环概括为:

Gather context → Take action → Verify results → Repeat

并指出 Claude Code 的 Agentic Loop 由“负责推理的模型”和“负责行动的工具”共同驱动,Claude Code 则作为 Harness 提供工具、上下文管理与执行环境。1


3. Claude Code Harness 的总体分层#

从生产级 Agent 架构角度,可以把 Claude Code 抽象成以下结构:

交互与接入层
会话生命周期层
Agent Runtime / 状态机
上下文编译与模型调用
工具执行与结果回注
恢复、继续或终止

同时,四个横切平面贯穿整个链路:

能力平面:Tools / Skills / MCP / Subagents
状态平面:State / Session / Transcript / Memory / Task
治理平面:Permissions / Hooks / Sandbox / HITL
观测平面:Metrics / Events / Trace / Transcript

3.1 接入层#

Claude Code 当前提供终端、IDE、桌面端、Web、CI/CD 和 Agent SDK 等多种界面。官方文档强调,不同界面共享相同的底层 Agentic Loop,变化的主要是交互方式和代码实际运行的位置。31

接入层负责把用户请求转换为统一任务输入,并附加:

  • 当前工作目录;
  • Git 状态;
  • 会话 ID;
  • 权限模式;
  • CLI 或 SDK 配置;
  • 附件和文件引用;
  • 最大轮数和成本限制;
  • 中断信号。

3.2 会话生命周期层#

这一层负责:

  • 接收和规范化用户输入;
  • 管理多轮消息;
  • 处理斜杠命令;
  • 保存本地 Transcript;
  • 恢复、继续或 Fork 会话;
  • 跟踪 Token、成本和轮次;
  • 将内部事件转换为终端或 SDK 输出。

官方文档确认 Claude Code 会把会话写入本地 JSONL,从而支持恢复、回退和 Fork。1

社区源码分析进一步将这层对应到 QueryEngine:它更关注一次用户交互是否成功、消息如何持久化、花费多少预算以及最终结果如何组装。4

3.3 Agent Runtime 层#

这一层是真正的 Agent Loop:

准备上下文
→ 调用模型
→ 解析工具请求
→ 执行工具
→ 回注结果
→ 判断继续或结束

官方文档从产品层确认了这种循环;社区仓库则进一步分析出一个以 query() 为中心的手写异步生成器状态机。14

3.4 能力层#

能力并不只包括文件读写和 Bash,还包括:

  • 内置工具;
  • MCP 工具;
  • Skills;
  • Subagents;
  • Agent Teams;
  • Hooks;
  • Web 和代码智能插件。

官方把这些能力区分为不同扩展机制:CLAUDE.md 提供常驻上下文,Skills 提供按需工作流,Subagents 提供隔离执行,MCP 连接外部系统,Hooks 提供确定性事件自动化。5

3.5 治理层#

Claude Code 可以直接在真实开发环境中产生副作用,因此必须有:

  • 细粒度工具权限;
  • Allow / Ask / Deny 规则;
  • 不同 Permission Mode;
  • 受保护路径;
  • Hooks;
  • Sandbox;
  • 用户审批;
  • 企业级 Managed Settings;
  • 审计事件。

官方文档把 Permissions 与 Sandboxing 定义为互补安全层:前者控制允许做什么,后者从操作系统层限制 Bash 实际能访问什么。67


4. 双层运行时:会话引擎与 Agent 状态机分离#

社区源码分析中最值得学习的设计之一,是将 Claude Code 运行时拆为两层:

QueryEngine:会话生命周期
query():单次 Agent 执行循环

双层架构:会话引擎与 Agent 循环

4.1 QueryEngine 解决什么问题#

QueryEngine 关注的是“这一轮用户交互”:

  • 输入是什么;
  • 是否是本地命令;
  • 系统提示词和记忆如何初始化;
  • 消息是否持久化;
  • 最大轮数和预算是否超限;
  • 工具权限是否被拒绝;
  • 最终输出是否有效;
  • SDK 应返回哪些结构化元数据。

它类似于应用服务层或会话级 Runtime。

4.2 query() 解决什么问题#

query() 关注的是一次 Agent Loop 内部的执行:

  • 上下文是否需要压缩;
  • 当前应该调用哪个模型;
  • 流式响应里是否出现工具调用;
  • 工具是否已经执行完成;
  • Tool Result 如何注入;
  • 是否进入下一轮;
  • 是否因为 Token、Hook、预算或错误进入恢复路径。

它类似于 Agent 状态机或执行内核。

4.3 为什么要分两层#

如果把所有逻辑都塞进一个循环,会形成以下耦合:

输入解析
+ 会话持久化
+ Prompt 构建
+ 模型重试
+ 工具调度
+ 权限等待
+ Token 统计
+ UI 输出
+ 最终结果提取

这会导致:

  • 很难对循环本身做单元测试;
  • 无法复用到 Headless 或 SDK;
  • 会话恢复和单轮执行相互污染;
  • 错误恢复路径难以表达;
  • UI 状态和核心 Runtime 紧密耦合。

双层架构把“对话外壳”和“执行内核”分开,使 CLI、SDK 和其他界面都能复用同一个 Agent Runtime。

4.4 这与 LangGraph 有什么不同#

LangGraph 使用显式的:

State + Node + Edge + Reducer + Checkpoint

Claude Code 的社区分析则显示,它更接近手写的:

State + while/continue + Transition + Async Generator

二者都在解决同一个问题:如何把非确定性模型调用约束在一个可追踪的状态机中。

差别在于:

  • LangGraph 把控制流外显为图;
  • Claude Code 把控制流编码在专用 Runtime 中;
  • LangGraph 更通用;
  • Claude Code 更贴近 Coding Agent 的具体工作负载,因此可以做更激进的定制优化。

5. 受控工具循环:为什么它不只是 ReAct#

受控 ReAct:状态机与恢复策略

一个最小 ReAct Agent 通常可以写成:

while True:
response = model(messages, tools)
if not response.tool_calls:
return response.text
results = execute_tools(response.tool_calls)
messages.extend(results)

Claude Code 的语义骨架与此相似,但生产运行时还必须处理:

上下文预算
提示缓存
工具并发
权限等待
Hook 阻断
输出 Token 截断
API 重试
用户中断
大结果落盘
会话恢复
成本上限
子 Agent 任务

因此,更准确的抽象是:

Context Compile
→ Model Decision
→ Tool Admission
→ Controlled Execution
→ Observation Injection
→ State Transition
→ Recovery / Continue / Terminal

5.1 模型控制语义,Runtime 控制状态#

模型负责判断:

  • 需要先读哪些文件;
  • 应该搜索还是执行测试;
  • 当前错误意味着什么;
  • 下一步修改哪个位置;
  • 是否已经完成任务。

Runtime 负责判断:

  • 这个工具是否存在;
  • 参数是否通过 Schema;
  • 是否允许执行;
  • 是否必须人工确认;
  • 是否可以与其他工具并行;
  • 是否超过最大轮数;
  • 是否应该继续、恢复或终止。

这是“开放式推理”和“确定性执行”的核心分界。

5.2 错误应该被建模为状态转移#

社区仓库分析出多个 Continue Site:正常工具续轮、上下文恢复、输出 Token 扩容、续写恢复、Stop Hook 阻止终止、Token 预算续跑等。4

这反映出一个生产级设计原则:

不同失败具有不同语义,不能全部交给统一重试器。

例如:

失败合理处理
API 暂时超时指数退避重试
上下文超限压缩后重试
输出 Token 用尽提高上限或注入续写指令
用户拒绝权限调整方案,不得原样重试
工具参数非法把结构化错误回给模型修正
高风险动作暂停等待人工
达到最大轮数终止并返回不完整状态

统一 retry() 会掩盖这些差异,并可能制造死循环或重复副作用。


6. Async Generator:把 Token、工具和状态统一成事件流#

社区源码分析认为,Claude Code 从模型 API 到终端 UI 广泛使用 async function* 异步生成器。8

其核心形态类似:

async function* query(): AsyncGenerator<RuntimeEvent, TerminalState> {
yield { type: "message_delta", ... }
yield { type: "tool_progress", ... }
yield { type: "permission_request", ... }
yield { type: "tool_result", ... }
return terminalState
}

流式事件:异步生成器驱动全链路

6.1 为什么不是普通 Promise#

Promise 适合“一次调用、一次结果”:

request → await → result

Agent 任务却需要持续产生:

  • 首 Token;
  • 工具调用开始;
  • 工具执行进度;
  • 权限请求;
  • 压缩边界;
  • API 错误;
  • 子任务通知;
  • 最终结果。

如果使用 Promise,开发者往往需要额外维护:

  • EventEmitter;
  • 回调;
  • 队列;
  • AbortController;
  • 完成状态;
  • 错误通道。

异步生成器可以自然表达:

yield:中间事件
return:最终状态
throw:不可恢复异常

6.2 流式不是 UI 特性,而是 Runtime 特性#

很多系统只在最后一层做 Token Streaming:

模型完整生成 → 后端逐字切片 → 前端显示

Claude Code 的思路更接近语义事件流:

模型事件
工具事件
权限事件
恢复事件
最终状态

这样,终端、IDE、SDK 和观测系统可以消费同一条执行流。

6.3 取消和 Backpressure#

异步生成器还天然有利于:

  • 消费者处理速度控制;
  • 用户中断;
  • API Abort;
  • 工具清理;
  • 资源释放;
  • 子任务取消。

对于长时间运行的 Agent,取消不是附加功能,而是 Runtime 的一等能力。


7. 上下文工程:每轮重新编译模型看到的世界#

官方文档明确指出,Claude Code 的上下文窗口包含:

  • 系统指令;
  • 对话历史;
  • 文件内容;
  • 命令输出;
  • CLAUDE.md;
  • Auto Memory;
  • Skills;
  • 工具定义;
  • MCP 能力。91

上下文不是“历史消息数组”,而是一个每轮重新生成的编译产物。

上下文工程:编译、缓存与多级压缩

7.1 三类上下文#

稳定前缀#

适合长期保持不变:

  • 核心 System Prompt;
  • 通用行为规则;
  • 安全指令;
  • 稳定的工具定义;
  • 输出格式约束。

会话级动态上下文#

变化频率中等:

  • 当前目录;
  • Git 状态;
  • CLAUDE.md;
  • 路径级 Rules;
  • 用户配置;
  • 已发现 Skills;
  • MCP 服务器信息;
  • Auto Memory 索引。

快速增长的工作历史#

每轮都会增长:

  • 用户消息;
  • Assistant 消息;
  • Tool Use;
  • Tool Result;
  • 文件全文;
  • Shell 输出;
  • 子 Agent 摘要;
  • 动态附件。

7.2 缓存感知的内容排序#

Anthropic 的 Prompt Caching 以完整前缀为缓存对象,并按照:

tools → system → messages

形成层级。缓存命中要求被缓存前缀完全一致,工具顺序、System 内容或早期消息变化都可能导致缓存失效。1011

这解释了为什么一个成熟 Harness 会:

  • 把稳定内容放在前部;
  • 避免在 System Prompt 中插入时间戳等高频变化字段;
  • 保持工具排序稳定;
  • 将动态信息放在后部;
  • 对不常用工具使用延迟加载;
  • 对会话历史使用增量缓存。

这不是简单的 Token 省钱,而是同时优化:

  • 首 Token 延迟;
  • 输入成本;
  • 速率限制;
  • 长会话吞吐。

7.3 CLAUDE.md 为什么不是硬配置#

官方当前文档说明,CLAUDE.md 会以用户消息形式进入上下文,而不是成为不可覆盖的系统配置。Claude 会尽力遵循,但不保证严格执行。12

因此:

CLAUDE.md = 行为上下文
不是权限系统
不是策略引擎
不是安全边界

真正需要强制执行的规则应该放在:

  • Permission Deny;
  • Hooks;
  • Sandbox;
  • CI;
  • Policy Engine;
  • 静态分析;
  • 代码审查。

7.4 渐进式压缩#

官方确认 Claude Code 在上下文接近上限时,会优先清理旧工具输出,再在需要时对对话进行摘要;如果压缩后立即再次溢出,还会停止反复压缩,避免 Thrashing。1

社区仓库进一步将其拆成更细的流水线:

Tool Result Budget
→ History Snip
→ Microcompact
→ Context Collapse
→ Autocompact

其中的核心思想是:

  1. 先使用本地、低成本、可逆的压缩;
  2. 再对局部结果做裁剪;
  3. 再生成只读折叠视图;
  4. 最后才调用模型做全局摘要。

这比“一满就总结全部历史”更稳健,因为全局摘要会:

  • 增加一次模型延迟;
  • 消耗 Token;
  • 丢失细节;
  • 可能改变早期指令语义;
  • 造成摘要漂移。

7.5 Tool Search 与延迟加载#

官方 MCP 文档说明,Claude Code 默认支持 Tool Search:MCP 工具定义可以延迟加载,启动时只让模型看到工具名称,真正需要时再加载完整 Schema。13

Anthropic 的 Tool Use + Prompt Caching 文档进一步说明,延迟工具不会插入稳定的 System 前缀,而是在发现后以 tool_reference 追加到历史中,因此不会破坏已有前缀缓存。11

这是一个非常重要的可扩展性设计:

能力数量可以很大
每轮都把所有能力 Schema 塞给模型

8. 工具系统:能力、权限、并发和 UI 的统一协议#

Claude Code 的所有真实行动都应经过 Tool:

  • 文件读取;
  • 文件编辑;
  • Shell;
  • 搜索;
  • Web;
  • MCP;
  • Skills;
  • 子 Agent;
  • 用户询问;
  • 任务管理。

官方工具文档明确列出了内置工具,并允许通过 Permission Rules、Hooks、Subagents 和 MCP 对其进行控制和扩展。14

社区源码分析则把 Tool 抽象为一个包含多种行为语义的对象,而不是简单函数。15

工具系统:统一接口、执行流程与并发策略

8.1 一个生产级 Tool 应包含什么#

一个成熟工具接口至少应表达:

interface Tool<Input, Output> {
name: string
description: string
inputSchema: Schema<Input>
call(input: Input, context: ToolContext): Promise<Output>
isReadOnly(input: Input): boolean
isDestructive(input: Input): boolean
isConcurrencySafe(input: Input): boolean
validateInput(input: Input): ValidationResult
checkPermissions(input: Input, context: ToolContext): PermissionResult
maxResultSize: number
interruptBehavior(): InterruptPolicy
}

这意味着 Tool 同时是:

能力描述
+ 参数协议
+ 执行函数
+ 风险声明
+ 并发声明
+ 权限入口
+ 中断语义
+ 结果格式

8.2 为什么安全属性必须贴近工具#

如果风险属性放在外部配置:

{
"Edit": { "readOnly": false },
"Grep": { "readOnly": true }
}

工具实现变化后,配置可能没有同步。

而如果工具自己实现:

isReadOnly(input)
isDestructive(input)
isConcurrencySafe(input)

安全语义与具体输入和实现绑定,能够处理:

  • 同一个 Bash Tool 中既有只读命令,也有破坏性命令;
  • 同一个文件工具对不同路径风险不同;
  • 某些操作只在特定环境可并发;
  • 某些参数组合必须升级审批。

8.3 Fail-Closed:默认走保守路径#

社区仓库分析的一个优秀设计是:

  • 新工具默认不可并发;
  • 默认不视为只读;
  • 默认不能自动批准;
  • 只有显式声明后才放宽。

这是一种 Fail-Closed 设计:

未知能力 → 保守处理
而不是
未知能力 → 默认放行

在 Agent 工具生态快速扩展时,这一点非常关键。

8.4 流式工具预执行#

社区源码分析指出,Claude Code 可能在模型流式输出期间,一旦某个完整 tool_use 块被解析出来,就立即把它送入执行队列,而不是等待整个模型响应结束。4

时间线从:

[模型完整生成][工具1][工具2][工具3]

变成:

[模型持续生成................]
[工具1]
[工具2]
[工具3]

工具延迟可以被隐藏在模型生成窗口中。

8.5 并发必须由工具语义决定#

正确的并发规则不是:

所有 Tool Call 都丢进线程池

而是:

Read / Grep / Glob
→ 通常可并行
Edit / Write / Bash / Deploy
→ 默认串行或独占
具有依赖关系的命令
→ 失败时取消兄弟任务

工程上可以将工具状态建模为:

queued → executing → completed → yielded

并保证:

  • 并发安全工具可以共存;
  • 非并发安全工具独占执行;
  • 结果按原始 Tool Call 关联;
  • 中断和错误可以传递;
  • 最终回注顺序可控。

9. 安全:不是一个 Guardrail,而是纵深治理体系#

安全体系:七层纵深防御

Claude Code 在用户真实机器上运行 Shell,因此面临:

  • 模型误操作;
  • Prompt Injection;
  • 恶意仓库配置;
  • 路径越界;
  • 命令注入;
  • 凭据泄漏;
  • 远程系统副作用;
  • 子 Agent 权限洗白;
  • 自动化疲劳导致的盲目批准。

9.1 官方确认的安全主线#

官方文档确认了以下机制:

  • 默认只读;
  • 文件修改和 Shell 需要授权;
  • Allow / Ask / Deny 规则;
  • Deny 优先;
  • 多种 Permission Mode;
  • 受保护路径;
  • Plan Mode;
  • Auto Mode;
  • Sandboxed Bash;
  • 工作目录写入边界;
  • 企业 Managed Settings;
  • 用户可随时中断。61617

9.2 Permissions 与 Sandbox 的分工#

官方明确说明二者互补:67

Permission:
决定 Agent 是否被允许尝试某项动作
Sandbox:
即使动作已经执行,也限制进程实际能访问的文件和网络

例如:

Deny Rule 阻止 Bash 尝试读取 ~/.ssh
+
Sandbox 从 OS 层禁止进程访问 ~/.ssh

即使 Prompt Injection 绕过了模型的行为判断,操作系统边界仍然存在。

9.3 社区总结的七层纵深防御#

社区仓库把安全进一步归纳为:

  1. 工作区信任;
  2. 权限模式;
  3. Allow / Deny / Ask 规则;
  4. Bash 语法与静态风险分析;
  5. 工具级输入和路径检查;
  6. Sandbox 与 Worktree 隔离;
  7. 用户确认。18

其中,AST 分析和具体静态检查数量属于社区源码分析结论,不应视为官方长期稳定接口;但“多种不同技术共同防守”的思想值得复用。

9.4 为什么 Prompt 不能成为安全边界#

Prompt 可以告诉模型:

不要执行危险命令
修改前先读取文件
高风险操作先询问

但 Prompt 可能因为以下原因失效:

  • 模型理解错误;
  • 上下文污染;
  • Prompt Injection;
  • 长对话压缩;
  • 指令冲突;
  • 工具描述不清;
  • 新模型行为变化。

因此安全必须落在确定性层:

Prompt:降低危险请求发生概率
Schema:阻止畸形参数
Permission:决定是否授权
Hook:执行组织级策略
Sandbox:限制实际能力
HITL:对高风险动作最终裁决
Audit:记录发生了什么

9.5 Checkpoint 只覆盖可逆的本地变化#

官方说明 Claude Code 会在文件编辑前保存快照,支持本地回退;但数据库写入、部署、发送消息、Git Push 等远程副作用无法通过文件 Checkpoint 撤销。1

因此高风险工具必须额外具备:

  • 幂等键;
  • Dry Run;
  • 审批;
  • 前置条件;
  • 后置状态校验;
  • 补偿动作;
  • 审计日志。

10. Hooks:把必须执行的策略从模型判断中移出#

官方 Hooks 文档将 Hook 定义为在会话事件发生时自动执行的 Shell、HTTP、Prompt、MCP Tool 或 Agent 处理器。它们可以:19

  • 在工具执行前阻断危险动作;
  • 修改输入;
  • 记录审计日志;
  • 在编辑后运行格式化;
  • 在停止前执行验证;
  • 发送通知;
  • 要求人工审批;
  • 管理会话资源。

10.1 Skill 与 Hook 的根本区别#

Skill:
把一套知识或流程交给模型使用
模型仍然决定如何执行
Hook:
当事件匹配时确定性触发
不依赖模型记得执行

例如:

“修改后最好运行 ESLint”
→ 写在 Skill 中,属于软指导
“每次 Edit 后必须运行 ESLint”
→ 使用 PostToolUse Hook,属于硬流程

10.2 Hook 是 Agent Runtime 的中间件#

可以把 Hook 看成:

Agent Middleware / Interceptor

它适合实现:

  • 安全策略;
  • 审计;
  • 输入清洗;
  • 组织级合规;
  • 工具结果后处理;
  • 生命周期通知;
  • 自定义审批。

但 Hook 本身也可能执行代码,因此:

  • 项目级 Hook 必须受工作区信任保护;
  • Hook 超时不能拖死主流程;
  • Hook 输出要经过 Schema 校验;
  • Hook 失败要区分阻断错误和非阻断错误;
  • Hook 也要被观测和审计。

11. Memory:区分常驻指令、自动记忆和当前状态#

记忆系统:只记不可推导的信息

官方当前文档区分两类跨会话知识:12

机制谁写适合存什么加载方式
CLAUDE.md用户或团队编码规范、工作流、项目架构、长期规则每次会话加载
Auto MemoryClaude发现的构建命令、调试经验、偏好和模式索引常驻,主题文件按需读取

11.1 State 与 Memory 不是一回事#

State:
当前任务正在发生什么
Memory:
未来会话可能复用什么

例如:

当前测试失败 3 个
→ State / Transcript
用户偏好所有 API 使用 Result 类型
→ CLAUDE.md 或 Memory
当前正在编辑 auth.ts
→ State
项目测试命令为 pnpm test
→ CLAUDE.md 或 Auto Memory

当前任务状态必须来自权威 Runtime,不能依赖模型“记住”。

11.2 MEMORY.md 是索引,不是数据库正文#

官方文档说明,Auto Memory 目录包含:

MEMORY.md
debugging.md
api-conventions.md
...

每次会话只加载 MEMORY.md 的前 200 行或 25KB,详细主题文件按需读取。12

这体现了两级结构:

常驻索引
→ 告诉模型有哪些记忆
主题文件
→ 真正需要时再加载

它本质上是在管理:

召回能力
上下文成本
之间的平衡

11.3 “只记不可推导信息”应作为设计原则,而非绝对事实#

社区仓库提出一个很有价值的原则:

尽量只保存不能从代码、Git 和文档重新推导的信息。20

这个原则可以防止记忆漂移。例如:

“认证模块位于 src/auth”

代码重构后可能过期,重新搜索代码更可靠。

不过官方 Auto Memory 示例包含构建命令、架构笔记和调试经验,其中一部分理论上也能从项目中重新推导。因此,更准确的工程建议是:

权威事实优先实时读取;
只有在重新推导成本高、语义隐含或来自用户反馈时,才进入长期记忆。

11.4 记忆写入必须有 Policy#

企业 Agent 不应让模型随意写长期记忆。需要判断:

  • 是否稳定;
  • 是否有未来价值;
  • 是否包含敏感信息;
  • 是否能从权威源重新获取;
  • 是否需要用户确认;
  • 是否有时效;
  • 是否与旧记忆冲突;
  • 是否需要记录来源和时间。

推荐结构:

type: feedback
source: user_explicit
created_at: 2026-07-24
confidence: high
expires_at: null

12. Skills:按需加载的行为包与工作流模板#

官方将 Skill 定义为一个以 SKILL.md 为核心的能力包,可以包含:

  • 描述;
  • 使用条件;
  • Prompt;
  • 工具白名单;
  • Supporting Files;
  • 动态上下文;
  • 子 Agent 执行配置;
  • 调用控制。21

12.1 Skill 适合解决什么#

当某段 CLAUDE.md 逐渐变成:

  • 多步骤流程;
  • 检查清单;
  • 大型参考文档;
  • 特定任务模板;
  • 可重复执行的操作说明;

就应该移入 Skill。

因为:

CLAUDE.md:每轮都加载
Skill:只在需要时加载

12.2 Skill 的渐进披露#

官方文档说明,模型可调用 Skill 时,启动阶段通常只加载名称和描述,完整正文在 Skill 被使用时才进入上下文。219

这是一种 Progressive Disclosure:

先暴露能力目录
→ 模型发现匹配能力
→ 再加载完整操作手册

它让系统能拥有大量技能,而不让每轮上下文爆炸。

12.3 Description 就是语义路由接口#

模型是否自动选择 Skill,主要依赖:

  • 用户请求;
  • 当前上下文;
  • Skill 的名称和描述。

因此 Description 不是普通文案,而是能力路由的核心协议。

好的描述应包含:

什么时候使用
什么时候不要使用
输入需要什么
会产生什么结果
有何风险或限制

12.4 Skill 不是强制策略#

Skill 进入模型上下文后,仍属于模型可解释的软指令。

如果流程必须每次执行,应使用 Hook、Workflow 或代码状态机,而不是依赖 Skill。


13. MCP:统一外部能力,但不能跳过 Harness 治理#

官方将 MCP 定义为连接 AI 应用与工具、数据源和服务的开放协议。Claude Code 可以通过 MCP 接入数据库、Issue Tracker、监控系统、设计平台和内部 API。13

13.1 MCP 解决的是能力互操作,不是自动安全#

MCP 提供:

  • 工具发现;
  • Schema;
  • Resources;
  • Prompts;
  • Transport;
  • OAuth;
  • 动态能力更新。

但 MCP 工具仍必须进入 Claude Code 的:

Tool Search
→ Tool Registry
→ Permission
→ Hook
→ Execution
→ Result Processing

外部工具不能因为是 MCP 就绕过安全层。

13.2 MCP Tool Search 的架构价值#

当工具数量从 10 个增长到 1000 个时,直接加载所有 Schema 会带来:

  • Context 爆炸;
  • Prompt Cache 失效;
  • 工具选择准确率下降;
  • 首次请求延迟升高;
  • 无关能力干扰。

官方 Tool Search 采用:

启动只加载名称
→ 需要时搜索
→ 按需加载完整工具定义

这使 Capability Registry 可以扩展,而不把复杂度全部转嫁给模型上下文。13

13.3 大结果必须外部化#

MCP、Shell、搜索和数据库查询都可能返回巨大结果。

成熟 Harness 应支持:

小结果 → 直接回注
中结果 → 截断 + 摘要
大结果 → 落盘 / 对象存储 + 返回引用

模型需要全文时再分块读取,而不是把 100K 字符一次塞入上下文。


14. 多 Agent:核心不是角色数量,而是隔离与治理#

官方 Subagent 文档强调,每个 Subagent 都拥有:22

  • 独立上下文窗口;
  • 自定义 System Prompt;
  • 特定工具权限;
  • 独立 Permission;
  • 可选择不同模型;
  • 完成后只向主会话返回摘要。

多 Agent 架构:隔离是核心

14.1 为什么 Subagent 要独立上下文#

一个搜索型子任务可能产生:

  • 数十个文件内容;
  • 大量日志;
  • 中间假设;
  • 无关搜索结果。

如果全部进入主上下文,会造成:

  • 主任务被噪声污染;
  • Token 费用上升;
  • 压缩提前触发;
  • 关键指令被稀释;
  • 后续推理注意力分散。

Subagent 用自己的上下文处理这些材料,只返回结论,相当于一种“语义 Map-Reduce”。

14.2 工具隔离比角色 Prompt 更重要#

只给角色名字:

你是安全专家

并不能构成真正隔离。

更重要的是:

Explore Agent
→ 只读工具
→ 快速模型
→ 独立上下文
Plan Agent
→ 只读工具
→ 强推理模型
→ 结构化计划
Implementation Agent
→ Edit / Bash
→ 更严格 Permission
→ Worktree 隔离

这体现最小权限原则。

14.3 文件系统隔离#

并行 Agent 如果共享同一工作区,可能同时修改同一文件。

可选策略:

  • 文件所有权分区;
  • 乐观锁;
  • Patch 合并;
  • Git Worktree;
  • 独立容器;
  • 最终 Integrator 统一合并。

官方文档把 Worktree 作为并行会话隔离方式;社区仓库也将 Worktree 视为多 Agent 避免代码冲突的重要手段。2324

14.4 从模型编排到确定性编排#

少量任务可以让模型动态决定:

派谁
何时派
如何汇总

但规模扩大后会遇到:

  • 重复派发;
  • 并发失控;
  • 等待屏障不合理;
  • 中间格式不稳定;
  • 无法断点续跑;
  • 成本不可预测。

社区仓库对 Dynamic Workflow 的黑盒分析提出一种值得关注的模式:由主模型生成确定性脚本,再由 Runtime 执行 agent()pipeline()parallel() 和 Schema 校验。25

该结论属于黑盒逆向,不应视为官方稳定接口,但它揭示了一条普遍规律:

开放探索留给 Agent
大规模编排交给确定性 Runtime

推荐演进路径:

小任务:单 Agent
独立副任务:Subagent
多 Worker 并行:Coordinator / Team
大批量任务:Deterministic Workflow + Agent Workers

15. 可观测性:把一次 Agent 任务做成 EXPLAIN#

可观测性:四层模型

官方 Claude Code 监控文档支持通过 OpenTelemetry 导出:

  • Metrics;
  • Events / Logs;
  • 可选 Traces;
  • 工具、权限、Hook 和 API 活动。26

同时,官方说明本地会话会保存为 JSONL Transcript,用于恢复、回放和 Fork。1

15.1 四种记录回答不同问题#

Metrics:总账#

回答:

  • Token 用了多少;
  • 成本是多少;
  • 有多少会话;
  • 修改了多少行;
  • 创建了多少 Commit 和 PR。

Metrics 必须控制标签基数,不能把每个 Prompt ID 都作为维度。

Events:离散行为#

回答:

  • 哪个工具被调用;
  • 是否成功;
  • Permission 是允许还是拒绝;
  • Hook 是否阻断;
  • MCP 是否连接失败;
  • API 最终是否重试耗尽。

Trace:因果链#

回答:

  • 哪个模型调用最慢;
  • 工具执行耗时多少;
  • 等待用户权限用了多久;
  • 子 Agent 属于哪个父任务;
  • 一次任务的调用树是什么。

Transcript:本地事实记录#

回答:

  • 会话发生过哪些消息;
  • Tool Use 和 Tool Result 是什么;
  • 如何 Resume;
  • 从哪个节点 Fork;
  • 压缩边界在哪里。

15.2 ID、树和聚合不能混为一谈#

社区仓库对观测系统的一个优秀总结是:

离散事件用 ID 关联
因果关系用 Span 树表达
聚合指标拒绝高基数 ID

这适用于任何 AgentOps 系统。

15.3 默认保护隐私#

官方 OTel 文档说明:

  • 用户 Prompt 默认不记录;
  • Tool 参数默认不记录;
  • Tool 内容默认不记录;
  • 原始 API Body 默认不记录;
  • 开启详细日志时可能暴露敏感数据,需要额外脱敏和后端治理。26

因此可观测性不是“记录越多越好”,而是:

可诊断性
×
隐私
×
成本
×
基数

之间的平衡。


16. 性能优化:为什么 Claude Code 给人“很快”的感觉#

用户感知速度不只由模型 Token/s 决定。

可以拆为:

首次可见反馈时间
+ 模型首 Token
+ 工具等待
+ 上下文预处理
+ UI 渲染
+ 用户权限等待

Claude Code 的 Harness 通过多种方式隐藏延迟。

16.1 全链路流式事件#

模型开始生成后立即输出,不等待完整响应。

16.2 工具流式预执行#

社区源码分析认为,完整工具块一旦解析完即可开始执行,将工具延迟覆盖在模型生成时间中。4

16.3 记忆与能力预取#

与模型调用并行:

  • Memory 召回;
  • Skill 发现;
  • MCP 连接;
  • 工具准备;
  • UI 初始化。

16.4 Prompt Cache#

稳定的 Tool 和 System 前缀可以复用缓存,减少重复输入处理。官方 Prompt Caching 文档确认,缓存命中可以降低延迟、费用并改善速率限制利用率。10

16.5 延迟加载#

不常用的:

  • MCP 工具;
  • Skill 正文;
  • 观测 Exporter;
  • 子 Agent 上下文;
  • 大型参考资料;

不进入冷启动关键路径。

16.6 速度的真正公式#

感知速度
尽早反馈
+ 并行隐藏等待
+ 缓存稳定前缀
+ 减少无效上下文
+ 限制串行关键路径

17. 可靠性:预算、恢复与终止条件#

开放式 Agent 最大风险之一是“不知道什么时候停”。

一个生产 Harness 至少要限制:

{
"max_turns": 30,
"max_tool_calls": 50,
"max_parallel_tools": 8,
"max_subagents": 10,
"max_cost_usd": 5,
"max_duration_seconds": 1800,
"max_context_compactions": 3
}

官方 CLI 和 Agent SDK 提供最大轮数、权限模式、工具列表和成本控制等接口。2728

17.1 恢复不能制造重复副作用#

如果模型调用后网络中断,不能简单重放所有工具。

副作用工具需要:

  • Idempotency Key;
  • Execution ID;
  • 状态查询;
  • Exactly-Once 的业务近似;
  • 可补偿操作;
  • Tool Result 持久化。

17.2 终止不是只看“模型不再调用工具”#

还应该检查:

  • 任务目标是否达成;
  • 测试是否通过;
  • 必要工具是否执行;
  • 是否有未处理错误;
  • 是否等待人工;
  • 是否达到预算;
  • 是否重复循环;
  • 是否出现上下文 Thrashing。

17.3 Verifier 优先使用确定性信号#

验证顺序建议:

测试 / 编译 / 数据库状态
>
静态规则 / Schema
>
专用判别器
>
通用 LLM 自我反思

只要存在可执行标准,就不应让模型仅凭文本判断“完成了”。


18. Claude Code Harness 的关键设计原则#

原则一:模型负责语义决策,代码负责硬约束#

模型决定下一步应该做什么;
Runtime 决定是否允许、如何执行、如何恢复。

原则二:所有副作用必须经过 Tool#

模型不能绕过能力接口直接操作真实世界。

原则三:上下文是编译产物#

每轮都要选择、排序、压缩、隔离和缓存,而不是机械拼接历史。

原则四:工具应携带风险与并发语义#

isReadOnlyisDestructiveisConcurrencySafe 应是一等属性。

原则五:默认保守#

未知工具、未知路径、未知副作用和未知权限应进入更严格流程。

原则六:错误是状态转移#

上下文超限、Token 截断、权限拒绝和工具失败不能走同一重试逻辑。

原则七:记忆不能替代权威数据源#

代码读代码,Git 读 Git,业务状态查业务系统。

原则八:多 Agent 优先做隔离#

独立上下文、最小工具、文件隔离、结构化结果比角色命名更重要。

原则九:规模越大,编排越应确定性化#

小范围探索可由模型控制,大规模 fan-out 应由 Workflow Runtime 管理。

原则十:观测系统必须与隐私和成本共同设计#

不是“全量记录一切”,而是按 Metrics、Events、Trace 和 Transcript 分层。


19. 从 Claude Code 抽象出的企业级 Harness 参考架构#

Client / API / IDE / Chat
Session Gateway
- auth / tenant / trace_id
- input normalization
- stream protocol
Context Compiler
- system policy
- conversation state
- memory retrieval
- RAG evidence
- capability discovery
- token budget
Agent Runtime
- planner / router
- model loop
- transition state machine
- max steps / cost / time
Tool Admission
- registry
- schema
- permission
- policy
- HITL
Tool Executor
- concurrency semantics
- timeout / retry
- idempotency
- sandbox
- result normalization
Verifier
- postcondition
- business state check
- tests / rules
State / Checkpoint / Transcript
Metrics / Events / Trace / Eval

19.1 哪些层可以交给框架#

LangGraph、Temporal、Agent SDK 等框架可以帮助实现:

  • 状态机;
  • Checkpoint;
  • 中断恢复;
  • 节点执行;
  • Streaming;
  • 工具循环;
  • 子图;
  • Trace。

19.2 哪些问题框架不会替你解决#

框架无法自动决定:

  • 哪些工具是危险的;
  • 哪些字段允许并发写;
  • 哪些结果是权威事实;
  • 哪些动作必须人工审批;
  • 业务完成条件是什么;
  • 哪些记忆应该持久化;
  • 什么错误可以安全重试;
  • 多 Agent 是否真的比单 Agent 更好。

Harness 的核心仍然是业务和系统设计。


20. 不应该照搬 Claude Code 的地方#

Claude Code 是 Coding Agent,其工作负载有特殊性:

  • 文件系统是主要工作空间;
  • Git 提供天然版本控制;
  • 测试和编译提供明确 Verifier;
  • Shell 是通用能力入口;
  • Worktree 可用于隔离;
  • 大多数操作发生在本地环境。

企业业务 Agent 可能完全不同:

Coding Agent企业业务 Agent
文件编辑可本地回滚订单、支付、消息可能不可逆
Git 是权威历史数据库和审计系统是权威历史
测试是主要验证器业务规则和状态查询是验证器
Worktree 隔离代码租户、权限和事务隔离业务数据
Shell 是通用工具工具通常是受控业务 API
用户是开发者用户可能不理解技术风险

因此不能简单复制:

开放 Bash
宽松自动审批
模型自主决定所有步骤
本地 Transcript 作为唯一状态

应该抽取其原则,而不是复制具体实现。


21. 结论#

Claude Code 的价值不只是“Claude 模型会写代码”,而是它构建了一套围绕模型的生产级执行系统。

可以用下面的公式概括:

Claude Code
=
LLM 决策循环
× 上下文编译器
× 统一工具协议
× 流式执行引擎
× 权限与沙箱
× 恢复状态机
× 记忆与技能
× 隔离式多 Agent
× 可观测运行时

真正决定系统质量的边界是:

模型提出动作
Harness 管理后果

模型能力升级会提高推理和生成上限,但如果 Harness 缺少:

  • 上下文治理;
  • 工具 Schema;
  • 权限;
  • 并发控制;
  • 状态持久化;
  • 终止预算;
  • 错误恢复;
  • Verifier;
  • 观测与评估;

Agent 仍然只能停留在 Demo。

Claude Code 给 Agent 工程最重要的启示不是“如何写一个更长的 Prompt”,而是:

把模型的不确定智能放进一个确定、可控、可恢复、可审计的运行环境中。


参考资料#

Anthropic / Claude 官方资料#

社区源码分析与黑盒研究#

Footnotes#

  1. How Claude Code works 2 3 4 5 6 7 8 9

  2. Windy3f3f3f3f/how-claude-code-works 2

  3. Claude Code overview

  4. 第 2 章:系统主循环 2 3 4 5

  5. Extend Claude Code

  6. Configure permissions 2 3

  7. Sandboxing 2

  8. 第 1 章:Claude Code 概述

  9. Explore the context window 2

  10. Prompt caching 2

  11. Tool use with prompt caching 2

  12. How Claude remembers your project 2 3

  13. Connect Claude Code to tools via MCP 2 3

  14. Tools reference

  15. 第 4 章:工具系统

  16. Choose a permission mode

  17. Security

  18. 权限与安全

  19. Hooks reference

  20. 记忆系统

  21. Extend Claude with skills 2

  22. Create custom subagents

  23. Run agents in parallel

  24. 多 Agent 架构

  25. Dynamic Workflows

  26. Monitoring Claude Code with OpenTelemetry 2

  27. Claude Code CLI reference

  28. How the Agent SDK loop works

Claude Code 中的 Harness:从受控工具循环到生产级 Agent 运行时
https://jupiter-ws.cn/posts/ai-coding/claude-code-harness-deep-dive/
作者
Jupiter
发布于
2026-07-24
许可协议
CC BY-NC-SA 4.0