我一直不认同那种「把需求丢进去、等它直接给出结果」的 Vibe Coding。一旦中间出了问题,你连哪一步错的都说不清,结果还要让 AI 帮你排查修复。而 Matt Pocock 这套 AI 编码 skill 偏偏反着来:让本来随机的模型每次都走同一条流程,也就是可预测。
一、设计内核
目标:可预测,而不是「做小」
「可预测」要说准一点:可预测的是流程,不是结果。模型输出本身就是随机的,同一个需求跑两遍,spec 和代码的细节难免有出入;但如果流程能稳住(先澄清、再写 spec、再拆任务、再实现,顺序和交接点每次都对得上),产出的随机就不会失控------每一步都落在一个你能审查的节点上,一旦跑偏,当场就能纠正,而不是拖到最后才发现。
还有一点容易误解:以为「可预测」就是「把每个 skill 做到尽量小」。其实做小只是可预测带来的结果。这一点很关键------它是后面所有取舍的依据:手段怎么选、机制怎么分、一个 skill 合不合格,最后都落到一句「它有没有让流程更可预测」。
手段:原语 + 手动编排
要让流程可预测,开头那种「丢进去、等结果」显然不行;一个直觉的解法,是套个框架约束流程。GSD、BMAD、Spec-Kit 走的就是这条路,而且做得并不差:分阶段,阶段之间也留了检查点,这点和这套 skill 一致。真正的分歧在于,框架的路子是「接管整条流程」------流程一旦交给它,控制权也跟着让了出去,出了问题也很难自己改。
这套 skill 把能力拆成一个个小而单一的原语(primitive),由你自己拼成流程:grill、to-spec、to-tickets、implement 一步步接起来,哪步不对就单换那一步。控制权始终在你手里,这正是「可预测」逼出来的走法。
至于原语该切多细、哪一步交给模型、哪一步留给自己------下面三条机制回答。
机制一:触发方式
「谁来触发」 skill,牵出两件事:该不该把它单拆出来,以及它按触发方式归哪一类。
先说拆不拆------这是一笔成本账。 每多拆一个 skill 都要付成本,分两种:
- 上下文成本:能被模型自动触发的 skill,它的描述每轮对话都占着上下文窗口;只要它在,就一直占着。
- 记忆成本:要你手动触发的 skill 不占窗口,但这份代价落到你的脑子里------你得记得它在、记得什么时候该用。
所以拆不拆是笔账:多拆出来的那点可预测,值不值这两笔成本。原语该多大,就是这笔账的平衡点。算下来的规则很简单------机械、能反复用的固定做法交给模型自动触发,只有靠人拍板的决策留作手动触发。
紧接着按触发方式区分类型。一种设置了 disable-model-invocation,模型不会主动调它,只能你手动触发,这一类 skill 之间也不会互相调用;相反,另一种没设限制,模型能自动触发、别的 skill 也能调用它,装的是可复用的固定做法,也就是原语(primitive),例如 grilling。
grilling是原语没有疑问,至于够不够小就不一定,只能靠上面的成本账、靠平时不断做减法来维持。
机制二:安全靠中间产物,由人来否决
每一步都会留下一份中间产物------澄清后的想法、spec、拆好的任务------你都能审,也都能拒。这套机制的前提是你真的有判断力,它是整套系统唯一的安全网。
这就带出一个关键:这套 skill 的价值,全看用的人判断力够不够 。你要是不认真审,这些检查点就毫无作用,系统又变回黑箱,甚至比框架更糟------它连框架那种最起码的默认保护都没有。所以它只对熟手才管用;新手该做的是用 teach 这个 skill 把判断力提上来。
机制三:组合靠共享状态
to-spec 从不说「我要读 grilling 的产物」,它只说「我读当前这段对话」。谁往对话里写了东西,它就能读到谁。正因为这样,好几个不同的入口最后都能汇进同一条流程。
这块共享状态其实有两层。一层是上下文窗口,只在当前 session 里管用,一结束就没了;另一层是 tracker 上的 issue,跨 session、跨 agent 都在,是最权威的那份状态。窗口装不下时,handoff 把状态挪到新 session;而后面主线要讲的 frontier------那批可领取的任务------正是从 issue 这层记下的阻塞关系算出来的。
二、四类文档
上一节说的那些能审、能拒的产出,落到具体就是四类文档。它们也是整个流程反复操作的对象------to-spec 产出 spec、to-tickets 拆出 issue、implement 按 issue 实现,所以先把这四样理清楚,后面讲主线就顺了。
一份需求走完流程,会拆成四类文档,各管一类信息------每一类都是你做判断时唯一该查的依据(single source of truth)。最忌讳把它们混写进一份「什么都记」的文档里,按生命周期分清就好:
| 文档 | 放在哪 | 装什么 | 生命周期 | 会改吗 |
|---|---|---|---|---|
| spec / PRD | tracker(如 .scratch/<feature>/PRD.md) |
意图:要建什么 | 建完就作废 | 建完前可改 |
| issue | tracker | 一个工作单元 | 做完就关闭 | 状态一直变 |
| ADR | docs/adr/NNNN-*.md |
已定的决策及其理由 | 永久保留 | 不改,只被新 ADR 取代 |
| CONTEXT.md | 仓库根目录 | 术语表:一个东西我们怎么称呼 | 长期维护 | 随术语增补 |
这里最容易混的是 issue 和 ADR,其实它们是一件事的两面:issue 记「接下来要做什么」,做完就结束;ADR 记「当初为什么这么定」,永久保存。ADR 只增不改------要推翻旧决策,就另写一份取代它,旧的原样留着。这么做是为了让「当时为什么这么想」始终查得到。连决策记录本身也是这样一份能审、能推翻的产出------这就是机制二用在决策上的样子。
三、主线流程
这里以完整的主线为例,实际场景会根据具体情况编排流程。
机制三说的共享状态,放到实际流程里就是这条主线:四步之间不互相直连,全靠「谁往对话 / tracker 里写,谁就能被下一步读到」连起来。
主线四步:grill-with-docs → to-spec → to-tickets → implement。
grill-with-docs:有代码库时,靠追问帮你把想法理清,整理出 CONTEXT 与 ADR。它的底层原语是grilling------一次问一个问题,事实由 Agent 查,决策留给你定。(grilling是原语;grill-with-docs和无代码库版的grill-me是它的两个入口,都要你手动触发;下文用 grill 泛指这一类追问能力。)to-spec:对话已经聊清楚后,综合成 spec。它绝不追问(追问是 grill 的能力),所以 to-spec 永远排在 grill 之后。to-tickets:面对跨多个 session 的大任务,把 spec 拆成 tracer-bullet 式的垂直切片(每片打通端到端、能独立验证),并声明切片之间的阻塞关系。implement:按 spec 或 issue 构建,内部自动跑tdd,收尾用code-review再提交。
这里有两条看起来相反的上下文规则,其实来自同一个原则:只保留对当前这一步有用的上下文。 前三步(grill、to-spec、to-tickets)是前后依赖的,后一步要用到前一步的结果,所以要一直保留上下文,to-tickets 之前不要清空;到了 implement,每个 issue 相互独立,别的 issue 的上下文反而会干扰,所以每个 issue 之间都要清空。两条规则相反,是因为「什么算有用」在这两段正好相反。这类规则大多只是当前模型能力下的临时办法,以后会过时;但把任务拆开这件事不会,因为它保证了人能掌控、任务能并行。
还有两个处理上下文的办法: handoff 是这套 skill 之一------把当前对话整理成一份文档,再开一个新 session 引用它,状态就转移过去了;compact 则是 agent 自带的内建命令,留在同一个对话里,把前面几轮压缩成摘要。两者都只适合在两个阶段之间用,别在同一个阶段的中途用。
最后回到共享状态,也就是 frontier。 to-tickets 会标明每个切片被哪些任务阻塞,这些信息存进 tracker 后,系统就能自动算出当前 open、unblocked、unclaimed 的那批任务------这就是 frontier。空闲的 worker 直接从中挑一个来做,不需要有人分配------这就是共享状态带来的好处。不过只有在多个 worker 同时工作时,这种方式才比人工分配更省事;如果只有你一个人、任务又是一个接一个的,两种方式差别不大。
四、全景速查表
| Skill | 触发 | 场景 | 什么时候用 |
|---|---|---|---|
grilling |
模型触发 | 一次一问、事实自查、决策归你,是各 grill 的底层 | --- |
grill-with-docs |
用户触发 | 有代码库,靠追问理清想法,整理出 CONTEXT + ADR | 你必须真回答;判不出就成了走过场 |
grill-me |
用户触发 | 无代码库,不保存状态 | 不留痕迹,它不会帮你建 CONTEXT |
to-spec |
用户触发 | 对话已聊清楚后综合成 spec | 不追问;顺序永远在 grilling 之后 |
to-tickets |
用户触发 | 跨多 session 的大任务,拆成 tracer-bullet | 大范围重构别硬套切片,改用 expand-contract(先扩后缩) |
implement |
用户触发 | 按 spec 或 issue 构建 | 任务之间清空上下文;内部自动 tdd + code-review |
tdd |
模型触发 | 红绿重构,一次一个垂直切片 | 只想先写测试来确定行为时,可单独用 |
code-review |
模型触发 | Standards 与 Spec 两方面同时审 | 也可单独审任意 branch 或 PR |
wayfinder |
用户触发 | 又大又模糊、一个 session 装不下 | 产出决策而非交付代码;理清后汇入 to-spec |
prototype |
模型触发 | 状态或 UI 要跑起来才判得清 | 一次性原型,留结论删代码,别当产品 |
teach |
用户触发 | 跨 session 学一个概念,帮新手提升到能胜任 | 提升你自己的判断力,不向项目交付代码 |
另外还有几个提供基础词汇的 skill(
domain-modeling、codebase-design)和维护类 skill(research、improve-codebase-architecture、writing-great-skills),和本文主题关系不大,不展开。