迭代出一份可执行的规约
前面两节建立了一个方法论框架:信息要分层,每层有三个维度,层与层之间用交叉验证检测漂移。这一节讲拿到这个框架之后,面对一个具体的需求,你实际上怎么操作。
一个关键的认知前提:spec 不是一次写出来的,是迭代出来的。你不需要坐下来一口气写出一份完美的 spec。你写一个起点,Agent 帮你展开,交叉验证暴露问题,你修正,再验证,直到收敛。整个过程更像是你和 Agent 之间的一轮结构化对话,而不是你单方面写一份文档。
迭代的起点:用户故事
前面讲了信息的四个层级:vision、架构、feature、task。这里的 spec 迭代工作在 feature 层和 task 层。它假设更上游的工作已经完成:产品的 vision 已经确定,系统的架构已经选型,这些高层信息已经沉淀在项目上下文里。从 vision 到 user journey 到具体需求的推导过程属于产品方法论的范畴,这本书不展开。但那个过程同样重要,它决定了你在 feature 层拿到的需求本身是否合理。
你的起点是一个可以表达为用户故事的需求。"作为博客作者,我希望能通过关键词搜索已发布的文章,这样我可以快速找到之前写过的内容。"
写出这句话需要你做三个决策:给谁用(博客作者,不是读者),干什么(关键词搜索已发布文章),为什么(快速找旧内容)。如果你连这一句话都写不出来,说明你对需求的理解还不够清楚,应该回到上游去澄清,而不是让 Agent 开始执行。
这句话不是 spec 的终稿。它是迭代的种子。你不需要在这一步想清楚搜索框放在哪里、结果怎么排序、空结果怎么处理。这些细节会在后面的迭代中浮现出来。
Agent 展开规约草稿
你把用户故事和大概的范围交给 Agent,让它生成一份 spec 草稿。
Agent 在这个过程中做的事情比你预期的多。它不只是把你的一句话扩写成更长的描述。它会去读项目上下文,找到跟搜索功能相关的已有模块。它会分析这个需求可能影响的代码范围。它会生成验收场景,尝试覆盖正常路径、边界情况和异常情况。它可能会想到你没考虑过的情况,比如搜索词包含特殊字符时怎么处理。它可能还会建议技术方案,比如建议用 PostgreSQL 的全文搜索而不是应用层的模糊匹配。
Ryan 的 /new-feature skill 就是这个过程的一个实现。Agent 加载 project context 和 CLAUDE.md,根据用户描述的需求分析用户价值点,生成 Gherkin 验收场景,评估 feature 的规模。几分钟之内产出一份覆盖三个维度的 spec 草稿。
这份草稿大概率有问题。也许它漏了你关心的一个边界情况。也许它对搜索范围的理解跟你不一样(你想的是只搜标题,它理解成了全文搜索)。也许影响分析里漏了一个应该被提到的模块。这些问题在这个阶段都是正常的。下一步会把它们暴露出来。
交叉验证检测漂移
你让 Agent 从 spec 推导 task list:要完成这个 feature 需要具体做哪些事、改哪些文件、按什么顺序。然后从 task list 推导 checklist:做完之后要检查什么,怎么才算通过。
前面已经讲了交叉验证的原理。在实际操作中,这一步的目的是用 Agent 的三次理解来检验 spec 的质量。第一次理解产出了 spec(从需求到方案),第二次理解产出了 task list(从方案到步骤),第三次理解产出了 checklist(从步骤到验收标准)。
你比较三份文档,找矛盾。
Checklist 里有一条"验证搜索支持中文分词"。但你的 spec 里完全没提过中文分词。这条是从哪来的?可能是 Agent 在生成 task 的时候根据项目上下文判断出产品有中文用户,自动补充了这个需求。这可能是一个合理的补充,也可能超出了你当前版本的范围。不管是哪种,你需要知道它的存在,然后决定是把它加进 spec 还是从 checklist 里移除。
Task list 里有一步"修改文章列表页的排序逻辑以复用搜索排序"。但你的 spec 约束维度里写了"只改搜索相关的代码,列表页的排序不动"。矛盾。Agent 在拆分步骤的时候做了一个超出约束范围的决策。你需要在 task list 里修正这一步。
三份文档一致,没有矛盾,也没有意外出现的新内容,说明 Agent 从三个不同的角度看到了同一个东西。
文档层纠偏
交叉验证发现的问题直接指向 spec 里需要改的地方。你补上遗漏的验收场景,澄清有歧义的描述,加上被忽略的约束。然后让 Agent 重新从修正后的 spec 生成 task list 和 checklist,再看一次。
这个循环通常跑一两轮就收敛。每一轮的成本很低,全是文档层面的操作:改几行 spec,让 Agent 重新生成两份文档,花几分钟比较。但这几轮迭代拦住的问题,如果拖到代码阶段才发现,修正成本可能是几小时甚至几天。
按用户价值拆分
有时候你会发现迭代收敛不了。你跑了两三轮交叉验证,每次修了一个矛盾又冒出来新的。Spec 越改越长,验收场景越加越多,场景之间的依赖关系越来越复杂。
这通常意味着 spec 里包含了多个独立的用户价值,它们之间的交互让 Agent 没法在一个 context 里同时处理好。
几个具体的信号:用户价值点超过三个,验收场景超过七八个且场景间有复杂依赖,影响的模块超过四五个。这些不是硬标准,但它们是经验上有效的预警。
这时候你需要拆分。拆分不是迭代之外的一个独立阶段,它是迭代过程中遇到"收敛不了"这个情况时的处理方式。
LLM 对自然语言需求有很强的理解能力,这让它在分解任务上非常有效。你只需要给它拆分的原则:按用户价值拆,每个子单元是一个用户可以独立验收的功能。
AILock-Step 的拆分提供了一个好的例子。"用户认证系统"这个需求,如果不拆,spec 可能涉及注册、登录、密码重置、OAuth、权限管理五个以上的用户价值点,很难在一个 context 里处理好。按用户价值拆分之后,变成三个独立的 feature:feat-auth-register(用户能注册,独立可交付)、feat-auth-login(用户能登录,依赖注册完成)、feat-auth-permission(用户能管理权限,依赖登录完成)。每个 feature 有自己的用户故事和验收场景,可以独立迭代。
按技术层拆(feat-auth-db、feat-auth-api、feat-auth-ui)做不到这一点。数据库表建好了,对不对?要等 API 和前端都做完才知道。每个子任务缺少独立的验收标准,你没法在那个粒度上做交叉验证。
拆分之后,每个子 feature 的 spec 进入同样的迭代循环:Agent 展开,交叉验证,修正,直到收敛。这里多了一层检查:子 spec 跟原始需求的对齐。如果原始需求提到了 OAuth 登录,但拆出来的 feat-auth-login 的 spec 里只有用户名密码登录,交叉验证会在 checklist 阶段暴露这个遗漏。
规约就绪的标准
交叉验证不再产生矛盾,三份文档从三个角度描述的是同一个东西。Spec 可以进入执行了。
这不意味着 spec 是完美的。你自己都没想到的边界情况不会出现在任何文档里。但它意味着 spec 内部是自洽的,Agent 对需求的理解在多个角度上是一致的。这是一个合理的置信度,可以开始写代码了。
如果某个维度确实没有内容(比如这个 feature 不影响已有模块),显式写"无"。空着和"无"的含义不同:空着意味着这个问题没被思考过,Agent 可能会把它当作遗漏来处理。"无"意味着思考过了,结论是没有。
意图对齐:人的专属判断
上面描述的迭代循环里,大部分工作是 Agent 在做:展开 spec、生成 task list、生成 checklist、做一致性检查。但有一个环节只有人能做。
交叉验证能检查文档之间的一致性。Spec 说 A,task list 说 A,checklist 说 A,三份文档完全一致。但交叉验证回答不了一个更根本的问题:A 是不是你真正想要的。
你的用户故事写的是"用户能搜索文章"。Agent 理解为全文搜索,展开了相应的验收场景和 task。交叉验证通过了,三份文档完全一致。但你脑子里想的是只搜标题。这个偏差不会在任何自动化检查中被发现,因为 Agent 的三轮思考都基于同一个理解,只是这个理解跟你的不一样。
意图只存在于你的脑子里。你是软件的最终使用者,或者是使用者的代理人。"这是不是我想要的"这个判断只有你能做,因为"我想要什么"这个信息 Agent 无法获取,它只能通过你写的文字去推测。
所以迭代循环里有一个环节必须由人来做:在 Agent 展开 spec 之后,看一眼用户故事和关键的验收场景。这里的"看一眼"是字面意思。你不需要逐行审查 task list,不需要逐条检查 checklist。你只需要确认两件事:用户故事描述的"谁""做什么""为什么"跟你脑子里的一致,关键的验收场景覆盖了你最关心的情况。
上层方向对了,下层不太会出大问题。Agent 的交叉验证会处理下层的一致性。上层方向错了,下面全白做。所以你的时间应该花在上层的意图检查上。
Ryan 的经验验证了这一点。他在整个 spec 迭代过程中实际花时间的地方只有一个:spec 生成后看用户故事和场景描述。他说"一个 user story 加几个场景描述,就已经能判断出 AI 跟我的理解了。"后面的 task 拆分、checklist 生成、代码实现,他基本不逐一介入。
Agent 擅长做的是另一类检查:跨文档的一致性。Spec 里说搜索结果按相关度排序,checklist 里写的是验证按时间排序,这是矛盾。Spec 提了五个验收场景,task list 只覆盖了三个,这是遗漏。Spec 里用"文章"这个词,task list 里有时候写"帖子",这是术语不一致。这些检查不需要知道意图是什么,只需要比较文档之间的结构是否吻合。Agent 能同时处理所有文档的完整内容,在这类检查上比人可靠得多。
整个迭代循环里的分工就是这样:Agent 负责展开、生成和一致性检查(这些是结构性的、可自动化的工作),人负责意图对齐的判断(这是语义性的、只有意图拥有者才能做的工作)。
迭代深度匹配返工代价
前面描述的迭代循环有一个完整的形态:写意图、Agent 展开、交叉验证、修正或拆分、人 review 用户故事、再验证直到收敛。但不是每个任务都值得跑完整个循环。
人的注意力是整个流程中最稀缺的资源。Agent 做交叉验证、一致性检查、生成 task list 和 checklist,这些几乎不花你的时间。但你 review 用户故事、判断意图是否一致、决定 spec 要不要修改,这些需要你的专注力。Agent 的部分可以自动化运行,人的部分不能。
什么任务值得你投入这些专注力?看返工成本。
改一个按钮的文案,做错了重做只需要一分钟。为这个任务写完整的 spec 然后 review 用户故事是不划算的。你直接告诉 Agent 改什么,加一句约束(别动别的地方),让它自己做交叉验证自检就够了。你甚至可以跳过交叉验证,因为即使做错了,返工成本也微乎其微。
一个涉及三个模块的新 feature,做错了可能浪费一整天的工作。值得花十分钟 review 用户故事和关键验收场景,确保方向正确之后再让 Agent 开始写代码。
一个涉及五个以上模块的大需求,做错了可能浪费一周。值得先拆分,每个子 feature 的 spec 都认真 review,每个都跑完整的交叉验证循环。
注意判断标准不是代码量,而是做错了有多疼。一个只改两行代码但影响支付流程的任务,返工成本可能比改一百行管理后台样式的任务高一个数量级。前者值得你认真 review spec,后者可能直接说一句话就行。
同一个迭代循环可以跑得很轻,也可以跑得很重。最轻的情况,你写一句意图加一条约束,Agent 自己展开和自检,你不 review,直接执行。最重的情况,你写意图,Agent 展开,你深度 review 每个验收场景,拆分成多个子 feature,每个单独迭代,每个都 review。这不是两个不同的流程,是同一个循环在不同风险等级下的不同运行方式。循环本身不变,变的是人在哪些环节介入、介入多深。