开发者Matt Pocock推出/grill-with-docs技能,通过建立共享语言文档和架构决策记录,让AI在提问前先理解代码库上下文,大幅提升编程协作效率。
作者背景:Matt Pocock是TypeScript专家和开发者工具创造者,专注于提升AI辅助编程效率
让机器先读懂你的代码库再提问
老办法/grill-me是让AI像记者一样疯狂追着你问问题。新办法是给AI塞一本你项目的字典,让它先搞明白你在说什么,然后再问问题。
几个月前我写了四句话,结果这四句话成了我写过最有用的东西。我把它们打包成一个叫/grill-me的技能。这个技能的核心特别简单:让AI像一个烦人的记者一样,不停追问你。
你每说一个想法,它就顺着这个想法往深处挖。你提一个功能,它就问这个功能和现有代码什么关系。你给出一个概念,它就追问这个概念到底怎么定义。一直追问到你俩对要做的事情达成完全一致的理解。
这个技能火了。每天大概有五个人跑来跟我说试过了超级爱。
但我这人有个毛病,闲不住。东西做出来之后我总想着还能不能更好。所以慢慢我发现/grill-me在真实项目里开始露馅了。
老办法遇到的最烦人的问题
用这个技能次数多了,我发现一个特别蛋疼的场面。AI有时候会变得特别啰嗦,一个很简单的事情翻来覆去问好多遍。
举个真实例子。我有次跟AI讨论一个功能,我说这个功能要在"独立视频"上加点东西。AI就开始疯狂追问:你说的独立视频是什么?它和课程什么关系?它的数据表结构什么样?它有没有可能被别的地方引用?
我一听就头大。因为这些信息我之前已经在别的会话里跟AI解释过了。但现在开了一个新会话,它全忘了,我得重新解释一遍。
有时候我会提醒AI说:这个概念已经有术语了,去查一下。但没有文档的情况下,AI也没地方查。更惨的情况是我自己脑子里的定义本来就是模糊的,AI在那追问,我自己都说不清楚。
最坑的是啥?是我们好不容易在对话里达成了共识,敲定了一批术语和概念。但对话一关,这些东西就丢了。下次再来,从零开始。
问题的本质特别简单:AI能听懂我说的话,但它记不住我项目的背景知识。每次都得重新介绍一遍这个代码库有哪些坑、哪些特殊规则、哪些术语是有特殊含义的。
所以我开始琢磨:给AI准备一个最薄的知识手册,到底需要写多少东西,就能让AI快速进入状态?
一个来自软件工程老书的解决方案
这个问题其实早有人研究过了。领域驱动设计里有一个概念叫"通用语言"。Eric Evans那本蓝色封面的厚书《领域驱动设计》专门讲过这个。
想法特别朴素。你要建立一套语言,这三类人都能用:
第一类是代码本身。变量名、函数名、类名都遵循这套语言。
第二类是写代码的开发者。大家在讨论的时候用这套词。
第三类是懂业务但不写代码的专家。他们用这套语言描述需求。
当三类人说同一套词的时候,魔法就发生了。业务专家说"这个推荐算法在首页表现不好",开发者马上知道要改哪个模块,代码里也确实有个RecommendationEngine专门管这事。
我意识到这个思路放在AI身上更管用。AI不需要懂业务,但它需要理解代码结构。所以我开始在/grill-me的追问环节里,每次发现哪个词说得不清晰,就用另一个叫/ubiquitous-language的技能去精炼这个词,然后写进一个叫UBIQUITOUS_LANGUAGE.md的文件里。
但同时跑两个技能太麻烦了。为什么不把它们合并成一个?
新技能的三板斧
新技能叫/grill-with-docs。它继承了/grill-me的核心追问机制,但往上加了三样硬货。
第一板斧:开机先找说明书
这个技能启动后的第一件事不是问你问题,而是满项目搜索一个叫CONTEXT.md的文件。这个文件里记录了你项目某个领域内所有的共享语言。
在领域驱动设计的术语里,一个"上下文"是你应用里的一块独立范围,这块范围内大家用同一套语言说话。如果你有个超大的仓库,里面塞了十几个不同功能,那你可以做多个CONTEXT.md文件,每个管一块。如果你就是个普通大小的项目,一套语言够用,那一个CONTEXT.md文件就能搞定。
第二板斧:边聊边改字典
在对话过程中,这个技能会额外干四件事。
它会拿你说的每个词去跟现有的术语表对质。你说要用"用户"这个词,但术语表里明明写着"用户指已注册且有登录凭证的人",你要是拿这个词去指代匿名访客,AI会当场指出来。
它会把模糊的词抠出来精炼。你说"这个数据要同步",AI会追问是实时同步还是定时同步?是单向同步还是双向同步?精炼到这个词没有歧义为止。
它会拿具体场景来测试边界情况。你定义了一个规则说"视频可以有附件的包装材料",AI马上会问:空包装允不允许存在?就是有标题没视频的那种。
它会把每次讨论的结果实时写回CONTEXT.md文件。一小时后你开个新会话,新AI直接读文档,不用从头解释。
第三板斧:记录那些说不清的坑
有些模糊两可的地方光靠精炼术语解决不了。有些决策不是想不明白,而是这个决策本身就很复杂,需要很长一段解释才能说清楚为什么要这么干。
这类情况需要建架构决策记录,简称ADR。这个东西就是一个简单的Markdown文件,专门用来记录那些不明显的决策。
什么情况需要建一个ADR?三种情况。
这个决策做出之后很难改。比如你选了某个数据库,换数据库几乎不可能。
这个决策如果没有上下文解释,别人看到会一脸懵。比如你故意没给某个表建外键,新人一来觉得这是个bug。
这个决策涉及真正的取舍,选了A就要放弃B的好处,而且这个后果会在后续开发中反复出现。
举个真实例子。你决定"删除某个包装的时候,如果它里面还绑着视频,就不让删"。这个决策值得记下来,因为按常理来说删东西直接删就行了,但你觉得不行,用户必须先手动解绑所有视频才能删。这个选择会影响删除的交互流程、前端的提示文案、后端的校验逻辑。
不值得记的例子是啥?你用React而不是Vue,这种决策太常见了,换框架也没那么难,不用专门写个ADR。
现场演示一遍这个技能怎么跑
我给你贴一个真实的对话记录,你看看这个技能是怎么一步步追问、精炼、最终落成文档的。
用户说:我想给我的课程管理系统加一个概念叫"包装"。一个包装就是围绕一个视频的标题、描述、以及我向观众推荐它的方式。这是受了野兽先生那个流派的启发:先定包装再做视频。我会先创建一堆包装,选出最好的那几个,然后把它们做成视频。
AI看到这个需求后,做的第一件事是查CONTEXT.md。文档里已经定义了一个概念叫"独立视频",指那些没有挂在任何课程下面的视频。
AI追问问题一:包装和独立视频之间是什么数量关系?
AI给出了三个选项。
一对一:一个包装对应一个视频。
一对多:一个包装对应多个视频。
多对多:一个视频可以属于多个包装。
AI推荐一对多。理由是包装就像一个"发布容器",一个视频可以有多个版本,比如预告片加正片。在数据库里实现也最简单,给视频表加一个包装ID的外键就行。
用户同意了一对多的方案。
AI追问问题二:术语冲突了怎么办?
CONTEXT.md里写着:独立视频指那些lessonId字段是空的视频。
但按照刚才的设计,被包装的视频也一样没有lessonId,那它到底算不算独立视频?如果算,那"独立"这个词就说不清了。如果不算,那就得重新定义整套术语。
AI给了两个解决思路。
方案A:保留"独立视频"表示lessonId为空。包装成为一个独立的描述信息。然后在独立视频下面细分两个子类:无包装独立视频和有包装独立视频。
方案B:重新定义"独立视频"为没有被包装的视频。然后新造一个词叫"被包装视频",和独立视频平起平坐。
AI推荐方案A。理由是"独立"这个词回答的是结构问题,即这个视频在不在课程里。而"被包装"回答的是元数据问题,即这个视频有没有包装材料。这两件事是正交的,一个视频可以同时满足这两个属性。
用户同意方案A。这个区别很重要,因为它会影响AI生成的所有变量名和文件名。
AI追问问题三:包装的状态是什么意思?
用户定义了三档状态:闲置、已排期、已发布。但这个定义太模糊了。
AI追问:状态是手动改的还是根据时间来计算的?状态转换是只能按顺序走还是可以在任意状态之间跳转?闲置、已排期、已发布这三个状态到底代表什么业务含义?
AI推荐混合方案:存储排期时间和发布时间,根据这两个时间戳计算当前状态。同时允许任意跳转。
但用户决定走另一条路。用户说:我要手动改状态,可以任意跳转,不要自动计算。
最终决策:状态就是一个普通字段,手动改,不存时间戳。
AI追问问题四:一个包装能不能没有视频?
AI推荐允许。理由是野兽先生那个流派就是先创建包装再拍视频。而且在业务上,闲置状态正好对应这种只有包装没有视频的规划阶段。
用户同意。决策:允许空包装存在。
AI追问问题五:删除一个包装的时候,它下面的视频怎么办?
数据库提供了三种行为。
设为空值:视频的包装ID字段变成空,视频回归完全独立的状态。
层叠删除:删包装的同时把下面所有视频也删了。
限制删除:如果包装下面还有视频,不允许删。
AI推荐设为空值。理由是包装只是描述信息,不是视频的源头,视频有价值,不应该因为包装删了就跟着消失。
但用户选择了限制删除。理由是强行要求用户在删包装之前必须先把所有视频解绑。用户本人就喜欢归档而不是真删,这种保守的删除策略更符合他的偏好。
讨论到这里,AI说:我把刚才所有的决策写入CONTEXT.md文件。但在写之前,还有两个细节需要确认。
标题和描述能不能是空的?AI推荐标题必须有,如果没有就默认叫"无标题"。
包装要不要像其他实体一样支持归档?AI推荐支持。
用户两个都同意了。
AI开始更新CONTEXT.md文件。
包装的定义写成了这样:一个包装是围绕零个或多个独立视频的打包材料,包含标题、可选的描述、以及状态。它独立于课程和章节的层级结构之外,不会和课程产生任何交互。
包装的状态写成了这样:三档分别是闲置、已排期、已发布。手动设置,可以在任意状态之间跳转。状态变化不产生也不附带任何时间戳。
有包装的独立视频定义成:包装ID字段不为空的独立视频。它仍然是独立视频,包装ID只是附加在独立视频上的额外描述信息。
无包装独立视频定义成:包装ID字段为空的独立视频,就是彻底独立的那个情况。
关系部分更新成了:独立视频要么是无包装独立视频,要么是有包装独立视频。一个包装有零个或多个有包装独立视频。删除包装的时候,如果还有视频绑在上面,不允许删,用户必须先解绑。包装永远不会引用课程。
为什么这么折腾一圈反而更快
你看完了整个对话,可能会觉得这不就是AI在替你操心数据库设计吗?确实是在操心。但核心好处不是这个。
第一个好处是省字数。
有了共享语言文档之后,AI说的话明显变短了。以前AI会说:"根据我们之前的讨论,独立视频指的是那些lessonId字段等于空值的视频记录。现在因为包装功能的需求,这些独立视频可能会附加一个包装ID。我们需要修改查询独立视频列表的逻辑,把那些有包装ID的排除掉还是包含进来?"
现在AI直接说:"独立视频要改,包装的展示逻辑得同步调一下。"
没了一堆重复解释,没了反复确认定义。AI用你文档里定好的术语,一句话说清楚。
第二个好处是AI自己想事情也更顺了。
AI在做链式推理的时候,内部要用语言组织思路。有了共享语言,它的内部推理效率也高了。需要的处理量变少了,给出的答案也更对齐你的预期。
而且因为你的规划文档和代码长一个样,代码本身也更好找了。你在代码库里搜"包装",所有相关的东西全出来了。不搜不知道这些文件散落在哪。
这就是为什么领域驱动设计对人有用,对AI同样有用。人和AI都靠语言思考,共享一套语言,大家都省事。
新旧技能怎么选
是不是/grill-with-docs完全替代了/grill-me?绝对不是。
/grill-me依然很能打。/grill-with-docs只是在你有一个代码库的时候更合适。
有人告诉我他用/grill-me写了一篇悼念妈妈的文章。AI通过不断追问,挖掘出了很多小时候的故事和细节。这个场景/grill-with-docs完全做不了,因为没有代码库。
选择规则特别简单。
你有一个代码库,不管代码多少,用/grill-with-docs。
你没有代码库,纯脑暴或者写文章之类的,用/grill-me。
哪怕项目刚开个头,只有一个README文件,我也推荐用/grill-with-docs。因为恰恰是这个阶段,你正在建立共享语言。早点定下来,后面少很多事。
我现在把这些技能重新整理过了。/grill-me放在生产力分类下,给没有代码库的通用场景用。/grill-with-docs是我的主力技能,只要手头有代码就开它。
我会一直更新这些技能。脑子里随时在转新的改进思路,或者更好的使用方式。如果你试了/grill-with-docs,欢迎告诉我体验怎么样。如果发现哪里还能改进,去技能仓库提个问题,我会看到。