w3ctech

智能体 Harness 工程(Agent Harness Engineering)

作者:Addy Osmani

编程智能体 = 模型 + Harness。 Harness 工程的核心理念,是将包围在模型外围的“脚手架”视为一种不断演进的生命体。

每当智能体出错时,我们要通过提示词、钩子(hooks)、工具、沙盒或子智能体,为其添加永久性的修复,确保它绝不再犯完全相同的错误。

一个还凑合的模型配上顶级的 Harness,能够完胜一个顶尖模型配上糟糕的 Harness。模型理论潜力与实际表现之间的鸿沟,很大程度上就是 Harness 的鸿沟。

设计 Harness 时,应从期望的行为结果倒推。每一个组件都必须能交付特定的结果,而组件的形态则是由项目过去的“失败教训”所塑造的。

随着模型的不断进步,Harness 并不会随之萎缩,而是会转移到新的约束条件和更高的视野上,逐渐演变成为类似“智能体编排编译器”的动态系统。


一个编程智能体,本质上是模型加上围绕它构建的所有配套设施的集合。Harness 工程将这套外围“脚手架”视为有生命力的产物——智能体每犯一次错,就将这套脚手架收紧一分。

简而言之:每当智能体失败时,你就要设计一个永久性的解决方案,让它再也不会犯同样的错误。

过去两年里,整个行业都在争论模型:谁最聪明、谁写的 React 代码最干净、或者谁的幻觉最少。虽然这些讨论很重要,但却忽略了系统的另一半。

模型仅仅是运行中的智能体的一个“输入源”。其余的部分全是 Harness:提示词、工具、上下文策略、钩子、沙盒、子智能体、反馈循环,以及包裹在模型外围、使其能真正完成任务的恢复路径。

优秀的 Harness 配上普通的模型,总是能击败糟糕 Harness 配上顶尖模型。越来越多最硬核、最有趣的工程工作,已不再是去挑选模型,而是去设计模型外围的这套脚手架。

这门学科现在终于有了一个名字。@Vtrivedy10 创造了**“Harness 工程(Harness Engineering)”**这个词,清晰地拆解了 Harness 到底是什么,以及其中每个部分存在的意义。行业中其他的声音也正在汇聚到大致相同的理念上:比如 @dexhorthy 在追踪行业涌现的新模式,HumanLayer 将智能体的失败归结为配置上的“技能问题(skill issues)”,Anthropic 工程团队发布了长生命周期应用的架构指南,Birgitta Böckeler 则深入探讨了用户侧体验。

这篇文章,正是为了将这些线索串联在一起。

Harness 到底是什么?

Trivedy 的核心定义一语中的:

智能体 = 模型 + Harness。如果你不是模型,那你就是 Harness。

Harness 包含了所有非模型本身的代码、配置和执行逻辑。一个“裸模型”并不是智能体。只有当 Harness 为其提供状态管理、工具执行能力、反馈循环和强制约束时,它才真正成为一个智能体。

具体来说,Harness 包括:

  • 指令层: 系统提示词、CLAUDE.mdAGENTS.md、技能文件以及子智能体指令。
  • 能力层: 工具、技能、MCP(模型上下文协议)服务器及其技术描述。
  • 基础设施: 捆绑打包的底层环境,例如文件系统、沙盒和无头浏览器(headless browsers)。
  • 编排逻辑: 用于派生子智能体、处理任务交接和路由不同模型的逻辑。
  • 拦截与中间件: 用于确保执行确定性的钩子(Hooks),比如代码规范检查(lint)或上下文压缩机制。
  • 可观测性工具: 用于日志记录、链路追踪、成本控制和延迟计量的工具。

智能体的核心本质,是一个循环运行工具以达成目标的系统。真正的技术壁垒在于如何设计这些工具,以及如何设计这个循环。

虽然这代表着一个极其庞大的接触面,但这完全是“你的”控制面,而不是模型提供商的。Claude Code、Cursor、Codex、Aider 和 Cline 本质上都是 Harness。它们跨平台的底层模型可能是一模一样的,但你所体验到的行为差异,完全是由 Harness 主导的。

重新定义“技能问题”

当智能体做出荒谬的举动时,工程师往往习惯性地责怪模型,然后把问题束之高阁,指望着“等下个版本的模型发布”来解决。

Harness 工程的思维模式拒绝这种默认的甩锅行为。失败通常都是有迹可循且可被解析的。

  • 如果智能体忽略了一个规范,就把它写进 AGENTS.md
  • 如果它运行了破坏性的命令,就写个钩子拦截它。
  • 如果它在一个长达 40 步的任务中迷失了方向,就把架构拆分为“规划者”和“执行者”。
  • 如果它总是输出有报错的代码,就在循环中接入由“类型检查”触发的背压信号(back-pressure signal),强迫其自我修正。

