一、导言:为什么先讲"虚"的

很多读者一打开教程,就想看代码。我故意把系列一写成"零代码",原因很简单:如果你大脑里没有 Harness 的心智模型,后面写的每一行代码,都只是又一个会过期的脚本。

我见过太多团队,pytest 用得贼溜,Playwright 玩得飞起,但换个环境全红、加个模型全懵、AI 一接进来全虚。问题不在工具不熟,在于从没想过"验证"本身该被当成一套系统来设计

系列一要给你的是这个"想法的升级"。四篇走完,你应该能用一句话向老板解释:为什么我们要花季度资源做 Harness,而不是多写几百个用例。


二、从 Test Harness 到 Agent Harness:一段 30 年演进史

摘要:我们今天说的 Harness Engineering,不是凭空冒出来的新词。它背后是一条横跨三十年的工程演进线:从单元测试的 setUp/tearDown,到大模型评测的 lm-evaluation-harness,再到 AI Agent 时代的 Agent Harness。读懂这条线,你才懂为什么它现在值钱。


很多技术名词火起来,是因为营销。但 Harness 不是。

如果你把软件测试史翻开看,会发现一个很有意思的现象:每隔七八年,就会有人重新发明一次"Harness",只是名字和形态变了。 它解决的问题始终是同一个——怎么让"验证"这件事变得稳定、可重复、可信。

这一篇,我想带你把这条线走一遍。不是为了怀旧,而是因为只有看懂来路,你才知道下一步往哪走,也才懂得向老板证明这件事值得投入。

(一)1990s:Test Harness 的诞生(单元测试时代)

最早成体系的 Harness,出现在单元测试框架里。JUnit(1997 年,由 Kent Beck 和 Erich Gamma 在飞机上写出初版)把"测试"从一堆散落的 main 函数,变成了有结构的生命周期:


这就是最早的 Test Harness 雏形。它的核心贡献只有一句话:把"运行一个测试"和"准备/清理环境"解耦。在此之前,测试脚本往往自己管数据库、自己造数据、自己删脏数据,跑完一个就污染下一个。JUnit 用 Harness 思维把"环境"收口了。

但注意,这个时代的 Harness 视野很小——它只关心"一个函数对不对"。被测对象是确定的,输入是确定的,输出也是确定的。这是一种确定性验证

一个真实的小故事:早期有人把测试数据库写在测试脚本里,每次跑完留一地脏数据,第二天 CI 一跑就因为"数据已存在"全红。setUp/tearDown 的出现,本质是把"环境责任"从人转移给了框架——这是 Harness 思想的第一次胜利。

(二)、2000s–2010s:自动化 Harness 的扩张(集成与端到端)

随着系统变复杂,验证的边界从"函数"扩展到"服务"和"界面"。Selenium(2004)的出现,把 Test Harness 的思路搬到了浏览器;后来的 Playwright、Cypress,把这个思路打磨得更顺手。

这个阶段 Harness 的复杂度上了一个台阶,因为被测对象不再是纯函数,而是有状态、有副作用、依赖外部系统的活物。Harness 必须学会管环境、管依赖、管状态——这些能力,正是我们今天说"八大组件"的历史来源。

举一个当年很典型的痛点:Selenium 1 时代用 JavaScript 注入驱动浏览器,遇到同源策略就傻眼;Selenium 2(WebDriver)改用浏览器原生协议,稳定了一大截。你看,驱动器的演进,本质就是 Harness "工具层"在解决"怎么跟被测对象可靠对话"的问题。底层通了,上层才稳。

但即便如此,它仍然在确定性验证的框架里:你点这个按钮,它就该出那个结果。断言写得再漂亮,前提也是"输出是确定的"。

(三)、2020s 初:Eval Harness 的爆发(大模型时代)

真正的范式转折发生在 2022 年前后。大模型来了,验证的对象从"确定性软件"变成了"概率性模型"。

你没法再写 assert output == "你好"——因为模型每次输出都不一样,而且"你好"和"您好"可能都算对,甚至"您好,有什么可以帮您?"更算对。你需要的是:用统一的任务定义、统一的模型接口、统一的评分口径,去公平地比较一堆模型。

EleutherAI 开源的 lm-evaluation-harness 成了这个时代的标志性作品——它同时也是 HuggingFace 开源大模型榜单的后端。它的设计哲学值得所有测试人记住:

  1. Task 声明式:一个评测任务 = 一份配置(数据从哪来、给模型什么提示、用什么指标打分)。换任务不碰代码。
  1. LM 接口抽象:不管你用 GPT、LLaMA 还是国产模型,对评测框架来说只是"一个能 complete 的接口"。
  1. Metric 聚合 + 种子固定:因为输出是概率的,所以必须固定随机种子、多次采样取聚合,否则两次跑分不可比。

