AI 编程工具的真正价值是什么?不是"让机器替代人",而是让人工作在更高的抽象层次上。这个认知转变,带出了一系列关于人机协作、Agent 架构设计的思考——也带出了一些值得正视的批判与反思。

一、从 Vibe Coding 到 SDD:一个被误解的问题

2024-2025 年,AI 编程助手的爆发带来了一个新词汇——Vibe Coding(基于感觉的编程)。开发者与 AI 对话,AI 即时生成代码,表面上效率提升显著。

但这里有一个被普遍忽视的根本矛盾:

Vibe Coding 优化的是"个人编码效率",但工程的真正瓶颈是"团队协作效率"。

一个人写代码快 10 倍,但团队沟通成本不变,整体效率提升极为有限。更糟的是,Vibe Coding 在协作层面制造了新的麻烦:

  • PM ↔ 研发:对话历史无法作为契约,验收时各执一词,“这不是我要的”
  • 前端 ↔ 后端:各自 vibe,联调时才发现接口格式对不上,浪费 1-2 天
  • 后端 ↔ 后端:订单服务调用 POST /inventory/deduct,库存服务只有 PUT /stock/reduce,集成测试时 404

这些问题不是工具不够好,而是 Vibe Coding 从根本上缺少一样东西:共同语言。

AI 编程的演进链条

理解 SDD 的必要性,需要先看清 AI 编程工具的演进轨迹——这是一条"问题→解决方案→新问题"的链条:

阶段 核心问题 解决方案 遗留问题
Vibe Coding 推一步走一步,依赖大量人工精力 自动迭代循环(Ralph Loop) 什么时候停下来?
自动迭代 没有明确的完成标准 单元测试驱动(测试通过即完成,即 Ralph Loop:编译→测试→修复的自主迭代循环) 如何生成有意义的测试?
测试驱动 AI 难以凭空生成有意义的测试 规范驱动(从需求生成测试) 不适合多系统环境
单应用规范驱动 单应用视角,无法处理服务间协作 系统行为层(BDD) 如何串联全流程?
BDD 单独使用时各层独立,缺乏跨系统端到端串联 三层 Agent 架构 —

这个演进揭示了一个关键洞察:端到端是一个软件工程管理问题,不是纯粹的研发问题。只停留在研发领域(代码生成、测试生成),永远无法做到端到端。必须向上延伸到需求分析,向外扩展到多系统协作。

SDD 的本质:规范先行,协作优先

SDD 的核心逻辑:在写代码前,先用一种"AI 易读、人类可维护"的结构化语言定义系统的每一个细节。

规范驱动开发(Spec Driven Development,SDD)的核心不是"AI 写代码",而是在写代码之前,先用结构化的方式定义"系统应该做什么",让这份规范成为:

  • 人类与 AI 之间的契约
  • 代码实现的验收标准
  • 团队协作的共同语言

这个思想并不新鲜。Kent Beck 在 1990 年代末开始实践 TDD(测试驱动开发),并于 2003 年通过《Test-Driven Development: By Example》将其系统化,其核心正是"先定义预期,再实现代码"。今天,这一思想在 AI 编程的语境下获得了新生——而且有了更丰富的工程含义。

类比:印刷术的发明不是让抄书匠失业,而是让知识的传播者从"复制者"升级为"创作者"。SDD 的意义与此相同——它不是让 AI 替代人,而是让人从低层级的编码工作中解放出来,去处理更高层的问题。

这就是"超级个体"的由来:不是一个人变得更聪明,而是一个人拥有了原本需要一个团队才能覆盖的能力边界。

超级个体:能力边界的重新定义

"超级个体"这个概念,值得在此多展开一些。

传统意义上,一个软件工程师的能力边界是清晰的:他擅长某个技术栈,熟悉某个业务领域,在团队中承担特定角色。跨越这个边界,需要学习、需要时间、需要经验积累——这是人类认知带宽的自然限制。

AI 工具的出现,并没有消除这个限制,但它大幅降低了跨越边界的摩擦成本。一个后端工程师,借助 AI,可以在不深入学习前端框架的情况下,产出质量可接受的 React 组件;一个研发,借助 AI,可以在不成为产品专家的情况下,将模糊的用户反馈转化为结构化的需求文档。

这不是"万能",而是**“够用”**——在特定场景下,AI 能把一个人的能力从"不会"提升到"够用",从"够用"提升到"熟练"。这个提升,在某些场景下足以让一个人独立完成原本需要 2-3 人协作的工作。

但超级个体的真正价值,不在于"一个人干多个人的活",而在于减少协作中的信息损耗。当一个人能同时理解产品意图、技术实现和测试验收,他就不需要在三个角色之间反复传递信息、对齐理解——而这种对齐成本,往往是团队效率的最大杀手。

SDD 正是超级个体的工程基础设施:它提供了一套结构化的语言(Gherkin、OpenAPI、Spec),让一个人能够在不同抽象层次之间自如切换,同时保持各层之间的一致性。没有这套基础设施,超级个体只是"一个人同时做多件事";有了它,才是"一个人在统一的认知框架下驱动多个层次的工作"。

二、正确的使用姿势:AI 代替已解决的问题

在讨论具体的使用姿势之前,需要建立一个核心认知框架:

软件工程效果 = 模型能力(基础) × 上下文质量(决定性因素)

其中,上下文包含领域知识、编码规范、历史经验——这些知识需要显性化。构建上下文是 AI 软件工程的核心活动。

这个公式揭示了一个常被忽视的事实:模型能力只是基础,真正决定输出质量的是上下文的丰富度和准确性。没有准确上下文的 AI,就像一个拥有超高智商但缺乏领域知识的新手——能快速学习,但容易犯错。

基于上述认知,AI 的正确使用姿势变得清晰:

不是让 AI 代替自己,而是让 AI 代替那些已经被解决的、重复的、昂贵的问题。

更进一步,有一个更激进但也更务实的原则:

凡是 AI 已能稳定胜任的,原则上都应优先交由 AI 完成。

这不是对 AI 的盲目信任,而是对人机协作效率的理性计算。当 AI 能够稳定、一致地完成某项任务时,继续由人工介入只会引入不必要的延迟和人为错误。人类的精力应该集中在那些真正需要判断力、创造力和跨领域整合的工作上——而不是与 AI 争夺那些它已然擅长的领域。

这里有一个关键区分:

类型 特征 应该由谁处理
已解决的问题 有明确答案、可重复执行、规则清晰 AI
新问题 边界模糊、需要判断、涉及权衡 人

AI 的价值在于稳定、一致地输出——它不会因为疲劳而出错,不会因为情绪而走神。当 AI 稳定地处理已知领域,人就可以把精力投入到拓展工作场域上,去解决那些还没有答案的问题。

这是一种正向循环:AI 处理旧问题 → 人解决新问题 → 新问题被解决后成为旧问题 → AI 接管 → 人继续探索更新的问题。

三、Spec 的理论根基:TDD 家族

SDD 的"规范先行"思想,有一套完整的理论谱系支撑——TDD 家族。理解这个谱系,才能理解 SDD 为什么有效,以及如何在不同层次上落地。

TDD 家族的分层体系

这四个方法论并非互斥,而是在不同抽象层次上相互补充:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
用户行为层(ATDD)
↓ 验收测试驱动开发
↓ 三方协作:PM + 研发 + 测试
↓ 产出:用户故事的验收标准

系统行为层(BDD)
↓ 行为驱动开发(Dan North, 2003-2006)
↓ 引入 Gherkin DSL:Given-When-Then
↓ 产出:可执行的规范文档

代码单元层(UTDD)
↓ 单元测试驱动开发
↓ 方法/函数/类级别
↓ 产出:通过的单元测试

产物驱动:每层的输出是下一层的输入

这里有一个关键洞察,值得单独点出:TDD 家族的三层不只是"并列的方法论",而是一条产物驱动的流水线——每一层的输出文档,就是下一层的输入上下文。

