一句话:Harness 是把「希望 AI 做对」变成「系统保证 AI 做对」的整套工程约束。 内容脉络:心智模型 → SDD → TDD → AGENTS.md → Lint → CI/CD → Ralph → 完整拼图


0. 心智模型:先搞懂 harness 是什么

为什么 AI 编码需要 harness

AI 编码 Agent(Codex / Claude Code / Cursor)很强:能读整个仓库、改代码、跑命令。但它有三个固有毛病:

  1. 会偏离:做着做着就按自己的想象发挥,不按你的需求
  2. 会自信地错:给出看起来很对的代码,实际有 bug
  3. 会自证:自己改完自己跑测试,然后说"通过了"

单靠 prompt 约束 → 看运气;靠系统约束 → 可预期。Harness 就是后者:让 Agent 在跑道内跑,越界就被护栏拦下

Harness 的三层结构

层次干什么组件
指令层(Input)告诉 Agent 做什么、怎么做SDD 规格、AGENTS.md、instructions.md
验证层(Output)客观检查做对没有TDD、Lint、CI/CD
编排层(Loop)拆任务、循环执行、记进度Ralph

两条核心原则

  1. 确定性机制 > 依赖 Agent 自觉:能机器验证的(lint、test、CI)不要靠 prompt 要求
  2. 每层只管一件事:指令层管"怎么做",验证层管"对不对",编排层管"推进到哪了"

1. SDD:规格驱动开发(先写规格,再写代码)

是什么

Spec-Driven Development:先产出规格(spec / PRD),再让 AI 基于规格生成实现。规格是单一事实来源,代码只是规格的一种实现。

解决什么问题

  • 一次性 prompt 生成的代码不可审查、不可复用:需求全在对话里,换会话就丢
  • 没有规格,Agent 按自己的想象实现,偏了就白干
  • 规格把「做什么、不做什么、怎么验收」固化下来,人审规格而不是逐行审代码

怎么落地

  1. spec.md:目标、范围(含"不做")、接口/数据结构、验收标准
  2. 让 Agent 先读规格 → 输出实现计划 → 确认后再编码
  3. 规格入库、版本化,像代码一样评审
  4. 把验收标准转成可执行测试(衔接下一节 TDD)

示例

spec.md 最小模板:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# 订单导出功能规格

## 目标
支持按时间范围导出订单为 CSV,最大 10 万行。

## 范围
- 做:REST 接口、后台任务生成、下载链接
- 不做:Excel 格式、权限细分(本期)

## 接口
GET /orders/export?from=&to=
响应:202 + job_id;GET /jobs/{id} 查状态

## 验收标准(Given/When/Then)
- 给定 1 万条订单,导出后 CSV 行数 = 订单数
- 超过 10 万条,返回 422 并提示缩小范围
- 未授权访问,返回 401

对应 prompt:

“先读 spec.md,输出实现计划和接口设计,确认后再动手,不要直接写代码。”

常见坑

  • 规格写太细 → Agent 变成打字员;写太粗 → 等于没写。粒度到「接口 + 验收标准」即可
  • 规格和代码不同步 → 改代码必须同步改规格,否则下一轮 Agent 按旧规格重做

2. TDD:测试驱动开发(先写测试,再写实现)

是什么

红-绿-重构:先写会失败的测试 → 实现到测试通过 → 重构。测试通过 = 客观的 done

解决什么问题

  • Agent 说"做完了",你怎么知道真的做完了?测试通过是唯一客观证据
  • 防回归:Agent 下一轮改代码时,旧测试自动拦住破坏
  • SDD 里写的验收标准,正好翻译成测试

怎么落地

  1. 让 Agent 按规格的验收标准先写测试
  2. 运行测试,确认失败(红)——证明测试真的在测
  3. 实现功能,测试全绿
  4. 重构,保持全绿
  5. 让 Agent 汇报:新增哪些测试、覆盖哪些路径、验证输出

示例

“按 spec.md 的验收标准先写测试,运行确认失败;再实现到全部通过;最后重构。逐条总结每个验收标准对应的测试和结果。”

1
2
3
npm test          # Node
pytest            # Python
go test ./...     # Go

常见坑

  • Agent 会删测试或改断言让测试变绿:关键测试文件要么人审,要么配置为 Agent 不可改
  • 只测快乐路径:验收标准必须含边界和异常(422、401)
  • 覆盖率不是目的:关键路径覆盖才是

3. AGENTS.md:给 Agent 的岗位说明书(指令层)

是什么

仓库根目录的 AGENTS.md,Agent 每次启动自动读取的持久化指令:项目命令、工作规则、代码规范、禁忌。

解决什么问题

  • 不用每次重复解释项目怎么跑、规范是什么
  • 团队统一:任何人的新会话行为一致
  • 把踩过的坑沉淀下来:Agent 每犯一次错,就加一条规则

怎么落地

  1. /init 生成骨架,再手工精简
  2. 内容四块:项目命令 / 工作规则 / 代码规范 / 验收标准
  3. 保持简短:默认上限 32 KiB,只写高频规则
  4. 分层:~/.codex/AGENTS.md(个人)→ 仓库根(团队)→ 子目录(专项),越近越优先

示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
## 项目命令
- 启动: npm run dev
- 测试: npm test
- 构建: npm run build
- 检查: npx eslint ./src

## 工作规则
1. 修改前先说明计划,最小改动
2. 修改后必须跑测试和 lint
3. 总结修改 + 验证结果

## 代码规范
- 优先 TypeScript,复用已有 class
- 不做无关重构

常见坑

  • 规则膨胀:写 50 条没人(模型也)记得住 → 只沉淀反复犯的错
  • 与 lint/CI 重复:能机器执行的(格式)交给 lint,AGENTS.md 只写机器管不了的判断

