Harness Engineering 与 Spec-Driven Development 的区别
Harness engineering 是为 AI 编码 agent 构建模型之外的一切:它读取的上下文、能调用的工具、必须遵守的约束,以及捕捉它出错的检查。Spec-driven development 则在 agent 动手之前,决定一次改动必须做什么。spec 是意图;harness 让 agent 照这个意图去做,并证明它做到了。让 agent 写真实代码的团队,最终两样都需要,而且缺了任何一样,失败的方式都可以预见。
本文讨论的是用 AI 编码 agent 构建软件。与电气工程里的线束(wiring harness)无关,也不是指 CI/CD 公司 Harness。
Harness engineering 是什么?
最短的定义是一个等式:Agent = Model + Harness。Birgitta Böckeler 在 martinfowler.com 上的文章把它表述为 “everything in an AI agent except the model itself”,也就是 AI agent 里除了模型之外的一切。对编码 agent 来说,这包括它能读到的指令和文档、它拥有的工具和权限、它必须遵守的架构规则、在它的产出上运行的测试和 linter,以及把失败反馈回它的循环。
这个词是在 2026 年 2 月火起来的。当时 OpenAI 的 Ryan Lopopolo 描述了一个持续五个月的实验:一个小团队在零行手写代码的前提下交付了一个内部产品,约一百万行代码、约 1500 个合并的 PR,最初由三名工程师推动,后来增加到七名。工程师不写代码,用原文的话说,他们的工作变成了 “design environments, specify intent, and build feedback loops that allow Codex agents to do reliable work”(设计环境、明确意图、构建反馈循环,让 Codex agent 可靠地工作)。原文的总结只有一句:“Humans steer. Agents execute.”(人来掌舵,agent 来执行。)
那篇文章里有四个想法,基本定义了这个领域的实践:
- 仓库就是唯一的事实来源。“From the agent's point of view, anything it can't access in-context while running effectively doesn't exist.”(在 agent 看来,运行时无法在上下文里拿到的东西,等于不存在。)在 Slack 或 Google Docs 里做的决定,写进仓库之前 agent 都看不见。
AGENTS.md是目录,不是百科全书。一个大约 100 行的短文件,指向更深的资料:设计文档、架构说明,以及提交进仓库的执行计划。- 约束靠机器强制执行。分层规则和“品味约束”由自定义 linter 和结构测试检查,lint 报错信息直接写成给 agent 的修复指引。
- 持续清理熵增。后台的 agent 任务定期扫描偏离约定原则的地方,开小的重构 PR,原文说这 “like garbage collection”,就像垃圾回收。
Anthropic 的工程博客从长时间运行 agent 的角度讲了同一件事。2025 年 11 月那篇关于长时间运行 agent 的 harness,用一个初始化 agent 加一个编码 agent:后者按功能清单逐项推进,每个功能只有在端到端测试通过后才标记为 passing。2026 年 3 月那篇关于长时间应用开发的 harness 设计,把工作拆给三个 agent:planner 把一句简短提示扩展成产品 spec,generator 负责实现,evaluator 按编码前商定的标准给结果打分。
Prompt、context、harness、loop
Harness engineering 常被介绍为 prompt engineering 和 context engineering 之后的下一步,现在也有人在搜 “loop engineering”。最好的理解方式是把它们看成层层嵌套的关系,每一层都包含前一层:
| 层 | 设计的对象 | 典型产物 |
|---|---|---|
| Prompt engineering | 一条指令、一轮对话 | 一段结构良好的 prompt |
| Context engineering | 窗口里放哪些 token;Anthropic 称之为在推理时 “curating and maintaining the optimal set of tokens” | 检索规则、压缩策略、精选示例 |
| Harness engineering | agent 周围的工具、约束和检查 | AGENTS.md、linter、结构测试、测试脚手架、CI 门禁 |
| Loop engineering | 决定 agent 下一步做什么、何时停止的系统;Addy Osmani 于 2026 年 6 月命名 | 触发器、验证器、停止规则、重试预算 |
spec 不是第五层。它是四层都要消费的输入:prompt 引用它,context 包含它,harness 拿它做检查,loop 用它的验收标准判断工作什么时候算完成。
Harness engineering vs spec-driven development
两者的区别在评审时最容易看清。Spec-driven development 在 diff 出现之前就决定代码应该做什么:目标、非目标、API 契约、边界情况、验收标准,以及评审者会要求的证据。Harness engineering 决定这个意图如何在每一次、每一个改动上被执行:agent 先读什么、能动哪些文件、它的产出要过哪些检查、检查失败之后怎么办。
| Spec-driven development | Harness engineering | |
|---|---|---|
| 回答的问题 | 这次改动必须做什么? | 怎样让任何改动都可信? |
| 范围 | 一次改动或一个功能 | 整个仓库,贯穿它的整个生命周期 |
| 主要产物 | spec.md、tasks.md、验收标准、evidence.md | AGENTS.md、文档、linter、结构测试、测试脚手架、CI、评审 agent |
| 什么时候改 | 每个新功能 | 同一种失败出现第二次时 |
| 消除的主要风险 | agent 自己猜缺失的需求 | agent 的产出悄悄拉低代码库质量 |
| 缺失时的症状 | 团队在代码评审里争论预期行为 | 团队对行为有共识,却没法低成本地证明 |
Böckeler 的分类法正好说明两者在哪里交汇。她把 harness 分成两类:guides(引导),是前馈控制,“anticipate the agent's behaviour and aim to steer it before it acts”,在 agent 行动之前预判并引导;sensors(传感器),是反馈控制,“observe after the agent acts and help it self-correct”,在 agent 行动之后观察并帮它自我纠正。两者都可以是计算型的(测试、linter、类型检查),也可以是推断型的(AI 代码评审)。她还按调节对象把 harness 分为可维护性、架构适应度和行为三类,并指出行为 harness 最不成熟、最依赖规格说明,功能规格在那里被列为一种前馈 guide。
所以,spec 是 harness 里的一个 guide,而且是管行为的最重要的那个,因为它是唯一一个随每个功能变化的 guide。OpenAI 的文章从人的角度说了同样的话:agent 写代码之后,人来 “prioritize work, translate user feedback into acceptance criteria, and validate outcomes”(排优先级、把用户反馈翻译成验收标准、验证结果)。这个“翻译”就是写 spec 的工作。
交汇点:spec 的每一节都变成一项检查
最实用的做法,是让 spec 的每个部分都对应一个 harness 控制点,这样写 spec 的同时,也告诉了 harness 要检查什么。我们用的对应关系如下:
| spec 章节 | harness 控制点 | guide 还是 sensor |
|---|---|---|
| 上下文、目标、非目标 | 从 AGENTS.md 链接过去,让 agent 先读 | Guide |
| 允许修改的文件 | diff 碰到清单之外的文件,CI 就失败 | 计算型 sensor |
| 验收标准 AC-1…n | 每条至少有一个以它命名的测试,缺了 CI 就失败 | 计算型 sensor |
| 边界情况 | 能复现它们的 fixture 和数据工厂状态 | 测试脚手架 |
| API 契约 | 从 OpenAPI 文档生成的契约测试 | 计算型 sensor |
| 证据 | PR 模板里 agent 必须填写的测试名和日志 | 推断型 sensor(评审者) |
| 回滚 | feature flag 存在且默认关闭 | 计算型 sensor |
其中最能拦住 agent 错误的两行,允许文件和验收标准,可以用一个很短的脚本强制执行。它读取 spec:diff 超出允许清单就失败,某条验收标准没有任何测试提到它也失败。我们在一个示例仓库里用 bash 3.2(macOS 自带的版本)测试过:
#!/usr/bin/env bash
# Usage: scripts/spec-gate.sh <change-name> [base-ref]
# Fails if the diff leaves the spec's allowed files, or an AC has no test.
set -euo pipefail
spec="specs/active/$1/spec.md"
base="${2:-origin/main}"
status=0
allowed=$(sed -n '/^## Allowed files/,/^## /p' "$spec" | sed -n 's/^- //p')
while read -r file; do
grep -qxF "$file" <<<"$allowed" || { echo "out of scope: $file"; status=1; }
done < <(git diff --name-only "$base"...HEAD)
for ac in $(grep -o 'AC-[0-9]\+' "$spec" | sort -u); do
grep -rqw -- "$ac" tests/ || { echo "no test mentions $ac"; status=1; }
done
exit "$status"
它要求 spec 里有一节 ## Allowed files,列出精确路径,验收标准编号为 AC-1、AC-2 等。-w 参数很关键:没有它,以 AC-10 命名的测试会被当成满足了 AC-1。接进 PR 检查时,用分支名作为变更文件夹名:
name: spec-gate
on: pull_request
jobs:
gate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- run: scripts/spec-gate.sh "$GITHUB_HEAD_REF" "origin/$GITHUB_BASE_REF"
这个脚本故意做得很小。它不证明测试写得好,只证明 agent 没有越界、也没有跳过任何一条标准。下一步是检查每个测试是否真的覆盖了它名字里的行为,见测试证据门禁。
Harness 实例:退款重试接口
拿一次改动举例:“支付网关超时时重试退款。”它的 spec 很短:
## Acceptance - AC-1: a gateway timeout retries once with the same idempotency key - AC-2: a second timeout leaves the refund pending, no duplicate refund_id - AC-3: every attempt writes an audit event ## Allowed files - src/billing/refund-worker.ts - tests/refund-worker.test.ts - specs/active/refund-retry/evidence.md
没有 harness 时,拿到这份 spec 的 agent 通常会写出一个看起来合理的重试循环,再写一个让网关 mock 在第二次调用时成功的测试。测试通过了,AC-2 却从来没被覆盖;agent 还顺手往 src/billing/ledger.ts 里加了个辅助函数,因为看起来更整洁。评审能抓到其中一部分,但已经晚了。
有了 harness,同样的运行在人看之前就会撞上三个 sensor:范围门禁标出 ledger.ts;验收标准检查报告没有任何测试提到 AC-2;而网关 mock 是测试脚手架的一部分,而不是每个测试临时拼凑的,它自带“连续两次超时”的状态,所以补上缺失的测试只需要 agent 写一行。spec 说清楚了什么重要,harness 让 agent 的捷径在还很便宜的时候就暴露出来。
为什么测试脚手架是一项独立的工程工作
两个词容易混淆的一个原因是,“test harness”(测试脚手架)比 “agent harness” 古老得多。测试脚手架是断言和被测系统之间的那一层。在 agent harness 里,它是行为 sensor,而且是一项独立的工程工作,不是写测试的副产品。一个在本地通过、在 CI 里因为数据库状态不同而失败的测试,不是“不稳定的测试”,而是一个没有脚手架的测试。
| 层 | 给 agent 提供什么 | 常用工具 |
|---|---|---|
| Fixtures | 每个测试都有一个已知的初始状态,与运行顺序无关 | pytest fixtures、数据库种子数据 |
| 数据工厂 | 只需写出关心字段的测试对象 | factory_boy、fishery |
| Mock 服务 | 行为和失败模式都可预测的外部服务 | Prism、WireMock |
| 契约测试运行器 | 证明 API 仍然符合发布的规格 | Schemathesis、Pact |
| 环境初始化 | 一条命令从零到可测试的环境 | Docker Compose、提交进仓库的 init.sh |
最后一行是 agent 最需要、而人最常跳过的。Anthropic 的长时间运行 agent harness 让初始化 agent 专门写一个 init.sh,就是这个原因:之后的每个会话都必须能启动应用并测试,而不用重新摸索怎么启动。契约测试这一层的细节,见契约测试计划:从 OpenAPI 到 CI。
缺了任何一个会怎样失败
只有 spec 没有 harness,就是一份没人执行的文档。agent 读了它,大体照做,在边缘处漂移:多一个字段、一次范围外的重构、悄悄丢掉一条标准。每一处都要靠人工评审发现,而人工评审是发现问题最贵、也最难随 agent 提交更多 PR 而扩展的地方。OpenSpec 社区 schema 目录里有一句话说得很直白:“OpenSpec only checks that artifacts exist.”(OpenSpec 只检查文件在不在。)在 harness 拿代码去对照这些文件之前,任何 spec 工具都是这样。
只有 harness 没有 spec,就是一条又快又绿、却瞄错目标的流水线。linter 通过,结构干净,测试充分,功能却做了没人要的事,因为环境里没有任何东西说明这个功能到底是什么。Anthropic 3 月那篇文章从评估的角度发现了一个相关的陷阱:“When asked to evaluate work they've produced, agents tend to respond by confidently praising the work.”(让 agent 评估自己的产出时,它们往往会自信地夸奖。)它的解法是一个独立的 evaluator,按编码前商定的标准打分。编码前商定的标准,换个名字就是 spec。
OpenAI 的文章还有一句值得记住的提醒:文中描述的 agent 行为 “depends heavily on the specific structure and tooling of this repository and should not be assumed to generalize without similar investment”,也就是高度依赖这个仓库特定的结构和工具,没有类似投入不要以为能直接照搬。harness 是建出来的,不是装上去的。
Spec-first 团队的入门 harness
不需要一个百万行的实验才能开始。对一个已经在写 spec 的团队,大约一周的工作量,按回报从高到低:
- 把
AGENTS.md写成地图。保持简短,指向 spec、架构说明和常用命令所在的位置。什么该写进它、什么该写进 spec,见面向 Spec-First 团队的 AGENTS.md。 - 每份 spec 都要有编号的验收标准和允许文件清单。规格包生成器会同时生成这两项。
- 把上面的 spec 门禁加进 CI。它是成本最低、拦截率最高的 sensor。
- 建好一层真正的测试脚手架。fixture、mock 服务、环境初始化,挑你的 agent 最常卡住的那一个。
- 在 PR 模板里加一节证据。每条标准对应的测试名、日志、截图。这给人工评审这个推断型 sensor 提供了具体的检查对象。
- 把重复出现的失败变成新的控制点。同一种 agent 错误出现第二次时,加一个 guide(
AGENTS.md或 spec 模板里的一行)或一个 sensor(一条 lint 或一个测试)。这就是 Böckeler 说的 steering loop,也是让 harness 朝正确方向生长的机制。
如果你在为 spec 这一侧选工具,OpenSpec vs Superpowers vs Spec Kit 对比了主要选项,包括怎么把规划工具和执行纪律工具组合起来。这既是 spec 的决策,也是 harness 的决策。
参考资料
- OpenAI:Harness engineering: leveraging Codex in an agent-first world(Ryan Lopopolo,2026 年 2 月)
- martinfowler.com:Harness engineering for coding agent users(Birgitta Böckeler,2026 年 4 月)
- Anthropic:Effective harnesses for long-running agents(2025 年 11 月)
- Anthropic:Harness design for long-running application development(2026 年 3 月)
- Anthropic:Effective context engineering for AI agents
- Addy Osmani:Loop Engineering(2026 年 6 月)
引文和数据已于 2026-09-24 对照上述来源核对。spec 门禁脚本在发布前于示例仓库中实际运行过。