【免费下载链接】learn-harness-engineering
Harness engineering beginner tutorial, from 0 to 1
端到端测试(E2E)是 AI 编码智能体(coding agent)验证体系中唯一能证明"系统级缺陷不存在"的环节:单元测试全部通过,组件边界缺陷却可能一个都抓不到。本篇以 learn-harness-engineering 仓库中《강의 10. 엔드투엔드 테스트만이 진정한 검증이다》(含配套代码示例与评审反馈转规则示例)为骨架,结合 projects/project-05/ 的 Electron 知识库应用源码,讲清四件事:单元测试为什么系统性盲视组件边界缺陷、E2E 如何改变智能体的编码行为、如何把架构约束与反复出现的评审意见自动化成"可执行的检查",以及怎样编写"面向智能体"的修复式错误消息。读完你将得到一套可复制的分层验证与规则固化方案。
一、问题场景:五个组件边界缺陷,单元测试一个都没抓到
课程给出了一个极具代表性的场景:让智能体为 Electron 应用实现"文件导出"功能。智能体依次完成了渲染进程组件、preload 脚本、服务层逻辑,并为每个组件编写了单元测试——全部通过。智能体宣布"完成"。然而当真实用户点击导出按钮时:
- 文件路径格式错误(渲染进程传相对路径,preload 期待绝对路径);
- 进度条不更新(导出进度没有通过 IPC 传回 UI);
- 大文件导出时内存泄漏(文件句柄未释放);
- 打包环境下的权限差异;
- 服务层异常没有传播到 UI 层。
共五个跨组件边界缺陷,单元测试零检出。这不是巧合,而是单元测试"隔离"设计哲学的必然结果——正如课程中合唱排练的比喻:每个声部戴着头戴耳机单独练习时都很完美,合到一起就有人快半拍、伴奏低半音。
仓库配套的 e2e-runner.ts 用最小实现把这种现象量化了出来:三个真实场景(导入文档并提问、删除文档并验证移除、多用户并发访问)下,每个步骤的单元测试都标记为unitTestPasses: true,但流水线实际行为(actualBehavior)却是partial或fails——例如索引器与检索器的 embedding 维度不匹配导致检索结果为空、索引清理超时留下孤儿 chunk、检索缺少用户级隔离导致跨用户结果串扰。运行npx tsx docs/ko/lectures/lecture-10-why-end-to-end-testing-changes-results/code/e2e-runner.ts后,输出会明确显示"False confidence count: 3 of 3",即三组测试全部是"单元测试通过但 E2E 失败",直观证明了仅靠单元测试会产生虚假信心(false confidence)。
二、单元测试的四个系统性盲点
课程将单元测试的盲区归纳为四类,每类都有清晰的成因:
- 接口不一致(Interface Mismatch):渲染进程传给 preload 的是相对路径,preload 期待绝对路径。由于单测各自用 mock,双方"各自正确",只有真实流程跑起来才会暴露。对应源码中的表现就是 preload.ts 通过
contextBridge暴露类型化 API,若渲染侧直接拼路径、绕过该桥接层,单测永远发现不了签名错配。 - 状态传播错误(State Propagation Errors):数据库迁移改了表结构,但 ORM 缓存层还持有旧 schema 的缓存项。单测每次提供全新 mock 环境,自然暴露不了跨层状态不一致。
- 资源生命周期问题(Resource Lifecycle Issues):文件句柄、数据库连接、网络 socket 的获取与释放分散在多个组件。单测为每个测试独立创建、销毁资源,测不出资源竞争与泄漏。
- 环境依赖(Environment Dependency):代码在一切被 mock 的测试环境中正确,却在真实环境因配置差异、网络延迟或服务不可用而失败。
课程的结论是:单测是 Google 测试金字塔的底座,但止步于单测,就会系统性地错过组件交互问题。对 AI 编码智能体而言危害更甚——智能体倾向于只跑最快的测试就宣告完成。
三、E2E 不只改变结果,更改变智能体的行为
这是课程最容易被忽略的洞察:当智能体知道自己提交的成果会被 E2E 检验时,它的编码行为本身会提前改变:
- 主动考虑组件交互:写代码时会想"这个接口如何与上游衔接",而不是只盯着单个函数。
- 遵守架构边界:在存在架构约束的系统中,E2E 强制智能体遵循边界规则,如同乐谱上标了渐强记号就必须照做。
- 处理错误路径:E2E 通常包含失败场景,迫使智能体考虑异常处理——排练时模拟"麦克风突然没声",真上台就不慌。
课程中的 mermaid 图清晰地对比了两种验证视角:单元测试只在"隔离的部件"层面分别检查 Renderer / Preload / Service;E2E 则让"Renderer 按钮点击 → Preload 桥 → 服务层 → 文件系统/OS → 真实导出文件"整条链路真实贯通。
四、验证分层:把 E2E 明示为完成的前置条件
课程给出了直接可用的验证层级模板,建议写进 harness(智能体运行框架)的指令文件:
## 검증 계층 구조(验证层级结构) - 레벨 1: 단위 테스트(단위 테스트,必须通过) - 레벨 2: 통합 테스트(통합 테스트,必须通过) - 레벨 3: 엔드투엔드 테스트(E2E 테스트,跨组件变更时必须通过) - 필수 레벨 건너뛰기 = 완료 아님(跳过必检层级 = 未完成)关键原则:凡涉及跨组件变更的任务,E2E 通过是完成的前提条件。这与仓库中 Project 05 的"Definition of Done"完全同构——gen-eval/AGENTS.md 明确要求"the required verification actually ran"(要求的验证真实跑过)且scripts/check-architecture.sh零违规才算完成,并禁止"只是加了代码就标记功能完成"。
五、架构规则自动化:从"文档上写着"到"CI 里跑着"
E2E 的前提是清晰可执行的系统边界。课程引用 OpenAI 的工程实践强调:对智能体生成的代码库而言,架构约束不是团队壮大后才考虑的事,而是第一天就要确立的初始前提。原因是智能体倾向于复制仓库里的既有模式,模式若不均匀,智能体每个会话都会引入更多偏差。
仓库中的落地示例是 check-architecture.sh,它把 ARCHITECTURE.md 声明的分层边界变成了机器可执行的检查:
- 检查 1:
src/renderer中不允许出现fs|path|os|child_process等 Node.js 核心模块 import(grep -qE "import.*\b(fs|path|os|child_process)\b"); - 检查 2:
src/services不允许 importelectron,也不允许出现ipcMain|ipcRenderer|BrowserWindow标识; - 检查 3:
src/services与src/main不允许 import React。
任何违规都会累计到VIOLATIONS计数,最终以非零退出码(exit 1)让 CI 失败。这正好把课程中的基础 grep 命令升级为完整的边界守卫:
# 렌더 프로세스가 Node.js API를 직접 호출하는지 검사(检查渲染进程是否直接调用 Node.js API) grep -r "require('fs')" src/renderer/ && exit 1 || echo "OK: no direct fs access in renderer"架构文档 ARCHITECTURE.md 进一步定义了四层职责:Renderer(仅通过window.knowledgeBase访问数据,禁止核心模块与 Electron API)、Preload(仅经contextBridge.exposeInMainWorld暴露类型化 API,只做通道映射)、Main(仅做请求路由,委托服务层)、Services(承载全部业务逻辑,文件系统访问一律走PersistenceService)。这种"强制不变量、不微观管理实现"的做法,正是课程强调的核心原则——例如规定"数据在边界处被解析",但不指定用哪个库。
六、评审反馈转规则:让 harness 逐月自动变强
课程配图文件 review-feedback-to-rule.md 浓缩了本节主题——一条反复出现的评审意见被"晋升"为 harness 规则:
评审意见:不要在渲染器中直接调用文件系统工具,请使用 preload 桥。
晋升后的规则:
- 增加 lint 或 import 规则,禁止渲染进程代码中使用
fs;- 增加说明 preload 边界的修复提示文本。
这就是课程定义的评审反馈晋升(Review Feedback Promotion):每当代码评审发现一种新型智能体错误,就把它转成自动化检查。一个月后,harness 会比一个月前显著更强——像合唱团的排练笔记,每次排练记录下来的问题,下次排练前就会被自动拦截。配套的 mermaid 流程图展示了完整闭环:
Review(评审反馈: 渲染器不能直接 import fs) → Rule(增加 fs import 检查) → Message(错误消息告知智能体把文件访问移到 preload) → Harness(把检查加入 harness) → Stronger(下次立即失败)在 Project 05 的 evaluator-rubric.md 中可以看到同一模式在"评审→修订"维度的实证:初始评分 2.8/5(平铺列表、无气泡、基础时间戳),经两轮修订后升至 3.3/5(气泡样式、引用计数徽章、空状态、120 字符截断),每轮修订都留下明确的证据记录——这正是"把评审反馈固化为可复现的规则"在数据上的体现。
七、面向智能体的错误消息:不只是报错,而是给出修复指令
课程引用 OpenAI 的强调:为智能体编写的错误消息必须包含修复指引。不要说"渲染器直接访问了文件系统",而要说:
ERROR: Found direct import of 'fs' in src/renderer/App.tsx:12 WHY: Renderer process has no access to Node.js APIs for security FIX: Move file operations to src/preload/file-ops.ts and call via window.api.readFile()消息三要素:什么错了(WHAT)、为什么(WHY)、怎么改(FIX)。这才把测试失败变成"自修复反馈回路"——如同指挥家不说"你错了",而是说"你这里快了半拍,听一下中提琴的节奏,第 32 小节进"。
仓库源码完整体现了这条原则的架构形态。分层职责由 preload.ts 承担——渲染进程不碰任何 Node 模块,一切文件与索引操作都通过contextBridge暴露的window.knowledgeBase类型化 API(documents.list/import/get/delete、indexing.start/status/chunks、qa.ask/history)走 IPC。当智能体违反边界时,check-architecture.sh 输出的VIOLATION: <file> imports Node.js core module会明确指出违规文件和具体 import,配合WHY与FIX消息,智能体就能自动把调用迁到正确层级。
八、成本与收益:15 秒的代价,换来系统级保障
课程给出了关键的成本数据:在该案例中,5 个缺陷全部由 E2E 抓住、单测一个没抓到,而代价只是测试时间从 2 秒增加到 15 秒——在智能体工作流中完全可以接受。
课程总结了五条核心结论:
- 单元测试对组件边界缺陷是系统性盲视的——隔离设计本身决定了它测不出交互问题;
- E2E 不仅能抓缺陷,还会改变智能体的编码行为——让它更关注集成与边界;
- 架构规则必须可执行——不是写进等人来读的文档,而是在每次提交时被自动检查;
- 错误消息要为智能体而设计——包含"如何修复"的具体步骤,形成自修复回路;
- 评审反馈晋升自动强化 harness——每一类被抓到的缺陷都成为永久防线。
配套的演练建议(见课程原文末尾"연습 문제"):选择一个涉及至少三个组件的修改,先只跑单测记录结果,再跑 E2E 对比多抓到的缺陷类型;挑一条架构约束改造成带智能体友好消息的可执行检查并集成进 harness;从评审历史中找反复出现的意见类型,用"评审反馈晋升"流程固化为自动检查,对比晋升前后的问题频率。这套方法在 projects/project-05/ 的starter与solution三种变体(single-role / gen-eval / plan-gen-eval)中均可直接实操验证。
【免费下载链接】learn-harness-engineering
Harness engineering beginner tutorial, from 0 to 1
相关推荐
将 Review 反馈提升为 Harness 规则:learn-harness-engineering 中端到端验证驱动的审查反馈闭环
将 Review 反馈提升为 Harness 规则:learn harness engineering 中端到端验证驱动的审查反馈闭环 本文基于 learn h
learn-harness-engineering:Electron 架构规则的 Harness 落地——从约束文档到可执行的端到端验证
learn harness engineering:Electron 架构规则的 Harness 落地——从约束文档到可执行的端到端验证 本教程对应 第 10
learn-harness-engineering 第十讲实战:只有端到端测试才算真正验证——从 e2e-runner 到可执行架构规则的 Agent 验证闭环
learn harness engineering 第十讲实战:只有端到端测试才算真正验证——从 e2e runner 到可执行架构规则的 Agent 验证闭环
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考