这也是近两年不少 SDD 实践会采用的工作流:

1
2
3
4
5
6
7
8
ATDD 层(用户行为)
产物:用户故事 Gherkin(Given-When-Then 场景)
↓ 作为输入
BDD 层(系统行为)
产物:接口契约(OpenAPI)+ 各服务系统行为 Gherkin + 任务分配清单
↓ 作为输入
UTDD 层(代码单元)
产物:通过的单元测试 + 可编译运行的代码

为什么 ATDD 和 BDD 都产出 Gherkin?

一个值得澄清的问题是:ATDD 层和 BDD 层都产出"Gherkin(Given-When-Then 场景)",两者有何区别?

关键区别在于视角和抽象层次:

层级 视角 Gherkin 描述的对象 主角是谁
ATDD 用户视角(黑盒) 用户与系统的交互行为 用户
BDD 系统视角(白盒) 系统内部各模块的协作行为 服务/模块

具体来说:

  • ATDD 层的 Gherkin(用户行为):描述的是用户故事,主角是"用户",关注的是"用户看到什么、做什么、得到什么"。PM 可以直接阅读并确认,不需要了解系统内部结构。

    1
    2
    3
    4
    5
    # ATDD 层:用户行为 Gherkin
    Scenario: 成功提交订单
    Given 用户已登录,购物车中有商品
    When 用户点击"提交订单"
    Then 用户应该看到订单号
  • BDD 层的 Gherkin(系统行为):描述的是服务间协作,主角是"服务/模块",关注的是"哪个服务做什么、服务间如何协作"。需要了解系统的技术架构才能编写。

    1
    2
    3
    4
    5
    # BDD 层:系统行为 Gherkin
    Scenario: 订单服务调用库存扣减
    Given 订单服务收到创建请求
    When 调用库存服务的 POST /inventory/deduct 接口
    Then 库存服务返回 200,库存数量减少

两者都使用 Gherkin 语法,但抽象层次不同——ATDD 是"黑盒视角"(用户不知道系统内部),BDD 是"白盒视角"(知道系统由哪些服务组成、如何协作)。这就是为什么它们需要分两个阶段、由不同角色的 Agent 来处理。


Augment Code 的研究将这一流程总结为四个核心阶段:Specify → Plan → Tasks → Implement,与上述三层有较强的对应关系(Debug 作为独立的最佳实践贯穿全程,而非标准化的第五阶段)。也有一些业界分析把 SDD 的重点概括为"将规划阶段(生成 spec)和实现阶段(生成代码)分离"——而分离的介质,就是这些层层传递的结构化文档。

这个机制的价值在于:每一层的产物都是对上一层意图的精确化和可验证化。用户故事 Gherkin 把模糊的 PRD 变成可确认的场景;系统行为 Gherkin 把单一的用户故事拆解为多个服务的职责边界;单元测试把系统行为变成可自动执行的验证。每一步都在减少歧义,每一步都在增加可验证性。

BDD 的核心创新在于:规范不仅是文档,还是可直接执行的测试。Gherkin 语法让 PM 能读懂、研发能执行、测试能自动化:

1
2
3
4
5
6
7
8
9
10
11
12
Feature: 用户下单

Scenario: 成功提交订单
Given 用户已登录,购物车中有可售商品
When 用户确认收货地址并点击"提交订单"
Then 订单应该创建成功,系统生成订单号
And 用户跳转到支付页面

Scenario: 库存不足无法下单
Given 用户购物车中商品库存为 0
When 用户尝试提交订单
Then 系统提示"库存不足,无法下单",订单不创建

这份 Gherkin 是 PM 与研发的共同契约:PM 可以直接阅读确认,研发以此为开发目标,测试将其转化为自动化用例。验收时,双方对着同一份文档检查,而不是各执一词。

四、企业级挑战:Spec 无法独立生成代码

理解了 TDD 家族之后,还需要面对一个现实:现有的 Spec 驱动工具(Spec-Kit、OpenSpec、Kiro 等)都有一个共同的隐含假设——

一个 Spec 对应一个应用。

但企业级开发的现实是:一个需求需要多个职责分明的应用协同完成。

以"用户下单后自动扣减库存"为例,这个看似简单的用户故事,实际上涉及一系列无法从 Spec 直接得出的决策:

问题 需要决策
谁负责扣减? 订单服务同步调用?库存服务异步处理?
失败怎么办? 重试?补偿?人工处理?
如何保证一致性? 分布式事务?最终一致性?
前端需要感知吗? 立即反馈?后台通知?

这些决策不是 Spec 能回答的,而是需要技术方案设计。这正是现有工具缺失的关键环节:

1
2
3
4
5
6
7
8
9
单体应用:  PRD → Spec → Code

企业级应用:PRD → Spec(用户行为)
↓
技术方案设计(判断归属 + 定义协作)
↓
Spec(系统行为)× N 个应用
↓
Code × N 个应用

存量知识:proposal 质量的天花板

在理解了"一个需求需要多个应用协同"这个挑战之后,还有一个更深层的问题值得正视:即使有了完整的 Spec 驱动流程,生成的 proposal 质量仍然受制于一个先决条件——知识库里的存量有多准确。

这里的"知识库"不局限于某种特定形式,可以是 project.md、AGENTS.md、service-catalog.yaml,也可以是 specs/ 目录下的历史规范——凡是 AI 在生成 proposal 之前会读取的上下文,都属于存量知识。

存量知识的作用链条是这样的:

1
2
3
4
5
存量知识(系统现状)
↓ 与 PRD 需求对齐
proposal(变更方案)
↓ 驱动实现
Code

存量设计的偏差会在这条链条上逐级放大。如果 service-catalog.yaml 里记录的库存服务能力已经过时,AI 生成的 proposal 就会基于错误的前提做出功能归属判断;如果 specs/ 里的历史规范与代码实际行为不符,AI 生成的接口契约就会与现实脱节。这不是 AI 的问题,而是 GIGO(Garbage In, Garbage Out)在 AI 工程中的具体表现。

这个认知带来一个实践结论:在大规模工程开始之前,维护准确的存量知识,比优化 prompt 更重要。 存量知识是 proposal 质量的天花板——天花板不够高,再好的 AI 也只能在低处徘徊。

五、分层 Agent 架构:按工程流程拆分 SDD

解决上述矛盾的方案,是按 TDD 家族的层级关系拆分 Spec 驱动流程,每一层由专门的 Agent 负责。

三层 Agent 模型

三层 Agent 模型

关键洞察:Agent 不只是"代码生成器",更是"协作规范生成器"。分层架构的核心价值,是让团队在开发前就对齐预期,而不是在联调时才发现问题。

协作成本的前置解决

协作关系 问题根源 分层 Agent 的解决方案
PM ↔ 研发 需求理解不一致 PRD 分析 Agent 生成 Gherkin,双方共同确认
前端 ↔ 后端 接口定义不一致 技术方案 Agent 生成接口契约,开发前对齐
后端 ↔ 后端 服务协作不明确 技术方案 Agent 定义服务间协作规范

与其在联调时发现问题并返工,不如在开发前就通过 Spec 对齐预期。

端到端工程实践:从阶段到接口

理解了三层 Agent 模型之后,还需要一套具体的工程实践来支撑落地。以下是一个前后端端到端交付项目的完整工程流程。

四阶段交付流程

一个完整的端到端项目,通常经历四个阶段:

阶段 输入 产出 方式
阶段一:需求分析 PRD + 历史需求 结构化需求文档 AI 做结构化分析 → 人工确认修正
阶段二:UI 设计 需求文档 UI 原型 AI 生成 → Iteration 反馈迭代
阶段三:详细设计 需求 + UI 稿 接口契约 + 领域模型 + 数据模型 AI 生成 → 人工评审关键决策
阶段四:代码生成(ATDD 驱动) 设计文档 + 参考代码 可运行代码 + 测试用例 先测试(红灯)→ 代码(绿灯)

