我最近在补 agent-spec + no-mistakes,这一路把 AI 编码从玄学补到了流水线
这事不是从“我要搞工程化”开始的是我被 AI 和 CI 轮流收拾烦了我一开始真没想那么大。最早就是一段很普通的活。仓库里有个流程要收口接口行为不能飘异常路径得补最好还能顺手把提交前的校验接一接。按理说这活挺适合让 AI 先干一轮我只要收尾就行。我当时也是这么想的。第一轮看着还真行。代码动了测试也补了注释写得比我自己还整齐。我差点就准备往下推了。再看两眼后背开始发凉。AI 把“收口”理解成了“顺便再帮你理一理”多改了不该动的地方。最烦的不是它直接爆红而是它长得像没问题。你打开 diff会觉得这人还挺懂你跑一遍 happy path也不一定炸你真让它进 PR才会发现脏东西混得挺自然。这种翻车最恶心的地方就在这儿。不是它写不出来。不是它一眼假。是它七分像三分悬。你明知道哪儿不太对又没法在第一秒把它钉死。最后只能靠自己一段一段扒一条一条试整个人像在给一个很会说话的实习生擦屁股。我那阵子真有点烦透了。我先怪模型怪着怪着发现锅不在模型身上我第一反应挺标准的。先怪模型不够强。再怪上下文没喂够。再怪 prompt 写得不够狠。最后连“要不要套个 agent 让它先 review 自己”这种路子我都想过。写提示词的时候我也不是没下功夫。我会写不要改无关文件保持兼容补测试输出前自检不要影响已有接口写得跟立军令状似的。结果还是不稳。后面我才承认一个不太舒服的事实这些话大半只是愿望不是契约。AI 看到了也可能理解了但你没法稳定验证它到底做到哪一步更没法在它跑偏的时候第一时间把它拦下来。卡到这里我脑子里那根筋才拧过来。问题根本不只是“AI 怎么写代码”。问题是“我到底给了它什么任务”以及“它写完以后到底谁来证明它真写对了”。这两件事没立住模型再强也容易写成玄学。我第一次看 agent-spec心里挺不屑的说实话第一次看到agent-spec这个名字我脑子里冒出来的话不太好听。我当时想的是这不就是把需求文档再换个格式写一遍吗因为太像了。有spec有结构有规则一眼看上去就容易被误判成“规范化写 prompt 的工具”。我一开始就是这么看它的。再往下摸才发现方向完全不是那么回事。它盯的不是 prompt 漂不漂亮。它盯的是 AI 下场写代码之前那层一直被大家糊过去的东西这次到底要干什么哪些技术决定已经拍板哪些地方能动哪些地方别碰什么行为算完成到这时我才反应过来agent-spec想补的不是写作层是编译层。以前我给 AI 的输入大半是 PRD、Issue、群聊、口头补充、临时共识。信息不是没有是太散散到最后全变成一句潜台词“你应该懂我意思吧。”AI 最会在这种地方出事。它不是完全不懂。它是懂一半补一半剩下一半靠猜。agent-spec干的事就一句话先别让它猜。真落到手上我先被 Task Contract 这玩意儿狠狠干老实了我前面一直把“需求写清楚”理解成一件挺主观的事。直到我真开始写 Task Contract。第一脚就摔了。我按普通 Markdown 的习惯写标题先来元信息跟上想着差不多就行。结果lint直接不认。那会儿我还不服心想格式问题能有多难补两条横线不就完了。结果补完照样不认。我又去对模板、看生成结果、盯解析逻辑来回折腾了几轮才发现这东西不是“像 YAML 就行”它就是一套自己的 DSL。字段怎么写分隔线放哪儿开头能不能先来个大标题它都认得死死的。这回把我心态直接掰正了。因为它不是在帮我排版。它是在逼我别糊弄。后面还有一脚更逗。我写场景的时候顺手写了“假设 / 当 / 则”。我脑子里完全没问题中文读着也顺。结果lint还是挑刺说我的场景 shape 不完整。我当时第一反应是不是我结果都写了你还想怎样回头再看才懂它对关键字就是硬匹配。你写则它不当Then。中文场景里它认的是那么。这事看着细打我打得挺疼。因为它把一个我之前一直模模糊糊知道、但没认真承认的事钉死了需求不光要让人看懂还得让机器稳定看懂。这句话一旦真吃进去Task Contract 的味道就完全变了。它不再是“我整理了一份文档”。它更像“我先把这次任务压成一块硬东西再把 AI 放进来干活”。又试了几轮我才反应过来它真正管的是题目我现在回头看agent-spec最值钱的不是命令多也不是概念新。它值钱的地方很土但很硬。它逼我先把一团散装需求压成四块Intent说清这次到底干什么Decisions把已经定死的技术选择锁住Boundaries提前讲明白哪里能动、哪里是红线Completion Criteria把“完成”落成场景不再靠感觉我前面一直想把 AI 训练成一个靠谱执行者。后面发现想快了。题目都没压实执行者再靠谱也得靠猜。这也是我对agent-spec彻底改观的地方。它不负责让 AI 更聪明。它负责让题目别那么飘。再往后跑我才慢慢把这条链走顺前面我有个误判老把agent-spec当成“先建个契约文件”的辅助工具。真跑过一遍以后才明白它好用不是因为单个命令多猛是因为你会被它一步步逼到一个没法偷懒的位置。我第一次真照着走是先敲了个init把任务骨架起出来。那一下最明显的感觉不是“模板生成了”而是我没法继续装糊涂了。目标写不写得明白哪些技术决定已经拍板哪些地方能动、哪些地方别碰全得先摊开。骨架一出来我本来还想直接塞给 AI。手都伸过去了又停住了先跑了个lint。这步后来成了我的固定动作因为人刚写完需求时特别容易自我感觉良好读着顺、看着全脑子里已经开始自动脑补“差不多了吧”。lint一下来模糊动词、缺失场景、格式乱写、验证入口没绑基本藏不住。前面我还老跳contract总觉得这一步有点像多余展示。真挨过几次打以后我不跳了。把契约渲染出来看一眼特别像把脑子里的散装要求摊平在桌上。只要这里还虚后面不管喂给 AI 还是喂给人都是继续碰运气。再往后我才慢慢把plan这步看重。前面我一直很乐观觉得仓库都在这儿了AI 自己看呗。真被它教育过几轮以后这种乐观基本没了。plan好用的地方不是“又多跑了个命令”而是它把契约、代码上下文、实施顺序一起压出来。到这时候AI 更像接到一张施工单不像抱着一份抽象要求在那儿猜。等 AI 真改完我现在第一反应也不是先打开 diff 感动自己而是直接跑lifecycle。这步我已经当主验收口看了。以前所谓“完成”经常只是 AI 说完成了、我看着像完成了、本地随手点几下没炸。现在不行lifecycle没过就是没过。最后才是guard。前面我第一次看到它的时候还觉得这是不是有点太严了。后面仓库里一旦变成多人并行、多个 agent 一起改才知道这玩意儿不是在装严格它是在替你守门。单任务我盯lifecycle整仓改动我看guard这样整个节奏才算真立住。写到这里我才发现最难删掉的其实是那些空话我前面老把“写契约”理解成一种格式劳动。后面发现根本不是。真正难的地方是它逼你把平时最爱说的那些空话全扔掉。比如这种句子把流程理顺补一补异常处理尽量保持兼容顺手清理代码这些话人都爱说AI 也最容易顺着这种话开始自由发挥。一旦你真把它写进 Task Contract就会发现这几句几乎都不够用。因为它们根本没回答最关键的东西兼容到哪一层哪个返回结构不能动哪条行为必须有测试证明哪些文件哪怕手痒也别碰我后面真正开始顺手是在我肯承认一件很尴尬的事以后不是 AI 总把需求理解歪。有时候是我给它的东西本来就不够硬。再看 no-mistakes我才明白它堵的是出口我前面还犯过另一个误判。我以为前面契约写实了后面差不多就能稳住。结果不是。契约解决的是“这次到底让 AI 干什么”。no-mistakes解决的是“它干完以后这堆改动有没有资格继续往前走”。这俩东西根本不是一个层面。我现在不会拿它们互相替代我会把它们一前一后摆好agent-spec在前面卡题目、边界、验证条件no-mistakes在后面卡推送校验、CI 预检、PR 整洁度这个分工一旦想通很多纠结会自己消失。需求没压实门禁拦不住“认真做错题”。门禁不够硬契约也挡不住“脏改动混进来”。我更愿意把no-mistakes放在三个口子上本地准备推送前先把明显脏改动拦住。CI 里再做一层预检别让主分支替我试错。PR 准备发出去的时候再确认这次改动值不值得让别人 review。这样一来AI 写码就不是“写完了大家来祈祷”而是“先过契约再过门禁最后才轮到人看”。这句顺序一改整件事的气质都变了。直到这两头接上我才第一次觉得它像工程我前面一直在找那个感觉。什么叫 AI 真进工程流程了不是聊天窗口里回得多快。不是一口气吐多少代码。也不是“它居然懂这个框架”。我现在认的那条线是这样的先把 PRD、Issue、口头补充要求压成 requirements。再把 requirements 压成 Task Contract。AI 按契约去实现。agent-spec用lint、plan、lifecycle、guard去验。no-mistakes再把推送、CI、PR 那几个出口堵住。这条线一摆出来我前面为什么老翻车就很好解释了。我老是把前两步省了直接把需求和代码焊在一起中间全靠模型脑补。写顺的时候像开挂写歪的时候像开盲盒。现在多出来的那几层看着麻烦实际上是在替整条链降噪。AI 还是会写错。需求还是会写歪。命令还是会跑崩。但这些错开始有地方接住不再全压在“人最后看一眼”上。再学下去我准备把知识这层也顺手补上前面我一直把agent-spec当任务级工具看。看到explain、wiki、图和架构视图以后我才反应过来它补的还有另一种老毛病知识总是散。一个需求做完最烦的不是代码本身是过两周以后你再回来看很多东西又要重新猜为什么当时要这么改。哪条行为到底被哪个场景证明过。哪块边界是刻意定下来的哪块只是碰巧现在能跑。这种时候wiki和解释视图就不是装饰品了。我前面挺烦“写完代码还得补知识资产”这件事。后面想开了反正最后都得补那不如在任务还热的时候把这些骨架顺手搭起来。这轮学下来我脑子里先记住的是这几个坑这轮折腾完我脑子里已经有一套很现实的排查顺序了。如果一开始就跑不起来我先看 Cargo 环境和工具本体是不是装好。如果lint一上来就骂我先看 front-matter、字段名、场景关键字是不是又拿普通 Markdown 的脑回路在写契约。如果场景写着写着发虚我先看Completion Criteria到底有没有真绑到可验证行为。如果guard把改动拦在门外我先查是不是边界没写死或者 staged/worktree 混进了不该动的东西。如果no-mistakes在推送前后继续拦我就默认它不是在找我麻烦而是在替我挡一次本来会进 PR 的事故。还有一件事我现在也想得很清楚。不是所有活都值得上这整套。改个文案、调个样式、重命名几个变量用这套确实有点重。但接口兼容、CLI 输出、协议行为、rewrite、迁移、parity 这种活我后面会优先上。再往上走到多 agent 协作、多人并行改仓库这套东西只会更值钱。下次再碰到这种任务我第一眼不会先盯模型版本也不会先想着把 prompt 再写狠一点。我会先看两件事这次任务有没有被写成真正能验的契约这次改动进仓库之前有没有硬门替我拦住侥幸这两个口子不堵AI 写得再花都不算稳。这两个口子堵住了模型就算没那么神活也终于更像工程。你们现在让 AI 改代码最容易翻车的地方是在题目这头还是在提交、CI、PR 这头我现在回头看后面这半截脏流水线带来的坑真不比模型乱写少。