Harness 工程:给 AI Coding 套上缰绳的第一次实践

Harness 工程:给 AI Coding 套上缰绳的第一次实践
Harness 工程给 AI Coding 套上缰绳的第一次实践让 AI 写代码不难让 AI 写对代码才难。这篇文章记录我在业务项目上第一次把 Harness 工程跑通的全过程——从AI 乱猜到AI 照着规矩来。目录你一定遇到过这个场景L1/L2/L3三层约束模型harness 长什么样9 步完整流程实操踩坑实录harness 在每一步的作用个人思考什么时候该用什么时候别用写在最后你一定遇到过这个场景你用 Claude Code后面简称 CC给运维管理平台加功能——问题记录模块支持视图切换描述字段不截断。你打开 CC把需求一丢等着它出活。结果它上来就写代码——字段猜的、返回格式自己造的、分页组件从别的模块复制过来参数还对不上。改了三轮勉强能跑但浑身别扭——字段少了一个返回格式跟其他接口不统一分页传参方式跟项目约定不一样。这不是 CC 笨。它有能力写对代码但不知道这个项目的规矩。接口约定、状态码、鉴权方式散落在代码各处有的甚至只在老员工脑子里。CC 只能反推推不准是常态。这就是 Harness 工程要解决的问题给 AI 一套它能读的「项目规矩」让它从「猜」变成「读」。Harness 本身不含业务代码是一套存规则和约束的文档体系——你项目的「指挥层」。AI 开工前先读这些文档再在约束下写代码。我第一次接触这个概念觉得又是那种多写文档就好了的万金油建议。但实际跑了一遍 9 步流程之后发现它确实改变了 AI 生成代码的质量。下面从原理到实操一步步讲。L1/L2/L3三层约束模型Harness 的核心是一条自上而下的约束链把系统知识拆成三层┌─ L1 架构层 ───────────────────────────────────────────────────┐ │ 系统边界 · 核心模块 · 数据流向 · 服务职责 · 外部依赖 │ │ 回答这个系统到底是什么 │ └─────────────────────────────────────────────────────────────────┘ │ 指导 ▼ ┌─ L2 模式与抽象层 ─────────────────────────────────────────────┐ │ 接口定义 · 组件/服务/仓储/Schema · 状态管理分层 · 跨服务契约 │ │ 回答系统应该按什么方式生长 │ └─────────────────────────────────────────────────────────────────┘ │ 约束 ▼ ┌─ L3 文件级代码层 ─────────────────────────────────────────────┐ │ 函数/类 · 页面/组件 · 配置 · 测试 · 各种具体实现 │ │ 回答具体怎么实现 │ └─────────────────────────────────────────────────────────────────┘L1 指导 L2L2 约束 L3。下层在上层的夹缝里生长不能反向定义上层。层典型内容变化频率谁维护L1 架构层系统边界、服务地图、部署形态、安全/性能约束极低动它等于动系统定义架构师/TLL2 模式与抽象层API 契约、分页约定、错误码、认证方式、模块内部约定低动它等于改契约前后端共同L3 代码层函数实现、页面组件、具体配置、测试用例高每个任务都在改开发者/AI关键决策Harness 只存 L1 和 L2L3 永远留在代码工程里。为什么因为 L3 太容易过时了。你今天在 harness 里写「问题列表页用ProblemList.vue」明天重命名一下组件harness 就过期了。文档和代码一旦不同步比没有文档还糟糕——AI 读了过时的文档会自信地写出错误的代码。判断口诀很好记一条信息如果改一行代码就可能过时它就是 L3不写进 harness。举个例子。「所有接口返回必须用AjaxResult包装」——这是 L2 契约写进 harness。「ProblemController.list()方法调用了ProblemService.selectList()」——这是 L3 实现细节不写。前者几个月不变后者可能每周都在改。harness 长什么样理论讲完看看实际目录结构harness-engineering-starter/ ├── AGENTS.md ← AI 入口每轮任务第一站 ├── LAYERS.md ← 三层模型原理说明 ├── SKILL.md ← 防腐化工具精简 AGENTS.md ├── docs/ │ ├── GUARDRAILS.md ← 改动护栏红区/灰区/绿区 │ ├── DONE.md ← 完成标准DoD │ ├── GLOSSARY.md ← 领域术语 │ ├── architecture/ ← L1系统边界、服务地图、约束、部署 │ ├── contracts/ ← L2API 响应、分页、错误码、认证 │ ├── flows/ ← 跨服务业务流程 │ └── decisions/ ← 决策记录ADR └── services/ ├── _template/ ← 新增服务的起点 ├── seal-frontend/ ← 前端服务事实层 └── seal-backend/ ← 后端服务事实层文档按两个维度分区全局层docs/存跨服务共享的架构、契约、流程单服务层services/svc/存各服务内部约定不上提到全局。三个关键防护文件GUARDRAILS.md— 改动护栏把代码区分为红/灰/绿三区。红区生产配置、schema、状态机、鉴权改前必须问人绿区页面实现、样式AI 可自改灰区改了要说明影响面。DONE.md— 完成标准DoD任一项不满足任务就算没完成。包括是否落在安全区、编译是否通过、契约是否被悄悄改动。SKILL.md— 防腐化工具。AGENTS.md 每轮全量加载必须精简。路径、函数名、目录清单这些容易过时的东西一律不写进去。9 步完整流程实操Harness 解决了AI 不知道规矩的问题但得有流程把它串起来。我在业务项目上走了一遍 9 步流程通过需修改第 0 步准备第 1 步探索第 2 步提案第 3 步人工审核第 4 步拆任务第 5 步TDD 实现第 6 步集成验证第 7 步代码审查第 8 步需求验收第 9 步归档沉淀9 步拆成三个阶段每阶段三步。规划三步曲准备 → 探索 → 提案这三步的目标是想清楚再动手。第 0 步准备。让 CC 先读GUARDRAILS.md和service-map.md确认改动属于红区、灰区还是绿区。CC 读完分析灰区后端 Service 绿区前端页面可以继续。我先读 GUARDRAILS.md 和 service-map.md告诉我这次改动属于哪个区域。 CC分析出灰区绿区涉及 ruoyi-business 和前端页面。 我确认没问题继续。brainstorm 探索方案。第 1 步探索。不写代码先 brainstorm。CC 提了三种方案前端切换视图改动最小、后端新增接口职责清晰、后端接口加参数一个接口两种模式。我选了第三种——复用现有接口加groupBy参数。第 2 步提案。用/opsx:propose生成正式的功能规格文档。提案里必须标注 GUARDRAILS 区域、涉及模块、验收标准、测试策略。这一步是把口头讨论变成可执行的规格。实现三步曲审核 → 拆任务 → TDD第 3 步人工审核。唯一的硬性人工卡点。逐条看 proposal 的设计、验收标准、边界情况有问题打回去通过才继续。第 4 步拆任务。proposal 拆成 TDD 任务每个 task 标注区域和契约约束。第 5 步TDD 实现。逐 task 走红绿灯先写测试红灯预期失败再写实现绿灯测试通过再重构。这步最能体现 harness 的价值——CC 写代码时要遵守constraints.md的技术约束和contracts/data.md的数据约定不会自己发明一套。我按 task TDD 实现遵守 constraints.md 和 contracts/data.md。 CC先写测试断言接口返回格式符合 AjaxResult 包装红灯。再实现 Controller/Service/Mapper绿灯。 我过。跑集成验证。收口三步曲集成验证 → 验收 → 归档第 6 步集成验证。编译 测试 arch-guard架构规则检查手动验证关键路径。第 7 步代码审查。按DONE.md逐条自查安全区、契约完整性、诚实声明。第 8 步需求验收 归档。openspec verify检查契约一致性。验收通过后判断有没有新知识需要回写 harness决策→decisions/、术语→GLOSSARY.md、契约→contracts/然后归档提交。9 步走下来最大的体感变化AI 不再「自由发挥」了。每一步有边界、有检查不是限制它是给它确定的跑道。踩坑实录流程看起来很顺畅但实际跑的时候CC 经常抄近道。以下是我遇到的几个典型坑。坑 1跳过 GUARDRAILS 就开工。有一次 CC 直接改了灰区的 Service 查询逻辑没说明影响面。发现方式输出里没有区域分析。纠正“先读 GUARDRAILS.md告诉我属于哪个区域再继续。”坑 2没写测试就开始写实现。CC 写完实现再补测试测试自然是绿的——按自己实现写的测试没有验证价值。纠正“停。按 TDD 来先写测试红灯再写实现绿灯。回退刚才的实现。”坑 3brainstorm 阶段就写代码。探索阶段聊着聊着就开始贴代码。纠正“现在是 brainstorm 阶段不要写代码。先探索方案对比优缺点。”坑 4改了红区文件。CC 改了鉴权注解红区改完甚至没提。纠正“你改了鉴权注解这是高危区。停下来我需要确认这个改动是否必要。”这些坑的共同特点CC 默认走捷径。不是故意违规是训练数据里没有先读护栏再开工这种模式。harness 把它变成显式指令——你不提醒它就跳过写进文档它大概率遵守。harness 在每一步的作用有人会问harness 不就是文档吗区别在于普通文档写完扔那harness 每一步都被 AI 主动读取——不是参考资料是执行指令。步骤harness 文件作用准备GUARDRAILS service-map确认边界能不能动、涉及哪些模块探索GUARDRAILSbrainstorm 时不会提出越界方案提案GUARDRAILS DONEproposal 里自带区域标注和验收标准拆任务GUARDRAILS contracts每个 task 标注边界和契约约束TDD 实现constraints contracts写代码时遵守约定不会自己发明一套集成验证arch-rules.yaml自动检查架构规则代码审查DONE contracts逐条核对完成标准需求验收contracts检查是否符合契约归档全部判断要不要回写知识沉淀每一步都有对应的 harness 文件做决策锚点——该读什么、遵守什么、输出什么都是明确的。本质区别文档是被动的harness 是主动的。它是 AI 工作流的一部分不是存档材料。个人思考什么时候该用什么时候别用跑完一遍 9 步流程后一些感受分享。适合用 harness 的场景多人协作的业务项目。两三个 AI 同时改一个项目没有约束层它们的理解会互相冲突。harness 让所有 AI 读同一套规矩。模块边界模糊的老项目。知识散落在代码注释、wiki 和老员工脑子里。harness 迫使你把隐性知识显式化对 AI 和新人都有好处。AI 生成代码占比高的项目。偶尔补一两个函数不需要 harness但 AI 承担 50% 以上编码时没有约束层就是赌运气。不适合用 harness 的场景小脚本、一次性工具。处理个 CSV 搞什么 harness直接让 AI 写。探索性原型。还不知道要做什么的时候约束反而拖慢探索速度。先试方向明确了再补。个人小项目。上下文都在你脑子里不需要传递就没必要搞 harness。成本 vs 收益说实话前期投入不小。读文档、理解三层模型、整理自己项目的 GUARDRAILS 和 contracts花了不少时间。但收益是持续的。最大的变化是AI 不再反复踩同一个坑了。以前没有 contractsAI 每次写接口都自己发明返回格式改了五次五次不一样。有了 contracts它第一次就写对了。还有人审代码变轻松了。以前得逐行核对字段和约定现在 CC 按 DONE.md 自己先过一遍我审的是它的自查结果。跟裸 AI Coding 的体感对比一句话从「猜」到「读」。裸 AI Coding 的体感每次对话都像带一个聪明但什么都不知道的新人你说什么它做什么但理解总有偏差大量时间花在纠正上。有 harness 的体感每次对话都带一个读过项目文档的同事知道规矩和边界。纠正时间少了讨论方案的时间多了。前者是你在教 AI 做事后者是你在跟 AI 合作做事。写在最后Harness 工程的本质就一句话让 AI 从「猜你的项目」变成「读你的项目」。它不解决 AI 的能力问题解决的是上下文问题——项目规矩、边界、验收标准。你不说AI 就猜你写下来让它读它就不猜了。推荐从这几个文件开始一份GUARDRAILS.md明确哪些能改、哪些要确认一份contracts/定义接口返回格式、分页约定、错误码一份DONE.md列出完成标准不用一次性写完从最痛的点开始——接口不统一先写 contractsAI 乱改配置先写 GUARDRAILS。慢慢补用几次就知道还需要什么了。第一次跑 9 步确实繁琐但跑过一遍很多步骤就变成肌肉记忆。而且 AI 记性比你好——你写进 harness 的规矩它每次都会读、每次都会遵守。