这四个阶段与三层 Agent 架构高度对应:需求分析对应 PRD 分析 Agent,详细设计对应技术方案设计 Agent,代码生成对应代码开发 Agent。UI 设计作为前端专属阶段,在详细设计之前完成,为接口契约提供前端视角的输入。

小步快跑:接口级迭代循环

在代码生成阶段,坚持小步快跑的节奏——每一轮只实现一个接口,完成后才进入下一轮:

接口级迭代循环

为什么要小步快跑? 每个接口都是一个可验证的交付单元。接口粒度的迭代让问题暴露得更早、修复成本更低,同时也让 Review 的认知负担保持在可控范围内——审查一个接口的实现,远比审查一个功能模块的全量代码更容易发现问题。

测试八问:接口级质量门禁

每个接口开发时,遵循测试八问作为质量门禁——这八个问题覆盖了接口可能遇到的所有典型场景,确保测试覆盖不留盲区:

# 问题 必须有测试覆盖
1 正常路径是否可走通? 创建成功、查询有数据
2 必填参数缺失会如何? 返回参数错误
3 参数格式错误会如何? 类型不匹配、超长、负数
4 关联数据不存在会如何? 父资源不存在时的处理
5 重复操作会如何? 幂等性验证
6 边界值会如何? 0、-1、空、最大值
7 状态流转是否正确? 非法状态转换拒绝
8 并发操作会如何? 同时操作不冲突

测试八问与 Ralph Loop 形成互补:Ralph Loop 保证"测试通过才算完成",测试八问保证"测试本身是有意义的"。没有测试八问的约束,AI 可能生成只覆盖正常路径的测试,让 Ralph Loop 在一个不完整的测试集上通过——这是一种更隐蔽的"偷懒"。

领域驱动分工与外部记忆

在团队协作层面,由领域资深工程师牵头,按以下顺序组织工作:

1
2
3
4
5
6
7
划分领域(Domain)
↓
拆分模块(Module)
↓
分层实现(Layer)
↓
纵横切分任务(Task Decomposition)

纵横切分的含义:

  • 纵切:按业务功能切分(订单、库存、支付各自独立)
  • 横切:按技术层次切分(接口层、服务层、数据层分别实现)

两个维度的切分共同作用,确保每个 Agent 的单次任务粒度足够小,避免上下文过载——这是 AI 协作中最常见的失败模式之一。

基于文件的外部记忆是应对上下文限制的关键机制。在工作过程中,将关键决策、进度状态、接口契约持久化到文件系统,而不是依赖对话上下文:

场景 外部记忆的价值
快速恢复上下文 新开一个 Agent 会话时,读取文件即可还原工作状态
多人协作 不同开发者的 Agent 通过共享文件同步进度,避免重复工作
上下文超限 即使对话历史被截断,关键信息仍保存在文件中,不丢失

这正是本章目录约定的深层价值:.specs/ 目录不只是"规范存放处",更是团队协作的外部记忆载体——每个 Agent 的输出都写入文件,每个 Agent 的输入都从文件读取,上下文通过文件而非对话历史传递。


落地:数据流与目录约定

三层 Agent 之间通过约定好的目录结构传递数据,无需额外的编排层:

1
2
3
4
5
6
7
8
项目根目录/
├── .specs/ # Spec 数据流转
│ ├── prd/ # 输入:PRD 文档
│ ├── user-behavior/ # 第一层输出:用户行为 Gherkin
│ ├── system-behavior/ # 第二层输出:系统行为 Gherkin
│ │ └── contracts/ # 接口契约(OpenAPI 格式)
│ └── service-catalog.yaml # 服务目录(多系统环境定义)
└── src/ # 第三层输出:代码

其中,服务目录是技术方案设计 Agent 的核心依赖——它描述了当前环境有哪些服务可用,以及每个服务的能力边界,Agent 据此做出功能归属判断:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# .specs/service-catalog.yaml
services:
- name: order-service
description: 订单服务,负责订单创建、状态管理
capabilities:
- 订单创建
- 订单查询
- 订单状态变更

- name: inventory-service
description: 库存服务,负责库存管理
capabilities:
- 库存查询
- 库存扣减
- 库存预占

这个约定的价值在于:Agent 之间通过文件系统解耦,每个 Agent 只需读取上一层的输出文件,写入自己的输出文件,无需了解其他 Agent 的内部实现。

工具链落地:OpenSpec 完整实操指南

理解了目录约定之后,一个自然的问题是:这些 Spec 文件由谁来管理?格式是否有要求?能否借助工具自动生成?

OpenSpec 可以看作这类规范管理工具中的一个轻量实现。它的核心理念与本文的 SDD 思想相当接近:在写代码之前,先让人类与 AI 就规范达成共识(align before code)。

OpenSpec 是一个轻量级的 SDD 工具链,通过标准化的目录结构和 CLI 命令,让开发者在写代码之前先与 AI 就需求规范达成共识。它解决了 AI 辅助编程中的三个核心问题:如何让 AI 理解项目上下文、如何规范需求描述格式、如何沉淀已实现的功能知识。OpenSpec 的核心机制包括项目知识库(project.md)、变更提案(proposal.md)、需求规范(spec.md)和实现任务清单(tasks.md),通过 init、new、apply、archive 四个核心命令,实现了从需求到代码的完整闭环。

更详细的实战教程:如果你希望了解 OpenSpec 的完整实操步骤(包括安装、初始化、核心概念详解、目录结构详解、完整工作流程等),请参考独立文章 OpenSpec 实战教程:从入门到精通,那里包含了从零开始使用 OpenSpec 的完整示例和最佳实践。

工具机制选择:Skills vs Slash Commands vs MCP vs Hooks

在构建 SDD 工作流时,一个关键问题是:生成脚手架、单元测试、集成测试、Mock、实现记忆等任务,应该使用哪种工具机制来实现?

这涉及 AI 编程工具生态中的四种核心机制:

机制 触发方式 典型用途 特点
Skills 自动激活(基于描述匹配) 领域知识注入、工作流自动化 上下文感知,零摩擦触发
Slash Commands 手动调用 可重复工作流、参数化任务 显式控制,适合标准化流程
MCP 外部服务集成 数据库、API、文件系统访问 突破沙箱,连接真实世界
Hooks 事件驱动 质量门禁、自动化检查 在特定时机自动执行,不可绕过

核心决策框架

选择哪种机制,取决于三个维度:

  1. 触发模式:自动触发(Skills/Hooks)vs 手动触发(Slash Commands)
  2. 上下文需求:需要丰富上下文(Skills)vs 需要外部资源(MCP)
  3. 执行确定性:概率性输出(Skills 调用 LLM)vs 确定性执行(MCP/脚本)

能力选型决策树

六项任务的具体分析

1. 生成脚手架 → Slash Command

  • 理由:脚手架生成是显式触发的任务,需要用户指定项目名称、模板类型等参数,属于"可重复工作流"
  • 实现:/scaffold <template> <project-name>,支持参数化配置
  • 为什么不选 Skill:脚手架生成不需要自动触发,用户明确知道何时需要创建新项目

2. 生成单元测试 → Skill

  • 理由:单元测试生成是 TDD 工作流的自然组成部分,应该在检测到新代码或代码变更时自动激活
  • 实现:Skill 描述包含"检测到新增函数/类时,自动生成对应的单元测试框架"
  • 为什么不选 Slash Command:测试驱动开发的核心是"测试先行"或"测试同步",不应该要求用户手动触发

3. 生成集成测试 → Skill + MCP

  • 理由:集成测试需要了解服务间依赖关系,可能需要访问服务目录、API 定义等外部资源
  • 实现:Skill 负责推理"测试什么、如何测试",MCP 负责获取服务目录和接口定义
  • 协作模式:Skill 调用 MCP 工具获取上下文,再基于上下文生成测试

