AutoHarness: AI时代的TDD

1. 什么是 Harness
OpenAI 最近的一篇工程博客 《Harness engineering: leveraging Codex in an agent-first world》 引发了很多讨论。他们分享了如何用 Codex 在 5 个月内生成约 100 万行代码,交付真实产品的经验,并将这种工程实践称之为 Harness Engineering。
Harness Engineering 讨论的一个核心转变是工程师从"自己编写代码"变成"设计编写代码的环境、明确编写代码的意图、构建编写代码的反馈回路"。
具体到工程实践里,它主要包括三方面内容:
- 怎么组织上下文:比如用 AGENTS.md、文档目录、执行计划,让模型知道这个仓库的地图长什么样。
- 怎么把规则落地:比如模块怎么分层、依赖该往哪个方向流、哪些边界不能跨,再加上 Linter、CI、测试,把“应该怎么写”变成系统里的硬约束。
- 怎么把结果喂回来:比如日志、指标、浏览器、工作树实例,再加上代码审查和后续纠偏,让模型不仅能写,还能看到自己写出来的东西跑成什么样。
这就是 Harness,它的作用很直接:给 LLM 加规则,减少跑偏。但现实中很多情况却是:LLM 往往知道规则,但执行时还是会犯错。
在最近的 Kaggle GameArena 国际象棋比赛上,Gemini-2.5-Flash 的失败任务中,有 78% 的原因不是策略出了问题,而是出现了"非法动作"。它知道象棋规则,但真正下棋时,还是会走出不合规则的动作。
这不是知识问题,而是执行问题。
传统的解决办法要么成本高(微调模型),要么劳动密集(人工校准 Harness)。而 Google DeepMind 的 《AutoHarness: improving LLM agents by automatically synthesizing a code harness》 提出了新的思路:让 LLM 自己生成约束代码。
2. AutoHarness 的核心思路
AutoHarness 的核心思路并不复杂:
- 第一步,先让模型写出控制代码。比如在游戏场景里,一个函数负责提出动作,另一个函数负责判断这个动作是否合法。这两部分合起来,构成了最基础的控制逻辑。
- 第二步,把这段控制代码放进真实环境里运行,看看会不会出现非法动作、执行失败或者低质量结果。
- 第三步,如果失败了,就把这些失败信息反馈给模型,让它继续修改这段控制代码。
- 第四步,系统同时保留多个候选版本,继续测试、继续修改,直到收敛到一个更稳定的实现。
也就是说,它修的不是某一次回答,而是回答后面那层控制代码。最后留下来的也不是一段 prompt,而是一段真会进执行链路的代码。
DeepMind 在 TextArena 的 145 个游戏上测试,最终把 Gemini-2.5-Flash 非法动作率压到了 0;在部分任务上,小模型加 AutoHarness 的表现还超过了更大的模型。换句话说,很多时候问题不在模型参数还不够大,而在它能不能稳定地遵循规则。
读到这里,很容易联想到另一个熟悉的概念:单元测试以及 TDD(测试驱动开发)。
3. AutoHarness 与 TDD 的区别
AutoHarness 和 TDD 会被放在一起讨论,不是没有原因。它们有一个很像的地方:都不相信“第一次实现就会自动正确”,而是依赖一套外部约束,把结果一点点逼到正确的方向上。
TDD 的做法是先写测试,再写实现,让代码去满足测试。AutoHarness 的做法则是先写一层约束代码,再把它放进环境里接受反馈,不断修改,直到它能把错误拦下来。
但不同的是,TDD 约束的是业务实现。先写测试用例,再写功能,目标是确保这段代码满足预期行为。AutoHarness 约束的则是 Agent 的控制逻辑。它修的不是具体业务功能,而是动作是否合法、结果会不会出错、系统在什么条件下应该拦截或重试。
简单说,TDD 更像是在问:这段实现对不对?AutoHarness 更像是在问:这个 Agent 在真实环境里会不会乱来?
顺着这个区别再往下看,AutoHarness 和 Harness Engineering 里另外两个常见概念——Linter 和 Ralph loop——也不是一回事。
4. AutoHarness 与 Linter、Ralph Loop 的区别
如果把 AutoHarness 放回 OpenAI 那篇文章的上下文里,它和 Linter、Ralph loop 的区别会更清楚。
Linter 是静态检查工具,它的规则是人定义的,检查发生在代码提交之前,产物通常是一份错误报告。它负责把明显不合规范的代码拦下来,比如命名不对、依赖越层、用法危险。
这类工具本质上就是把规则写成静态检查:简单的是扫语法树和依赖关系,复杂一点的会接到 CI 里,把团队自己的架构约束也一起检查。换句话说,Linter 解决的是“这段代码看起来对不对”。
AutoHarness 处理的则是另一类问题。它面对的是运行时动作是否合法、执行是否出错,最后生成的不是一份提醒,而是一段会真的进入决策链路的代码。它解决的是“这件事做出来会不会犯规”。
Ralph loop 也不一样。它更像一个朴素的反复试错流程:写出来,跑一轮,看反馈,再改一轮,再继续。它优化的是当前这次实现、这轮 PR、这次修复。
AutoHarness 则更进一步。它不是只修一次输出,而是在修“以后每次动作执行前都要经过的那层控制器”。如果说 Linter 是门口保安,Ralph loop 是反复审稿,那 AutoHarness 更像是在重写整个门禁系统。
也正因为它修的不是一次输出,而是那层持续生效的控制器,那要如何实现 AutoHarness,也就成了下一个自然的问题。
5. AutoHarness 的实现思路
DeepMind 论文中并没有给出 AutoHarness 的完整实现方式,如果按工程实现的视角来看,论文的方法大致可以分成三层。
第一层是通用框架。也就是先把需要模型生成的函数签名、规则描述和基本约束写清楚,让 LLM 知道自己要生成什么。提示词大致如下:
system_prompt = """你是 {domain} 的代码专家。请实现以下函数:
- propose_action(...)
- is_legal_action(...)
要求:
1. 根据当前状态提出动作
2. 严格按照 {domain_rules} 判断动作是否合法
3. 只输出代码,不要解释"""第二层是具体项目配置。不同任务有不同规则,游戏、API、工具调用都不一样,所以这一层负责把具体约束填进去。
config = {
"domain": "API 参数验证",
"function_signatures": "def validate_request(...)...",
"domain_rules": "必须包含 user_id 和 timestamp..."
}第三层是执行循环。生成代码、放进环境里运行、收集反馈、继续修改,直到结果收敛。
def auto_harness(task_spec):
code = llm.generate(task_spec)
while not converged:
result = execute(code)
if result.failed:
feedback = extract_error(result)
code = refine(code, feedback)
else:
break
return code真正需要随着场景变化去调整的,主要是第二层的具体配置;第一层的通用框架和第三层的执行循环,通常可以复用。
6. AutoHarness 的社区反馈
从社区讨论来看:AutoHarness 的结果很亮眼,但离真正大规模工程落地,还有一段距离。
它的优势有:
- 小模型也能打赢大模型:关键不只是模型大小,而是外面的控制结构
- 把软约束变成硬约束:不是靠提示词提醒,而是靠代码执行
- 自我改进更自动化:系统自己生成防护代码,不再全部依赖人工维护
但它的局限性也很明显:
- 还没有开源实现,外部很难直接复现
- 泛化能力没有完全验证,目前主要还是游戏这类规则清晰的环境
- 实现门槛不低,不是套个 prompt 就能跑起来
- 场景有前提,必须有明确的成功/失败反馈,系统才知道怎么改
所以它更适合什么场景,也比较清楚。凡是规则明确、可以执行、能拿到清晰反馈的任务,都有可能借用这种思路:比如 API 参数验证、MCP Adapter、数据库事务、CI/CD 检查,甚至更广义的工具调用约束。
7. 结语
如果拿种地打比方,传统软件开发像人工种地,Vibe Coding 像自动种地。而 Harness Engineering 则是先把地圈起来,搭成一个按既定规则运行、能自动种菜、也有温湿度传感器和监控反馈的蔬菜大棚。
但即使这样,也还是会出现一种情况:系统明明知道不能往菜地里浇热水,最后却还是这么做了。AutoHarness 要解决的,就是这个问题。它相当于给这个蔬菜大棚再加上一层防呆系统,专门防止它继续往菜地里浇热水。
从这个角度看,把 AutoHarness 称作 AI 时代的 TDD,因为它们都试图解决同一个问题:别把质量寄托在运气上,而是尽量把约束写进系统里。