AB
AiBoss
Tutorials

为编码 AI 代理搭建 Harness:从指令文件到自检闭环

Tutorials

为编码 AI 代理搭建 Harness:从指令文件到自检闭环

AI 代理的产出稳定性不只取决于模型,还取决于模型周围那套「harness」:指令文件、上下文组织、工具与校验、执行记录。本文按准备工作、操作步骤、完整示例的顺序,讲清如何为编码代理设计引导与校验两类机制,如何用功能清单与进度文件支撑长时间任务,以及如何用多角色分工和失败分析持续改进这套结构。

AI 代理(agent)可以拆成两部分:模型,以及模型周围的那套结构。后者通常被称为 harness——它包含提示与上下文、可调用的工具、执行后的校验、以及过程记录。同一个模型,换一套 harness,产出质量可能差出很远。本文面向正在把编码代理接入实际项目的人,讲清楚 harness 由哪些部件组成、每个部件怎么落地、以及如何用一套循环不断改进它。适合已经能让代理跑起来、但发现它「时好时坏」的读者。

需要先说明一点:harness 不是让模型变聪明的魔法,它做的是把「模型做不到或容易忘的事」变成外部机制。因此它的每个部件都带有时代性——当模型能力提升后,某些部件就该被重新审视甚至拆掉。

准备工作

在动手设计 harness 之前,先把下面几件事准备好。这些不是可选项,缺一项后面的步骤都会打折扣。

一个可被代理操作的项目仓库

代理需要能读写文件、执行构建与测试命令、查看版本历史。仓库应当是干净的,构建与测试命令是确定可复现的。如果构建需要一堆手工步骤,代理无法自行验证,harness 的校验环节就形同虚设。

一份放在仓库根目录的指令文件

业界常见的做法是在仓库根目录放一个 AGENTS.md(不同工具可能使用别的文件名,但作用相同)。它是代理每次进入项目时最先读到的内容。准备阶段要做的是:把项目里「必须这样做」和「绝对不能这样做」的事情列出来,先写草稿,后面再精简。

可自动执行的校验手段

至少要有:构建命令、测试命令、以及一个能快速跑的子集测试方式。如果项目有性能要求,还需要一个能输出计数值的测量脚本。没有这些,代理只能靠「看起来对」来判断,而这正是最容易出问题的地方。

执行记录的留存方式

代理的每一次运行都会产生日志、命令输出、失败信息。这些记录是后续改进 harness 的原料,需要能保存下来并被检索。可以放在仓库外的目录,也可以作为构建产物归档,关键是不要随手丢掉。

明确的人类决策点

在开始之前想清楚:哪些事情必须由人拍板。通常包括做什么功能、什么算通过、以及是否接受某个取舍。把这些提前写下来,代理在遇到这类判断时才会停下来询问,而不是自行发挥。

操作步骤

第一步:把 harness 拆成四个部件

一个便于操作的划分方式是把 harness 分成四块:

  • 指令:告诉代理该怎么做、不该怎么做,通常落在指令文件里。
  • 上下文:代理在每一步能看到哪些信息,包括文档、代码、历史记录。
  • 工具与校验:代理能调用什么,以及做完之后用什么检查。
  • 记录:过程与结果如何留存,供人和代理回看。

这个四分法只是一种组织方式,不必当成标准。它的用处是让你在排查问题时能定位到具体是哪一块缺失。例如代理反复用错构建命令,那是指令问题;代理改完代码不做验证,那是校验问题;代理在长任务中忘记之前做过什么,那是记录与上下文问题。

第二步:区分「引导」与「校验」两类机制

harness 的机制可以按时间点分成两类:

  • 引导(guide):在代理行动之前施加影响,比如指令文件里的规则、任务开始前给出的计划模板、可用的工具清单。
  • 校验(sensor):在代理行动之后进行检查,比如测试、静态检查、性能测量、结构约束检查。

两类机制必须配合。只有引导,你无法知道规则是否被遵守;只有校验,代理会反复犯同一个错误,因为没有人告诉它正确做法。设计时对每一条规则都问两个问题:它靠什么在事前传达?它靠什么在事后被发现违反?如果某个规则只有其中一个,就补上另一个。

第三步:写一份精简的指令文件

指令文件最容易犯的错误是写成一本厚手册。代理的上下文有限,过长的文件会让关键信息被稀释。可行的做法是把它写成一份目录:只保留高频、必须、容易出错的条目,详细的设计文档、执行计划、产品规格放到单独的文档目录里,由指令文件指向它们。