4. 生成 Mock → Skill + 确定性脚本

  • 理由:Mock 生成分为两个阶段:决策(哪些接口需要 Mock、返回什么数据)和执行(生成 Mock 文件)
  • 实现:Skill 负责决策(概率性推理),脚本负责生成 Mock 文件(确定性执行)
  • 原则:遵循"概率与确定性分离"原则——LLM 决定"What",脚本执行"How"

5. 实现记忆 → CLAUDE.md + PKB Skill

  • 理由:记忆分为两类:持久化规范(存放在 CLAUDE.md/AGENTS.md)和动态知识(通过 PKB Skill 管理)
  • 实现:
    • CLAUDE.md:项目级规范,如编码风格、架构决策(极低频变更)
    • PKB Skill:管理个人/团队级的品味、规范、规则,支持冷热分层和自动归档
  • 协作模式:CLAUDE.md 提供静态上下文,PKB Skill 提供动态知识注入

6. 特定代码生成 → 视情况而定

  • 模板化代码(CRUD、DTO):确定性脚本,效率优先
  • 业务逻辑代码:Skill,需要领域知识注入
  • 涉及外部 API 的代码:Skill + MCP,需要获取 API 定义

SDD 工作流的 Skill 化架构

将上述决策应用到三层 Agent 架构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
PRD 分析 Agent
├── Skill: prd-analysis(自动激活)
│ ├── 领域模型注入
│ └── Gherkin 模板约束
└── 输出:用户行为 Gherkin

技术方案设计 Agent
├── Skill: technical-design(自动激活)
│ ├── 架构模式注入
│ └── 服务目录读取(MCP)
├── Slash Command: /scaffold(手动触发)
│ └── 生成新服务脚手架
└── 输出:系统行为 Gherkin + 接口契约

代码开发 Agent
├── Skill: test-generator(自动激活)
│ └── 单元测试生成
├── Skill: mock-generator(自动激活)
│ └── Mock 决策 + 脚本执行
├── Hook: write-time-lint(强制执行)
│ └── 代码写入前的质量门禁
└── 输出:通过测试的可运行代码

理论支撑:近两年的常见实践

这一决策框架,与近两年 AI 编程工具社区里常见的分工思路大体一致:

  • Anthropic 官方文档:Skills 用于"上下文增强和自动触发",MCP 用于"外部服务集成",两者互补而非替代
  • Claude Code 最佳实践:Write-Time Hooks(如 plankton)证明"强制执行"比"事后检查"更有效
  • Cursor 社区:Slash Commands 适合"可重复的复杂工作流",Skills 适合"零摩擦的自动化"

核心洞察:机制选择不是"非此即彼",而是"各司其职"。正确的做法是:

  • 让 Skills 处理"什么时候该做什么"(自动感知)
  • 让 Slash Commands 处理"用户明确要求的重复任务"(显式控制)
  • 让 MCP 处理"需要外部资源"(连接真实世界)
  • 让 Hooks 处理"强制执行的质量门禁"(不可绕过)

代码开发层:Ralph Loop 防偷懒机制

一个正在浮现的趋势是:10 倍开发者(10x Developer)的核心技能不再是解决复杂 Bug 的能力,而是设计自动化测试体系与反馈循环的能力——这些机制能让模型的十六个并行实例替代人工解决问题。

代码开发 Agent 面临一个特殊挑战:AI 模型在 TDD 场景下容易"偷懒"——声称"测试已通过"但实际没运行,或跳过 Red 阶段直接写实现。

解决方案是引入 Ralph Loop(全称 Ralph Wiggum Loop,由 Geoffrey Huntley 提出)——一种自主迭代机制:

1
2
3
4
5
while 未满足完成条件:
执行编译检查(mvn compile)
执行测试(mvn test)
如果任何检查失败 → 继续修复
如果全部通过 → 退出循环

Ralph Loop 反压机制

关键在于 Backpressure(反压)机制:用编译结果和测试结果验证 AI 的输出,防止自欺欺人。只有当所有检查都通过时,Agent 才能声明完成。

验证手段 作用
编译检查 语法正确性
单元测试 逻辑正确性
代码规范检查 代码质量
类型检查 契约一致性

这套机制的本质,是把"完成"的定义权从 AI 手中夺回来,交给客观的验证标准。

从源头约束:Shift Left 与 Write-Time Quality Gates

Ralph Loop 解决的是"生成后验证"的问题,但在 SDD 的多步骤流水线中,一个更深层的挑战是:迭代后半程的输出量太大,Code Review 压力过重。几百行 Spec 可能产生几千行代码,问题发现越晚,修复成本越高。

这个问题的解决思路是 Shift Left(左移)——将质量约束从 Review 阶段前移到代码生成阶段,甚至生成之前。

Plan/Spec Mode:生成前的意图对齐

Claude Code 的 Plan Mode(Shift+Tab 切换)提供了一个非破坏性的规划阶段:

  • 只读探索:Plan Mode 下禁止写入文件,AI 只能读取代码、分析依赖、输出规划
  • 意图显式化:人类确认规划无误后,再进入执行阶段
  • 避免返工:在生成代码前对齐预期,而不是在 Review 时发现问题

