读:Agent Harness Engineering——AI 智能体不只是模型,还有套件
目录
Addy Osmani 的这篇文章提出了一个被忽视的观点:我们花了两年争论哪个模型最聪明,却忽略了决定 agent 表现的另一半——套件(harness)。公式很简单:
Agent = Model + Harness
模型只是方程式的一个参数,剩下的提示词、工具、上下文策略、钩子、沙箱、子智能体、反馈循环、恢复路径全是套件的活。
更直白地说:一个还行的模型配上优秀的套件,能打败一个优秀的模型配上糟糕的套件。这篇文章讲的就是怎么设计那套"套件"。
什么是套件,到底包含什么
Viv Trivedy 给出了最清晰的定义。一个裸模型不是智能体因为它只能一问一答。只有当套件为它配上状态管理、工具执行、反馈循环和可执行的约束之后,它才变成一个能干活的智能体。
套件具体包含这些东西:
- 提示词层 :system prompt、CLAUDE.md、AGENTS.md、skill 文件、子智能体提示词
- 工具层 :skills、MCP 服务器、工具描述
- 基础设施层 :文件系统、沙箱、浏览器
- 编排层 :子智能体调度、任务交接、模型路由
- 执行保障层 :钩子(hooks)、中间件—负责确定性执行,比如压缩、续接、lint 检查
- 可观测性 :日志、链路、成本和延迟计量
imon Willison 把核心循环提炼成一句话:智能体就是"在循环中运行工具来达成目标的系统"。设计智能体的关键,在于同时设计好工具和循环本身。
如果你用过 Claude Code、Cursor、Codex、Aider、Cline 中的任何一个,你用的就是套件。底下的模型可能一样,但你感受到的行为差异,主要来自套件的设计,而不是模型的能力。
"不是模型不行,是配置不行"
多数工程师遇到 agent 犯蠢时的反应是:等下一个模型版本。套件工程思维不认同这种默认反应。
当agent 做了一件蠢事,先问它:为什么会这样做?如果:
- 它不知道某个约定 → 写进 AGENTS.md
- 它执行了破坏性命令 → 加一个 hook 拦截
- 它在 40 步的任务中迷失了 → 拆成规划者和执行者
- 它反复提交有 bug 的代码 → 把类型检查接入反馈循环
HumanLayer 的说法是:套件工程不把责任推给模型,而是积极通过配置来解决问题。
有一个例子很能说明问题。在 Terminal Bench 2.0 基准测试中,同一个 Claude Opus 4.6 模型,在 Claude Code 里的得分远低于跑在定制套件里的得分。Viv 的团队只改了套件(更贴合代码库的工具、更精准的提示词、更紧的反馈信号),就把排名从 Top 30 拉到 Top 5。模型还是那个模型,能力没有变——是套件把"被浪费的能力"释放出来了。
这跟"等 GPT-6 就好了"的想法完全不同。当前模型能做的事和你在实际使用中看到它做的事之间的差距,就是套件的差距。
棘轮模式:每次犯错都变成永久规则
套件工程中最重要的习惯:把 agent 的每一次错误都当作积累的一次机会。
agent 提交了一个注释掉的测试,你不小心合并了?这不是"下次注意"的事。正确的做法是:
- 在 AGENTS.md 里加一条规则:"永远不要注释掉测试;要么删除,要么修复"
- 在 pre-commit hook 里加一个检查,扫描 diff 中是否有
.skip()或xit() - 让审查子智能体把"注释掉的测试"标记为阻塞项
关键约束:只在真正出过错的地方加规则,只在更强的模型证明某条规则多余时才删掉。AGENTS.md 里的每一行都应该能追溯到一次具体的失败。这也是为什么套件工程是一门学科而不是一个框架——适合你代码库的套件是由你的失败历史塑造的,没法下载。
对抗上下文腐败
上下文腐败(context rot)是指:随着上下文窗口越来越满,模型的推理和任务完成能力会下降。上下文是稀缺资源,套件在很大程度上就是上下文工程(context engineering)的交付机制。
文章总结了三种对抗手段:
- 压缩(Compaction) :当窗口快满时,套件智能地总结和卸载旧上下文,让 agent 继续工作。让 API 直接报错不是生产级套件能接受的。
- 工具调用卸载 :大型工具输出(比如 2000 行的日志文件)直接塞进上下文,信号很少、噪声很多。套件只保留头尾若干 token,把完整输出卸载到文件系统,agent 需要时可以按需读取。
- 渐进式披露(Progressive Disclosure) :启动时把所有工具和 MCP 服务器都加载到上下文里,会导致 agent 还没开始干活就已经被信息淹没。Skills 机制让套件只在任务真正需要时才揭示相关指令和工具。
Anthropic 的团队还补充了第四种手段:对于特别长的任务,彻底重置上下文——套件销毁当前会话,从一个紧凑的交接文件重建新会话。他们明确说,光靠压缩不够;有时候你需要带着一份结构化摘要从头开始。这更像是"给新工程师做交接"而不是我们通常理解的"记忆"。
长程执行:让 agent 干几个小时不跑偏
让 agent 自主完成长任务是最难做对的事情。当前模型的问题是:过早停止(觉得干完了其实没干完)、复杂问题分解能力差、跨多个上下文窗口后行为不连贯。套件必须为所有这些问题兜底。
几个关键模式:
- Ralph Loop :一个钩子拦截 agent 会话结束的事件,把原始提示词重新注入一个新的上下文窗口,迫使 agent 继续朝完成目标推进。每次迭代从干净状态开始,但通过文件系统读取上一次的状态。这是一个出奇简单的技巧——用一个钩子就把单次会话 agent 变成了多会话 agent。
- *规划-生成-评估分离 * :Anthropic 的实践表明,把生成和评估交给不同的智能体,效果远好于自我评估——因为 agent 给自己的工作打分时,几乎总是偏乐观。这本质上是文字领域的 GAN(生成对抗网络)。配套的模式是 *sprint contract*——生成者和评估者在代码写出来之前先谈好"什么叫完成"。Addy Osmani 的个人经验是:在开始之前写下完成条件,比任何提示词调整都能抓到更多的范围蔓延。
- 自验证 :每完成一步,hook 运行预定义的测试套件,把失败信息反馈给模型让它自我修正;或者模型按明确标准审查自己的输出。
Hooks:从"告诉它做 X"到"系统强制执行 X"
Hook 是套件中最关键的分界线——它把"我告诉 agent 要做 X"变成了"系统保证执行 X"。
Hook 是在特定生命周期点运行的脚本:工具调用前、文件编辑后、提交前、会话开始时。适合放那些 agent 应该永远记住但经常忘记的事情:
- 每次编辑后自动运行类型检查、lint、测试,把失败信息送回循环
- 拦截破坏性命令(
rm -rf、git push --force、DROP TABLE) - 打开 PR 或推送到 main 前要求人工审批
- 写文件后自动格式化,省得 agent 浪费 token 在空白符上
HumanLayer 强调的原则是: 成功时静默,失败时详细 。类型检查通过了?agent 什么也听不到。失败了?错误文本直接注入循环,agent 自动修正。这让反馈循环在正常情况下几乎零成本,出问题时立刻可操作。
AGENTS.md 和工具选择的设计原则
根目录下的那个 markdown 规则文件仍然是投入产出比最高的配置点——因为它出现在每轮对话的系统提示词里。放什么?包管理器用什么、测试框架是什么、格式化规则、"不要碰 /legacy 目录"、"统一用我们的 logger"。两条经验:
- 保持简短 。HumanLayer 把他们的控制在 60 行以内。每条规则都在争夺注意力,规则越多,每条规则的分量越轻。这是飞行员检查单,不是风格指南。
- 每条规则都要"挣"来的 。规则应该追溯到一次具体的失败或一个硬性外部约束。如果追溯不到,就是噪声。棘轮式增长,不要头脑风暴式增长。
同样的纪律也适用于工具。每个工具的名字、描述和 schema 每次请求都被塞进提示词。十个专注的工具胜过五十个功能重叠的工具,因为模型能记住十个工具各自干什么,但记不住五十个。HumanLayer 还指出了一个安全风险:工具描述本身就是提示词的一部分,所以你安装的每一个 MCP 服务器都是受信文本。一个粗劣或恶意的 MCP 可以在你输入任何内容之前就对 agent 执行提示词注入。
模型变好了,套件怎么办
Anthropic 的一个重要观察:随着模型变强,套件不会缩小,只会转移。
直觉上你会觉得更好的模型让套件过时——模型能规划了就不需要规划器,模型在长上下文中能保持连贯了就不需要上下文重置。确实,Opus 4.6 基本消灭了"上下文焦虑"导致的提前收工(Sonnet 4.5 及更早版本会在接近自认为的上下文极限时匆忙结束工作),这意味着半年前为这个问题写的一大堆缓解脚手架现在都是死代码。
但天花板跟着模型一起提高了。以前做不到的任务现在能做了,而这些新任务有自己的失败模式。上下文焦虑的脚手架消失了,取而代之的是你需要多天记忆策略、需要协调三个专业智能体的套件、或者针对生成 UI 的设计质量评估器。
Anthropic 说得清楚:"套件中的每一个组件都编码了一个关于'模型靠自己做不到什么'的假设。"当模型在某方面变强了,对应的组件就不再承重,应该移除。当模型解锁了新能力,新的脚手架又需要建起来。
还有一个值得注意的现象:模型和套件之间存在训练循环。今天的 agent 产品在后训练阶段就把套件纳入了循环——模型会专门变强于套件设计者认为它应该擅长的事:文件系统操作、bash、规划、子智能体调度。这就是为什么同一个模型在不同套件里表现差异很大,也是为什么改一个工具的逻辑有时会引发奇怪的退化。
Harness-as-a-Service:从 API 到运行时
Viv 提出的另一个重要框架:我们正在从基于 LLM API 的构建(给你一个补全结果)转向基于套件 API 的构建(给你一个运行时)。Claude Agent SDK、Codex SDK、OpenAI Agents SDK 都指向同一个方向——你开箱即得循环、工具、上下文管理、钩子和沙箱原语,然后做定制。
这让"技能问题"变得可操作了。每次出问题你不需要从头重建 agent,而是在一个已经分好层的配置面上做调整。
Viv 还有一句很适合作为行动指南的话:"好的 agent 构建是一个迭代练习。如果你没有 v0.1,就没法做迭代。"
这门学科的方向
看看顶级的代码智能体——Claude Code、Cursor、Codex、Aider、Cline——它们的套件架构之间的相似度,远高于它们所用的不同模型之间的差异。模型不同,套件模式在收敛。这不是巧合,是行业在慢慢找到把生成模型变成可交付产品的关键脚手架。
文章列出的开放问题中最令人兴奋的方向:编排多个智能体并行工作在共享代码库上;agent 分析自己的运行轨迹来识别和修复套件级别的失败模式;套件不再是预配置的静态配置,而是在任务到来时动态组装合适的工具和上下文。
最后一个方向尤其让人期待——那意味着套件从静态配置变成了某种更接近编译器的系统。