Agent Harness 工程
- 原文链接:addyosmani.com/blog/agent-…
- 原文作者:Addy Osmani
2026 年 4 月 19 日
编码智能体等于模型加上你围绕它构建的一切。Harness 工程把这套脚手架当作真正的工程产物;每当智能体失手,Harness 就再收紧一圈。
大致来说:每当你发现智能体犯了一个错,你就花时间设计一套方案,让智能体再也不会犯同样的错。
过去两年,我们争论的几乎都是模型:哪个更聪明、哪个写的 React 更干净、哪个更少幻觉。这类讨论本身没问题,但漏掉了系统的另一半。模型只是运行中的智能体的一个输入;其余部分是 Harness:提示词、工具、上下文策略、hooks、沙箱、子智能体、反馈环路与恢复路径——它们包裹在模型外面,让它真正能把事情做完。
一个还不错的模型配上优秀的 Harness,胜过一个优秀模型配上糟糕的 Harness。 我在自己的工作中反复看到这一点。而且越来越明显的是,有意思的工程不在选模型,而在设计模型周围的脚手架。
这门学科现在有了名字。Viv Trivedy 创造了 Harness engineering 这一术语,他的 《Anatomy of an Agent Harness》 一文最清楚地推导了 Harness 究竟是什么、以及每个部件为何存在。Dex Horthy 一直在追踪这一模式如何成形。HumanLayer 把大多数智能体失败框定为「技能问题(skill issue)」——根子在配置,而非模型权重。Anthropic 工程团队 发布了我认为目前公开资料里最好的、如何为长时运行任务设计 Harness 的拆解。Birgitta Böckeler 则从用户侧很好地概述了这看起来像什么。
本文是我尝试把这些线索拢在一起的一次综合梳理。
Harness 究竟是什么?
Viv 用一句话就把核心说透了:
Agent = Model + Harness。如果你不是模型,你就是 Harness。
Harness 是除模型本身以外的一切代码、配置与执行逻辑。裸模型不是智能体;一旦 Harness 赋予它状态、工具执行、反馈环路与可强制执行的约束,它才成为智能体。
具体而言,Harness 包括:
- 系统提示词、
CLAUDE.md、AGENTS.md、skill 文件与子智能体提示词 - 工具、skills、MCP 服务器及其描述
- 捆绑基础设施(文件系统、沙箱、浏览器)
- 编排逻辑(子智能体生成、交接、模型路由)
- 用于确定性执行的 hooks 与中间件(压缩、续跑、lint 检查)
- 可观测性(日志、追踪、成本与延迟计量)
Simon Willison 把循环部分提炼到本质:智能体是一个在循环中运行工具以达成目标的系统。技艺在于工具与循环二者的设计。
如果听起来覆盖面很大,确实如此。而且这是 你的 覆盖面,不是模型提供商的。Claude Code、Cursor、Codex、Aider、Cline:这些都是 Harnesses。底下的模型有时相同,但你体验到的行为主要由 Harness 决定。
coding agent = AI model(s) + Harness
这个等式——由 Viv 提出、HumanLayer 呼应——才是工作真正所在之处。关于等式左侧的争论很响;大多数实际杠杆在右侧。
「技能问题」的重新解读
我常见工程师陷入一种模式:智能体做了件蠢事,工程师怪模型,然后把责任归档为「等下一版」。
Harness 工程思维拒绝这种默认。失败通常是可读的。智能体不知道某项约定,你就把它写进 AGENTS.md。智能体跑了破坏性命令,你就加 hook 拦截。智能体在 40 步任务里迷路,你就拆成规划器与执行器。智能体一再「完成」有问题的代码,你就把 typecheck 反压信号接进循环。
HumanLayer 说:「这不是模型问题,是配置问题。」 Harness 工程就是你当真对待这句话之后发生的事。
Viv 的撰文与 HumanLayer 都出现了一个引人注目的数据点:在 Terminal Bench 2.0 上,Claude Opus 4.6 跑在 Claude Code 里,得分远低于同一模型跑在定制 Harness 里。Viv 的团队只改 Harness,就把编码智能体从 Top 30 拉到 Top 5。模型在后训练阶段会与训练时针对的 Harness 耦合。把它迁到另一套 Harness——配上更贴合你代码库的工具、更紧的提示词、更锐的反压——可以解锁原 Harness 留在地板上的能力。
这与「等着 GPT-6 就行」的叙事相反。今天模型能做的事与你实际看到它做的事之间的差距,多半来自 Harness。
棘轮:每个错误都变成一条规则
Harness 工程里最重要的习惯,是把智能体的错误当作永久信号。不是拿来一笑了之的偶发故事,不是「坏的一次运行」重试即可。是信号。
如果智能体提交了一个注释掉测试的 PR,而我误合并了,那就是输入。下一版 AGENTS.md 写「永远不要注释掉测试;删掉或修好它们」。下一版 pre-commit hook 在 diff 里 grep .skip( 和 xit(。下一版 reviewer 子智能体把注释掉的测试标为 blocker。
你只在见过真实失败时才加约束;只在更强模型使约束冗余时才移除。一份好的 AGENTS.md 里每一行都应能追溯到某件具体出错的事。
这也是 Harness 工程是学科而非框架的原因。适合你代码库的 Harness 由你的失败史塑造。你下载不来。
从行为倒推
Viv 的论述里,我在真正设计 Harness 时觉得最有用的是:从你想要的行为出发,推导出交付它的 Harness 部件。他的模式是:我们想要(或想修复)的行为 → 帮助模型达成它的 Harness 设计。
这样推导的好处是,每个 Harness 组件都有明确职责。如果你说不出某个组件存在是为了交付什么行为,它大概不该在那里。
本节余下部分大致按 Viv 的顺序走各部件,并加入我认为值得借鉴的具体模式。
文件系统与 Git:持久状态
文件系统是最基础的 primitive(原语),往往因「无聊」而被低估。模型只能直接操作放进上下文的东西。没有文件系统,你就是在聊天窗口里复制粘贴——那不是工作流。
有了文件系统,智能体就有工作区可读数据、代码与文档;有地方卸载中间产物,而不是全堆在上下文里;还有人与多智能体通过共享文件协调的表面。加上 Git 就免费得到版本管理,智能体可以跟踪进度、回滚错误、分支实验。
大多数其他 Harness primitive 最终都会指向文件系统的某处。
Bash 与代码执行:通用工具
今天的主智能体循环是 ReAct 循环:模型推理,通过 tool call 行动,观察结果,重复。但 Harness 只能执行它写了逻辑的工具。你可以为每种可能行动预建工具,也可以给智能体 bash,让它按需现场造工具。
Willison 的看法是,智能体已经很擅长 shell 命令;多数任务会坍缩成几条选得好的 CLI 调用。Harness 仍会内置聚焦工具,但 bash 加代码执行已成为自主问题求解的默认通用策略。这就像教某人用单一厨房小家电,与递给他一整间厨房的区别。
沙箱与默认工具链
Bash 只有跑在安全地方才有用。在笔记本上跑智能体生成的代码有风险,单一本地环境也撑不住大量并行智能体。
沙箱给智能体隔离的运行环境。Harness 不本地执行,而是连到沙箱跑代码、查文件、装依赖、验证工作。你可以白名单命令、强制网络隔离、按需拉起新环境、任务结束拆掉。
好的沙箱自带好的默认项:预装语言运行时与包、Git 与测试 CLI、用于网页交互的无头浏览器。浏览器、日志、截图与测试 runner 让智能体观察自己的工作,闭合自验证循环。
模型不会配置自己的执行环境。智能体跑在哪、有什么可用、如何验证输出——都是 Harness 层决策。
记忆与搜索:持续学习
模型除了权重与当前上下文外没有额外知识。不能改权重时,加知识的唯一途径是上下文注入。
文件系统再次是 primitive。Harnesses 支持像 AGENTS.md 这样的记忆文件标准,每次启动注入。智能体编辑该文件时,Harness 重新加载,一个会话的知识带进下一个。这是粗陋但有效的持续学习形式。
对训练时不存在的知识(新库版本、当前文档、今日数据),网页搜索与 Context7 等 MCP 工具跨过训练截止点。这些是值得内置进 Harness、而非留给用户的原语。
对抗上下文腐烂(context rot)
上下文腐烂是指:随着上下文窗口填满,模型推理与完成任务的能力变差。上下文稀缺,Harnesses 本质上是交付优质上下文工程的机制。
在 Harness 实践中,下面三种技术会反复出现:
压缩(Compaction)。 窗口接近满时,总得牺牲点什么。生产 Harness 不能让 API 直接报错,所以 Harness 会智能摘要并卸载较旧上下文,让智能体继续工作。
工具调用卸载(Tool-call offloading)。 大型工具输出(比如 2000 行日志)会挤占上下文却加不了多少信号。Harness 在阈值以上保留头尾 token,把完整输出卸载到文件系统,智能体按需读取。
渐进披露的 skills。 启动时把每个工具与 MCP 都载入上下文,会在智能体尚未行动前就拖慢性能。Skills 让 Harness 只在任务真正需要时才逐步展示指令与工具。
Anthropic 的 Harness 文章为真正长任务加了第四种:完整上下文重置——Harness 拆掉会话,从紧凑的交接文件重建。他们明确说仅靠压缩对长任务不够;有时你需要带着结构化简报重新开始。这更接近人类 onboarding 新工程师,而非我们通常想的「记忆」。
长时程执行:Ralph 循环、规划、验证
自主长时程工作是圣杯,也是最难做对的事。今天的模型容易早停、复杂问题分解差、工作跨多个上下文窗口时不连贯。Harness 必须围绕这一切设计。
我曾在 自改进智能体 与 2026 趋势 里写过 Ralph Loop 这类自主编码循环,但在此语境下值得重述:hook 拦截模型试图退出,把原始提示词重新注入新的上下文窗口,迫使智能体对着完成目标继续。每次迭代干净启动,但通过文件系统读取上一轮状态。这是把单会话智能体变成多会话的出奇简单技巧,也是你从「用更聪明模型就行」推不出的原语。
规划(Planning) 是模型把目标分解为步骤序列,通常写入磁盘上的 plan 文件。Harness 用提示词与提醒支持这一点,说明如何使用 plan 文件。每步之后,智能体通过自验证检查工作:hooks 跑预定义测试套件,把失败与错误文本回传模型;或模型按明确标准审自己的输出。
规划器 / 生成器 / 评估器拆分。 Anthropic 的长时运行 Harness 工作明确说,把生成与评估拆成独立智能体,优于自评——因为智能体给自己打分时会可靠地偏乐观。这是散文版的 GAN。相关模式是 sprint contract(冲刺契约):生成器与评估器在写代码前协商「完成」到底指什么。在我自己的工作流里,开始前写下完成条件,比任何 prompt 改动都更能抓住范围漂移。
Hooks:强制层
Hooks 区分「我告诉智能体做 X」与「系统强制 X」。
Hook 是在特定生命周期点跑的脚本:tool call 前、文件编辑后、commit 前、session 启动时。它们适合放智能体不应忘记却常忘的事:每次编辑后跑 typecheck、lint、测试并呈现失败;拦截破坏性 bash(rm -rf、git push --force、DROP TABLE);开 PR 或 push 到 main 前要求批准;写入时自动格式化,免得智能体在空白上浪费 token。
HumanLayer 强调、我也认同的原则是:成功静默,失败详尽。 typecheck 通过,智能体什么都听不到;失败则错误文本注入循环,智能体自修正。这使反馈循环在常见情况下几乎无成本,出错时又可立刻行动。
AGENTS.md 与工具选择
仓库根目录那份扁平 Markdown 规则书仍是单点最高杠杆配置——因为它每轮都进系统提示词。约定写这里:包管理器、测试框架、格式化、「永远不要动 /legacy」、「永远用我们的 logger」。两条血泪教训:
保持简短。HumanLayer 把自家规则控制在 60 行以内。每一行都在抢注意力,规则越多每条越不重要。飞行员检查单,不是风格指南。
每一行都要挣来。规则应追溯到具体过往失败或硬性外部约束;否则是噪声。棘轮;不要头脑风暴。
工具同样纪律。每个工具的名称、描述与 schema 每请求都写入 prompt。十个聚焦工具胜过五十个重叠的,因为模型能把菜单记在脑子里。HumanLayer 还指出真实安全隐患:工具描述会进入提示词,所以你装的任何 MCP 服务器都是模型会读的受信文本。草率或恶意 MCP 可以在你还没打字前就 prompt-inject 你的智能体。
生产里长什么样
我见过最清晰的成熟 Harness 公开图景,是 Fareed Khan 对 Claude Code 架构 的(估算)拆解,值得对着图看一分钟。
上一节几乎每个概念在这张图上都有命名组件。上下文注入是知识层。循环状态在 memory store 与 worktree isolator。破坏性 action hooks 在 permission gate 后面。子智能体 context firewall 就是整个多智能体层。tool dispatch registry 是 MCP 服务器与 bash 的接入点。Khan 的论点与 Viv 相同,只是走通了一个已上市的产品:Claude Code 的轨迹,至少与底下模型一样,是关于 Harness 的。
Harnesses 不会缩小,只会迁移
Anthropic 文章里更好的观察之一是:随着模型改进,有意思的 Harness 组合空间不会缩小,只会迁移。
天真叙事是:更好的模型让 Harnesses 过时。模型会规划,就不要 planner;模型长时程连贯,就不要 context reset。没错,Opus 4.6 大体消弭了「上下文焦虑」失败模式(Sonnet 4.5 曾以为自己接近 context limit 就过早收尾),意味着我六个月前写的一整类缓解上下文焦虑的脚手架现在是死代码。
但天花板随模型一起上移。曾经够不着的任务进入射程,它们有自己的失败模式。缓解焦虑的脚手架消失,取而代之你需要多日记忆策略,或协调三个专用智能体的 Harness,或生成 UI 设计质量的评估器。假设在变,编码这些假设的脚手架也在变。
Anthropic 说得很干净:「Harness 里每个组件都编码了关于模型独自做不到什么的假设。」 模型在某事上变强,该组件就不再承重,应拿掉。模型解锁新能力,就需要新脚手架触达新天花板。
模型—Harness 训练循环
还在发生的另一件事——Viv 明确点名——是 Harness 设计与模型训练之间的反馈循环。
今天的智能体产品在后训练阶段把 Harness 放进训练循环。模型会更专精于 Harness 设计者认为它该擅长的事:文件系统操作、bash、规划、子智能体调度。这就是为什么 Opus 4.6 在 Claude Code 里与在别人 harness 里感觉不同,也是为什么改工具逻辑有时会引起奇怪回归。真正通用的模型本不应在意你用 apply_patch 还是 str_replace,但联合训练会造成过拟合。
实际含义有两层。Harness 是活系统,不是一次性设好的配置文件。 「最好」的 Harness 不一定是模型训练时所在的那套;而是为你的任务设计的那套。Viv 在 Terminal Bench 从 Top 30 到 Top 5 的跃升,是我见过最清晰的证明。
Harness-as-a-Service(HaaS)
Viv 的另一贡献是 HaaS 框架:Harness-as-a-Service(HaaS)。观察是:我们从构建于 LLM API(给你 completion)转向构建于 Harness API(给你 runtime)。Claude Agent SDK、Codex SDK、OpenAI Agents SDK 都指向同一方向。你开箱得到 loop、工具、上下文管理、hooks 与沙箱原语,再定制它们。
转变重要,因为默认路径曾经是:自建 loop、自己接 tool-calling、自己管对话状态、自己发明 approval flow。现在默认路径是:选 Harness 框架,按四支柱(系统提示词、工具、上下文、子智能体)配置,把其余精力放在领域特定 prompt 与工具设计。
这让「skill issue」变得可处理。你不是每次出错都从零重建智能体;你在已经结构良好的配置面上调参。
Viv 关于这点还有一句最好的起步论证:「做好智能体是迭代练习。没有 v0.1 就做不了迭代。」
走向何方
把主流编码智能体并排看(Claude Code、Cursor、Codex、Aider、Cline),它们彼此看起来比底下模型更像。 模型不同,Harness 模式在收敛。我不认为是偶然:行业在慢慢找到把生成式模型变成能交付之物的关键脚手架。
Viv 对开放问题的论述最让我兴奋:编排许多智能体并行处理共享代码库;智能体分析自己的 trace,识别并修复 Harness-level 失败模式;Harnesses 为给定任务即时动态组装对的工具与上下文,而不是启动时预配置。
最后这一点尤其像 Harnesses 从静态配置变成更接近编译器的东西。