正如 HumanLayer 所言:“这不是模型的问题,这是配置的问题。” 看看性能基准测试就明白了:同一个顶尖模型,在开箱即用的通用框架中运行,得分通常远低于在其深度定制、高度优化的 Harness 中的得分。将模型放入一个配备了更优代码库工具、更严谨提示词和更敏锐背压信号的环境中,能够解锁底层模型被原始环境所埋没的巨大潜能。

当今模型理论上能做到的,与你实际看到它们做到的,这二者之间的鸿沟很大程度上就是 Harness 的鸿沟。

棘轮效应:每一个错误都沉淀为规则

在 Harness 工程中,最重要的习惯是把智能体的错误视为永久性的信号——而不是重试一次就抛之脑后的偶然偏差。

如果智能体提交了一个包含“被注释掉的测试用例”的 PR 并被意外合并了,这就是一个输入信号。

  • 下一版 AGENTS.md 必须明确指出:“永远不要注释掉测试代码;要么删除,要么修复它。”
  • 下一个 Git pre-commit 钩子应该自动拦截代码 Diff 中的 .skip( 字符。
  • 负责代码审查的子智能体也必须更新规则,以阻截被注释掉的测试。

只有当你观察到真实的失败时,才应该添加约束;也只有当更强大的模型让这些约束显得多余时,才应该移除它们。一个优秀的系统提示词中的每一行,都应该能追溯到一个具体的、曾经发生过的历史失败教训。

正因如此,Harness 工程是一门系统性的方法论,而不是一套万能模板。最适合某个特定代码库的 Harness,完全是由它独一无二的失败历史所“喂”出来的。

从行为倒推设计

设计 Harness 最有效的方法,是从期望的行为出发,反向构建交付该行为的组件:期望的行为 → 实现该行为的 Harness 设计。

Harness 的每一个部分都必须有明确的职责。如果你说不清楚某个组件是为了交付什么特定行为而存在的,那就果断删掉它。

文件系统与 Git —— 持久化状态

文件系统是根基。模型只能处理装得进其上下文窗口的信息。文件系统提供了一个读取数据的工作区、一个卸载中间工作成果的缓冲区,以及一个供多智能体协同交互的界面。
引入 Git 则提供了零成本的版本控制,允许智能体追踪进度、做分歧实验并在出错时回滚。

Bash 与代码执行 —— 通用工具

大多数智能体运行在 ReAct 循环(推理、行动、观察、重复)中。与其为每一种可以想象到的操作预先死磕出对应的工具,不如直接给智能体 Bash 权限,让它即兴动态构建自己需要的东西。
智能体通常非常擅长编写 Shell 命令,这使得 Bash 和动态代码执行成为了自主解决问题时的默认策略。

沙盒与默认环境

Bash 只有在安全运行的前提下才有用。沙盒为智能体提供了一个隔离环境来运行代码、检查文件并验证工作,且不会危及宿主机。
一个好的沙盒会自带强大的默认配置:预装语言运行时、测试 CLI 工具以及无头浏览器,让智能体能够观察自己的工作成果,完成“自我验证”的闭环。

记忆与搜索 —— 持续学习

除了预训练权重和当前上下文,模型本身没有额外的知识。Harness 通过“记忆文件”(如 AGENTS.md)来弥合这一短板,将知识注入到每一次会话中。
对于获取最新库版本或实时数据,网络搜索和 MCP 工具会被直接内嵌进 Harness 中以供调用。

对抗上下文衰变 (Context Rot)

当上下文窗口渐渐被填满时,模型的推理能力会显著退化。Harness 使用三种主要技术来管理这种稀缺资源:

  1. 压缩 (Compaction): 智能地总结并卸载旧的上下文,以防止触发 API 容量报错。
  2. 工具输出卸载 (Tool-call offloading): 将海量的工具输出(比如长达 2000 行的日志)直接转存到文件系统中,只在上下文中保留关键的开头和结尾。
  3. 渐进式披露 (Progressive disclosure): 不要在启动时一股脑加载所有内容,而是仅在任务明确需要时,才向模型展示对应的指令和工具。

长线任务执行 (Long-Horizon Execution)

全自动、长生命周期的任务经常饱受“过早停止”和“任务拆解不佳”的困扰。Harness 通过结构化设计来应对:

  • 循环拦截 (Loops): 拦截模型想要退出的企图,强制它在一个干净的新上下文窗口中,对照着最终目标继续工作。
  • 强制规划 (Planning): 迫使模型将目标分解成一个按步骤执行的计划文件,并在每一步完成后通过自我验证钩子来检查工作。
  • 角色隔离 (Splits): 将“生成代码”和“评估代码”的任务拆分给不同的智能体,以规避模型在给自己打分时天然自带的“过度自信偏见”。

钩子 (Hooks) 是你的强制执行层

钩子弥合了“请求动作”和“强制执行”之间的鸿沟。它们运行在特定的生命周期节点:调用工具前、编辑文件后或提交代码前。钩子可以拦截高危的破坏性命令,强制自动格式化代码以节省 Token,以及运行测试用例。

理想状态是:成功时悄无声息,失败时信息详尽。 如果类型检查通过了,智能体什么也听不到;如果失败了,报错信息会被直接注入回循环中,触发其自我纠错。

规则手册与工具选择

代码仓库根目录下的那个极其简单的 Markdown 文件,依然是杠杆率最高的配置点。然而,必须把它当作飞行员起飞前的“检查单(checklist)”来对待,而不是一本长篇大论的代码风格指南。 保持简短,并确保其中的每一条规则都是靠过去的失败换来的。

同样的纪律也适用于工具。十个高度聚焦的工具,永远胜过五十个功能重叠的冗余工具。
此外,因为“工具的描述信息”也会占用提示词空间,恶意或草率的外部集成(比如未体验证的 MCP 服务器),甚至在智能体还没开始干活之前,就会向其注入一堆糟糕的提示词污染。

在生产环境中的实际形态

目前我所见过的展示成熟 Harness 全貌最清晰的公开图解,是 Fareed Khan 对 Claude Code 架构的(预估)拆解。

前文提到的几乎每一个概念,都在这张图上以具名组件的形式出现了。上下文注入构成了知识层;循环状态存储在内存库工作树隔离器中;拦截破坏性操作的钩子藏在权限网关之后;子智能体的上下文防火墙构成了整个多智能体层工具调度注册表则是 MCP 服务器和 Bash 的接入枢纽。
Claude Code 能取得今天的体验,至少有一半功劳要归于它底层模型外围的这套 Harness。

Harness 不会萎缩,只会转移

随着模型的不断进化,对 Harness 的需求并没有消失——而是转移了。

人们很容易想当然地认为,更聪明的模型会让这些“脚手架”变得可有可无。例如,最近模型能力的升级,确实极大地降低了我们去缓解“上下文焦虑”的需求。但是,随着能力下限的提高,上限也在随之提高。 以前遥不可及的复杂任务现在被提上了日程,这同时也带来了全新的、前所未见的失败模式。

Harness 中的每一个组件,都编码了一个关于“模型自身无法搞定什么”的底层假设。当模型进化时,过时的脚手架应该被拆除,但同时必须搭建全新的脚手架,以支撑我们攀登下一座高峰。

训练循环与 Harness 的反哺

在 Harness 设计和模型训练之间,存在着一个活跃的反馈循环。

如今的模型在进行后训练(post-trained)时,通常会把特定的 Harness 纳入训练回路中,这甚至造成了一定程度的“过拟合”。模型会变得对其 Harness 设计者优先考虑的特定动作(比如文件系统操作、Bash 脚本编写、子智能体调度)极其擅长。

这使得 Harness 成为了一个有生命的动态系统,而非一纸静态配置文件。这也证明了,世界上“最好”的 Harness,永远是那个专门为你的独特任务和工作流深度定制优化的 Harness。

Harness 即服务 (HaaS, Harness-as-a-Service)

整个行业正在经历一次范式转移:从基于提供文本补全的 LLM API 进行构建,转向基于提供运行时的 Harness API(Harness 即服务)进行构建。现在的 SDK 已经开箱即用地打包了循环控制、工具链、上下文管理、钩子和沙盒。

现代的默认开发模式,不再是从零开始手写编排逻辑,而是直接选择一个 Harness 框架,配置好核心支柱,然后将全部精力纯粹地投入到领域特定的提示词和工具设计上。

正是这一点让故障排查具备了可扩展性:你是在调试一个结构清晰的配置面,而不是在重新发明整个智能体架构。

未来的走向

如果你观察当今最顶级的那些编程智能体,你会发现它们彼此之间的相似度,甚至远高于它们底层模型之间的相似度。 尽管底层模型百花齐放,但 Harness 的设计模式正在迅速趋同。整个行业正在快速达成共识,找出那些把“生成式文本”转化为“可交付软件”所不可或缺的承重脚手架。

最激动人心的前沿课题正在超越单一智能体的范畴:如何并行编排多智能体;如何让智能体自己分析执行轨迹,以修复 Harness 层面的失效;以及如何构建一个能根据需要进行 JIT(即时)动态组装工具的环境。

最终,在这个阶段,Harness 将不再是静态的配置文件,而是开始表现得更像是一个编译器。

如果你正在寻找一个出色的智能体 Harness 框架,@FredKSchott 编写了 Flue。它非常硬核,而且显然是受到了本文早期版本的启发!

w3ctech微信

扫码关注w3ctech微信公众号

共收到0条回复