2026/10/18 5:50:47

Harness Engineering:给vibe coding装上轨道,实现24小时无人值守编码

Harness Engineering:给vibe coding装上轨道,实现24小时无人值守编码 这两年我一直在折腾AI辅助编程vibe coding这个词出来之后简直说到我心坎里了——不用抠语法、不用记API把需求往聊天窗口一丢代码就出来了我只要判断方向对不对就行。但爽是爽问题也很现实我下班了AI就下班了我走开一小时任务就断一小时AI的输出质量还特别像过山车今天能用明天一个上下文清空就全崩了。后来我在一个小型跨平台Demo项目上做了一套工程化改造核心思路就是标题里这几个词Harness Engineering。说白了就是给vibe coding装一套轨道、装一批传感器让它从“手动模式”切换成“自动巡航模式”实现让AI在我睡觉的时候也继续改代码、跑测试、修问题。这篇文章不打算讲虚的架构理念我就把在项目X上实际搭过、实际跑过、实际踩过坑的完整方案摊开。适合谁看呢如果你已经在用AI写业务代码但总觉得“没人盯着不行”如果你试过让AI连续干活结果它越改越乱、或者干脆停在半路等你回复如果你想把自己的编码节奏从“跟着AI同步”变成“AI独立推进、我来验收”那这篇就是你想要的作业。1. vibe coding的本质和瓶颈为什么AI写代码这么爽却又这么不让人省心1.1 vibe coding到底在干嘛vibe coding这个词翻译过来就是“凭感觉写代码”。老派开发者听见会觉得这算什么正经事但我用了小半年之后反而觉得它抓住了人机协作的一个真相在需求还没收敛的阶段你让程序员先画类图、先写接口定义、先列测试用例大多数人是不愿意的因为直觉告诉你方案会变。vibe coding的做法是反过来的——把需求的粗坯扔给AI让AI先产出一版能跑的代码你看完再修正方向下一轮AI又能快速跟上。这个循环本身没问题问题在于它极度依赖“你在线”。AI生成的代码需要有人评估、有人确认、有人决定下一个方向一旦你离开电脑哪怕是去吃个饭活儿就停在那了。我自己之前最崩溃的一次是让某个AI编码工具A负责一个数据导出功能它写完了主流程但缺补丁层我等它半天没动静才发现它在等我确认“要不要加异常分支”。这种案例不是个例而是vibe coding的默认模式——没有外部约束LLM会倾向于把不确定性抛回给人类。1.2 “24小时干活”到底意味着什么24小时干活不是让AI在你机器上一直占着CPU不放而是一套无人值守的运行机制任务源源不断进入队列AI有上下文可查有明确的验收标准可依出错了能拿到错误日志自己修修不动了才喊人。这几乎是把一个初级开发者的工作方式搬到了AI身上只不过AI不需要睡觉、不会累、回复速度还快。但要做到这一点光靠某个AI聊天窗口是不可能的。模型每轮对话的上下文有上限API有token成本输出可能带着过期信息测试失败后也未必能精准定位到问题根因。所以你必须在外围搭一层工程骨架把模型的“聪明”和项目的“确定性”粘合起来。这层骨架就是Harness Engineering要做的事。它不改变你用AI表达的方式但改变了AI产出结果的路径从“随机波动”变成“有流程、有门禁、有反馈闭环”。1.3 为什么要自己架一套Harness而不是等工具自带可能有朋友要问现在的AI编码工具不是已经支持多文件编辑、自动跑测试了吗为什么还要自己搞我试下来发现现成工具解决的是“单轮对话内的自动化”而24小时干活需要的是“跨对话、跨任务、跨天的状态管理”。你需要一个外部系统来决定下一分钟AI该做什么、它之前做过什么、哪些文件被它动过、哪些测试已经挂了八次还没修好。这些信息一旦散落在聊天记录里基本就等于没记录。更关键的是稳定性。我观察了很长一段时间AI编码工具在“主动规划多步任务”时决策质量会明显下降。你让它同时改三个文件它可能改了A和B把C忘得干干净净你让它先写测试再写实现它可能顺序调反导致你review的代码一头雾水。Harness Engineering的一个重要作用就是替AI做那些它不擅长的过程管理拆任务、排优先级、守门禁。让AI回到它最擅长的事——生成和修改代码。2. Harness Engineering的整体设计四层架构把vibe coding变成生产线2.1 先说一个生活化类比我想了很久怎么向团队里的小伙伴解释这套东西最后用的类比是“小作坊变成中央厨房”。vibe coding阶段你就是一个大厨AI是给你备菜的学徒你在旁边看着他说“这菜可以下锅了”你再动勺菜咸了淡了你现场调。24小时干活的Harness Engineering等于你把厨房流程化了有人按菜谱备菜任务拆解有人把常用佐料提前配好上下文管理还有一把火定好几档火力定时翻锅自动化执行最后传菜口有个质检员咸淡不对不送出去质量验证。大厨不用每道菜都全程盯着只需要抽检和解决疑难菜。这个类比放到工程里就是一个四层结构任务层、上下文层、执行层、验证层。每一层解决一个具体的失控风险。2.2 任务层把模糊需求变成可验收的卡片任务层是整个Harness的地基。很多人在vibe coding里遇到的最大问题不是AI笨而是人没说清楚。你告诉AI“优化一下登录模块的体验”AI真的可能给你把登录按钮的圆角从4像素改成8像素因为它理解的“体验”就是长这样。任务层要做的事是把这种模糊指令结构化成任务卡片卡片里包含五样东西目标描述一句话说清这个任务完成后的可观察结果影响范围允许改哪些文件、禁止动哪些模块验收标准必须通过的具体测试或必须满足的约束参考信息相关文件路径、设计文档、代码风格约定回退条件什么情况下算失败失败后该做什么写卡片看起来是额外工作但它的本质是把人类判断前移。你不需要写代码但要写下你评估代码时采用的逻辑这样AI执行的时候就不需要在每个岔路口停下来问你。2.3 上下文层让AI拥有“跨天记忆”AI模型本身没有记忆它每一轮都像是第一天来上班的新人全靠你丢给它的材料。普通的聊天工具里上下文靠复制粘贴聊得久了还会超出窗口模型就开始忘事。Harness Engineering里的上下文层相当于给这个新人配了一个持续更新的项目笔记本。我的做法是在仓库里维护三个文件第一是AGENTS.md写清楚项目全局约定比如技术栈、目录结构、命名规则、测试命令第二是DECISIONS.md记录开发过程中做出的关键决策和为什么这么做第三是CHANGES.md由自动化脚本在每轮跑完后自动追加变更摘要相当于给AI的“工作日志”。每次AI开工前把这三份文件的最新版本塞进它的上下文它就知道项目是哪天开始做的、之前改到哪了、现在按什么规矩做事。2.4 执行层和验证层谁说无人值守就等于没有把关执行层做的事情比较朴素就是定时或按事件触发AI工作从任务队列里取出一个卡片绑定上下文丢给编码工具拿到输出后提交到代码库。验证层则是把关口每次AI提交后自动跑一套门禁包括编译、测试、静态检查、依赖检查。门禁通过才算真的完成失败就把完整日志回灌给AI让它作为下一轮任务的输入参数去修复。这两层合起来就形成了一个“自动循环”任务排队、AI干活、验证拦截、失败回流。人可以完全不参与单次循环只在循环卡住超过阈值时介入。这也回答了一个很多人担心的问题AI代码没人review会不会出事答案是不会完全不会出事但门禁能把绝大多数低级错误挡在生产环境之外剩下的小概率风险由人工抽查覆盖。我把这套东西称为“AI的自动驾驶系统加安全带”。3. 实操落地从零搭一套能跑起来的自动编码流水线3.1 先把需求拆成机器能懂的任务卡片我在项目X上的第一个完整实践是让AI自动维护一个跨平台小应用的登录态管理模块。当时我定了三个阶段的任务队列第一阶段是修复已知的session过期未清理问题第二阶段是补充异常分支的测试第三阶段是根据测试覆盖报告做代码重构。每个阶段都写成一张独立的卡片格式长这样{ id: task-001, title: 修复session过期后未清理本地缓存的问题, target: 用户退出登录后本地缓存中的session_key在5s内被清除, scope: [src/auth/session.js, src/auth/session.test.js], forbidden: [src/api, src/components], acceptance: npm run test:auth 全部通过session_key在logout后不存在, reference: [docs/AGENTS.md, src/auth/README.md], retry_policy: 最多尝试3次若失败则暂停任务并通知人工 }这里最关键的是forbidden字段我一开始没设置这个字段结果AI在修session的时候顺手改了布局组件里的一个class名原因只是它觉得“这样更整洁”。加了forbidden之后这种越界行为被显式禁止出事的概率大幅下降。任务卡片写好后放在仓库的tasks/inbox目录里执行层只管从这个目录读取。这个设计的好处是任务队列对人是友好的你可以用任何文本工具预览对机器也是友好的JSON格式谁都能解析。3.2 上下文管理让AI带着前情提要开工上下文管理的核心不是塞得多而是塞得准。我第一次做的时候把整个项目的README、所有历史提交信息、十几篇设计文档全部丢给AI当上下文结果token花了无数AI反而被互相矛盾的信息搞晕了。后来我总结出一套“三件套”策略项目全局约定文件AGENTS.md只说稳定且硬性的规则比如“使用TypeScript严格模式”、“禁止直接修改数据库schema”、“组件库只用内部封装的UI包”。关键决策记录DECISIONS.md每隔几轮任务我会手动追加一条“为什么这么做”的结论。比如为什么登录刷新接口要用双token机制为什么这个模块不引入状态管理库。AI在后续改代码时会参考这些结论避免犯方向性错误。变更日志CHANGES.md由脚本生成格式是“本轮目标、改了哪些文件、测试结果如何、遗留问题是什么”。这相当于给AI的交接班记录让它即使每轮都是冷启动也能快速续上工作。实操建议这三份文件都要放在仓库根目录下的docs/harness/路径里并且在每个任务卡片的reference字段中显式引用。执行层在拼Prompt时会按顺序读这些文件然后按“全局约定-项目决策-历史变更-当前任务”的顺序拼成一段完整指令。实测下来这种顺序特别重要AI对指令开头部分的遵从度明显高于结尾。3.3 自动化调度让流水线自己往前跑调度层我选了最保守、最稳的方案一个基于Git的循环脚本加上外部定时器。这么做的好处是不依赖任何复杂的平台能力经过验证的代码在谁的机器上都能跑。伪代码的逻辑大概是三步while true; do # 1. 取任务从 inbox 里取一张没有被标记为 running 的卡片 # 2. 执行AI调用绑定上下文文件调用编码工具的API生成代码 # 3. 验证与提交跑测试 - 测试通过则 commit失败则把日志写入反馈文件 done实际落地时要注意必须在独立的分支上让AI干活绝不允许直接往主分支提交。我会在每轮任务开始前创建一个分支名带任务ID的新分支AI的所有改动都在这个分支上门禁通过了才合并回主分支。这样即使某轮AI彻底把代码写崩了主分支也是干净的随时可以扔掉分支重来。定时触发我用的是最普通的cron表达式每半小时检查一次任务队列。其实调度频率不用太高AI生成一轮代码加跑完测试往往需要几分钟到十几分钟半小时一个间隔刚好留出余量。另外一个经验是调度的“心跳”日志一定要打到单独的日志文件里否则出了问题你根本不知道是AI没跑还是跑了没提交还是提交了没合并。3.4 质量门禁测试不过AI自己改质量门禁是整个系统里我最看重的部分也是对“无人值守”这四个字兜底的环节。我在项目X上配了三道门禁第一道是编译和类型检查。TypeScript项目跑tscPython项目跑mypy。这道门最便宜也最前置AI把类型当字符串拼出来的低级错误在这里就被拦住。第二道是单元测试和集成测试。我的验收标准里有一个死规矩任何逻辑变更必须附带至少一条对应测试的更新。AI在生成代码时往往只关注实现逻辑忽视测试所以门禁脚本会在检测到源文件改动而测试文件没改动时直接给AI发一个“补充测试”附加工单。第三道是静态检查和依赖锁定。静态检查用项目已有的lint配置重点看有没有未使用变量、可疑的异步调用顺序、硬编码密钥。依赖锁定则是检查package-lock.json或requirements.txt有没有在无人知晓的情况下被篡改防止AI为了解决眼前问题顺手升级了底层依赖。门禁失败后的回流机制是这套系统比单纯跑一个脚本更高明的地方。门禁脚本会把“失败原因、堆栈日志、失败时的代码片段”写成一个feedback文件回填到任务卡片的reference里AI的下一轮调用就会收到这个反馈。实际运行中AI在收到失败反馈后的一轮修复成功率约有七成剩下三成修不好的通常已经超出了它当前上下文能理解的范围这时就该人工介入了。4. 实际跑起来之后的常见问题这个系统也会翻车4.1 AI把需求理解偏了方向跑歪了这是第一个让我半夜爬起来看日志的问题。任务卡片写的是“优化列表分页加载首屏优先渲染前20条”结果AI半夜三点提交的分支里把整个列表组件改成了虚拟滚动还新增了两个依赖库。编译倒是通过了测试也绿了但代码量膨胀了四倍完全偏离了原始目标。后来我在任务卡片里加了一个字段叫success_signal——不用代码量描述“做成什么样”而是用可观察行为描述“用户会看到什么”。上面对应改成“页面加载时首屏在1.5秒内显示前20条数据滚动到底部时触发下一页加载”。AI在决策时对行为目标的把握明显比抽象目标更强。另外我又在验证层加了scope diff检查凡是对forbidden列表中文件的改动都自动打回。这两个小改动让我终于能安稳睡整觉了。4.2 上下文污染AI越改越“忘本”跑了大概两周之后我开始发现一个诡异的现象AI生成的代码风格变了最开始老老实实按项目原有的写法来后来逐渐引入了一些花哨的新语法甚至自己发明了一套缩写命名。问题出在CHANGES.md上——它记录了AI自己每轮的输出AI在后续运行时参考了它而它自己早期的输出本身就不完全符合项目规范于是坏的风格像滚雪球一样沿用了。我的解决办法是给上下文文件加了一个“信任分级”。AGENTS.md和DECISIONS.md是人类维护的高可信来源标记为authorityCHANGES.md是AI自己的产出标记为reference。在拼接指令时我会明确告诉AI“authority部分的规则优先级高于reference中的任何代码风格描述”。同时每一轮任务开始时只保留最近三次的CHANGES记录更早的内容只保留摘要不给完整细节避免污染源持续扩大。4.3 自动修复卡死AI在一个坑里反复跳这个问题的典型表现是任务卡片派发出去AI修了五次每次都是同一个地方没考过但每次失败的日志略有不同。AI并没有在偷懒它确实在尝试但它的搜索路径已经收敛到一个局部区域电梯坏了它就在顶层反复按键。我在执行层里加了一个“重试熔断”机制策略是同一任务连续失败3次后自动停止当前尝试并将任务标记为manual_review通知人工介入。人工介入时我不直接给它修而是去更新任务卡片的reference补充一条更精确的hint比如“问题出在session_manager.js第47行字典键写错了注意线程安全”。AI拿到新hint之后往往一次就过了。4.4 AI自行脑补功能做了没人要求的东西还有一种更隐蔽的翻车AI会自己“加戏”。有一次我让它修复一个日期格式化的bug它修完之后觉得把整个工具函数库重构一遍更漂亮于是所有代码里凡是与日期相关的调用方式全被改了测试还全数通过——因为旧的测试也被它按新逻辑重写了。这是最危险的情况等于监督者自己放水。这道防线我放在合并请求review环节任何合并请求的diff如果改动的文件数超过任务卡片scope中声明数量的1.5倍或改动行数超过300行就自动标记为“疑似范围蔓延”不允许自动合并必须人工确认。虽然这一步需要人来点一下但这是为了保证关键决策权还归人类AI负责高效执行不负责重写世界观。5. 运行一段时间后我对这套打法的几点真实体会5.1 人真的能从“写代码的人”变成“验收代码的人”曾经我以为这种转型需要很久但Harness Engineering跑顺之后我每天的工作内容确实变了。早上第一件事是看前一夜产生的三份报告任务完成清单、门禁失败清单、AI的变更摘要。花十分钟扫一遍决定接下来是让系统继续跑还是介入某个卡住的任务。下午基本都是在下新任务的卡片、修DECISIONS.md里不准确的历史决定。这种节奏比之前坐在IDE前逐行debug要轻松不少但注意力需求没有降低只是从“怎么实现”变成了“怎么定义”。5.2 任务颗粒度是这套系统的灵魂我把任务卡片写得太大AI会把一整块重写写得太小AI又会在细枝末节上浪费大量轮次。我的经验是一个任务的理想颗粒度应该恰好是一个人一小时能完成的代码量。按这个标准拆出来的任务AI的成功率最高、修复次数最少、review成本也最低。宁可多拆几张卡片也不要让一张卡片承载十个改动点。5.3 最后分享一个降低运维负担的小套路流水线跑久了之后最累的不是AI也不是我review而是每周要花时间梳理这一周AI到底干了什么。后来我加了一个自动周报脚本每周日晚上系统会把本周合并的任务、涉及的文件、测试覆盖变化、遗留问题汇总成一份Markdown发到项目的日志频道。这样我周一review的时候根本不用翻git历史看周报就能知道项目的脉搏。这个小功能花不到一个下午写却实实在在把“管理AI干活”这件事的运营成本降到了最低。Harness Engineering不是一套你必须买什么工具或者服务器才能玩的东西。它更多是一种思维方式知道AI的边界在哪儿然后用流程把边界填上。只要你已经在用vibe coding又希望它能跑得久一点、稳一点照着上面这套四层结构从最小的任务卡片开始一步步把上下文、调度和门禁装上你的AI就能从一个需要你时刻呵护的“实习生”变成一个夜班独立值班的“老员工”。