OpenAI Codex 团队也在探索类似能力(GitHub Discussion #7355),核心设计问题是:模态 vs 非模态、持久化 vs 临时、访谈式 vs 自由形式。社区反馈强烈倾向于硬模态切换——明确的 Plan Mode 能提供安全感和可预测性。

Write-Time Quality Gates:写入时强制检查

plankton 项目(GitHub: alexfazio/plankton)将 Shift Left 推向极致:

“Code quality gate enforcement at write-time — The agent is blocked from proceeding until its output passes your checks”

其核心是 Claude Code Hooks 的三阶段流水线:

1
2
3
4
5
6
7
8
9
10
11
12
13
文件编辑触发
↓
Phase 1: Auto-format(自动格式化)
- ruff, shfmt, biome, taplo — 静默修复风格问题
↓
Phase 2: Violation Collection(违规收集)
- 20+ linters 输出结构化 JSON
↓
Phase 3: AI Fix(智能修复)
- 专用 Claude 子进程推理修复剩余问题
↓
全部通过 → 允许写入
任何失败 → 阻塞并重试

关键机制:

  • PreToolUse Hook:在工具调用前拦截,阻止不符合规范的代码写入磁盘
  • Config Tamper-Proof:禁止 AI 修改 lint 配置来绕过检查
  • Model Routing:简单问题用小模型,复杂问题用大模型,节省 Token

这套机制的价值在于行为塑造——AI 在生成代码时就知道约束存在,会主动避免违规,而不是生成后再被动修复。

分层 Agent 中的约束映射

将 Shift Left 思想应用到三层 Agent 架构:

层级 约束目标 机制
PRD 分析 Agent Gherkin 完整性 Skill 约束:必须使用 Given-When-Then,覆盖正常+异常路径
技术方案 Agent 架构合理性 Skill 约束:明确服务边界、幂等性、失败策略
代码开发 Agent 代码质量 Write-Time Gates:编译检查 → 静态分析 → 单元测试

核心洞察:约束不是限制 AI 的能力,而是将人类的工程经验编码为可执行的质量门禁。与其在 Review 时反复指出同类问题,不如让 Skill 在生成时就避免这些问题。

这与 Ralph Loop 形成互补:

  • Ralph Loop:生成后验证,确保输出正确
  • Shift Left + Write-Time Gates:生成前/生成中约束,减少错误产生

两者结合,才能从根本上缓解 SDD 流水线后半程的 Review 压力。

放权 SOP:人机协作的执行节奏

Shift Left 解决了"约束在哪一层落地"的问题,但还有一个并行问题:在一次具体任务里,人和 Agent 的控制权应该怎样交接?

如果人全程微操,强模型的探索能力被压制;如果人完全撒手,风险失控。二者之间需要一套可复用的执行 SOP——它不是 Prompt 技巧,而是任务交付格式。

核心原则:控制强度随任务调整

1
控制强度 = 任务风险 × 地形不确定性 / 反馈速度

这不是数学公式,是判断标尺:

  • 低风险 + 地形清楚 + 反馈快(如单文件 bug、文档补缺)→ 放开,给 checkpoint 就让 Agent 自跑
  • 高风险 + 地形不清 + 反馈慢(如鉴权、数据迁移、跨仓库联动)→ 收紧,显式走 Research / Plan / Approval / Review

常见错误有两个:该放的时候微操(探索阶段就锁死每一步),该收的时候放飞(执行阶段又让 Agent 重开方案)。正确的节奏恰好相反——探索给空间,执行给边界,验收给硬证据。

任务包五件事:交出去之前必须打包的内容

"帮我修一下这个 bug"之所以失败率高,是因为它只给了目标,缺了四样东西。可交付的任务包必须包含:

# 要素 为什么需要 具体做法举例
1 目标 防止 Agent 理解歪 “修复导入失败导致的测试报错”(而非"修一下 bug")
2 上下文 让 Agent 先进入地形 “先读 README、AGENTS.md、相关测试文件和报错日志”
3 边界 防止顺手重构扩大影响面 “优先做最小修复,不要重构模块结构”
4 验收 让"完成"不只是一句自然语言 “跑 pytest tests/test_import.py,说明失败是否消失、有无新增失败”
5 回收 把一次性经验沉淀成可复用资产 “若发现测试入口/导入规则不清楚,补到 AGENTS.md 或 skill 里”

检验方式:让 Agent 复述这五件事。如果它复述不清目标、说不出验证方式、或一上来就想重构——先别让它改。这些地方拦住,后面省很多时间。

五步交付循环:任务推进的主循环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
┌─────────────────────────────────────────────────────────────────┐
│ 放权 SOP 主循环 │
└─────────────────────────────────────────────────────────────────┘

[人] [Agent] [系统/地形]
│ │ │
│ ① 打包任务 │ │
│ (目标/上下文/边界/ │ │
│ 验收/回收) │ │
├───────────────────────▶│ │
│ │ ② 读地形 │
│ │ (README/AGENTS/ │
│ │ 代码入口/日志) │
│ │◀─────────────────────────┤
│ │ │
│ │ ③ 复述 + checkpoint │
│ │ (文件清单/修改意图/ │
│◀───────────────────────┤ 最小方案/不做什么/ │
│ │ 验证计划) │
│ ④ 控盘判断 │ │
│ ┌──────────────────┐ │ │
│ │ 懂目标吗? │ │ │
│ │ 范围合理吗? │ │ │
│ │ 能证明做对吗? │ │ │
│ └────────┬─────────┘ │ │
│ │ pass │ │
├──────────┴────────────▶│ ⑤ 自主推进 │
│ │ (改代码/跑测试/ │
│ │ 修错/补漏) │
│ │◀─────────────────────────┤
│ │ │
│ │ ⑥ 证据验收 │
│ │ (diff/测试结果/ │
│◀───────────────────────┤ 未覆盖风险) │
│ ⑦ 经验回收 │ │
│ ┌──────────────────┐ │ │
│ │ 要不要修河床? │ │ │
│ │ → AGENTS.md │ │ │
│ │ → skill/spec │───┼─────────────────────────▶│
│ │ → 经验资产 │ │ │ 下一次任务
│ └──────────────────┘ │ │ 的地形
│ │ │

这个循环里,人只在 4 个关键点出现:打包任务、审 checkpoint、看证据、决定回收。其余时间 Agent 自跑。这就是"多线程控盘"的来源——人不再被单条任务的连续执行锁死,而是在多个 Agent 任务间切换判断。

每个阶段的具体动作

① 打包任务:见上文"任务包五件事"。

② 读地形(Agent 先探索,不急着改):让 Agent 先回答——

1
2
3
4
5
6
先不要改代码。先读相关上下文,告诉我:
1. 你理解的目标是什么
2. 当前代码大概怎么实现
3. 可能影响哪些文件
4. 最大风险是什么
5. 你准备用什么方式验证

③ checkpoint 五项(改代码前的强制关卡):

# Checkpoint 项 对应风险
1 文件清单 防止影响面无意识扩大
2 修改意图 确认 Agent 没理解歪
3 最小实现方案 避免一上来重构半个系统
4 明确不做什么 给任务设边界
5 验证计划 让"完成"有可检验证据

好的 checkpoint 应该让人在几十秒内判断三件事:懂不懂目标、有没有扩大范围、知不知道怎么证明自己做对。

④ 自主推进:checkpoint 通过后,不再每行指挥。此时人高频插手反而会打断 Agent 的连续推理——让它自己找入口、拆局部、修失败、根据测试迭代。

⑤ 证据验收六问:Agent 说"完成了"时,继续追问——

1
2
3
4
5
6
7
请给出:
1. 实际改动摘要
2. 跑过的验证命令
3. 验证结果
4. 哪些目标已覆盖
5. 哪些风险尚未覆盖
6. 如果失败,下一步定位路径是什么

最危险的不是 Agent 报错(报错反而是反馈面),而是它把"阶段性完成"说成"任务完成"、把"测试跑过一个入口"说成"风险关闭"。

⑥ 修河床(经验回收):一次任务如果只留聊天记录,水流就过去了。复利来自把本次经验固化——

  • 本次确定的项目规则 → AGENTS.md
  • 踩过的坑 → 经验资产 / skill
  • 稳定的操作方式 → 新 skill
  • 验证入口 → README / 测试文档
  • 业务边界 → spec
  • Review 发现的风险 → checklist

不是所有任务都需要修河床。只有暴露了稳定规则、常见坑、测试入口、项目约定或反模式的任务,才值得沉淀。

放权 SOP 与 SDD 分层架构的映射

这套 SOP 不是 SDD 之外的东西,它是 SDD 分层 Agent 在单次任务执行粒度上的投影:

放权 SOP 阶段 对应 SDD 层级 产物
打包任务(目标/上下文/边界) PRD 分析 Agent 用户行为 Gherkin
读地形 + checkpoint 技术方案 Agent 接口契约 + 系统行为 Gherkin
自主推进 代码开发 Agent 通过单测的代码
证据验收 Ralph Loop + 测试八问 测试通过证据
修河床 知识沉淀层 AGENTS.md / skill / spec 更新

核心洞察:SDD 解决"规范如何层层传递"的架构问题,放权 SOP 解决"一次任务里人机控制权如何交接"的流程问题。前者是横向分层,后者是纵向节奏——合在一起,才是完整的人机协作工程。

六、上下文管理:Skills 渐进披露机制

分层 Agent 架构还需要解决一个技术层面的根本限制:LLM 的上下文窗口是有限的。

当 Agent 拥有大量 Skill(技能)时,会遇到一个矛盾:

  • Skill 越多,Agent 能力越强
  • 但把所有 Skill 的完整内容都加载进上下文,Token 消耗巨大,且注意力会被稀释(无关信息越多,模型对关键指令的遵循能力越弱)

这就是上下文拥挤问题(Context Crowding)。

解决方案:声明层 + 实现层的分离

一种有效的做法是将 Agent 的能力库分为声明层和实现层,通过多趟调度实现按需加载:

这里还有一个更深层的协作原则值得强调:

对话优于僵硬指令,协作上下文优于单纯追求更强模型。Spec 的正确性,会直接影响项目成败。

这意味着:

  • 对话优于指令:与其给 AI 一长串静态指令,不如通过多轮对话逐步澄清意图、修正理解。对话是动态的、可迭代的,能够暴露假设、消除歧义。
  • 上下文优于模型:与其一味追求更强大的模型,不如投入在更精确的协作上下文(Spec)上。一个准确的 Spec 能让普通模型表现出色,而一个错误的 Spec 即使交给更强模型,也可能南辕北辙。

Spec 的正确性直接决定项目的输出质量上限——错误的 Spec 比没有 Spec 更危险,因为它会让整个团队在错误的方向上高效执行。

Skill 渐进式加载架构

关键洞察:LLM 不需要知道所有 Skill 的细节,只需要知道"有哪些 Skill 可用"(注册表)。一旦选定 Skill,再加载细节。这就像操作系统的动态链接库——程序启动时不加载所有库,只在调用时才链接。

在实践中,Skills 使用标准化的目录结构组织业务知识:

1
2
3
4
5
6
7
8
9
10
11
.claude/skills/
├── prd-analysis/ # PRD 分析 Agent 的领域知识
│ ├── SKILL.md # 技能描述(入口,极低 Token)
│ ├── domain-model.md # 领域模型(按需加载)
│ └── gherkin-examples.md
├── technical-design/ # 技术方案设计 Agent 的领域知识
│ ├── SKILL.md
│ ├── architecture-patterns.md
│ └── service-catalog.md
└── shared/
└── business-rules.md # 共享业务规则

这套机制的核心思想:不是一次性加载所有背景,而是按需分批加载。就像程序员需要时查阅文档,而不是先背诵所有文档一样。

为什么要引入确定性脚本(Script)?

这是架构中值得深入理解的一层。LLM 是概率模型,它的输出本质上是"最可能正确的答案",而不是"一定正确的答案"。对于精确计算、严格的格式转换、数据库查询、文件系统操作,让 LLM 去"模拟"这些操作,既昂贵又不可靠。

正确的做法是:LLM 决定调用哪个脚本,脚本负责精确执行。

维度 LLM 确定性脚本
擅长 意图理解、模糊处理、创意生成 精确计算、严格逻辑、一致输出
不擅长 精确计算、完全一致的重复输出 理解自然语言、处理歧义

LLM 是指挥官,Script 是工兵。不要让指挥官去挖战壕,也不要让工兵去制定战略。

七、三大设计原则

阅读说明:本章的三条原则聚焦于 Agent 架构设计层面——如何构建高效的 AI Agent 系统(上下文管理、概率与确定性分离、规范先行)。第九章的五条原则则聚焦于 SDD 工程实践层面——如何在团队中落地规范驱动开发(规范先行、分阶段验证、规范即上下文、警惕 Spec 瀑布、Bug 是 Spec 缺口)。两套原则视角不同、互为补充,共同构成 AI 时代人机协作的完整工程哲学。

从上述架构模式中,可以提炼出构建高效 AI Agent 的三条设计原则。

三大设计原则

原则一:上下文最小化(Minimal Context Exposure)

“The model should only know what it needs to know to make the next immediate decision.”

永远不要把所有知识一次性灌输给 LLM。上下文是稀缺资源,过多的无关信息会产生两个副作用:

  1. Token 浪费:直接的成本问题
  2. 注意力稀释(Attention Dilution):LLM 的注意力机制会被无关内容分散,导致对核心指令的遵循能力下降

实践:使用摘要(Summary)、索引(Index)和注册表(Registry)作为入口,细节按需披露。

原则二:概率与确定性分离(Separation of Probabilistic and Deterministic Logic)

“Use LLMs for intent and variability; use Scripts for precision and reliability.”

实践:通过 Function Call(工具调用)机制实现——LLM 输出结构化的调用意图,运行时将其路由到对应的确定性代码。

原则三:规范先行(Specification First)

“Humans define the ‘What’ and ‘Why’; AI handles the ‘How’.”

这是对整篇文章人机协作哲学的技术化表达:

  • Human:定义接口(Interface)、约束(Constraints)、验收标准(Acceptance Criteria)
  • AI:填充实现细节(Implementation)

人类从"写代码(Coding)"升级为"设计系统(System Designing)“和"审查(Reviewing)”。这不是降低了人的价值,而是提升了人工作的抽象层次,让人能够统摄更大的系统复杂度。

八、完整案例演练:用户下单功能

理念最终要落地。以"用户下单"这个经典场景为例,走一遍三层 Agent 的完整流程。

用户下单完整案例流水线

Step 1:PRD 分析 Agent → 用户行为 Gherkin

输入(.specs/prd/user-order.md):

1
2
3
4
5
6
7
8
## 功能描述
1. 用户可以将商品加入购物车
2. 用户可以提交订单
3. 下单成功后库存自动扣减

## 业务规则
- 下单时检查库存,库存不足则拒绝
- 订单创建成功后生成唯一订单号

输出(.specs/user-behavior/user-order.feature):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@用户行为 @ATDD
Feature: 用户下单
作为一个已登录用户
我想要能够提交订单
以便购买我选中的商品

Background:
Given 用户已登录

Scenario: 成功提交订单
Given 购物车中有以下商品
| SKU | 名称 | 数量 |
| PHONE-001 | 智能手机 | 1 |
And 商品库存充足
When 用户提交订单
Then 订单应该创建成功
And 用户应该看到订单号
And 库存应该扣减相应数量

Scenario: 库存不足无法下单
Given 购物车中有商品 "PHONE-001" 数量 10
And 商品 "PHONE-001" 库存只有 3
When 用户提交订单
Then 应该提示"库存不足,当前库存: 3"
And 订单不应该创建

这份 Gherkin 是 PM 与研发的共同契约,PM 可以直接阅读确认,无需理解任何技术细节。

Step 2:技术方案设计 Agent → 系统行为 + 接口契约

Agent 读取服务目录,分析每个场景涉及的能力点,匹配到对应服务,然后输出:

功能归属判断:

用户行为步骤 匹配能力 归属服务
“用户提交订单” 订单创建 order-service
“库存应该扣减” 库存扣减 inventory-service
“用户应该看到订单号” 用户界面展示 frontend-app

输出:订单服务系统行为(.specs/system-behavior/order-service.feature):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@系统行为 @BDD @订单服务
Feature: 订单服务

Scenario: 成功创建订单
Given 收到创建订单请求,包含有效的用户 ID 和商品列表
When 调用库存服务扣减接口
And 库存服务返回成功
Then 创建订单记录,状态为 "CREATED"
And 返回 201 和订单详情

Scenario: 库存不足创建失败
Given 收到创建订单请求
When 调用库存服务扣减接口
And 库存服务返回库存不足
Then 不创建订单记录
And 返回 409 和错误信息

输出:任务分配清单(.specs/system-behavior/task-assignment.md):

服务 任务 依赖
库存服务 实现 POST /inventory/deduct 无
订单服务 实现 POST /orders 库存服务接口
前端 实现下单页面 订单服务接口

开发顺序建议:库存服务(无依赖)→ 订单服务 → 前端。

Step 3:代码开发 Agent → TDD 实现

Agent 按 Red-Green-Refactor 循环逐场景实现,Ralph Loop 在后台持续验证:

Round 1 - RED:先写失败的测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Test
void should_create_order_when_stock_sufficient() {
// Given
CreateOrderRequest request = CreateOrderRequest.builder()
.userId("user_123")
.items(List.of(new OrderItem("PHONE-001", 1)))
.build();
when(inventoryClient.deduct(any())).thenReturn(DeductResponse.success());

// When
OrderResult result = orderService.createOrder(request);

// Then
assertThat(result.isSuccess()).isTrue();
assertThat(result.getOrder().getStatus()).isEqualTo("CREATED");
verify(orderRepository).save(any(Order.class));
}

运行测试 → 编译失败,OrderService 不存在 ❌

Round 2 - GREEN:写最小实现,测试通过 ✅

Round 3:继续下一个场景,直到所有 Gherkin 场景都有对应的通过测试。

TDD 与 Ralph Loop 双层循环

整个过程中,Ralph Loop 持续运行编译和测试检查。只有当所有检查都通过时,Agent 才能退出循环,声明完成。

案例小结

这个案例展示了三层 Agent 的核心价值:

  • PRD 分析 Agent:把模糊的需求文档转化为 PM 和研发都能确认的 Gherkin 契约
  • 技术方案设计 Agent:把单一的用户故事拆解为多个服务的职责边界和协作规范
  • 代码开发 Agent:在 Ralph Loop 的约束下,确保每一行代码都有对应的测试覆盖

三层之间通过文件系统传递数据,每一层的输出都是下一层的输入,形成一条从 PRD 到代码的完整流水线。

九、SDD 的五项核心原则

如果说第七章讨论的是 Agent 架构怎么设计,那么这里要收束的是 SDD 在工程实践中如何落地。下面五条原则更偏交付、协作与质量控制,而不是再重复一次前面的架构讨论。

原则一:规范先行,而非文档补写

“先定义预期,再实现代码”——不是写完代码后的文档补充,而是驱动实现的前置契约。

传统软件开发中,文档往往扮演着"事后说明"的角色:代码写完后,再补一份设计文档或接口说明。这种模式下,文档与代码脱节是常态,"文档过时"成为永恒的抱怨。

SDD 彻底反转这一顺序:

传统模式 SDD 模式
Code → Document → 维护时文档已过时 Spec → Code → Spec 即验收标准
验收时各执一词 双方对着同一份 Gherkin 确认
接口变更靠口头同步 接口契约变更需更新 Spec

规范先行的本质,是将"意图"从隐性的对话历史中提取出来,固化为可验证、可传递的结构化知识。 这正是 OpenSpec 工作流中 proposal.md → tasks.md → spec.md 的核心价值——在写第一行代码之前,人类与 AI 必须先就"要做什么"达成共识。

从 Harness 的角度看,Spec 也是任务箱体的边界。目标、非目标、验收条件和禁止改动范围共同构成一组“优势法则”:它把模型从训练数据里的平均经验拉回当前项目的局部最优路径。这个更底层的解释见 《Harness 的本质:把随机模型锁进可验证的箱体》。

原则二:分阶段验证,拒绝模糊推进

每一层都有明确的完成标准和验证机制,拒绝"差不多行了"的模糊推进。

Vibe Coding 的最大风险在于进度幻觉——AI 声称"完成了",但实际上只是输出了看起来合理的代码,并未经过客观验证。SDD 通过分层验证机制解决这一问题:

1
2
3
4
5
6
7
PRD 分析层:Gherkin 是否覆盖所有场景?PM 是否确认?
↓ 验证通过
技术方案层:接口契约是否完整?服务边界是否清晰?
↓ 验证通过
代码实现层:Ralph Loop 是否全部通过?测试是否覆盖?
↓ 验证通过
完成

关键机制:

层级 验证手段 拒绝模糊的方式
PRD 分析 PM 人工审查 Gherkin 未确认不进入下一层
技术方案 服务目录匹配 + 架构评审 功能归属不明确不分配任务
代码实现 Ralph Loop 强制检查 编译/测试/规范任一失败即阻塞

这种**分阶段门控(Stage-Gate)**机制,将"完成"的定义权更多交给客观可验证的标准。实践里未必每个团队都会采用同样严格的门禁,但核心思想是一致的:不要让未经验证的产物一路向下游扩散。

原则三:规范即上下文,赋能多 AI 协同

Spec 不仅是人与 AI 的契约,更是多 Agent 协同的"共同语言"和上下文载体。

当多个 Agent 协作完成一个需求时,最大的挑战是上下文传递——如何让 Agent B 理解 Agent A 的输出,并在此基础上继续工作?

SDD 的解决方案是:让 Spec 成为上下文本身。

1
2
3
4
5
6
7
项目根目录/
├── .specs/
│ ├── prd/user-order.md # Agent A 的输入(人工编写)
│ ├── user-behavior/user-order.feature # Agent A 的输出 = Agent B 的输入
│ ├── system-behavior/order-service.feature # Agent B 的输出 = Agent C 的输入
│ └── system-behavior/contracts/openapi.yaml # 接口契约(共享上下文)
└── src/ # Agent C 的输出

规范作为上下文的三重价值:

  1. 契约价值:人与 AI 之间的承诺——“你按这个规范实现,我按这个规范验收”
  2. 验收价值:代码实现的客观标准——“测试通过即符合规范”
  3. 协作价值:团队共同语言——PM、研发、测试对着同一份文档工作

这也回到了文章开篇的主线:Vibe Coding 最大的问题不是代码写得快不快,而是缺少可共享、可传递、可验证的共同语言;Spec 的价值,恰恰在于把这种共同语言显式化。

原则四:警惕 Spec 瀑布,拒绝 Markdown 怪兽

规范是必要的,但过度的规范是负担。警惕 Spec 瀑布,不要制造 Markdown 怪兽。

SDD 强调"规范先行",但这不意味着要写无穷无尽的文档。实践中存在一个危险的陷阱:为了规范而规范,最终产生大量无人阅读、无人维护的 Markdown 文件,成为项目的累赘而非资产。

Spec 瀑布的表现:

反模式 症状 后果
过度设计 一个简单需求产出 50 页 Spec 维护成本超过实现成本
文档膨胀 每个变更都要更新 5 个文件 开发者逃避更新,文档迅速过时
形式重于内容 追求完美的格式而非清晰的意图 Spec 成了表演,而非协作工具

健康的 Spec 应该遵循"刚好足够"原则:

  • 长度:能说明白即可,不追求篇幅
  • 格式:工具可解析即可,不追求排版
  • 维护:变更时顺手更新,不成为额外负担

Spec 的价值在于减少沟通成本,而不是增加文档工作量。 当写 Spec 的时间超过了写代码的时间,就是 Spec 瀑布的警告信号。

原则五:Bug 是 Spec 缺口,而非代码问题

很多值得长期修复的 Bug,最终都应该回到源头——在 Spec 文档中补上缺口。

传统观念认为 Bug 是代码写错了,但在 SDD 的视角下,Bug 的本质是 Spec 的缺口——要么是 Spec 没有覆盖这个场景,要么是 Spec 的描述不够精确,导致实现偏离了预期。

Bug 根因分析:

Bug 类型 传统视角 SDD 视角
边界条件未处理 代码遗漏 Spec 未定义边界行为
异常流程报错 异常处理缺失 Spec 未覆盖异常场景
功能与需求不符 实现错误 Spec 描述模糊,理解有歧义
性能不达标 算法优化不足 Spec 未定义性能验收标准

在源头闭合缺口意味着:

  1. 发现 Bug 时,先问 Spec:这个场景在 Spec 中有定义吗?定义清晰吗?
  2. 修复 Bug 时,先改 Spec:补充缺失的场景描述,明确预期行为
  3. 验证修复时,对照 Spec:确保实现与更新后的 Spec 一致

这不是在推卸代码质量的责任,而是尽量把质量保障左移到更便宜的阶段——越早发现问题,修复成本通常越低。

一个无法回写到 Spec 的 Bug 修复,往往还不算完整。 只有把缺口补回规范层,团队才更有机会避免它以别的形式再次出现。


把这五条放在一起看,它们更像一组彼此制衡的工程准则:

五项核心原则制衡关系

  • 规范先行回答了"做什么"
  • 分阶段验证约束了"什么时候算完成"
  • 规范即上下文支撑了"如何协作传递"
  • 警惕 Spec 瀑布提醒不要把规范本身做成负担
  • Bug 是 Spec 缺口则要求把经验真正沉淀回体系

它们共同指向一个更务实的目标:让规范既能驱动实现,又不反过来拖垮实现。

十、SDD 的边界与风险:一种必要的自省

前九章构建了 SDD 的完整正面论证。但任何方法论如果不经受批判性审视,就容易从"有用的工具"滑向"危险的信仰"。以下是对 SDD 叙事中几个值得警惕的盲区的正面回应。

"Spec"这个词被滥用了吗?

在严格的软件工程语境中,Specification(规约)是一个具有形式化含义的术语——从 Z notation 到 TLA+,从 IEEE 830 到军工级的 DO-178C,"Spec"背负着几十年的严肃分量:形式化、可验证、具有契约约束力。

而 AI 编程社区所说的"Spec",本质上是一份结构化的自然语言文档,用来给大模型提供足够的上下文信息。它通常包含项目背景、技术栈选型、目录结构约定、功能需求描述——换言之,是一份格式化过的 PRD + 技术备忘录。

这个命名确实存在概念通胀的风险。 当一份 Prompt 模板被冠以"Spec"之名时,容易暗示它具有规约级别的精确性和完备性——而这在当前阶段并不成立。更诚实的叫法或许是"AI Context Document"或"Prompt Blueprint"。

本文选择沿用"Spec"这一称呼,是因为 SDD 的目标方向确实是向着可验证、可执行的规范靠拢(Gherkin 场景可以直接转化为自动化测试),而非停留在纯自然语言描述。但必须承认:当前阶段的 SDD Spec 与传统形式化规约之间,仍然存在显著的精确性鸿沟。 对这个鸿沟保持清醒,是避免过度承诺的前提。

上下文准备是入口动作,不是核心动作

将需求整理成结构化的、适合大模型消费的上下文文档,确实有用——大模型的上下文窗口有限,注意力机制会在长文本中衰减,提供清晰、分层、重点突出的输入,能显著提升生成代码的质量。

但需要警惕的是:把"如何跟 AI 说清楚需求"包装成一种开发范式,存在言过其实的风险。 上下文准备是 AI 辅助开发的入口动作,不是核心动作。真正的工程挑战在后面:

  • 生成的代码是否正确?架构决策是否合理?
  • 异常路径是否覆盖?性能是否可接受?
  • 安全性是否达标?后续如何维护和演进?

如果 90% 的精力花在"怎么写文档",只有 10% 花在"怎么验证输出",这个比例本身就说明了问题。这也是本文在第五章强调 Ralph Loop、Write-Time Quality Gates 等验证机制的原因——SDD 的价值不在于 Spec 本身,而在于 Spec 驱动的验证闭环。 没有验证闭环的 Spec,只是一份更精致的 PRD。

代码责任不可委托

这是 SDD 叙事中最需要正视的风险。

在任何严肃的软件工程组织中,代码的责任归属是清晰的:谁提交的代码,谁就要为它负责。git blame 上写的是提交者的名字,出了线上事故,复盘会上站起来解释的也是提交者——不管这段代码是手写的、从 Stack Overflow 复制的、还是 AI 生成的。

"AI 写的"不是免责声明。 SDD 流程中的 Spec 确认、Ralph Loop 验证、Write-Time Gates 等机制,都是在帮助工程师更好地履行这份责任——而不是替代它。

"对代码负责"意味着:

维度 含义
理解 知道这段代码为什么这样写,而不仅仅是它能跑
边界 理解边界条件,而不仅仅是 happy path
可解释 能在凌晨三点被叫醒时,解释清楚这段代码的行为
影响评估 能评估修改这段代码对系统其他部分的影响

当通过"写 Spec → AI 生成"的方式产出大量代码时,工程师是否仍然具备上述能力?如果不具备,那就是在往项目里埋地雷——而且是连自己都不知道埋在哪儿的地雷。

SDD 的正确定位:它是帮助工程师更高效地生产和验证代码的工具链,而不是让工程师可以不理解代码的许可证。Code Review 的对象始终是代码本身,而不是生成这段代码的 Spec。

虚假的确定性与隐性技术债

SDD 的心理吸引力在于:它把一个本质上充满不确定性的过程(软件开发),包装成了一个看似确定性的流程(写文档 → 得到代码)。

但大模型是概率性的、不完全可靠的、会产生幻觉的。无法通过一份文档来"控制"它的输出——只能影响它的分布。今天跑一遍和明天跑一遍,结果可能完全不同。

更隐蔽的风险是隐性技术债。传统的技术债,至少开发者知道哪里走了捷径:"这个地方用了全表扫描,以后要加索引。"但 AI 生成的技术债是隐性的:不知道哪里有坑,因为从一开始就没有深入理解过那段代码。

每一段没有仔细审查就合并的 AI 生成代码,都是一笔技术债——而且是最隐蔽的那种。

大型项目中的一致性挑战

在大型项目中,代码库的一致性——命名规范、错误处理模式、日志规范、测试覆盖策略、模块间的接口约定——需要在数百万行代码中保持统一。

大模型在不同的对话中,面对相似但不完全相同的上下文,会生成风格和模式差异巨大的代码。今天它用策略模式,明天它用 if-else 瀑布;今天它用自定义错误类型,明天它直接 throw 字符串。当多个开发者各自拿着各自的 Spec 去生成代码,然后合并到同一个代码库时——那个代码库会变成一个拼凑起来的混合体。

此外,Spec 本身也会变成一个需要持续维护的工程产物:代码改了 Spec 要不要同步更新?Spec 和实际代码不一致时以谁为准?不同模块的 Spec 之间有依赖关系怎么办?——这正是第九章原则四"警惕 Spec 瀑布"试图应对的问题,但必须承认,这个问题在大型项目中远比小型项目严峻。

正视批判后的务实立场

上述批判指向一个核心论点:Spec 开发的叙事存在将"描述问题"偷换为"理解问题"的风险。但理解和描述之间,隔着一整个工程学科。

这个批判是成立的。SDD 的正确定位不是"写好 Spec 就万事大吉",而是:

维度 SDD 能做的 SDD 不能做的
需求对齐 提供结构化的共同语言,减少沟通歧义 替代对业务领域的深入理解
代码生成 在明确约束下提升生成质量 保证生成代码的正确性和最优性
质量保障 通过验证闭环(Ralph Loop + Gates)降低缺陷率 替代人工 Code Review 和架构判断
团队协作 通过 Spec 传递减少信息损耗 替代面对面的技术讨论和设计评审
知识沉淀 将隐性知识显式化为可维护的文档 自动保持文档与代码的一致性

更合理的姿态:

  • AI 生成,人类审查——对于核心代码,每一行都要过眼,每一个逻辑分支都要验证
  • AI 建议,人类决策——架构选型、技术权衡、安全策略,这些必须是人做的判断
  • 投资于验证能力——Code Review 能力、测试策略设计、调试能力、安全意识,这些比"写更好的 Spec"更值得投入

代码生成可以交给工具,但判断力和责任感不可以。 SDD 的价值在于让人工作在更高的抽象层次上——但"更高的抽象层次"意味着更大的责任,而不是更少的责任。

十一、总结

这几个层次的思考,实际上是同一个核心洞见在不同尺度上的展开:

1
2
3
4
5
6
哲学层:  人 → 高层决策,AI → 低层执行
问题层: Vibe Coding 优化个人效率,SDD 优化团队协作效率
方法论层:已解决的问题 → AI,新问题 → 人
工程层: PRD → 用户行为 Spec → 技术方案 → 系统行为 Spec → Code
架构层: 注册表(路由)→ 按需加载(规划)→ 脚本(执行)
实践层: 目录约定(解耦)→ Ralph Loop(防偷懒)→ 服务目录(归属判断)

六层架构全景

AI 的演进方向,是从"生成器"转变为"调度器"和"决策器"。而人的演进方向,是从"操作员"晋升为"架构师"。

Vibe Coding 时代,关注点是"如何让 AI 更快地写出代码"。SDD 时代,关注点转向"如何让团队更高效地协作"。理念落地的关键,是把三层 Agent 架构从概念变为可运行的实现——而这需要的不只是工具,更是一套工程约定。

但正如第十章所讨论的,SDD 不是银弹。它的价值在于提供了一套结构化的协作语言和验证闭环,而不是承诺"写好 Spec 就万事大吉"。SDD 的正确定位是:让人工作在更高的抽象层次上,同时承担更高抽象层次上的责任。 代码生成可以交给工具,但判断力、责任感和对系统的深层理解不可以。

这不是零和游戏,而是一场认知升维的协作。而这一切的核心,仍然是那个从 Kent Beck 时代就开始的理念:先定义预期,再实现代码——然后对实现的结果负责到底。