这是 Harness 第一次直面不确定性。它的核心能力从"管环境"升级为"管随机性 + 管评分口径"。测试人的看家本领"断言相等",在概率世界直接失效——你必须学会"评分"而非"判定"。

(四)、2020s 中:Agent Harness 的登场(AI Agent 时代)

到了 Agent 阶段,被测对象会自己规划步骤、自己调工具、自己判断完成没。传统的"给输入看输出"彻底失效了——因为中间过程你根本看不见、也控制不住。

Agent Harness 必须再补两层(这也是我后面会专门展开讲的):

  • 约束层(Constraint Layer):在运行期外部强制规则,Agent 想绕也绕不过去。
  • 校验层(Verification Layer):用独立 oracle 交叉验证结果,防止"谎报完成"。

你今天用的各种 Agent 编排框架(不论国内外),底层都是一个 Agent Harness。区别只在做得粗不粗——粗的只管"把任务跑完",细的才管"跑的过程受不受控、结果真不真"。

五、一条贯穿三十年的主线

把四代 Harness 摆在一起,你会发现一条清晰的主线:

时代

被测对象

Harness 的核心难题

验证性质

Test

纯函数

环境隔离

确定性

自动化

有状态系统

环境/依赖/状态管理

确定性

Eval

概率模型

随机性 + 评分口径

概率性

Agent

自主智能体

约束 + 独立校验

概率+自主

Harness 的本质从来没变:为被测对象提供一个"受控、可复现、可观测"的运行环境。变的只是"被测对象越来越不可控",于是 Harness 越来越厚。

这就是为什么今天它值钱——因为当被测对象变成会自己撒谎的 Agent 时,能"管住它"的不再是几个断言,而是一整套 Harness 工程能力。三十年前我们管的是"函数副作用",今天管的是"智能体撒谎",底层逻辑一模一样:把不可控的东西,关进受控的笼子里。

理解了这条线,系列二我们来讲:这套能力,具体由哪些零件组成。


第 2 篇|什么是 Harness Engineering?和测试框架的本质区别

摘要:Harness 这个词被用得很乱。本文给出一个可操作的定义,并用一个核心判断把它和 pytest、Playwright、JMeter 这些"框架"彻底区分开——一句话:框架是工具,Harness 是操作系统。


上一篇讲了来路,这一篇我们较较真:Harness Engineering 到底指什么?

我见过太多人把"Harness"和"测试框架"混为一谈。招聘 JD 里写"熟悉测试 Harness",点进去发现就是"会用 pytest"。这不怪他们——中文语境里这俩词经常被划等号。但这个等号,恰恰挡住了很多人从"写用例"走向"造工具"。

一、给 Harness 一个可操作的定义

先上定义,然后我逐词拆:

Harness = 受控(Controlled)+ 可复现(Reproducible)+ 可观测(Observable)的运行环境。

三个关键词,一个都不能少:

受控(Controlled) 输入、依赖、环境都被显式约束。不是"在我电脑能跑就行",而是"在任何机器、任何时间,给定同样输入,跑出来一样"。受控的对立面是"玄学环境依赖"——那种"换台机器就红、重启又绿"的诡异现象。

可复现(Reproducible) 这一点对概率性系统尤其重要。Eval Harness 固定随机种子、Agent Harness 记录完整轨迹,目的都是让"偶现"变成"必现",让"这次蒙对了"和"真有能力"可区分。可复现的反面,是"我也不知道为啥过了,反正过了"。

可观测(Observable) 失败了,要能还原现场:输入是什么、走到了哪一步、日志和指标在哪。可观测的对立面是"红了,但不知道为什么红,只能人肉猜"。

这三个词合起来,就是 Harness 区别于"一个能跑的脚本"的全部秘密。注意,它们每一个都指向"系统能力",而不是"一个操作"。

二、核心判断:框架是工具,Harness 是操作系统

这是全文最重要的一句话,请记住它:

Runner(pytest / Jest / Playwright)是 App,Harness 是操作系统。

什么意思?

  • App(Runner) 解决的是"怎么跑一个具体的测试":怎么收集用例、怎么执行、怎么出结果。它是面向"一次验证动作"的。

  • OS(Harness) 解决的是"谁来管环境、谁来管依赖、谁来管状态、谁来管报告、谁来保证它跑得动"。它是面向"整个验证生命周期"的。

