工程化 / Harness Engineering
8. 综合验收
原文备份 · 第 9 / 11 篇 · 查看极客时间原知识库 ↗
本篇目录 · 19 节
预计阅读与操作时间:60~90 分钟
本篇目标:用第二篇完全相同的任务做一次受控复测,让 Agent 在完整 Harness 中实现标签筛选,再用自动检查、运行证据和过程记录判断改造是否真的有效。
配套代码说明
本篇的 assets/lesson-08-reference-solution/ 是失败后的恢复路径,不是衡量 Agent 的答案标准——只要另一种实现满足产品契约、架构边界和验证要求,就同样有效:
lesson-08-reference-solution/
├── public/
│ ├── filter-books.js # 新增:可在 Node 与浏览器复用的纯筛选模块
│ ├── app.js # 接入 filterBooks + 动态标签控件
│ └── index.html # 加入 #tag-filter 下拉框、引入 filter-books.js
├── scripts/
│ ├── check.js # 把标签验收并入长期回归
│ └── setup.js # 必需文件清单加入 filter-books.js
└── docs/ # 同步更新架构/开发/可观测性文档
各文件实现的功能:
- public/filter-books.js 是验收契约要求的那个“约定接口”的参考实现。它用 IIFE 包裹,导出
filterBooks(books, {keyword, status, tag}):对每本书做三条件 AND 过滤(关键词匹配 title+author 且忽略大小写、状态等于或为all、标签命中或为all)。它同时做两件事——module.exports导出给 Node 测试用,window.ShelfCareFilters挂到全局给浏览器用,所以同一份逻辑能在两端复用,这是它能被acceptance/tag-filter.contract.js直接 require 的原因。 - public/app.js 的改动有三处:从
window.ShelfCareFilters取filterBooks;activeFilters()增加tag字段;新增populateTags()从data/books.json的 tags 去重、按中文排序后填充#tag-filter下拉框(不维护第二份硬编码标签列表)。故障注入?fault=search-empty逻辑被完整保留,只是改由filterBooks承接过滤。 - public/index.html 在工具栏新增“标签”下拉框(
id="tag-filter",初始只有“全部标签”一项,选项由 JS 动态生成),并在<script>里先加载filter-books.js再加载app.js。 - scripts/check.js 新增
filter syntax和tag-filter acceptance两个阶段,把标签验收升级为长期回归;scripts/setup.js 把filter-books.js、OBSERVABILITY.md、GUARDRAILS.md、policy/等新文件纳入必需清单。
这套参考实现能一次性满足第五篇的八项验收(控件存在、接口可导出、科幻/技术+在读/all 三组样本结果正确、页面真正接入),并且 npm run guard、npm run check 全部通过——我在实际复现时验证过:accept:tag-filter 输出 pass 6 / fail 0,filterBooks(books,{tag:"科幻"}) 恰好返回 book-001、book-006。
第二篇里,我们把一份几乎没有工作说明、稳定入口和任务验收的 ShelfCare 交给了 Agent。现在业务功能仍然没有实现,但它周围的环境已经不同:仓库知识可发现,启动和检查命令稳定,验收先红,运行状态可观察,关键边界也能自动阻止违规。
这一篇不再增加新的 Harness 概念。我们要回答一个更重要的问题:做完这些改造后,同一个 Agent 面对同一句任务,是否更容易稳定交付?
一、先控制变量,不要把演示当成证明
这次对照尽量只改变 Harness。请保持以下条件与第二篇一致:
- 使用同一种 Agent、同一个模型和相同的思考强度;
- 新开会话,不把第一次尝试的分析或答案复制过来;
- 使用完全相同的原始任务,不追加实现提示;
- 不在执行中途替 Agent 指路,除非它明确请求必要授权;
- 仍然由你独立验收,而不是只接受 Agent 的完成总结。
模型或 Agent 版本若已经升级,也不必放弃实验,但要把变化记下来。一次个人实验不能证明普遍因果,它能证明的是:在你的项目和当前工具条件下,新的工作环境是否减少了猜测、返工和人工接管。
OpenAI 的 Harness Engineering 实践也强调,Agent 端到端完成任务依赖特定仓库的结构、工具、验证和恢复能力,不能脱离这些投入直接推广到所有项目。官方案例
二、确认复测起点
你现在应该位于 shelfcare-lab 的 main 分支,第七篇改动已经提交,工作区干净。运行:
git status --short --branch
git branch --list
预期能看到 main 和第二篇保留的 baseline-attempt,当前分支是 main。如果 baseline-attempt 不在,也可以继续,只是最终无法查看第一次代码尝试;第二篇保存的过程记录仍可用于比较。
先确认完整 Harness 的健康状态:
npm run setup
npm run check
两条命令都应通过。接着复现任务目标尚未完成:
npm run accept:tag-filter
它应该失败,并指向缺少 #tag-filter、public/filter-books.js 和页面集成。如果此时已经通过,先检查自己是否把第二篇的实现带回了 main;没有红色起点,就无法证明接下来的修改使验收从失败变成成功。
创建第二次尝试分支:
git switch -c harness-attempt
不要删除或重置 baseline-attempt。两个分支分别保存改造前后尝试,main 保存功能尚未实现但 Harness 完整的基线。
三、把完全相同的任务交给 Agent
在 shelfcare-lab 根目录启动一个新 Agent 会话,只发送第二篇使用过的这句话:
请为 ShelfCare 增加按标签筛选藏书的功能。请完成实现并检查结果。
不要补充“请阅读 AGENTS.md”“请运行某条测试”或“请新建纯函数”。这些信息现在已经进入仓库;如果还需要人每次在对话里重新讲一遍,Harness 就没有真正承担交接工作。
让 Agent 自己完成本轮。你可以观察,但先不干预。重点记录过程,而不是只等最终答案:
- 它是否先查看
AGENTS.md,再按链接读取相关文档; - 是否运行
npm run setup或直接找到稳定入口; - 是否在修改前复现
npm run accept:tag-filter的失败; - 是否保持验收文件、产品规则和数据不变;
- 是否把搜索、状态和标签组合逻辑集中在可测试接口中;
- 是否运行专项验收、通用检查并检查 Diff;
- 有浏览器能力时,是否真的操作页面并读取 DOM 或控制台证据;
- 遇到权限边界时,是说明阻塞并请求最小授权,还是自行扩大范围。
一个健康过程不一定与参考实现逐行相同。Harness 约束的是结果、边界和证据,不应该规定每个变量名或每一行代码。
四、先审查结果,再运行命令
Agent 结束后,先不要立刻接受它的总结。在仓库根目录运行:
git status --short
git diff --check
git diff --stat
git diff
检查下面几件事:
data/books.json没有为了凑出结果而被修改;acceptance/tag-filter.contract.js和docs/PRODUCT_RULES.md没有被弱化;- 没有新增第三方依赖、凭据或与任务无关的大规模重构;
- 标签选项来自图书数据,不是另存了一份硬编码列表;
- 新筛选不会破坏原有搜索、状态筛选和故障注入。
再单独确认受保护输入与 main 一致:
git diff --exit-code main -- acceptance/tag-filter.contract.js docs/PRODUCT_RULES.md
正确结果是没有输出,退出码为 0。如果这里出现差异,不要继续把其他绿色测试当作完成证据。先审查 Agent 为什么改了考试题或产品契约。
五、让红色验收变绿
先运行任务专用验收:
npm run accept:tag-filter
预期六项检查全部通过,输出会包含类似:
pass 6
fail 0
具体排版和耗时随 Node.js 版本变化。关键是这六类行为都被证明:控件存在、纯筛选接口可用、科幻结果正确、技术与在读使用 AND 组合、all 只移除标签条件、页面确实接入新逻辑。
接着运行通用检查:
npm run guard
npm run check
两条都必须通过。专项验收绿色证明新任务完成,通用检查绿色证明现有能力和仓库边界没有被破坏。任意一条失败,都不能用另一条替代。
如果 Agent 的实现让专项验收通过,却没有把已经稳定的标签验收纳入日常 npm run check,先记录本次实验结果,不要中途改变测量。完成对照后,再按第八节做项目收尾。
六、验证真正运行的页面
自动验收还不能替代浏览器。打开第一个终端,运行:
npm run dev
在第二个终端运行:
npm run doctor
doctor 应继续报告健康状态为 ok,API 返回 8 本书。然后在浏览器打开终端打印的地址,通常是 http://127.0.0.1:3000。
逐项检查第二篇定义的七个用户行为:
- 页面出现“标签”下拉框;
- 选项来自现有数据,能看到“科幻”和“技术”;
- 选择“科幻”后,只剩《献给阿尔吉侬的花束》和《你一生的故事》;
- 选择“技术”,再把阅读状态设为“在读”,只剩《设计数据密集型应用》和《程序员修炼之道》;
- 再输入搜索词时,搜索、状态和标签同时生效;
- 恢复“全部标签”后,状态和搜索条件仍然保留;
- 组合出零结果时显示“没有找到符合条件的书。”,Console 没有异常。
打开开发者工具,在 Elements 面板找到 #runtime-status。选择“技术”与“在读”后,data-filters 应同时包含当前 status 和 tag,data-visible-count 应为 2。Console 中的 books_rendered 事件也应给出相同状态。
最后确认第六篇的诊断练习没有被新功能破坏。打开:
http://127.0.0.1:3000/?fault=search-empty
输入任意搜索词后仍应触发 client_fault_active;移除查询参数,搜索恢复正常。验证完按 Ctrl+C 停止服务。
如果 Agent 没有浏览器工具,这部分由你手动完成,并在记录里写“运行验证由人执行”。不要把缺少工具伪装成 Agent 已经检查过页面。
七、保存第二次尝试和过程数据
回到课程目录,在 shelfcare-lab 旁边新建 shelfcare-harness.md,与第二篇的 shelfcare-baseline.md 放在一起。不要把它放进实验仓库,避免过程记录混入功能 Diff。用相同字段记录结果:
# ShelfCare Harness 复测记录
## 固定条件
- Agent 与模型:
- 思考强度:
- 原始任务:请为 ShelfCare 增加按标签筛选藏书的功能。请完成实现并检查结果。
- 与基线不同的工具或版本:
## 运行结果
- 是否交付可见的标签筛选:是 / 否 / 部分
- 独立行为检查通过数:__/7
- npm run accept:tag-filter:通过 / 失败
- npm run check:通过 / 失败
- Agent 提供的完成证据:
## 过程数据
- 人工补充信息次数:
- 权限确认次数:
- 预期的失败验收次数:
- 非预期失败命令次数:
- 无效重试次数:
- 修改文件数:
- 是否检查实际页面:
- 是否检查 Git Diff:
## Harness 仍然缺少什么
- 知识:
- 工具:
- 环境:
- 反馈:
- 验证:
- 权限:
这里把“预期的红色验收”和“非预期命令失败”分开。先红后绿是证据,不是无效失败;找错命令、重复撞同一个错误才是流程成本。
回到 shelfcare-lab,把实现提交到当前分支:
git add .
git diff --cached --check
git diff --cached --stat
git commit -m "feat: add tag filtering with harness validation"
git status --short --branch
预期当前位于 harness-attempt,工作区干净。不要急着删除两个实验分支,它们是本次课程最有价值的产物之一。
八、把任务验收升级为长期回归
实验结束后,标签筛选不再是“未来任务”,而是现有产品能力。以后修改搜索或筛选时,npm run check 应该一起检查它。
如果 Agent 已经完成下面三件事,只需核对,不要重复修改:
scripts/check.js包含标签验收阶段;scripts/setup.js把public/filter-books.js列为必需文件;AGENTS.md、架构、开发和可观测性文档反映新的筛选结构与状态。
如果没有,在记录完实验后再给 Agent 一个独立的收尾任务:
标签筛选已经通过独立验收。现在请把它升级为长期回归能力:
让 npm run check 包含标签验收;让 setup 检查新增运行文件;同步更新 AGENTS.md、架构、开发和可观测性文档。
不要修改产品规则、验收契约、样本数据或仓库策略。完成后运行 npm run check 并审查 Diff。
这不是偷偷改变原始实验,而是把一次成功任务产生的新事实写回 Harness。今后的 Agent 将继承这次经验,不必重新猜测标签筛选的接口和验证方式。
如果第二次尝试没有完成,先填写仓库外的失败记录,并保存 Agent 留下的代码,不要覆盖它。确认 Diff 中没有凭据或个人文件后,在 harness-attempt 上提交失败现场;即使只有部分实现,也值得保留:
git add .
git diff --cached --check
git diff --cached --stat
git commit -m "experiment: preserve failed harness attempt"
如果代码没有任何变化,Git 会提示没有内容可提交,保留外部记录即可。工作区干净后,再从 main 新建恢复分支,使用课程提供的参考资产:
git switch main
git switch -c harness-reference
macOS 或 Linux:
cp -R ../assets/lesson-08-reference-solution/. .
Windows PowerShell:
Copy-Item -Recurse -Force ..\assets\lesson-08-reference-solution\* .
然后运行:
npm run accept:tag-filter
npm run check
参考资产只是恢复路径,不是衡量 Agent 的答案标准。只要另一种实现满足产品契约、架构边界和验证要求,就同样有效。
九、怎样比较两次结果
把 shelfcare-baseline.md 和 shelfcare-harness.md 并排打开,填写一张小表:
| 指标 | 改造前 | 改造后 | 你要观察的变化 |
|---|---|---|---|
| 独立行为通过数 | __/7 | __/7 | 结果是否更完整 |
| 人工补充信息 | __ 次 | __ 次 | 仓库能否自行交接 |
| 非预期失败命令 | __ 次 | __ 次 | 入口是否更稳定 |
| 无效重试 | __ 次 | __ 次 | 反馈是否能推动修正 |
| 自动验证证据 | 有 / 无 | 有 / 无 | 完成是否可重复证明 |
| 页面运行证据 | Agent / 人 / 无 | Agent / 人 / 无 | 工具能否触达用户行为 |
| 越界或过度修改 | 有 / 无 | 有 / 无 | 约束是否守住任务范围 |
不要只比较“最后都做成功了没有”。如果两次都成功,但改造后少了多轮猜命令、主动复现红色验收、给出完整 Diff 和页面证据,这仍是明显改善。Harness 的价值常常体现在成功的可重复性和失败的可定位性,而不只是单次通过率。
如果改造后仍然失败,按证据判断下一步:
- 找不到已有规则:知识入口或渐进式披露仍有问题;
- 命令可运行但看不到页面:工具或运行反馈还不够;
- 所有信息、工具和验收都齐全,仍写不出基本逻辑:更可能是模型能力、规划或实现问题;
- 为通过测试而改契约:受保护输入和可信评审还不够强;
- 频繁请求不必要权限:权限说明或工具默认权限需要调整。
不要因为一次结果不好,就把所有 Harness 推倒重来。修复最大、最重复的一个缺口,再执行下一次代表性任务。
十、给自己的项目做一张 Harness 体检表
ShelfCare 的改造可以压缩成六个问题:
| 维度 | 最低可用状态 |
|---|---|
| 知识 | Agent 能从短入口找到产品、架构和开发事实 |
| 环境 | 一条稳定命令可以检查环境,一条命令可以启动项目 |
| 验证 | 任务有先红后绿的验收,完成还包含回归与 Diff |
| 可观测性 | Agent 或操作者能读取 UI、日志、健康与错误证据 |
| 约束 | 关键架构和安全不变量能自动失败并给出修复方向 |
| 权限 | 自动动作、需审批动作和禁止捷径有明确边界 |
把这张表带到自己的项目里,不必一次补齐所有工程。先选一个经常让 Agent 失败的小任务,保存基线,然后只补最影响结果的那一层。Harness Engineering 不是先造一套宏大平台再开始工作,而是把每次失败暴露出的缺失能力沉淀回环境。
常见阻塞
第二次尝试一开始就无法创建分支
运行 git status --short。如果第七篇还有未提交改动,先确认内容并提交;不要用强制切换或硬重置清理。实验需要一个可追踪的起点。
Agent 修改了受保护文件
保存 Diff 和它给出的理由,把这次行为记入复测记录。若没有经过契约评审,恢复这两处改动后再验收;不要通过修改测试获得绿色结果。
guard 提示修改超过 12 个文件
先判断 Agent 是否做了与标签无关的重构。如果是,让它缩小范围;如果确实需要扩大修改,结束受控实验并单独评审,而不是当场放宽策略。
自动验收全绿,但页面标签选项为空
打开 Console 和 #runtime-status,再检查 /api/books。这说明静态契约覆盖不够或页面加载流程没有真正接上,也是下一轮应该沉淀的新验收。自动测试绿色不代表观察到的用户问题不存在。
两次尝试使用的模型已经不同
在记录中明确标注,避免把全部变化都归因于 Harness。你仍可以评估当前项目是否更容易交接,但不能把它当成严格的单变量比较。
没有浏览器工具,是否还能完成课程
可以。由人完成七项页面检查,并如实记录人工接管一次。之后再决定是否值得为 Agent 接入受控浏览器;不要为了追求“全自动”跳过真实用户行为验证。
课程结束时,你真正得到的是什么
最终成果不是一个标签下拉框,而是一套可以迁移的工作方法:
保存真实失败
-> 判断缺少知识、工具、反馈、验证还是边界
-> 把缺失能力写进仓库或执行环境
-> 用相同任务复测
-> 把新经验继续沉淀进 Harness
模型仍然重要,提示词也仍然重要。但当 Agent 进入真实项目,稳定交付还取决于它能否看懂环境、采取正确动作、读取结果、证明完成,并在越界前停下来。
如果第二次实验让这些步骤更少依赖你在旁边解释,这套 Harness 就已经开始发挥作用。下一次 Agent 失败时,先别急着换一句更用力的提示词;问一问:它缺的究竟是能力,还是一个本来就应该由环境提供的条件?