AI Agent写了代码就说“完成了“?一套Harness模板,把它钉死在轨道上
AI Agent写了代码就说"完成了"?一套Harness模板,把它钉死在轨道上
从失忆、假完成、越界改代码——到跨会话稳定交付的实战指南
*“代码已经写好了,功能应该可以用了。”
如果你用过 AI 编码 Agent(Claude Code、Codex、Cursor……),这句话你一定不陌生。Agent 自信满满地说"完成",你打开一看:测试没跑、功能只有一半、甚至改着改着把别的地方搞坏了。
更糟糕的是,下一轮会话开始,Agent 完全失忆——它不知道上一轮做了什么、做到哪了、哪里还是坏的。你只能从头解释一遍,然后祈祷它别又跑偏。
这不是某个 Agent 的问题,这是当前所有 AI 编码 Agent 的结构性缺陷。而今天要讲的这套 Harness 模板,就是用来解决这个问题的。
01 问题:AI编码Agent的三宗罪
在长周期、多会话的编码任务中,AI Agent 几乎都会犯同样的三类错误。不是模型不够聪明,是缺少约束机制。
第一罪:跨会话失忆
每一轮会话都是独立的。Agent 不记得上一轮做了什么、做到哪了、哪些验证通过了。它会读聊天记录,但聊天记录是不可靠的——里面充斥着"我试试看""应该可以了"这种模糊表述,根本无法作为可靠的工作状态。
结果:每一轮会话都在"重新理解项目",大量时间浪费在重建上下文上。
第二罪:假完成
Agent 天然倾向于取悦用户。它写了代码,就声称"功能已完成"。但"写了代码"和"功能能用"之间,隔着一条巨大的鸿沟:
- 验证步骤跑了吗?——没有。
- 跑过了,结果记录了吗?——没有。
- 重启之后还能用吗?——不确定。
- 功能清单更新了吗?——什么功能清单?
结果:你以为做完了,实际上一堆半成品。越往后做,积攒的技术债越重,直到某天全线崩盘。
第三罪:范围越界
你让 Agent 做功能 A,它做着做着发现功能 B 也需要改,于是顺手改了。改完发现功能 C 的测试挂了,又去修 C。最后你回头一看:A 没做完,B 改了一半,C 被改出了新 bug。
结果:改动像滚雪球一样失控,每一轮会话结束时代码库都处于不确定状态。
02 什么是Harness?一句话说清
Harness 的本意是"马具"——套在马身上的缰绳和马鞍,用来控制马匹方向、让它跑在正确的道路上。
在 AI Agent 语境下,Harness 是一套围绕 Agent 的工作流约束系统。它不是代码框架,不是中间件,而是一组文件 + 规则 + 流程,把 Agent 钉在轨道上:
目标不是尽可能快地产出代码,而是让每一轮会话结束后,下一个会话仍然能无猜测地继续工作。
这句话来自这个项目的根指令文件 AGENTS.md,也是整个 Harness 哲学的核心。
打个比方:没有 Harness 的 Agent 像是一个没有项目管理的新人程序员——能力很强,但没人告诉他先做什么后做什么、做完要检查什么、怎么交接。Harness 就是那个项目管理机制。
03 Harness的六大组件
这套模板项目提供了六个核心文件,每个文件在 Agent 工作流中各有分工。它们不是"可选的文档",而是 Agent 必须读写的工作件。
AGENTS.md → init.sh → feature_list.json → progress.md → handoff.md → checklist.md
① AGENTS.md / CLAUDE.md —— 根指令文件
这是 Agent 每次开工会第一个读的文件。它定义了三件事:
- 开工流程:确认目录 → 读进度日志 → 读功能清单 → 看最近提交 → 运行 init.sh → 跑 smoke test
- 工作规则:一次只做一个功能、不假完成、不越界、不偷偷弱化验证
- 完成定义:目标行为已实现 + 验证真的跑过 + 证据已记录 + 仓库可重启
AGENTS.md 和 CLAUDE.md 内容一致,只是格式适配不同 Agent——前者给 Codex 等通用 Agent,后者给 Claude Code。
关键设计:完成定义那一段是整个 Harness 的核心。项目文档里明确写了:“完成定义那一段别改——那是整个 harness 最关键的部分。” 四个条件缺一不可,没有讨价还价的余地。
② init.sh —— 统一启动入口
一条命令完成三件事:安装依赖 → 跑验证 → 打印启动命令。
#!/usr/bin/env bash
set -euo pipefail
# 按你的项目实际情况替换这些命令
INSTALL_CMD=(npm install)
VERIFY_CMD=(npm test)
START_CMD=(npm run dev)
echo "==> 当前目录: $PWD"
echo "==> 同步依赖"
"${INSTALL_CMD[@]}"
echo "==> 运行基础验证"
"${VERIFY_CMD[@]}"
echo "==> 启动命令"
printf ' %q' "${START_CMD[@]}"
这个文件的设计哲学是:如果基础验证一开始就失败,先修基础状态,不要在坏的起点上继续叠新功能。
这个原则极其重要。很多开发者拿到 Agent 后让它直接写新功能,完全不管现有测试是不是绿的。结果新功能写在一个已经损坏的基础上,越写越乱。init.sh 强制每一轮会话都从验证通过的状态开始。
③ feature_list.json —— 功能状态的唯一事实来源
这是一个机器可读的 JSON 文件,列出项目的所有功能,每个功能包含:
| 字段 | 说明 |
|---|---|
id |
短唯一标识,如 chat-001 |
priority |
整数,越小越优先 |
area |
功能所属区域,如 chat、import |
user_visible_behavior |
功能正常时用户能看到什么 |
status |
四种状态之一(见下文) |
verification |
逐步验证步骤 |
evidence |
验证通过的记录 |
四种状态是 Harness 最精妙的设计之一:
| 状态 | 含义 |
|---|---|
not_started |
还没碰 |
in_progress |
当前唯一正在做的(同一时间只能有一个) |
blocked |
有记录的阻塞问题,推不动 |
passing |
验证通过,证据已记录 |
铁律:Agent 任何时候只能有一个功能处于 in_progress。 这条规则直接消灭了"范围越界"的问题——你想做别的?先把当前的做完或标记为 blocked。
④ claude-progress.md —— 进度日志
每轮会话往里写,每轮新会话先读它。它记录的不是流水账,而是结构化的状态快照:
- 当前已验证状态:仓库根目录、标准启动路径、标准验证路径、当前最高优先级未完成功能、当前 blocker
- 会话记录:每轮一条,包含目标、完成内容、验证结果、证据、提交记录、已知风险、下一步
这个文件解决的是"失忆"问题。Agent 不需要读聊天记录猜上次做了什么——进度日志就是唯一真相。
⑤ session-handoff.md —— 交接摘要
比进度日志更简短的交接文档。适用于会话较长或项目有多个并行区域时,让接手的人或 Agent 快速了解现状:
- 哪些是确认能用的
- 本轮改了什么
- 仍损坏或未验证的部分
- 下一步该做什么、哪些不要动
- 启动、验证、调试命令速查
⑥ clean-state-checklist.md —— 收尾检查清单
每次会话结束前逐项检查,确保仓库处于下一轮可直接开工的状态:
[ ] 标准启动路径仍然可用
[ ] 标准验证路径仍然可运行
[ ] 当前进度已经记录到进度日志
[ ] 功能状态真实反映了 passing 和未验证的边界
[ ] 没有任何半成品步骤处于未记录状态
[ ] 下一轮会话无需人工修复即可继续
最后一条尤其关键:没有假 passing。功能清单里的状态必须真实反映实际情况——通过了就是通过了,没验证就是没验证,不存在"应该可以了吧"这种灰色地带。
04 四条铁律:Harness的约束引擎
六大组件是"硬件",四条铁律是驱动它们的"软件"。这些规则被写进了 AGENTS.md,Agent 每次开工都会读到:
铁律一:一次只做一个功能
功能清单里同一时间只能有一个 in_progress。想做别的?先把当前的推到 passing,或者明确标记为 blocked 并记录原因。
这条规则看起来简单,但它从根源上消灭了"范围越界"。Agent 不能再以"我顺手改一下"为借口扩散改动范围。
铁律二:没有证据不算完成
不要因为"代码已经写了"就把功能标记为完成。
完成定义有四个条件,缺一个都不行:
1. 目标行为已经实现
2. 要求的验证真的跑过
3. 证据记录在 feature_list.json 或 claude-progress.md
4. 仓库仍然能按标准启动路径重新开始工作
第4条最容易被忽略:你改完代码,仓库还能正常启动吗?如果不能,功能就不算完成——哪怕代码本身是对的。
铁律三:文件是唯一事实来源
优先依赖仓库里的持久化文件,而不是聊天记录。
聊天记录是易失的、模糊的、不可验证的。仓库里的文件(进度日志、功能清单、交接摘要)是持久的、结构化的、可验证的。Harness 把所有状态都写到文件里,让 Agent 的每一轮会话都从一个确定的起点出发。
铁律四:每轮必须干净收尾
收尾流程是固定的:
1. 更新 claude-progress.md
2. 更新 feature_list.json
3. 记录仍未解决的风险或 blocker
4. 在工作处于安全状态后,用清晰的提交信息提交
5. 保证下一轮会话可以直接运行 ./init.sh
第5条意味着:你关掉终端后,另一个人(或另一个 Agent)打开仓库,运行 ./init.sh,就能直接继续工作。不需要人工修复,不需要解释上下文,不需要"你先跑一下xxx"。
05 质量闭环:评审与质量快照
六大组件 + 四条铁律解决了"工作流"问题。但还有两个问题没回答:
- 这轮 Agent 做得好不好?
- 这个项目是在变强还是变弱?
Harness 用两个文件回答这两个不同的问题。
evaluator-rubric.md —— 评单次输出质量
六个维度,每个 0-2 分:
| 维度 | 核心问题 |
|---|---|
| 正确性 | 实现出来的行为是否符合目标功能? |
| 验证 | 要求的检查是否真的跑过,并留下证据? |
| 范围纪律 | 这一轮是否基本保持在选定功能范围内? |
| 可靠性 | 结果是否能在重启或重跑后继续工作? |
| 可维护性 | 代码和文档是否清楚到足以交给下一轮会话? |
| 交接准备度 | 新会话是否能只靠仓库内工件继续推进? |
结论三选一:Accept(达标)/ Revise(需修补)/ Block(有根本性问题)。
⚠️ 重要提醒:Evaluator 需要校准
项目文档里有一段非常清醒的警告:“开箱即用的 agent 做评审很弱——它会发现问题,然后把自己说服到通过。” 你需要用 evaluator 给已完成的 sprint 打分,和人工判断对比,把分歧写进 rubric,重新跑,反复 3-5 轮直到对齐。这不是可选步骤——不做校准的 evaluator 会变成橡皮图章。
quality-document.md —— 评代码库本身质量
给项目的每个产品领域和架构层打 A-D 的等级,跟踪代码库随时间是变强了还是变弱了:
- 产品领域(如文档导入、问答流程):按验证状态、Agent 可读性、测试稳定性、关键缺口打分
- 架构层(如 Main Process、Renderer、Services):按边界执行和 Agent 可读性打分
这两个文件回答的是不同的问题——一个是"这轮做得好不好",一个是"项目整体健不健康"。两者配合,构成了 Harness 的质量闭环。
06 实战流程:一轮完整会话长什么样
把所有组件串起来,一轮标准会话的流程是这样的:
开工阶段(读)
# 1. 确认位置
$ pwd
/home/project/my-app
# 2. 读进度日志 → 知道上次做到哪了
# 3. 读功能清单 → 知道该做什么
# 4. 看最近提交 → 知道最新改动
$ git log --oneline -5
# 5. 运行 init.sh → 安装依赖 + 跑验证
$ ./init.sh
==> 当前目录: /home/project/my-app
==> 同步依赖
==> 运行基础验证
==> 启动命令: npm run dev
# 6. 基础验证通过?→ 选一个功能开干
# 基础验证失败?→ 先修基础状态,不叠新功能
工作阶段(做)
只围绕一个功能工作。实现 → 验证 → 记录证据。不碰别的功能,不弱化验证规则,不偷偷改功能清单的状态。
收尾阶段(写)
# 1. 更新进度日志
# 2. 更新功能清单状态
# 3. 记录未解决的风险
# 4. 过一遍收尾检查清单
# clean-state-checklist.md
[x] 标准启动路径仍然可用
[x] 标准验证路径仍然可运行
[x] 当前进度已经记录到进度日志
[x] 功能状态真实反映了 passing 和未验证的边界
[x] 没有任何半成品步骤处于未记录状态
[x] 下一轮会话无需人工修复即可继续
# 5. 提交
$ git add -A && git commit -m "feat(chat): chat-001 创建新会话 - 验证通过"
下一轮会话开始时,Agent 运行 ./init.sh,读进度日志和功能清单,就知道该做什么、做到哪了。零猜测、零重复解释、零状态丢失。
07 如何落地到你的项目
四步快速上手:
第一步:复制四个核心文件
把以下文件复制到你的项目根目录:
AGENTS.md(或CLAUDE.md,取决于你用的 Agent)init.shclaude-progress.mdfeature_list.json
项目简单时,这四个文件就够了。等项目变复杂了,再补 session-handoff.md、clean-state-checklist.md、evaluator-rubric.md 和 quality-document.md。
第二步:改 init.sh 的三个变量
# 你的依赖安装命令
INSTALL_CMD=(npm install) # 或 pip install -r requirements.txt
# 你的基础验证命令
VERIFY_CMD=(npm test) # 或 pytest
# 你的开发服务器启动命令
START_CMD=(npm run dev) # 或 python manage.py runserver
第三步:填功能清单
把 feature_list.json 里的示例功能换成你自己的。每个功能填上 id、priority、user_visible_behavior 和 verification 步骤。
第四步:填进度日志的初始状态
把 claude-progress.md 里的"当前已验证状态"填上:仓库根目录、启动路径、验证路径、最高优先级未完成功能。
给 init.sh 加执行权限:chmod +x init.sh。然后运行 ./init.sh,确认整个链路跑通。
适配指南:把 AGENTS.md 开工流程里的路径和命令换成你项目的。工作规则按团队约定调整。但完成定义那一段别改——那是整个 Harness 最关键的部分,改了就等于拆掉了刹车。
08 深层思考:Harness的本质是什么?
这套 Harness 模板里最深刻的洞察,藏在 quality-document.md 的说明里:
Harness 里的每个组件都编码了一个假设——“模型做不到这件事”。
模型变强后,这些假设就过时了。
这句话值得反复品味。来拆解一下:
每个组件都对应一个"模型做不到的事"
| Harness 组件 | 它编码的假设 |
|---|---|
| AGENTS.md(根指令) | 模型不会自己建立工作流程 |
| init.sh(启动脚本) | 模型不会先验证再干活 |
| feature_list.json(功能清单) | 模型记不住所有功能的状态 |
| claude-progress.md(进度日志) | 模型跨会话会失忆 |
| session-handoff.md(交接摘要) | 模型不会主动总结交接信息 |
| clean-state-checklist.md(检查清单) | 模型收尾时不全面 |
| evaluator-rubric.md(评审表) | 模型不会自检质量 |
| quality-document.md(质量快照) | 模型不会跟踪项目健康度 |
模型变强后,Harness 应该变薄
Harness 不是越多越好。项目文档里给出了一个清晰的验证方法——移除实验法:
- 拍一份 quality-document 快照
- 移除一个 Harness 组件
- 跑基准测试
- 再拍一份快照
- 对比——评级没降,说明那个组件是多余的;降了,就恢复
这是一个进化型系统的设计思路:Harness 会随着模型能力的提升而逐渐变薄。今天必须有的约束,可能在下一代模型上就变成了多余的官僚主义。
但关键是:在你还没验证模型能自己做到之前,约束就必须存在。 与其赌模型能自律,不如用 Harness 兜底。
总结:Harness 解决了什么问题
回到开头说的三宗罪:
| 问题 | Harness 怎么解决 |
|---|---|
| 跨会话失忆 | 进度日志 + 功能清单作为持久化状态文件,替代聊天记录 |
| 假完成 | 完成定义四条件 + 证据记录 + 收尾检查清单 |
| 范围越界 | 功能清单的单一 in_progress 约束 + 范围纪律评审维度 |
本质上,Harness 做的事情只有一件:把 Agent 从"看起来在干活"变成"真的在交付"。
它不帮你写代码,但它确保每一轮会话结束时,代码库处于一个确定的状态——下一轮可以直接接着干。这才是长周期 AI 编码的真正难题:不是写代码,是管理状态。
如果你正在用 AI Agent 做多轮、多功能的编码项目,这套模板值得直接拿来用。四个文件,四条铁律,从今天开始把你的 Agent 钉在轨道上。
更多推荐
所有评论(0)