AutoHarness: AI时代的TDD

AutoHarness: AI时代的TDD

March 21, 2026

cover

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,因为它们都试图解决同一个问题:别把质量寄托在运气上,而是尽量把约束写进系统里。

最后更新于