一个常见的目录结构如下:

AGENTS.md
ARCHITECTURE.md
docs/
├── design-docs/
├── exec-plans/
├── product-specs/
└── references/

指令文件本身控制在百行左右,承担索引作用。具体条目应当写成可执行的命令或明确的禁止项,而不是抽象原则。参考一个真实项目的写法,其中三条是:

  • 构建使用 zig build。
  • 测试用 -Dtest-filter 缩小范围执行,避免跑全量慢测试。
  • 不要创建 Issue 或 PR。

这三条分别对应三种典型需求:固定正确命令、给出高效做法、明确禁止行为。写自己的指令文件时可以照这个思路:每条都指向一个具体动作或一个具体禁令。

第四步:用机器而不是文字来守规则

写在指令文件里的规则,代理可能忘记,也可能理解偏差。凡是能用程序检查的规则,都应该做成自动检查。典型例子是依赖方向:规定业务领域内部按固定层次组织,依赖只能单向流动,不允许反向引用。

实现方式通常是自定义 linter 加结构测试。关键细节是错误信息要写清楚怎么改,而不只是说「违反了规则」。代理读到错误信息后能直接修正,这比人去解释一遍高效得多。

可以按下面的顺序推进:

  1. 列出项目里最重要、最容易被破坏的三到五条结构约束。
  2. 为每条约束写一个检查脚本,能对当前代码库跑出通过或失败。
  3. 把检查接入构建或测试流程,让代理每次改动都会触发。
  4. 调整失败信息,确保它包含「哪里错了」和「应该怎么改」。

第五步:让代理自己验证结果

代理改完代码后,不能只靠阅读代码判断对错。要让它运行程序并观察输出。如果项目有性能指标,就把指标变成可测量的数字,例如启动时间上限、关键操作的耗时上限,然后让代理在每次改动后自行测量并对照。

这一步的前提是测量脚本已经存在且输出稳定。如果测量本身波动很大,代理会得到互相矛盾的信号,反而降低可靠性。先确保测量可重复,再把它交给代理。

第六步:为长任务准备功能清单

长时间运行的任务有一个典型失败模式:代理一次做太多,做到一半上下文耗尽,留下半成品;或者看到部分进展就误以为已经完成。解决办法是把工作拆成可勾选的清单。

清单用结构化格式保存,每一项包含类别、描述、验证步骤和完成标记。例如:

{
  "category": "functional",
  "description": "在新对话中开启一个新的会话",
  "steps": [
    "打开主界面",
    "点击「新对话」按钮",
    "确认出现了新的会话"
  ],
  "passes": false
}

要点是:每一项都要有可执行的验证步骤,而不只是一句描述;passes 只有在实际验证通过后才改为 true;清单规模可以很大,逐项推进,不要一次全部展开。

第七步:规定每次会话的开场流程

长任务会跨越多个会话。每次重新开始时,代理需要先恢复现场,而不是直接开始写代码。可以把开场流程固定成下面几步:

  1. 确认当前所在目录(pwd)。
  2. 读取进度记录与功能清单。
  3. 查看变更历史(git log)。
  4. 用启动脚本(例如 init.sh)拉起服务,确认基本功能正常。
  5. 从清单中挑选一个未完成项开始实现。
  6. 通过实际操作验证,把该项的 passes 改为 true。
  7. 提交变更,并更新进度文件(例如 claude-progress.txt)。

同时要明确禁止某些行为,例如删除或改写已有测试来让检查通过。这类禁令必须写死在指令文件里,因为它是代理最容易「走捷径」的地方。

第八步:拆分角色,让生成与评估分离

当任务规模变大时,让一个代理同时负责计划、实现和验收,容易出现自我确认偏差。可行的做法是拆成三个角色:

  • 计划角色:拆解任务、确定顺序与验收标准。
  • 生成角色:按计划实现。
  • 评估角色:独立检查产出,指出具体缺陷。

评估角色的价值在于它能发现生成角色自己看不到的问题。实际案例中,评估角色指出过这类缺陷:填充工具只在起点和终点放置图块,而不是覆盖整个范围;排序处理因为 URL 定义顺序错误而返回 422 错误。这些都是「看起来完成了、实际不成立」的问题,只有独立检查才会暴露。