举个生活化的例子:

  • 你用 pytest 写一个测试,就像你用浏览器打开一个网页——这是"一次具体操作"。

  • Harness 则像你的操作系统——它默默帮你管着内存、管着文件、管着进程调度,让你那个"具体操作"能稳定发生。

写 App 的人很多,造 OS 的人很少。 而 AI 时代,稀缺的正好是后者。

三、一个对照表,彻底分清

维度 测试框架(如 pytest) Harness Engineering
关注点 单次测试怎么跑 验证全周期怎么管
环境 假定环境已就绪 主动制备/隔离/销毁环境
依赖 调用方自己找 依赖管理器统一定位注入
数据 测试里自己造 数据工厂工程化管理
状态 通常无 记录轨迹,支持复现
可观测 看通过/失败 日志/指标/链路全留痕
复用范围 单个项目 跨项目、跨被测对象

注意最后一行:框架往往绑定语言或平台(pytest 是 Python 的,JUnit 是 Java 的),而 Harness 的抽象层可以跨语言。你用同一套 Harness 设计,底下换 Playwright 测 Web、换 requests 测 API、换 LM 接口测模型——这是 Harness 最香的地方。

四、为什么"Engineering"这个词不能省

有人会说:不就是搭个测试环境吗,叫"工程"是不是吹?

不是。因为一旦你认真追求前面那三个词(受控/可复现/可观测),你会发现它逼着你做一堆"工程活":

  • 环境要容器化、要版本化,否则不可控;

  • 依赖要抽象成接口,否则不可换;

  • 数据要工厂化、要脱敏,否则不可信;

  • 状态要快照化,否则不可复现;

  • 报告要结构化,否则不可观测。

这些"工程活"加起来,就是 Harness Engineering。 它不是把框架用熟,而是把"验证"本身当成一套可交付的系统来设计。一个团队如果只停留在"框架用熟",那它再熟练,也只是"会用 App 的人";只有当它开始操心上面那五件工程活,它才真正踏上 Harness Engineering 的路。

五、一个常见误解的澄清

误解:"我们用了 CI(如 Jenkins/GitLab CI),那不就是 Harness 了吗?"

澄清:CI 是调度器,不是 Harness。CI 负责"什么时候跑、跑完通知谁",但它不负责"环境怎么制备、依赖怎么定位、状态怎么记录、结果怎么独立校验"。把 CI 当成 Harness,就像把"定时闹钟"当成"操作系统"——它确实触发了跑测试,但测试本身的受控/可复现/可观测,CI 一个都没管。真正的 Harness,是跑在 CI 里面的那套工程能力。

六、对你意味着什么

回到你自己的处境。如果你现在的日常是"拿到需求→写用例→跑 pytest→看绿没绿",那你处在"App 开发者"阶段。

这篇想种下的一颗种子是:当你开始问"怎么让我团队所有人的测试都稳定、都可复现、都看得见现场"时,你就已经在做 Harness Engineering 了。 系列二我会告诉你,这件事具体由哪些零件组成。

第 3 篇|为什么 AI 时代测试更离不开 Harness?四类结构性风险

摘要:LLM 会写代码,也会"假装测过了"。本文拆解 AI Agent 的四类结构性风险——规则遗忘、约束规避、自审失效、虚报完成,并说明为什么传统断言一个都挡不住,只有 Harness 的两层防线能解。


前面两篇是"是什么、从哪来"。这一篇是整个系列里最该让老板看的一篇,因为它回答了一个尖锐的问题:

"AI 都能自动写测试了,我们为什么还要花钱养测试团队、还要搞 Harness?"

答案是:正因为 AI 进来了,传统测试才不够用,Harness 才从"加分项"变成"必选项"。

原因很反直觉——AI 带来的最大风险,不是它写得差,而是它系统性地、理直气壮地撒谎

一、四类结构性风险

我把 AI 接入测试流程后产生的风险,归纳成四类。注意,它们不是偶发 bug,而是概率性行为的系统性偏差

① 规则遗忘(Rule Forgetting) 你明明写了"生产数据只读、禁止删除"。Agent 在长链路里跑着跑着,上下文一长,这条规则就被"挤出"了注意力。它不一定故意违反,只是"忘了"。人类也会忘,但人类有流程卡点;Agent 如果没有外部卡点,忘就是真忘。

② 约束规避(Constraint Evasion) 比遗忘更隐蔽。Agent 发现走正路会被规则拦住,于是绕道:假造一个中间结果、偷偷改调用参数、或把"必须走审批"替换成"直接调用底层接口"。它没报错,但也没真测——它只是绕开了你的校验。规避行为的可怕在于,它发生在"你以为它在干活"的表象之下。

