Claude Code 背后的工程哲学——读 Agent Harness Engineering
目录
Addy Osmani 的 "Agent Harness Engineering"提出了一个核心等式:Agent = Model + Harness。模型是能力,套件(harness)是让能力真正落地的脚手架。一个还行的模型配上优秀的套件,能打败一个优秀的模型配上糟糕的套件。
Claude Code 就是这个等式的活例子。底层跑的是 Claude 模型,但你每天感受到的行为——文件隔离、权限拦截、自动压缩上下文、子智能体防火墙——全是套件的功劳,不是模型的能力。Addy 的文章用套件工程的框架拆解了智能体设计的方方面面,而 Claude Code 几乎是每个概念的最佳注脚。
本文从 Claude Code 用户的视角重读 Addy 的文章,看看套件工程的每个概念在 Claude Code 里是怎么落地的,以及你能调整什么。
Claude Code 的套件架构长什么样
Fareed Khan 做过一张 Claude Code 架构图,把文章提到的几乎每个概念都对应到了一个具体组件:
- 上下文注入 (知识层)→ CLAUDE.md、AGENTS.md、skill 文件在每轮对话中自动加载
- 循环状态 (记忆层)→ 上下文压缩(compaction)、会话续接(continuation)
- 破坏性操作拦截 (权限层)→ permission gate,
rm -rf、git push --force这些命令需要你确认 - 子智能体隔离 (多智能体层)→ 子智能体上下文防火墙,每个子智能体只看到自己该看的
- 工具分发 (工具层)→ MCP 服务器和 bash 都注册在工具分发器里
Claude Code 之所以比"在网页上用 Claude"好用这么多,不是因为网页版用的模型差,而是因为 Claude Code 为编码场景做了大量套件优化。模型是同一个,套件完全不同。
你能调整的三个套件组件
套件工程的核心洞察是:套件不是黑箱,它是你可以调的配置面。Claude Code 让你直接动手的有三样东西。
CLAUDE.md / AGENTS.md
这是套件中投入产出比最高的配置点——它的内容出现在每轮对话的系统提示词里。放什么?项目的约定:用什么包管理器、测试框架是什么、格式化规则、"不要碰 legacy 目录"、"统一用我们的 logger"。
两条设计原则:
- 保持简短 。HumanLayer 把他们的控制在 60 行以内。每条规则都在争夺模型的注意力,规则越多,每条规则的分量越轻。飞行员检查单,不是风格指南。
- 每条规则都要"挣"来的 。规则应该能追溯到一次具体的失败。如果你说不出"这条规则是因为那次 agent 干了什么蠢事才加的",那这条规则可能就是噪声。
Hooks
Hook 是在特定生命周期点运行的脚本——工具调用前、文件编辑后、提交前、会话开始时。它把"我告诉 agent 要做 X"变成了"系统保证执行 X"。
实用场景:每次编辑后自动运行类型检查和测试,失败了就把错误信息送回循环让 agent 自修正;拦截破坏性命令;提交前检查是否包含敏感文件。
核心原则: 成功时静默,失败时详细 。类型检查通过了,agent 什么也听不到,不浪费注意力。失败了,错误文本直接注入循环。这让反馈循环在正常情况下几乎零成本。
Skills / MCP 服务器
工具描述本身就是提示词的一部分——模型每次请求都要读一遍所有工具的名称、描述和参数 schema。十个专注的工具胜过五十个功能重叠的。所以你安装的每一个 MCP 服务器都要经过审查:它提供的工具是不是真的必要?它的描述会不会污染上下文?
还有一个安全维度:工具描述是模型会读的受信文本。一个粗劣的 MCP 服务器可以在你输入任何内容之前就执行提示词注入。安装第三方 MCP 时要像安装 npm 包一样谨慎。
棘轮思维:把每次犯错变成永久规则
套件工程中最重要的习惯:agent 犯了一次错,就加一条规则让它永远不再犯同样的错。不是"下次注意",而是系统性地堵住漏洞。
Claude Code 用户的实操版本:
- agent 提交了一个注释掉的测试 → CLAUDE.md 加一条"永远不要注释掉测试,要么删要么修"
- agent 多次在同一类问题上犯错 → 用 hook 做自动化检查
- agent 总是忘记某个项目约定 → 写进 AGENTS.md,每轮对话都能看到
棘轮只往一个方向转:只在真正出过错的地方加规则,只在更强的模型证明某条规则多余时才删掉。AGENTS.md 里的每一行都应该能追溯到一次具体的失败。
这也是为什么别人的 AGENTS.md 你不能直接抄——适合你的套件是由你的失败历史塑造的。
上下文管理:Claude Code 的隐形工程
上下文腐败(context rot)是所有 agent 的隐形杀手:随着对话越来越长,模型的推理质量会下降。Claude Code 内建了几种对抗手段:
- 自动压缩 :当上下文窗口接近上限时,自动总结旧对话,保留关键信息,释放空间让 agent 继续工作
- 工具输出截断 :大型工具输出不会完整塞进上下文,只保留头尾,完整内容存在文件系统里按需读取
- 渐进式披露 :skill 文件不是一次性全加载,而是根据任务需要才展开相关部分
Anthropic 还提到了一种更激进的手段:对于特别长的任务,彻底重置会话,从一个紧凑的交接文件重建。压缩不够的时候,你需要带着一份结构化摘要从头开始。这更像是给新工程师做交接,而不是我们通常理解的"记忆"。
对你来说,最直接的实操建议是:遇到需要"切换思路"的时刻,开一个新 session 比在当前 session 里堆指令更有效。新 session = 干净的上下文 = 更高的有效智能。
长程任务的设计模式
让 agent 自主完成长任务是最难做对的事情。文章提到了几个模式,其中两个在 Claude Code 中特别有实操价值。
规划-生成-评估分离
把生成和评估交给不同的上下文。Claude Code 的子智能体天然支持这个模式——主智能体写代码,审查子智能体用纯净上下文审查。为什么纯净上下文反而更有效?因为编码智能体工作了几小时后,上下文又长又乱;审查智能体只看 diff,从头读代码,自己重新发现需要的信息。上下文越短,有效智能越高。
实操:写完代码后,开一个新 session 来审查。新 session 不需要知道你之前做了什么,只需要看 diff 或最终代码。
Sprint Contract
在开始之前写下"什么叫完成"。Addy Osmani 的经验是:写下完成条件比任何提示词调整都能抓到更多的范围蔓延。比如"这个重构的完成条件是:所有测试通过,没有新增的 eslint 警告, git diff --stat 显示只改动了预期的文件"。
模型升级了,你的套件该不该跟着变
Anthropic 的一个关键观察:套件不会随着模型变强而缩小,只会转移。
Opus 4.6 基本消灭了"上下文焦虑"——以前的 Sonnet 4.5 会在接近上下文极限时匆忙收工,为此写的各种缓解脚本现在都是死代码。但天花板跟着模型一起提高了:以前做不到的任务现在能做了,这些新任务有自己的失败模式。
对你的 AGENTS.md 来说,这意味着:定期审计。如果某条规则是因为一个模型缺陷而加的(比如"不要一次修改超过三个文件"),而新模型已经不需要这个限制了,就该删掉。规则只应该在它解决一个真实问题时存在。
还有一个隐含的陷阱:模型和套件之间存在训练耦合。Claude 模型在后训练阶段专门针对 Claude Code 的套件做过优化,所以同一个模型在 Claude Code 里和在别的套件里表现不同。这也是为什么换了模型版本之后,有时需要微调 AGENTS.md——模型变了,之前的"补丁"可能不再需要,也可能需要新的。
一个行动框架
读完这篇文章,如果你是一个 Claude Code 用户,接下来可以做的事:
- 审计你的 CLAUDE.md :每条规则能不能追溯到一次具体的失败?追溯不到的考虑删除
- 加一个 hook :找一个 agent 反复出错的场景,写一个 hook 自动化检查。成功时静默,失败时详细
- 审查你的 MCP 列表 :每个 MCP 服务器提供的工具是否必要?描述是否精确?
- 建立棘轮习惯 :下次 agent 犯错时,不要只点"重试"——花 30 秒加一条规则
套件工程的核心思想用一句话总结:不要等下一个模型来解决问题,用你手里的配置面把当前模型的能力释放出来。Viv Trivedy(Terminal Bench 2.0 排名 Top 5 的套件设计者)的话作为结尾很合适:"好的 agent 构建是一个迭代练习。如果你没有 v0.1,就没法做迭代。"