|
| 1 | +#+TITLE: Claude Code 背后的工程哲学——读 Agent Harness Engineering |
| 2 | +#+AUTHOR: lujun9972,Claude Code |
| 3 | +#+TAGS: AI, Claude Code, Harness Engineering, agent |
| 4 | +#+DATE: [2026-04-27 Sun] |
| 5 | +#+LANGUAGE: zh-CN |
| 6 | +#+OPTIONS: H:6 num:nil toc:t \n:nil ::t |:t ^:nil -:nil f:t *:t <:nil |
| 7 | + |
| 8 | +[[https://addyosmani.com/blog/agent-harness-engineering/][Addy Osmani 的 "Agent Harness Engineering"]]提出了一个核心等式:Agent = Model + Harness。模型是能力,套件(harness)是让能力真正落地的脚手架。一个还行的模型配上优秀的套件,能打败一个优秀的模型配上糟糕的套件。 |
| 9 | + |
| 10 | +Claude Code 就是这个等式的活例子。底层跑的是 Claude 模型,但你每天感受到的行为——文件隔离、权限拦截、自动压缩上下文、子智能体防火墙——全是套件的功劳,不是模型的能力。Addy 的文章用套件工程的框架拆解了智能体设计的方方面面,而 Claude Code 几乎是每个概念的最佳注脚。 |
| 11 | + |
| 12 | +本文从 Claude Code 用户的视角重读 Addy 的文章,看看套件工程的每个概念在 Claude Code 里是怎么落地的,以及你能调整什么。 |
| 13 | + |
| 14 | +* Claude Code 的套件架构长什么样 |
| 15 | + |
| 16 | +Fareed Khan 做过一张 [[https://fareedkhan.com/2025/01/12/breaking-down-claude-code/][Claude Code 架构图]],把文章提到的几乎每个概念都对应到了一个具体组件: |
| 17 | + |
| 18 | +- *上下文注入* (知识层)→ CLAUDE.md、AGENTS.md、skill 文件在每轮对话中自动加载 |
| 19 | +- *循环状态* (记忆层)→ 上下文压缩(compaction)、会话续接(continuation) |
| 20 | +- *破坏性操作拦截* (权限层)→ permission gate, =rm -rf= 、 =git push --force= 这些命令需要你确认 |
| 21 | +- *子智能体隔离* (多智能体层)→ 子智能体上下文防火墙,每个子智能体只看到自己该看的 |
| 22 | +- *工具分发* (工具层)→ MCP 服务器和 bash 都注册在工具分发器里 |
| 23 | + |
| 24 | +Claude Code 之所以比"在网页上用 Claude"好用这么多,不是因为网页版用的模型差,而是因为 Claude Code 为编码场景做了大量套件优化。模型是同一个,套件完全不同。 |
| 25 | + |
| 26 | +* 你能调整的三个套件组件 |
| 27 | + |
| 28 | +套件工程的核心洞察是:套件不是黑箱,它是你可以调的配置面。Claude Code 让你直接动手的有三样东西。 |
| 29 | + |
| 30 | +** CLAUDE.md / AGENTS.md |
| 31 | + |
| 32 | +这是套件中投入产出比最高的配置点——它的内容出现在每轮对话的系统提示词里。放什么?项目的约定:用什么包管理器、测试框架是什么、格式化规则、"不要碰 legacy 目录"、"统一用我们的 logger"。 |
| 33 | + |
| 34 | +两条设计原则: |
| 35 | + |
| 36 | +1. *保持简短* 。HumanLayer 把他们的控制在 60 行以内。每条规则都在争夺模型的注意力,规则越多,每条规则的分量越轻。飞行员检查单,不是风格指南。 |
| 37 | +2. *每条规则都要"挣"来的* 。规则应该能追溯到一次具体的失败。如果你说不出"这条规则是因为那次 agent 干了什么蠢事才加的",那这条规则可能就是噪声。 |
| 38 | + |
| 39 | +** Hooks |
| 40 | + |
| 41 | +Hook 是在特定生命周期点运行的脚本——工具调用前、文件编辑后、提交前、会话开始时。它把"我告诉 agent 要做 X"变成了"系统保证执行 X"。 |
| 42 | + |
| 43 | +实用场景:每次编辑后自动运行类型检查和测试,失败了就把错误信息送回循环让 agent 自修正;拦截破坏性命令;提交前检查是否包含敏感文件。 |
| 44 | + |
| 45 | +核心原则: *成功时静默,失败时详细* 。类型检查通过了,agent 什么也听不到,不浪费注意力。失败了,错误文本直接注入循环。这让反馈循环在正常情况下几乎零成本。 |
| 46 | + |
| 47 | +** Skills / MCP 服务器 |
| 48 | + |
| 49 | +工具描述本身就是提示词的一部分——模型每次请求都要读一遍所有工具的名称、描述和参数 schema。十个专注的工具胜过五十个功能重叠的。所以你安装的每一个 MCP 服务器都要经过审查:它提供的工具是不是真的必要?它的描述会不会污染上下文? |
| 50 | + |
| 51 | +还有一个安全维度:工具描述是模型会读的受信文本。一个粗劣的 MCP 服务器可以在你输入任何内容之前就执行提示词注入。安装第三方 MCP 时要像安装 npm 包一样谨慎。 |
| 52 | + |
| 53 | +* 棘轮思维:把每次犯错变成永久规则 |
| 54 | + |
| 55 | +套件工程中最重要的习惯:agent 犯了一次错,就加一条规则让它永远不再犯同样的错。不是"下次注意",而是系统性地堵住漏洞。 |
| 56 | + |
| 57 | +Claude Code 用户的实操版本: |
| 58 | + |
| 59 | +1. agent 提交了一个注释掉的测试 → CLAUDE.md 加一条"永远不要注释掉测试,要么删要么修" |
| 60 | +2. agent 多次在同一类问题上犯错 → 用 hook 做自动化检查 |
| 61 | +3. agent 总是忘记某个项目约定 → 写进 AGENTS.md,每轮对话都能看到 |
| 62 | + |
| 63 | +棘轮只往一个方向转:只在真正出过错的地方加规则,只在更强的模型证明某条规则多余时才删掉。AGENTS.md 里的每一行都应该能追溯到一次具体的失败。 |
| 64 | + |
| 65 | +这也是为什么别人的 AGENTS.md 你不能直接抄——适合你的套件是由你的失败历史塑造的。 |
| 66 | + |
| 67 | +* 上下文管理:Claude Code 的隐形工程 |
| 68 | + |
| 69 | +上下文腐败(context rot)是所有 agent 的隐形杀手:随着对话越来越长,模型的推理质量会下降。Claude Code 内建了几种对抗手段: |
| 70 | + |
| 71 | +- *自动压缩* :当上下文窗口接近上限时,自动总结旧对话,保留关键信息,释放空间让 agent 继续工作 |
| 72 | +- *工具输出截断* :大型工具输出不会完整塞进上下文,只保留头尾,完整内容存在文件系统里按需读取 |
| 73 | +- *渐进式披露* :skill 文件不是一次性全加载,而是根据任务需要才展开相关部分 |
| 74 | + |
| 75 | +Anthropic 还提到了一种更激进的手段:对于特别长的任务,彻底重置会话,从一个紧凑的交接文件重建。压缩不够的时候,你需要带着一份结构化摘要从头开始。这更像是给新工程师做交接,而不是我们通常理解的"记忆"。 |
| 76 | + |
| 77 | +对你来说,最直接的实操建议是:遇到需要"切换思路"的时刻,开一个新 session 比在当前 session 里堆指令更有效。新 session = 干净的上下文 = 更高的有效智能。 |
| 78 | + |
| 79 | +* 长程任务的设计模式 |
| 80 | + |
| 81 | +让 agent 自主完成长任务是最难做对的事情。文章提到了几个模式,其中两个在 Claude Code 中特别有实操价值。 |
| 82 | + |
| 83 | +** 规划-生成-评估分离 |
| 84 | + |
| 85 | +把生成和评估交给不同的上下文。Claude Code 的子智能体天然支持这个模式——主智能体写代码,审查子智能体用纯净上下文审查。为什么纯净上下文反而更有效?因为编码智能体工作了几小时后,上下文又长又乱;审查智能体只看 diff,从头读代码,自己重新发现需要的信息。上下文越短,有效智能越高。 |
| 86 | + |
| 87 | +实操:写完代码后,开一个新 session 来审查。新 session 不需要知道你之前做了什么,只需要看 diff 或最终代码。 |
| 88 | + |
| 89 | +** Sprint Contract |
| 90 | + |
| 91 | +在开始之前写下"什么叫完成"。Addy Osmani 的经验是:写下完成条件比任何提示词调整都能抓到更多的范围蔓延。比如"这个重构的完成条件是:所有测试通过,没有新增的 eslint 警告, =git diff --stat= 显示只改动了预期的文件"。 |
| 92 | + |
| 93 | +* 模型升级了,你的套件该不该跟着变 |
| 94 | + |
| 95 | +Anthropic 的一个关键观察:套件不会随着模型变强而缩小,只会转移。 |
| 96 | + |
| 97 | +Opus 4.6 基本消灭了"上下文焦虑"——以前的 Sonnet 4.5 会在接近上下文极限时匆忙收工,为此写的各种缓解脚本现在都是死代码。但天花板跟着模型一起提高了:以前做不到的任务现在能做了,这些新任务有自己的失败模式。 |
| 98 | + |
| 99 | +对你的 AGENTS.md 来说,这意味着:定期审计。如果某条规则是因为一个模型缺陷而加的(比如"不要一次修改超过三个文件"),而新模型已经不需要这个限制了,就该删掉。规则只应该在它解决一个真实问题时存在。 |
| 100 | + |
| 101 | +还有一个隐含的陷阱:模型和套件之间存在训练耦合。Claude 模型在后训练阶段专门针对 Claude Code 的套件做过优化,所以同一个模型在 Claude Code 里和在别的套件里表现不同。这也是为什么换了模型版本之后,有时需要微调 AGENTS.md——模型变了,之前的"补丁"可能不再需要,也可能需要新的。 |
| 102 | + |
| 103 | +* 一个行动框架 |
| 104 | + |
| 105 | +读完这篇文章,如果你是一个 Claude Code 用户,接下来可以做的事: |
| 106 | + |
| 107 | +1. *审计你的 CLAUDE.md* :每条规则能不能追溯到一次具体的失败?追溯不到的考虑删除 |
| 108 | +2. *加一个 hook* :找一个 agent 反复出错的场景,写一个 hook 自动化检查。成功时静默,失败时详细 |
| 109 | +3. *审查你的 MCP 列表* :每个 MCP 服务器提供的工具是否必要?描述是否精确? |
| 110 | +4. *建立棘轮习惯* :下次 agent 犯错时,不要只点"重试"——花 30 秒加一条规则 |
| 111 | + |
| 112 | +套件工程的核心思想用一句话总结:不要等下一个模型来解决问题,用你手里的配置面把当前模型的能力释放出来。Viv Trivedy(Terminal Bench 2.0 排名 Top 5 的套件设计者)的话作为结尾很合适:"好的 agent 构建是一个迭代练习。如果你没有 v0.1,就没法做迭代。" |
0 commit comments