第九步:用失败记录驱动改进

harness 的改进应当是一个循环,而不是一次性设计。可以按四步走:

  1. 收集:保存每次运行的执行记录,尤其是失败的那几次。
  2. 分析:让模型阅读执行记录,归纳失败模式,而不是逐条人工排查。
  3. 修改:针对归纳出的模式,修改指令、增加检查、调整上下文组织方式。
  4. 复测:用同一套评测任务重新跑,确认改动有效且没有引入退化。

有公开案例显示,在模型保持不变的前提下,仅通过改进 harness,代理在包含 89 个任务的评测集上的排名从三十名左右提升到前五。这说明 harness 的改进空间可以相当大。具体评测集名称与排名数据请以发布方当前信息为准。

一个完整示例

下面把前面的步骤串成一个最小可运行的流程。假设你要让代理为一个已有项目增加一个小功能,并且要求它在无人值守的情况下完成。

1. 建立指令文件

# AGENTS.md

## 构建与测试
- 构建:`make build`
- 快速测试:`make test-fast`
- 全量测试只在提交前运行:`make test`

## 结构约束
- 依赖只能从上层指向下层,反向引用会被 linter 拒绝
- 新增文件必须放在对应领域的目录内

## 禁止事项
- 不要修改或删除已有测试
- 不要创建 Issue 或 PR

## 详细文档
- 架构说明见 ARCHITECTURE.md
- 执行计划见 docs/exec-plans/
- 产品规格见 docs/product-specs/

2. 准备功能清单

[
  {
    "category": "functional",
    "description": "列表页支持按名称筛选",
    "steps": [
      "启动服务",
      "打开列表页",
      "在筛选框输入关键字",
      "确认结果只包含匹配项"
    ],
    "passes": false
  },
  {
    "category": "performance",
    "description": "列表页首屏渲染在预算内",
    "steps": [
      "启动服务",
      "打开列表页",
      "读取测量脚本输出的首屏耗时",
      "确认不超过预算值"
    ],
    "passes": false
  }
]

3. 固定开场流程

pwd
cat claude-progress.txt
cat features.json
git log --oneline -20
./init.sh

代理执行完这些命令后,应当能说清:当前在哪个目录、上次做到哪里、还剩哪些未完成项、服务是否正常。

4. 实现并验证

代理从清单中取第一项,实现筛选功能,然后按 steps 逐条验证。验证通过后把该项的 passes 改为 true。接着处理第二项,运行测量脚本,对照预算值判断是否达标。

5. 提交并记录

git add -A
git commit -m "feat: 列表页名称筛选"

随后更新 claude-progress.txt,写清本次完成了哪些项、遇到什么问题、下次从哪里继续。这份记录就是下一次会话开场时读取的内容。

6. 复盘

任务结束后,把本次的执行记录交给模型分析,找出失败或返工的原因。如果原因是「代理不知道某个命令」,就补进指令文件;如果是「代理改完没验证」,就增加自动检查;如果是「代理忘了之前的决定」,就改进进度记录的写法。改完之后用同样的任务再跑一遍,确认问题消失。

注意事项

部件会过时

harness 的每个部件本质上都是在弥补模型当前做不到的事。当模型能力提升后,某些部件可能变成负担,增加复杂度却不带来收益。应当定期回看:这条规则还有必要吗?这个检查还在发现真实问题吗?不再需要的就拆掉。

正确性无法完全靠自动检查保证

可维护性、结构约束这类属性容易用机器验证,但功能的正确性很难仅靠自动生成的测试来保证。代理写的测试可能和实现犯同样的理解错误,从而一起通过。因此关键功能仍需要人工确认,或者用独立于实现的验收标准来检查。

公开案例的适用范围有限

目前可参考的实践多来自各团队对自身项目的报告,环境、代码库规模、任务类型都各不相同。在别的环境中能否复现同样的效果,并没有被系统验证。把这些做法当作起点,用自己的项目数据去检验,而不是当作保证。

人的决策不能外包

做什么、什么算合格、在冲突目标之间如何取舍,这些判断仍然由人负责。harness 能做的是让代理在这些判断之外的部分稳定运行,而不是替人做判断。把决策点明确写进流程,代理才会在需要时停下来。

易变信息以官方为准

本文涉及的工具名称、评测集、排名、性能预算等具体数值,可能随版本更新而变化。实际使用时请以相关项目与产品的官方当前信息为准。