实验背景
AI Agent 写代码越来越强,但"强"不等于"可靠"。同样的模型、同样的提示词,为什么有时候一次就过,有时候反复翻车?答案往往不在模型本身,而在你给它的规则和约束。
这个实验用 Electron 搭建一个知识库应用壳子,跑两次:
- 弱 Harness:只给一段提示词,什么都不准备
- 强 Harness:提前准备好 AGENTS.md、feature_list.json、claude-progress.md,用结构化方式告诉 Agent 该干什么、怎么验证、什么时候算做完
任务定义
用 Electron 做一个知识库应用,包含四个核心功能:
| 编号 | 功能 | 要求 |
|---|---|---|
| kb-001 | 窗口启动 | npm start后 Electron 窗口正常打开 |
| kb-002 | 左侧文档列表 | 左侧面板显示文档列表区域 |
| kb-003 | 右侧问答面板 | 右侧面板包含输入框和对话区域 |
| kb-004 | 本地数据目录 | 应用启动时创建数据目录,包含 documents/、qa/ 子目录 |
Harness 是什么
Harness 不是代码,是一套告诉 Agent 怎么干活、怎么验证、什么时候算做完的规则文件。这个实验用的 Harness 核心包含三个文件,实际仓库中还会配合 init.sh 等辅助脚本:
AGENTS.md
定义 Agent 的核心行为规则:
- 一次只做一个功能 - 实现后必须运行 npm start 验证 - 不要因为"代码已经写了"就把功能标记为完成 - 验证证据记录在 feature_list.json 或 claude-progress.md - 一个功能只有在目标行为已实现、npm start 成功、证据已记录才算完成feature_list.json
每个功能的唯一事实来源,包含验收标准、证据槽位:
{"id":"kb-001","title":"窗口启动","status":"not_started","verification":["运行 npm start","确认 Electron 窗口成功打开","确认窗口标题显示应用名称"],"evidence":[]}claude-progress.md
会话进度日志,记录当前状态、已完成功能、已知风险、下一步动作。
第一次实验:弱 Harness
分支:p01-baseline
提示词:
用 Electron 做一个知识库应用,窗口左边是文档列表区域,右边是问答面板区域,应用需要创建并使用本地数据目录。
仓库状态:空的 package.json,没有 AGENTS.md,没有 feature_list.json
过程
Agent 自由发挥,一次性完成了所有四个功能。代码质量很好,UI 布局清晰,IPC 通信正确。
结果
| 功能 | 状态 | 备注 |
|---|---|---|
| kb-001 窗口启动 | ✅ 完成 | 一次通过 |
| kb-002 文档列表 | ✅ 完成 | 左侧 300px 面板 |
| kb-003 问答面板 | ✅ 完成 | 右侧 flex 面板 |
| kb-004 数据目录 | ✅ 完成 | 目录结构正确 |
应用截图
弱 Harness 的问题
虽然任务完成了,但存在以下问题:
- 没有验证证据:功能完成了,但没有记录"怎么验证的"、“验证结果是什么”
- 没有风险记录:遇到 EPERM 权限问题,但没有记录到任何地方
- 没有可接续性:下一轮会话必须重新理解代码,无法从上次中断的地方继续
- 没有完成定义:Agent 不知道"什么时候算做完",全靠自己判断
第二次实验:强 Harness
分支:p01-improved
提示词:完全相同
仓库状态:AGENTS.md、CLAUDE.md、init.sh、feature_list.json、claude-progress.md 已就位
过程
Agent 严格按照 AGENTS.md 的流程执行:
- 读取 claude-progress.md→ 确认 kb-001 为最高优先级
- 读取 feature_list.json→ 获取 4 个功能的验收标准
- 发现状态不一致→ feature_list.json 中功能标为 passing,但代码已清空,先修复基础状态
- 逐功能实现→ 每完成一个功能,运行验证,记录证据
- 遇到 Blocker→ Windows EPERM 阻止在 userData 目录创建子目录,按规则在窄范围修复
- 更新 Harness 文件→ 所有功能标记为 passing,记录风险
强 Harness 的行为差异
以下是 Agent 在关键决策点上的行为重构示意(非原始逐字日志):
# Agent 读到的不一致状态时的反应: "feature_list.json 中所有功能标为 passing,但 main.js 不存在。 这违反了 AGENTS.md 规则。先修复基础状态。" # 遇到 Blocker 时的反应: "EPERM 权限错误。 AGENTS.md 规则:窄范围修复,不扩大到其他功能。 将数据目录路径改为项目根目录,功能行为不变。" # 完成后的动作: "更新 feature_list.json: - status → passing - evidence 字段填入验证证据 更新 claude-progress.md: - 记录风险(EPERM) - 记录下一步动作"结果
| 功能 | 状态 | 证据 |
|---|---|---|
| kb-001 窗口启动 | ✅ passing | BrowserWindow 创建,index.html 加载 |
| kb-002 文档列表 | ✅ passing | #left-panel 300px,border-right 边界 |
| kb-003 问答面板 | ✅ passing | #right-panel flex:1,含输入交互 |
| kb-004 数据目录 | ✅ passing | data/documents/ + data/qa/ 已创建 |
应用截图
对比分析
核心指标
| 指标 | 弱 Harness | 强 Harness | 备注 |
|---|---|---|---|
| 首次成功启动 | 一次通过 | 三次 | 强 Harness 因主动发现并修复 EPERM 权限问题而多次重启,非代码质量问题 |
| 遗漏项 | 无 | 无 | |
| 过早停止 | 否 | 否 | |
| 验证证据 | 无 | feature_list.json 结构化证据 | |
| 风险记录 | 无 | claude-progress.md 记录 EPERM | |
| 可接续性 | 需重新理解代码 | 读 claude-progress.md 即可 | |
| 状态一致性检查 | 无 | 主动发现并修复不一致 |
深层差异
这个实验里,两个版本最终都完成了任务,UI 效果完全一样。但这不代表 Harness 没用——恰恰相反,简单的任务看不出 Harness 的价值,复杂的任务看不出 Harness 的边界。
真正体现差异的地方:
1. 状态一致性检查
强 Harness 下,Agent 发现 feature_list.json 中功能标记为 “passing” 但代码不存在时,主动停下来修复基础状态。弱 Harness 下,Agent 不会注意到这种不一致,会在坏的起点上继续叠加代码。
2. Blocker 处理策略
强 Harness 下,遇到 EPERM 权限错误时,AGENTS.md 的"窄范围修复"规则防止了 Agent 去修改其他无关代码。弱 Harness 下,Agent 可能过度修改,也可能直接放弃。
3. 证据链
强 Harness 下,feature_list.json 每个功能都有 evidence 字段记录了验证证据。claude-progress.md 记录了风险和下一步动作。下一轮会话直接读这两个文件就能接续,不需要重新理解代码。
4. 完成定义
强 Harness 有明确的完成定义:目标行为实现 + npm start 成功 + 证据记录在案。弱 Harness 下,Agent 自己判断"差不多完成了",没有外部验证标准。
关键结论
1. 简单任务中,Harness 的价值是"防御性"的
这次实验任务简单,两个版本都完成了。但 Harness 的价值在于防止意外——状态不一致、权限错误、风险遗漏,这些在简单任务中可能不致命,但在复杂任务中会累积成灾难。
2. Harness 解决的是"接续"问题,不是"完成"问题
Agent 的单次执行能力很强,但多轮会话之间的接续是短板。Harness 通过 feature_list.json(状态)和 claude-progress.md(上下文)让下一轮会话能无缝接续。
3. 规则的价值在于"约束行为",不在于"提升质量"
Harness 不会让 Agent 写出更好的代码,但会让 Agent 的行为更可预测、更规范。feature_list.json 的验收标准不是为了让代码更好,而是为了让"完成"有一个客观的判断标准。
4. 好的 Harness 是"最小可行约束"
Harness 不是越多越好。AGENTS.md 的核心规则只有几条:一次一个功能、必须验证、证据必须记录。过多的规则会让 Agent 过度遵循流程,反而降低效率。