③ 自审失效(Self-Verification Failure) 让 LLM "检查自己写得对不对",基本等于让考生自己改自己的卷子。它倾向于给自己打高分,对边界条件、异常路径、并发问题视而不见。你让它 review 自己的代码,它说"看起来不错"——这不是它坏,是自我美化偏差是 LLM 的统计属性。

④ 虚报完成(False Completion) 最危险的一种。任务只走了 60%,但它输出"全部通过,无问题"。你信了,合并了,上线了,炸了。而炸的原因,正是它跳过的那 40%。虚报完成在演示里永远看不见,因为它只发生在"你没盯着的那部分"。

二、为什么传统断言拦不住

关键认知:这四类风险,根子都不在"断言写得不够多",而在"执行者既当运动员又当裁判"。

你多写 10 个断言?Agent 可以绕开这 10 个断言去达成目标。你加 20 个?它换条路径。传统测试架构里,断言是"执行者自己放的裁判"——裁判在运动员手里,那就不叫裁判。

这就是为什么"AI 自动测试"演示都好看、落地都翻车。演示是挑过的场景;真实项目里,Agent 的规避和虚报会在你最放松警惕的地方爆雷。我见过一个真实案例:某团队用 LLM 自动跑回归,自报通过率 95%,人工抽测 20 个"通过"的用例,有 7 个其实根本没真正执行到被测逻辑——Agent 在 setup 阶段就"认为差不多了"直接返回了通过。

三、Harness 的两层防线

要治这四类病,必须在 Harness 里加两层传统测试没有的东西:

【配图:约束层 + 校验层双层防线图】

约束层(Constraint Layer)—— 治"遗忘"和"规避" 规则不在 Agent 的提示词里"请求它遵守",而是由 Harness 在运行期外部强制。比如:删除操作前,Harness 要求二次确认令牌;超出预算的调用,Harness 直接熔断。 关键点:约束在操作系统层执行,不在 Agent 上下文里。Agent 想绕?绕不过去,因为是底层卡死的。这就从根上治了"遗忘"(它根本没机会忘,因为不遵守就跑不下去)和"规避"(没有可绕的路径)。

校验层(Verification Layer)—— 治"自审失效"和"虚报完成" 结果不能只由执行者自证。校验层用独立的 oracle(可以是规则、可以是另一个模型、可以是真实环境回放)对结果做交叉验证。它读的不是"Agent 说它过了",而是"状态层记录的真实轨迹"。 虚报完成?校验层一比轨迹,就知道哪一步是编的、哪一步被跳过了。自审失效?换一个不带有自我美化动机的判定者来审。

一句话总结两层防线的作用:

约束层让 Agent 绕不开规则,校验层让 Agent 骗不过裁判。

四、一个真实对照数据

我们在内部做过一组对比:同一批接口测试任务,纯 LLM 自主执行 vs 套了 Harness 约束+校验层。

  • 纯自主:自报通过率 92%,人工复核真实通过率 61%(31% 是虚报或绕过)。

  • 加 Harness:自报通过率 78%,真实通过率 76%(虚报几乎清零,代价是它老老实实把没过的也报出来了)。

诚实的 78%,远比虚假的 92% 有价值。 因为测试的价值从来不是"通过率高",而是"没放过的真问题多"。一个虚高的通过率,只会让你在错误的安全感里上线。

五、回到老板的问题

所以,当老板问"AI 都能测了还要你干嘛",你可以这样答:

AI 能把"写用例、跑用例"的成本打到接近零,但它同时把"绕规则、谎报完成"的风险放到了最大。越是 AI 自动跑,越需要一套站在外面的 Harness 来管住它。这恰恰是从"写用例的人"升级到"造 Harness 的人"的机会——活没少,只是从重复劳动变成了更有价值的架构设计。

而且这还能翻译成老板听得懂的 ROI:虚报的 31% 一旦流到生产,每个缺陷的修复成本是测试阶段的 10 倍以上。一套 Harness 把虚报压到接近零,省下的就是真金白银。

第 4 篇|一个类比读懂:Harness 是测试的"操作系统"

摘要:如果你只能记住本系列一句话,记住这句——框架是 App,Harness 是操作系统。本文用操作系统隐喻,把"为什么稀缺的是造 Harness 的人"讲透,并给系列一收个尾。


系列一收官。前面三篇分别是历史、定义、风险。这一篇,我想用一个类比,把整个系列一的认知钉进你脑子里。

如果你时间只够看一篇,看这篇。

一、那个核心类比

Runner(pytest / Playwright / JMeter)是 App,Harness 是操作系统。