4. Lint:代码规范的机器裁判(验证层)

是什么

静态检查:格式、命名、明显 bug、安全弱点的自动扫描。常见工具:ESLint / Ruff / golangci-lint / Checkstyle。

解决什么问题

  • Agent 生成的代码风格必然不一致 → 机器强制统一
  • 明显错误(未用变量、空 catch、硬编码密钥)提前拦下
  • 比 code review 便宜:机器先扫,人只看机器看不出的

怎么落地

  1. 配好项目 lint 命令,写进 AGENTS.md
  2. 三层执行:本地 → pre-commit → CI
  3. Agent 改完必须跑 lint 并修到通过

示例

1
2
npx eslint ./src
pre-commit run --all-files

常见坑

  • 别为了"让 AI 通过"乱关规则:lint 规则是团队共识的固化
  • 只 lint 不 test:lint 管风格,逻辑正确性交给测试

5. CI/CD:最后的门禁(验证层)

是什么

把 lint + test + build + 类型检查自动化进流水线,PR 合并前必须全绿。

解决什么问题

  • Agent 本地可能"说谎":改完没跑、或跑的是旧代码 → CI 从仓库状态重新跑一遍,是中立裁判
  • 强制验证不可跳过:门禁面前人人(agent 也)平等
  • 让 Agent 的改动走和人类一样的评审通道

怎么落地

  1. PR 触发流水线:lint → test → build(可选加类型检查、AI 审查)
  2. Agent 完成改动后开 PR,CI 结果就是"验证结果"
  3. 失败必须修到绿,不许 bypass
  4. 高级:接入 codex review,PR 自动 AI 审查

示例

GitHub Actions 最小配置:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
name: ci
on: pull_request
jobs:
  check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci
      - run: npm run lint
      - run: npm test
      - run: npm run build

常见坑

  • CI 与本地环境不一致 → 锁版本(package-lock.json、go.sum)、用容器
  • 测试不稳定(flaky)→ 先修 flaky 再谈门禁,否则 Agent 和人都会被带偏
  • CI 只验证"没坏",不验证"符合规格":规格符合性靠测试 + 人审

6. Ralph:把上面串起来的循环引擎(编排层)

是什么

开源 CLI harness:以 PRD 为输入,循环驱动编码 Agent(Codex / Claude / Cursor)逐任务完成,自动记录进度。灵感来自 Geoffrey Huntley 的 “Ralph loop” 方法论。

解决什么问题

  • 手动一轮轮 prompt 不可扩展:大项目几十个任务,人盯不过来
  • Agent 会话会"忘":Ralph 把状态落到磁盘,每次新会话先读进度再继续
  • 把 SDD(PRD 规格)+ Agent(实现)+ 验证(你配的命令)串成闭环

怎么落地

  1. 安装并初始化,选择 agent(codex)
  2. 维护 PRD 任务清单
  3. ralph run 循环:Agent 查进度 → 做当前任务 → 记进度 → commit → 下一个
  4. 每轮 Agent 跑你项目里的验证命令(衔接 lint / test / CI)
  5. 项目级 instructions.md 给 Agent 加项目规则(Ralph 版 AGENTS.md)

示例

1
2
3
4
5
6
7
# 安装(需已装 codex / claude / cursor 之一)
curl -fsSL https://raw.githubusercontent.com/nitodeco/ralph/main/scripts/install.sh | bash

ralph init              # 初始化,选择 agent
ralph run 10            # 循环跑 Agent,最多 10 轮
ralph task list         # 查看任务清单
ralph progress          # 查看进度

常用配置(~/.ralph/config.json):agentmaxRetriesagentTimeoutMs(单轮超时)、stuckThresholdMs(卡住判定)、webhookUrl(完成通知)。

常见坑

  • Ralph 驱动的是"Agent 自行验证":验证命令必须真实存在,且 CI 兜底,否则又是自证陷阱
  • 迭代次数用尽 ≠ 完成:跑完看 task list 和 CI 结果
  • 长跑要配超时和通知,别半夜干等

7. 完整拼图与落地路线图

完整流程

需求 → SDD 规格 → AGENTS.md 约束 → Ralph 拆任务驱动 Agent
     → TDD 测试 → Lint → CI/CD 门禁 → 合并上线

每一层失败时的处置:

失败表现处置
SDD需求漂移回到规格,改规格再改代码
AGENTS.mdAgent 违反规范加规则,沉淀
TDD测试红Agent 修到绿,不许删测试
Lint风格/低级错误Agent 修到通过
CI/CD门禁红阻断合并,修到绿
Ralph卡住/超时配置超时重试,人工介入

最小可行 Harness(一周落地)

  1. 第 1 天:AGENTS.md(命令 + 规则)+ lint 配好
  2. 第 2–3 天:CI 门禁(lint + test + build)
  3. 第 4 天:让 Agent 走 SDD 闭环(先规格后代码)
  4. 第 5 天:用 Ralph 跑一个真实小项目
  5. 持续:把 Agent 反复犯的错写回 AGENTS.md / 测试

8. 常见坑与原则(总结)

  • 自证陷阱:Agent 改完自己跑测试不算最终验证,CI 说了算
  • 规则靠人肉:能自动化的(lint / test / CI)别只靠 prompt
  • 权限与沙箱:给 Agent 最小权限,危险命令(rm、push)要审批
  • 密钥安全:AGENTS.md / 配置里的密钥绝不入库,用环境变量
  • 人审不可省:Agent 负责快,人负责方向和最终判断
  • 迭代式加固:不是一次性搭完,而是"每翻一次车,加一条规则 / 一个测试"