Harness Engineering:当模型趋同,胜负在模型之外
同一匹马,套上不同的挽具,有的能拉着马车一路狂奔,有的却原地打转。
今天的大模型也是如此。模型只是那匹马,决定它能干多少活的,是套在它身上的那套"挽具"——Harness。
引言:为什么同一个模型,能力却天差地别?
关注 AI Coding Agent 的人,大概都撞见过一个反常的现象。
在 SWE-bench(一个用真实 GitHub issue 考验 AI 修 bug 能力的基准)榜单上,同一个底层模型被不同工具调用,分数能差出二三十个百分点:同样的 Claude 或 GPT,套在 SWE-agent 上是一个成绩,套在 Aider、OpenHands、Claude Code 上又是另一个成绩。模型没变,权重一个比特都没动,能力却像换了一个档次。
你自己多半也体验过:同一个模型,在 A 工具里像个磕磕绊绊的实习生,换到 B 工具里突然又快又准。换的不是马,是马身上那套家伙什。
差距来自模型外面那一层——它怎么读代码、怎么调工具、怎么管理上下文、出错了怎么回退、什么时候该停。这套包裹在模型外围的工程系统,就是本文的主角:Harness(智能体外壳 / 挽具);围绕它的设计与实践,正被越来越多人称为 Harness Engineering。
随着模型越来越强、越来越同质化,一个判断正成为共识:
模型决定能力的上限,Harness 决定你能榨出多少。
第一部分:harness 一词从哪来——从"测试外壳"到"智能体外壳"
1.1 老程序员都熟悉的 “Test Harness”
“Harness” 在软件工程里并不新鲜。写过自动化测试的人,都听过 Test Harness(测试外壳 / 测试夹具):为了让"被测系统"能自动跑起来,在它外围搭的那一整套支撑设施——喂入输入、驱动执行、捕获输出、比对结果。被测代码是核心,但没有这层外壳,它既跑不起来,也无法被反复、稳定地验证。
harness 的英文本义是马具、挽具——套在马身上、把马力传导到马车上的那套皮带和装置。这个隐喻非常精准:
- 被测系统 / 模型 = 马(提供原始动力)
- harness = 挽具(约束、引导、把动力传导到正确的方向)
马再有劲,没有挽具也只是一匹野马;套上挽具,它才能拉着车往你要去的地方走。
1.2 大模型时代,“外壳"重新变得关键
当 LLM 从"对话补全"走向"自主完成任务”,一个老问题换上新形式回归:一个只会预测下一个 token 的模型,没法直接干活。它需要被放进一个循环里——
- 感知:读取当前任务和环境状态
- 思考:决定下一步做什么
- 行动:调用工具(读文件、跑命令、搜索……)
- 观察:拿到工具返回的结果
- 回到第 1 步,直到任务完成或失败
这个"感知—思考—行动—观察"的循环,加上支撑它运转的所有代码——工具定义、上下文拼装、提示词模板、错误处理、停止判断、权限控制——合在一起,就是 Agent Harness(智能体外壳)。
它和 Test Harness 是同一个思想的延续:核心组件(模型)不变,真正决定它能不能稳定干活的,是外面那层工程。
1.3 SWE-bench 把这件事彻底说清楚了
真正让 harness 这个概念出圈的,是 Coding Agent 的爆发。当大家拿同一个模型在 SWE-bench 上跑出截然不同的成绩,行业突然意识到:
我们一直以为在比模型,很多时候比的其实是 harness。
同样一个模型,给它一个只能看全文件、不能搜索的 harness,它会被海量无关代码淹没;给它一个能精准 grep、能读特定行、能跑测试拿反馈的 harness,它就像换了个人。同一匹马,不同的挽具。于是一个新领域被命名出来:Harness Engineering——专门研究"如何给模型这匹马,套上最趁手的挽具"。
平心而论,harness engineering 还算不上写进教科书的正式学科,更像这一两年从一线实践里长出来的共识词。但翻翻 Anthropic 的《Building Effective Agents》、各家 Coding Agent 团队的工程复盘、以及 SWE-bench 榜单背后的讨论,会发现大家都在围着同一件事打转:模型之外的那层工程,到底该怎么做。 给它起个名字,叫 harness engineering,恰如其分。
第二部分:到底什么是 Harness Engineering
2.1 先厘清:什么是 “Engineering”
我们天天把工程挂在嘴边,但"engineering"分量很重,它和"写代码"不是一回事。
工程的本质是:在约束条件下,用系统化、可重复、可验证的方法,把不确定的东西做成可靠的东西。
拆开看,它至少包含四个特征:
| 特征 | 含义 |
|---|---|
| 系统化 | 不靠灵感和运气,而是有方法、有流程、有结构 |
| 可重复 | 同样的输入,能得到同样可靠的结果,而不是"碰巧成功" |
| 可验证 | 有明确的标准判断"做对了没有",能测量、能复现 |
| 权衡取舍 | 在性能、成本、复杂度、可靠性之间做有意识的 trade-off |
偶尔调出一个效果惊艳的 prompt,那是手艺;把"如何稳定地让模型完成某类任务"变成一套可复现、可测量、可迭代的系统,才是工程。这个区分,恰好划清了 prompt engineering 和 harness engineering 的边界。
2.2 Harness Engineering 的定义
两条合起来,定义就清晰了:
Harness Engineering 是一门工程学科,研究如何设计、构建和持续优化包裹在大模型外围的那套系统(harness),使模型能够稳定、可靠、安全地完成复杂任务。
它关心的不是"模型本身有多聪明",而是:
- 怎么把任务和环境信息,以模型最容易理解的方式喂进去?
- 给它哪些工具?工具的接口怎么设计模型才用得好?
- 上下文窗口有限,留什么、丢什么、什么时候压缩?
- 模型走错了路,怎么让它拿到反馈、自我纠正?
- 怎么判断任务完成了?怎么防止它做出危险操作?
- 这一切,怎么测量、怎么持续改进?
模型是给定的,harness 是你能动手做的工程。
2.3 一条清晰的演进线:Prompt → Context → Harness
要理解 harness engineering 的位置,最好把它放进这两年的演进脉络里:
| 阶段 | 关注点 | 隐喻 | 局限 |
|---|---|---|---|
| Prompt Engineering | 怎么"问"一句话 | 给马指一个方向 | 单轮、靠技巧、难复现 |
| Context Engineering | 在每一步给模型看什么 | 决定马的视野范围 | 仍聚焦"输入信息"这一环 |
| Harness Engineering | 模型运转的整个系统 | 整套挽具 + 马车 + 路线 | —— |
三者不是替代关系,而是层层包含:
┌─────────────────────────────────────────────┐
│ Harness Engineering │
│ (循环 / 工具 / 反馈 / 停止 / 安全 / 评测) │
│ ┌─────────────────────────────────────────┐ │
│ │ Context Engineering │ │
│ │ (每一步给模型看什么信息) │ │
│ │ ┌───────────────────────────────────┐ │ │
│ │ │ Prompt Engineering │ │ │
│ │ │ (单次怎么措辞 / 怎么提问) │ │ │
│ │ └───────────────────────────────────┘ │ │
│ └─────────────────────────────────────────┘ │
└─────────────────────────────────────────────┘
Prompt engineering 是"一句话怎么说",context engineering 是"这一步给它看什么",而 harness engineering 是"整个任务怎么跑下来"——它是最外层、也是最工程化的那一层。
第三部分:Harness Engineering 涵盖哪些范围
很多人以为"做一个 agent"就是写个 while 循环加几个工具。真做过的人都知道,魔鬼全在细节里。一个成熟的 harness,至少要处理下面六块。
3.1 Agent Loop:循环的骨架
这是 harness 的心脏——“思考—行动—观察"的主循环。看似简单,工程上却要回答一堆问题:
- 每一轮,模型能调几个工具?串行还是并行?
- 工具返回后,结果怎么拼回上下文?
- 循环什么时候停?任务完成、达到上限、还是模型主动声明结束?
- 卡住了(反复试同一个错误操作)怎么打破死循环?
# 一个极简的 agent loop 骨架
while not done:
response = model.generate(context) # 思考
if response.has_tool_calls:
results = execute_tools(response) # 行动
context = update_context(context, results) # 观察
else:
done = check_completion(response) # 停止判断
举个最容易踩的坑:死循环。你让 agent 跑测试,挂了;它改一行再跑,又挂了——于是它开始鬼打墙,把那一行改过来、改回去、再改过来,能烧掉几十次 API 调用还在 happy 地耗 token。一个不长脑子的 loop 发现不了,它只知道"模型还想干活,那就接着转”。harness 要做的,是替它装上刹车:检测到重复操作就打断,把"你已经试过这招了,没用,换个思路"明明白白塞回上下文。
还有:循环什么时候算结束?是模型说一句"我搞定了"就信(它很爱自我感觉良好),还是必须等测试真的绿了才算?这些都不是写代码的活,是判断的活。真实世界的 harness,就是在这十几行骨架的每一行上,都堆满了这类工程决策。
3.2 Tool Design:工具是 harness 的"手脚"
模型本身只会输出文字,能影响真实世界全靠工具。工具设计的好坏,几乎直接决定 agent 的天花板。关键经验:
- 接口要对模型友好,而不只是对程序员友好。 工具的名字、参数、描述,本质上是写给模型看的提示词。
- 粒度要合适。 一个万能的
run_anything工具,模型反而不会用;一组职责清晰的小工具(读文件、搜索、跑测试)效果更好。 - 返回值要可读、可行动。 报错信息要让模型知道"下一步该怎么改",而不是吐一堆栈。
- 要给"撤销"和"验证"留口子。 改完文件能否跑测试确认?这是 agent 能自我纠错的前提。
举个特别说明问题的例子。你给模型一个 edit_file 工具,让它传"整个文件的新内容"——听起来挺合理?结果是:改一个 500 行文件里的两行,模型得把 500 行原样吐一遍,又慢又贵,还动不动手抖改坏别处。换个设计:让它传"把这段旧文本替换成这段新文本",模型只需盯着要改的那几行,又快又准,出错率断崖式下降。模型还是那个模型,只因工具的"形状"变了,效果天差地别。工具设计,本质上是在替模型设计一种它用着顺手的语言。
3.3 Context Management:在有限窗口里做减法
上下文窗口是稀缺资源。harness 必须主动管理:
- 该塞什么进去:当前任务、相关代码片段、历史操作、工具结果。
- 该丢什么 / 压什么:老旧的、无关的对话,要么裁剪,要么摘要压缩。
- 怎么检索:不是把整个代码库塞进去,而是按需 grep / 检索出最相关的几段。
一句话:context engineering 是 harness engineering 的一个核心子模块——给模型看对的东西,比给它看更多的东西重要得多。
3.4 反馈与自我纠错:让 agent 学会"试错"
人类工程师厉害,不是因为不犯错,而是犯错后能根据反馈快速修正。好的 harness 要把这种能力工程化:
- 跑命令拿到 stderr → 喂回模型让它修
- 跑测试拿到失败用例 → 让它定位并修复
- 引入"验证步骤"(linter、类型检查、单测)作为客观反馈源
这里有个很反直觉的点:让 agent 变强的,往往不是更聪明的模型,而是更快、更狠的反馈。 不让它跑测试,它写完只能"凭感觉觉得对了",交上一堆似是而非的东西;让它每改一次就立刻跑测试,它就能像真人调试那样一点点逼近正确答案。反馈回路越及时、信号越干净,agent 看起来就越"聪明"——其实它没变聪明,是你给了它一双眼睛。
没有反馈回路的 agent,本质上是在闭着眼睛走路。
3.5 安全与权限:给挽具装上"刹车"
agent 能动手,就意味着能闯祸。harness 必须内建护栏:
- 危险操作(删文件、改生产配置、对外发请求)要不要先确认?
- 哪些目录 / 命令是禁区?
- 怎么防止 prompt injection 让 agent 干出格的事?
权限模型不是产品功能的附属品,它是 harness 工程的一等公民。
3.6 评测(Eval):没有度量,就没有工程
这是"手艺"和"工程"的分水岭。改了 harness 之后到底是变好还是变差,你怎么知道?答案是:建立可复现的评测集。
- 收集一批有标准答案的真实任务
- 每次改动 harness,跑一遍,看通过率、成本、耗时
- 把"我觉得变好了"变成"通过率从 62% 提到 71%"
SWE-bench 之所以重要,正是因为它给整个行业提供了一把可比较的尺子。
说句扎心的实话:很多人调 agent,全程靠"我跑了俩 case 感觉这版顺多了"。这不叫工程,叫玄学。今天改个 prompt 觉得变好,明天换个任务可能就崩了,而你压根不知道——因为你从没量过。评测集哪怕只有十几条,也比一万句"我觉得"值钱。
小结:harness 的六大模块
| 模块 | 一句话职责 | 核心问题 |
|---|---|---|
| Agent Loop | 驱动任务运转的主循环 | 怎么转、何时停、卡住怎么办 |
| Tool Design | 模型影响世界的接口 | 给什么工具、接口怎么设计 |
| Context Management | 管理有限的上下文窗口 | 留什么、丢什么、怎么检索 |
| 反馈与纠错 | 让 agent 试错并修正 | 反馈从哪来、怎么喂回 |
| 安全与权限 | 给 agent 装护栏 | 哪些能做、哪些要确认 |
| 评测 Eval | 度量 harness 的好坏 | 怎么证明"改好了" |
第四部分:如何应用 Harness Engineering——一条落地路径
道理讲完,落到自己项目里该怎么做?给一条务实的五步路径。
第 1 步:先定义"任务"和"成功"
别一上来就写 agent。先回答:
- 我要 agent 完成的,到底是哪一类任务?(修 bug?写报告?处理工单?)
- 什么叫"做对了"?用一句能被验证的话写下来。
这一步本质上是在为评测集打地基。 任务边界越清晰,后面的工程越好做。
第 2 步:搭一个"能跑通"的最小 harness
别追求完美。用最简单的 loop + 两三个工具,让它端到端跑通一个最简单的 case。目标是先有一根能跑的基线,而不是一开始就上全套。
第 3 步:建评测集,把基线量出来
哪怕只有 10 个任务,也要把基线通过率测出来。从这一刻起,优化才有了坐标系。没有 baseline 的优化,全是自我感动。
第 4 步:针对失败案例,逐个模块改进
去看 agent 失败在哪一步——
- 是没找到相关代码?→ 改 context / 检索
- 是工具用错了?→ 改 tool 接口和描述
- 是改错了没发现?→ 加 验证 / 反馈回路
- 是兜圈子停不下来?→ 改 loop 的停止逻辑
每改一处,跑一遍评测集,确认是真的变好,而不是按下葫芦浮起瓢。
第 5 步:加护栏,再放量
通过率稳定了,再补上权限、确认、日志、可观测性这些"上生产"必需的东西,然后逐步放大使用范围。顺序很重要:先证明有效,再保证安全,最后才谈规模。
定义任务 → 最小 harness → 量基线 → 看失败 → 改模块 → 重测
↑___________________│
(循环,直到达标)
→ 加护栏 → 放量
第五部分:推荐的实践方式(Do & Don’t)
把行业里反复被验证的经验,浓缩成一张正反对照表。
| ✅ 推荐这样做 | ❌ 避免这样做 |
|---|---|
| 从最小可用 harness 起步,迭代演进 | 一开始就设计"大而全"的框架 |
| 先建评测集,用数据驱动每一次改动 | 凭感觉"我觉得这样更好" |
| 工具接口写给模型看,描述清晰、粒度合适 | 把工具当内部 API,参数晦涩 |
| 主动管理上下文,按需检索 | 把所有信息一股脑塞进窗口 |
| 为每个能动手的工具配上验证/反馈手段 | 让 agent 执行后"听天由命" |
| 把权限和安全当一等公民 | 等出事了再补护栏 |
| 让 agent 可观测:记录每一步决策 | 黑盒运行,出错无从复盘 |
再补充三条更"高阶"的心法:
简单优先(Bias to Simplicity)。 能用 workflow(固定流程)解决的,就别上自主 agent。自主性越高,越难控、越贵、越难调。先问自己:“这个任务真的需要模型自己做决策吗?”
把 harness 当产品迭代,而非一次性脚本。 模型在升级,任务在变化,harness 需要持续维护和回归测试。今天最优的挽具,换一匹更强的马,可能就要重调。
盯成本,不只盯效果。 多一轮工具调用、多塞一段上下文,都是真金白银的 token。harness engineering 的核心 trade-off,永远是效果 vs. 成本 vs. 延迟。
结语:模型在收敛,工程在分化
我们正站在一个有趣的拐点上。
一方面,底层模型在快速收敛——头部模型的能力差距在缩小,能力本身正在变成一种"商品"。另一方面,真正拉开差距的工程能力,正在向模型之外转移:同样的模型,谁的 harness 做得好,谁就能把它的潜力榨得更干净。
这正是 Harness Engineering 作为一门独立学科浮现的根本原因。它接续了软件工程里 “test harness” 的古老智慧,又回应了大模型时代最现实的问题:
当人人都能用上同一匹好马时,胜负就取决于谁的挽具做得更好。
Prompt engineering 教我们怎么问一句话,context engineering 教我们每一步该看什么,而 harness engineering 把我们带到更高的视角——如何系统化地设计模型运转的整个世界。
如果说过去十年,软件工程师的核心竞争力是"把意图翻译成代码";那么接下来十年,一项越来越值钱的能力,或许是把模型这匹野马,套上一副最趁手的挽具。
这,就是 Harness Engineering。