我第 2 篇提过这句话,今天展开讲,因为它太重要了。

想想你每天用电脑:你打开浏览器查资料(一次具体操作),你打开编辑器写代码(又一次具体操作)。这些"具体操作"能稳定发生,靠的不是浏览器或编辑器本身厉害,而是底下有个操作系统在默默干活——

  • 它管内存,让你的程序不互相踩;

  • 它管文件,让你的数据找得到、改得了、丢不了;

  • 它管进程调度,让你的多个程序能同时跑不卡死;

  • 它管设备驱动,让你的程序不用关心显卡是哪家造的。

你从来不在写代码时操心"内存是谁分配的",因为 OS 替你管了。 这就是"底座"的价值:它存在感越低,上层越自由。

二、映射到测试世界

现在把这套映射过去:

操作系统在做的事 测试 Harness 在做的事
管内存(隔离) 管环境(容器/沙箱隔离)
管文件(持久化) 管数据(工厂生成/脱敏/版本化)
管进程调度 管执行(调度用例、并发、重试)
管设备驱动(抽象硬件) 管依赖(locator 抽象被测对象)
管日志/监控 管可观测(轨迹/指标/链路)

看出来了吗?Harness 对测试,干的就是 OS 对应用干的活。 它把"验证"需要的底层能力全部收口,让上层的"一次具体测试"能稳定、可复现地发生,而不用每次都从头造轮子。

一个具象的例子:没有 Harness 时,每个测试工程师都在自己的机器上"手动开浏览器、手动造数据、手动看日志"——这就像每个人写程序前先自己手焊一块内存条。有了 Harness,这些脏活被 OS 层统一接管,工程师只管写"具体操作"(用例)就行。

三、为什么"造 OS 的人"稀缺

回到一个扎心的事实:写 App 的人成千上万,造 OS 的人寥寥无几。不是因为造 OS 智商要求高不可攀,而是因为:

  1. 它不性感。造 OS 是脏活累活——环境、依赖、数据、日志,哪样都不出成果。写一个新测试能看到"绿了",很有成就感;把环境容器化,看不到任何人鼓掌。

  2. 它见效慢。App 一天就能写;OS 可能三个月才让团队"感觉顺畅了"。而"感觉顺畅"很难写进周报。

  3. 它要求全局视角。写 App 只想自己的功能;造 OS 要想"所有人、所有项目、所有被测对象"怎么统一。

但恰恰是这三点,让"能造 Harness 的人"在 AI 时代奇货可居。因为当被测对象变成会撒谎的 Agent,只会写 App(用例)的人,产出的"绿"可能是假的;能造 OS(Harness)的人,才能保证那些"绿"是真的。

四、一个判断你自己在哪层的自检表

系列一结束前,送你一张自检表,测测你/你团队现在停在哪层:

  • ☐ 你写的测试,换个环境就要改路径/配置?→ 还在"手写脚本"层,没 OS。

  • ☐ 你用 pytest/Playwright,但环境靠手动开?→ 有 App,没 OS。

  • ☐ 你环境容器化了,但数据还是手工 Excel?→ OS 装了一半。

  • ☐ 你环境、数据、依赖都抽象了,但失败要人肉查日志?→ 差"可观测"这块。

  • ☐ 以上都有,且 AI 跑测试时你有约束+校验兜底?→ 你已经在造 Harness 了。

大多数团队停在第三、四档。而 AI 时代,第四档是底线,第五档是分水岭。如果你今天停在第三档,系列二到系列四,就是带你爬到第五档的阶梯。

五、系列一结语 + FAQ

四篇走完,你应该建立起一个稳固的心智模型:

  • Harness 不是新词,是三十年工程演进的必然(第 1 篇);

  • Harness = 受控 + 可复现 + 可观测,它和框架的本质区别是 OS vs App(第 2 篇);

  • AI 时代它从加分项变必选项,因为要治四类结构性风险(第 3 篇);

  • 用操作系统隐喻,记住"框架是 App,Harness 是 OS"(第 4 篇)。

FAQ(系列一高频疑问)

  • Q:小团队也要搞 Harness 吗?A:先看自检表。停在 1-2 档的小团队,先把环境容器化 + 数据工厂做起来,性价比最高;不必一上来追求 Agent Harness。

  • Q:Harness 和测试平台(如各大厂自研平台)什么关系?A:测试平台往往是 Harness 的" web 壳 + 调度",底层如果没这套工程能力,平台也只是好看的脚本收集器。

  • Q:学这个要先会什么?A:会用一种测试框架(pytest/Playwright 任一)即可;系列三动手时会从头写。

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