目录

[Project] 8. NL 胶水层 — Vibe-Coding 时代的需求工程实践

我的Vibe-Coding实践方向——NL胶水层

问题:AI写代码太快了

Vibe-Coding正在改变软件开发的节奏。AI编码工具让代码生成速度提升了10倍甚至100倍。但这种速度带来了一个显而易见的问题:代码失控

Vibe-Coding的甜蜜陷阱

Vibe-Coding的体验是这样的:你告诉AI——“帮我搭一个订单系统,要支持下单、支付、退款,支付用webhook回调,退款要走审批流,所有操作要有审计日志”。

十分钟后,你面前出现了:

  • order_service.py — 完整的订单状态机(pending → paid → shipped → completed / refunded)
  • payment_gateway.py — 支付网关集成,带签名验证、幂等性检查、超时重试
  • refund_workflow.py — 退款审批流,三级审批,自动通知
  • audit_logger.py — 审计日志,异步写入,支持查询
  • models.py — 数据库模型,带索引、约束、软删除
  • tests/ — 一整套单元测试,覆盖率看着有90%+

你跑一下测试,全绿。启动服务,调几个API,都通。代码质量看着也不错——类型标注完整、错误处理到位、日志分级清晰。

但这只是开始。你不可能一句话就搞定整个系统——你得一轮一轮地和AI对话,逐步细化:

  • 第一轮:“搭一个订单系统的基本框架”→ 生成了订单模型和基础CRUD
  • 第二轮:“加上支付集成,用webhook回调”→ 生成了支付网关和签名验证
  • 第三轮:“退款要走审批流,三级审批”→ 生成了退款工作流和审批链
  • 第四轮:“所有操作要有审计日志”→ 生成了审计日志模块
  • 第五轮、第六轮……每一轮都在补充细节、修复边界情况、调整业务规则

每一轮对话,AI都根据你的描述生成了高质量的代码。你跑测试,全绿。你提交了。感觉自己一天干了别人一周的活。

然后问题开始出现了。

第二周,你让AI加一个"部分退款"功能。AI生成了partial_refund.py,逻辑完整,测试通过。但你没注意到:

  • 原来的refund_workflow.py里,退款金额校验是refund_amount == order_amount(全额退款)
  • 新的partial_refund.py里,校验是refund_amount <= order_amount
  • 两个模块各自独立运行,没有共享校验逻辑
  • 如果有人同时调用全额退款和部分退款接口,订单金额可能被退两次

第三周,支付网关那边改了webhook的签名算法。你让AI更新签名验证。AI改了payment_gateway.py里的verify_signature()函数。但你没注意到:

  • refund_workflow.py里也有一个签名验证——是AI当初生成退款模块时独立写的,用的是旧算法
  • 支付验证通过了,退款验证失败了,但失败被try/except吞掉了,只打了一行warning日志

第四周,你回头看这个系统——12个文件,3000行代码,每个文件都写得很专业,但没有人(包括你自己)真正理解它们之间的所有交互。你不知道哪些模块共享了逻辑,哪些模块各自为政,哪些隐含的假设在某个角落被违反了。

这就是Vibe-Coding的甜蜜陷阱:每一次生成都是完美的,但累积起来是混乱的。AI每次只看到一个文件,它不知道整个系统的全貌。而你,因为代码生成得太快,也来不及建立这个全貌。

代码失控的三个信号

这种混乱不是突然爆发的,它有三个渐进的信号。

信号一:重复实现

订单系统里,退款金额校验这个逻辑,AI在三个地方各写了一次:

Figure 1 **Figure 1.3 — 退款金额校验的三个实现**

每个实现都能跑,每个实现都有自己的"风格"。但当你需要修改退款规则——比如加上"退款金额不能超过实付金额"——你需要找到所有三个地方,分别修改,还要确保修改后的行为一致。

AI不会帮你做这件事。它每次只看到一个文件。

信号二:隐性依赖

payment_gateway.py的webhook处理器依赖audit_logger.py的一个特定行为——“当日志写入失败时,主流程不应该中断”。但这个依赖没有写在任何地方,它只存在于AI生成代码时的那次对话里。

三周后,你让AI优化审计日志的性能,AI把异步写入改成了批量写入。批量写入失败时会抛异常。支付webhook处理器没有捕获这个异常,导致支付成功但订单状态没更新。

没有人知道这两个模块之间有依赖关系。代码里没有注释,没有文档,没有契约。

信号三:知识流失

代码写完后,连作者自己都记不清"为什么这么写"。你看着refund_workflow.py里的三级审批逻辑,完全不记得为什么是三级而不是两级。

是因为金额阈值?是因为合规要求?还是AI当时就这么生成的,你觉得能跑就接受了?

三个月后的你,和一个新加入团队的成员,面对的是同样的困境:只有代码,没有上下文。

Figure 2 **Figure 1.1 — 代码失控的三个信号**

为什么需要在代码上抽象一层

问题的根源是:代码是细节层,不是理解层

当你直接让AI从意图生成代码,中间没有任何缓冲层:

Figure 3 **Figure 1.2 — 直接生成 vs 抽象后生成**

没有抽象层时,每次AI生成都是一次独立的"翻译"——从自然语言到代码。每次翻译都可能产生不同的结果,因为AI没有记忆,没有上下文,没有标准。

有了抽象层,情况完全不同:

  • 你先把意图写成结构化的自然语言(比如User Story)
  • AI从这个结构化的描述生成代码
  • 每次生成都有同一个"真相源"作为参照
  • 代码可以变,但意图不变

这个抽象层的目的不是减慢速度,而是:

  • 提供一个稳定的理解锚点——不管代码怎么变,意图是固定的
  • 让AI生成的代码有一个可追溯的源头——每段代码都能追溯到哪条需求
  • 在代码变化时,保持意图的连续性——重构代码时,你知道"应该做什么"不变

这个抽象层可以是API文档、接口契约、或者需求文档。我选择的是User Story

选择:为什么是User Story

抽象层的选择决定了整个工作流的形态。不同的抽象层有不同的特性和局限。

抽象层的候选方案

常见的抽象层包括:

  • API文档:描述接口签名、参数、返回值
  • 接口契约:描述模块间的协议、数据格式、行为约束
  • 需求文档:描述功能目标、用户场景、验收标准
  • User Story:以用户视角描述需求,包含角色、目标、价值

每种方案都试图在代码之上建立一个"真相源",但效果不同。

Figure 4 **Figure 2.1 — 抽象层候选方案对比**

API文档的局限

API文档的优势是精确

  • 函数签名、参数类型、返回值都是确定的
  • 可以直接从代码生成,保持同步

但API文档的局限是缺乏上下文

  • 知道"这个函数做什么",但不知道"为什么要做"
  • 知道"参数是什么",但不知道"什么场景下会用到"
  • 无法回答"这个功能对用户有什么价值"

一个具体的例子:你看到exportCSV(filters: FilterOptions): Promise<Buffer>这个签名,你知道它导出CSV。但你不知道:

  • 谁在用这个功能?(运营?数据分析师?)
  • 为什么要导出?(做报表?做审计?)
  • 导出的数据量级是多少?(100条?100万条?)
  • 导出失败时应该怎么处理?(重试?通知用户?)

这些信息对AI生成正确的代码至关重要,但API文档里完全没有。

Figure 5 **Figure 2.2 — API文档的信息覆盖**

接口契约的局限

接口契约的优势是约束明确

  • 定义模块间的边界和协议
  • 可以验证是否符合契约

但接口契约的局限是过于技术化

  • 描述的是"系统内部如何协作",而不是"用户需要什么"
  • 对于非技术角色(产品、运营)难以理解
  • 无法作为"需求的真相源"

接口契约告诉你"模块A通过gRPC调用模块B的ProcessOrder方法,超时时间5秒,重试3次"。但它不告诉你"用户下单后,系统应该在3秒内确认订单,如果支付网关超时,应该保留订单并通知用户稍后重试"。

前者是实现细节,后者是业务需求。接口契约只能粘合代码和代码,粘不住人和代码。

Figure 6 **Figure 2.3 — 接口契约的信息覆盖**

User Story的优势

User Story的结构是:

1
2
3
4
As a {角色}, I want {目标}, so that {价值}

Acceptance Criteria:
- Given {上下文}, When {动作}, Then {结果}

这个结构的优势:

  • 以用户为中心:从用户的视角描述需求,而不是从系统的视角
  • 包含上下文:角色、目标、价值、场景都有明确的描述
  • 可验证:Acceptance Criteria提供了具体的验收标准
  • 易于理解:非技术角色也能读懂

回到订单系统的例子,User Story会这样写:

1
2
3
4
5
6
7
As a 买家, I want to 对已支付的订单申请部分退款,
so that 当部分商品有问题时,我不需要退掉整个订单。

AC-1: Given 订单状态为已支付且未退款, When 申请部分退款且金额≤实付金额, Then 创建退款单并进入审批流
AC-2: Given 退款金额超过实付金额, When 申请退款, Then 拒绝并返回"退款金额不能超过实付金额"
AC-3: Given 同一订单已有进行中的退款单, When 再次申请退款, Then 拒绝并返回"存在未完成的退款申请"
AC-4: Given 退款审批通过, When 执行退款, Then 调用支付网关退款接口并更新订单状态为部分退款

AI读到这个描述,它知道:

  • 用户是谁(买家)
  • 要做什么(部分退款)
  • 为什么(部分商品有问题)
  • 边界情况怎么处理(超额退款、重复退款、审批流程)

这些信息足以生成正确的代码,也足以生成正确的测试。更重要的是——当AI生成partial_refund.py时,它不会再和refund_workflow.py里的全额退款逻辑冲突,因为User Story已经明确了金额校验规则。

但User Story的价值不止于此。它提供了两个关键能力:

全局视野:当你有20个User Story描述整个订单系统时,你可以一眼看到系统的全貌——哪些功能已经定义了,哪些还缺失,哪些之间有依赖关系。这是代码做不到的,因为代码分散在几十个文件里,没有人能一次性读完所有代码并理解它们的关系。

从故事到代码的清晰路径:User Story不是写完就扔在一边的文档,它是开发的起点。工作流变成了:

  1. 写User Story → 2. 从Story生成代码 → 3. 从Story生成测试 → 4. 验证代码和Story一致

这条路径是明确的、可追溯的。每一段代码都能追溯到哪条User Story,每一条User Story都能检查是否有对应的代码实现。

Figure 7 **Figure 2.4 — User Story结构** Figure 8 **Figure 2.5 — User Story的信息覆盖**

User Story作为胶水的定位

User Story不是需求的终点,而是胶水层

  • 向上粘合用户意图(产品需求、业务目标)
  • 向下粘合代码实现(函数、模块、接口)
  • 向外粘合测试用例(Acceptance Criteria → Test Case)
Figure 9 **Figure 2.6 — User Story作为胶水层**

作为胶水层,User Story的核心职责是保持一致性

  • 代码实现是否符合User Story的描述
  • 测试用例是否覆盖了User Story的验收标准
  • 文档是否反映了User Story的意图

这就是为什么我选择User Story而不是API文档或接口契约——User Story是唯一能同时粘合人、代码和测试的抽象层

核心原则:NL是中心,代码是细节

一旦确立了User Story作为胶水层的定位,整个工作流的视角就发生了转变:NL(自然语言)是中心,代码是实现细节

NL不需要"完整",需要"可靠"

传统的文档思维追求"完整性":

  • 每个功能都要有文档
  • 每个细节都要描述
  • 文档要和代码100%同步

但作为胶水层,NL不需要完整:

  • 可以只描述核心功能,忽略边缘情况
  • 可以只描述"应该是什么",不描述"现在是什么"
  • 可以有重复、有冗余、有粗略的描述

唯一的要求是:NL不能错。

Figure 10 **Figure 3.1 — NL质量状态转换** Figure 11 **Figure 3.2 — NL的质量要求**

NL可以重复,不能错误

重复的NL不是问题:

  • 同一个功能在多个User Story中描述,不会造成混乱
  • 重复反而提供了多个视角,增强理解

错误的NL是致命的——这里说的"错误"不是指代码和NL不一致(那是代码的问题),而是NL本身就是错的、自相矛盾的

  • US-05说"退款金额不能超过实付金额",US-08说"退款金额可以包含补偿金,允许超过实付金额"——两条Story自相矛盾,开发者不知道该听哪个
  • US-12说"退款审批只需主管一级",US-15说"所有退款必须经过财务审批"——两条Story对审批流程的描述冲突
  • US-03说"支付成功后立即发货",但业务规则其实是"T+1结算后才能发货"——NL本身就写错了,和真实业务规则不符

这些NL错误比代码bug更危险——因为NL是胶水,是真相源。如果NL本身是错的,那基于它生成的代码、测试、文档全都是错的,而且你很难发现,因为"文档里就是这么写的"。

NL可以粗略,不能模糊

粗略的NL是可以接受的:

  • “订单支持退款”——虽然没有细节,但方向是对的
  • “支付回调要安全”——虽然没有量化,但意图是清楚的

模糊的NL是无法作为胶水的:

  • “系统应该表现良好”——什么是"良好"?无法验证
  • “处理用户的请求”——什么请求?怎么处理?无法实现
  • “更好的体验”——什么是"更好"?无法衡量
Figure 12 **Figure 3.3 — NL质量分类**

从"代码覆盖率"到"NL落地率"

传统的覆盖率思维是:代码被测试覆盖了多少

NL胶水层的覆盖率思维是双向的:

  • NL落地率:NL在代码中落地了多少(NL → Code)
  • 代码NL率:代码被NL覆盖了多少(Code → NL)
Figure 13 **Figure 3.4 — 覆盖率思维的转变**

这两个指标回答不同的问题:

  • NL落地率:你写的需求有多少变成了代码?(未实现的NL是backlog)
  • 代码NL率:你写的代码有多少被需求描述了?(未覆盖的代码是"野生代码",没人知道它为什么存在)

代码NL率低意味着系统中有大量"野生代码"——它们存在,但没有NL解释它们为什么存在。这些代码在重构时最危险,因为没有人知道它们的业务背景,改了可能破坏某些隐含的业务规则。

这个转变的意义:

  • 不再关心"代码有没有被测试覆盖"(那是测试覆盖率的事)
  • 关心"NL描述的功能有没有在代码中实现"(NL落地率)
  • 关心"代码有没有被NL描述"(代码NL率)
  • 未实现的NL是backlog,不是问题
  • 未覆盖的代码是风险,需要补充NL或确认是否可以删除
  • 已实现但与NL矛盾的代码是问题,必须修复

NL覆盖率是这个思维转变的度量工具——它回答的核心问题不是"代码好不好",而是"NL落地了多少、落地得对不对、代码被NL覆盖了多少"。

三个核心指标

基于"NL是中心"的原则,我定义了三个核心指标来度量NL的健康度。

NL实现率:这条NL落地了吗

定义:已实现的NL数量 / 总NL数量(排除模糊的NL)

含义:有多少User Story的Acceptance Criteria在代码中找到了对应的实现。

示例

1
2
3
4
5
6
7
US-05: As a 买家, I want to 查看订单的退款进度

AC-1: Given 订单存在退款单, When 查看退款进度, Then 显示当前审批节点和预计完成时间
  → 代码中有 get_refund_progress() 函数 → ✅ 已实现

AC-2: Given 退款单被驳回, When 查看退款进度, Then 显示驳回原因和可重新申请的提示
  → 代码中没有驳回原因的展示逻辑 → ❌ 未实现

未实现的NL是backlog。在敏捷开发中,backlog指的是"已识别但尚未实现的需求列表"——简单说就是"待办事项"。未实现的NL就是待办事项,让你知道哪些功能还没做。这不是问题,而是正常的开发节奏。

NL准确率:NL和代码一致吗

定义:已实现且一致的NL数量 / 已实现的NL数量

含义:在已实现的NL中,有多少是和代码实际行为一致的。

示例

1
2
3
4
5
US-08: As a 财务, I want to 审批退款申请

AC-1: Given 退款金额≤1000元, When 主管审批通过, Then 自动进入财务审批
  → 代码中阈值写死为 500 元 → ⚠️ 冲突
  → NL说1000元,代码是500元

冲突是最严重的问题:NL作为胶水断了,它会误导所有人。

代码NL率:代码被NL描述了吗

定义:被NL覆盖的代码行为数量 / 总代码行为数量

含义:有多少代码行为有对应的NL描述。与NL实现率(NL→Code)相反,这是Code→NL方向的度量。

示例

1
2
3
4
5
6
7
代码中存在一个 retry_with_backoff() 函数,实现了指数退避重试逻辑
  → 检查所有User Story,没有找到描述"重试机制"或"退避策略"的AC
  → ❌ 未覆盖:这段代码没有NL解释它为什么存在

代码中存在一个 validate_signature_v2() 函数
  → US-03 AC-2 描述了"webhook签名验证使用HMAC-SHA256"
  → ✅ 已覆盖:这段代码有NL解释其业务目的

未覆盖的代码是"野生代码":它们存在,但没有NL解释它们为什么存在。这些代码在重构时最危险,因为没有人知道它们的业务背景,改了可能破坏某些隐含的业务规则。

指标之间的关系

三个指标形成了一个双向度量体系

Figure 14 **Figure 4.1 — NL覆盖率双向度量**
  • NL实现率(NL→Code):你写的需求有多少变成了代码?未实现的NL是backlog
  • NL准确率(NL↔Code一致性):已实现的NL中,有多少和代码行为一致?不一致的是冲突
  • 代码NL率(Code→NL):你写的代码有多少被NL描述了?未覆盖的代码是"野生代码",存在风险

NL覆盖率不是单一数字,而是这个双向度量的完整画像——它告诉你NL落地了多少、落地得对不对、代码被NL覆盖了多少。

为什么不打分

传统的做法是给NL打分:

  • 精确度80分、完整度70分、一致性90分
  • 综合得分80分,“良好”

但打分有问题:

  • 阈值怎么定:80分算好还是70分算好?不同项目标准不一样
  • 难以行动:知道"70分",但不知道具体哪里有问题
  • 容易作弊:为了分数而优化,而不是为了质量而优化

我的做法是直接报问题

  • 不告诉你"这条NL得70分"
  • 告诉你"这条NL和代码有冲突:NL说X,代码做Y"
  • 告诉你"这条NL模糊:‘快速’没有定义"

问题比分数更有行动价值。

三个核心检测

基于三个指标,我设计了三个检测机制来发现问题。

冲突检测:NL说X,代码做Y

目标:发现NL和代码之间的矛盾。

检测方法

  1. 提取NL中的关键值(数字、条件、行为)
  2. 在代码中找到对应的实现
  3. 比较NL描述和代码实际行为
  4. 如果不一致,标记为冲突
Figure 15 **Figure 5.1 — 冲突检测流程**

示例

1
2
3
4
5
6
7
US-08, AC-1: "退款金额≤1000元时,主管审批通过后自动进入财务审批"
  → 代码 refund_workflow.py:67 阈值写死为500元
  → ⚠️ 冲突:NL说1000元,代码是500元

US-03, AC-2: "支付webhook签名验证使用HMAC-SHA256"
  → 代码 payment_gateway.py:34 使用的是MD5
  → ⚠️ 冲突:NL说HMAC-SHA256,代码是MD5

冲突的优先级最高:它直接破坏了NL作为胶水的价值。

模糊检测:粘不住下游的NL

目标:发现无法作为下游输入的NL。

检测方法

  1. 检查NL是否包含具体的值(数字、条件、状态)
  2. 检查NL是否使用了主观语言(“快速”、“良好”、“更好”)
  3. 检查NL是否可以转化为测试用例
  4. 如果无法转化,标记为模糊
Figure 16 **Figure 5.2 — 模糊检测流程**

示例

1
2
3
4
5
6
7
US-12, AC-1: "退款审批流程要安全"
  → "安全"没有定义 → ⚠️ 模糊
  → 建议:改成"需要三级审批,每级审批人不能是申请人"

US-15, AC-1: "支付回调要处理好"
  → 什么算"处理好"?幂等?重试?超时?→ ⚠️ 模糊
  → 建议:具体化幂等策略、重试次数、超时时间

模糊不是错误:但它需要改进,否则无法粘住下游。

Backlog检测:待办事项可视化

目标:发现还没有在代码中落地的NL,形成待办事项清单。

检测方法

  1. 提取NL的Acceptance Criteria
  2. 在代码中搜索对应的实现
  3. 如果找不到,加入backlog
Figure 17 **Figure 5.3 — Backlog检测流程**

示例

1
2
3
4
5
US-09, AC-1: "支持退款到原支付渠道(微信/支付宝/银行卡)"
  → 代码中只实现了退款到余额,没有原路退回逻辑 → 📋 加入backlog

US-11, AC-1: "退款审批支持批量操作(一键通过/驳回多条)"
  → 代码中只有单条审批接口 → 📋 加入backlog

Backlog不是问题:它是待办事项的可视化,让你知道哪些功能还没做。

检测的优先级

三个检测的优先级:

Figure 18 **Figure 5.4 — 检测优先级**
  • 冲突:NL和代码矛盾,胶水断了,必须修复
  • 模糊:NL无法粘住下游,建议改进
  • 未实现:NL还没落地,是backlog,不是问题

实践:从处理代码到处理User Story

理论的最终目的是指导实践。NL胶水层的建立,意味着工作范式的转变。

工作流的转变

传统的工作流:

1
需求 → 代码 → 测试 → 部署

NL胶水层的工作流:

1
需求 → User Story → 代码 → 测试 → 验证NL一致性
Figure 19 **Figure 6.1 — 工作流对比**

关键的变化:

  • 起点变了:从"写代码"变成"写User Story"
  • 终点变了:从"代码跑通"变成"NL和代码一致"
  • 验证变了:从"测试通过"变成"NL落地且准确"

NL覆盖率工具的设计思路

基于上述理论,一个理想的NL覆盖率工具应该具备以下特征:

核心功能

  • 提取所有User Story的Acceptance Criteria
  • 在代码中搜索对应的实现
  • 检测冲突、模糊、未实现
  • 生成NL覆盖率报告

设计原则

  • 不打分:直接报问题,不给分数
  • 可行动:每个问题都有具体的描述和建议
  • 优先级清晰:冲突 > 模糊 > 未实现

输出示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# NL Coverage Report

## 冲突(NL和代码不一致)
| Story | AC | NL描述 | 代码实际行为 | 位置 |
|:---|:---|:---|:---|:---|
| US-08 | AC-1 | 退款金额≤1000元主管审批 | 阈值写死500元 | refund_workflow.py:67 |

## 模糊(无法粘住下游)
| Story | AC | 描述 | 模糊点 |
|:---|:---|:---|:---|
| US-12 | AC-1 | 退款审批流程要安全 | "安全"无定义 |

## 未实现(NL还没落地)
| Story | AC | 描述 |
|:---|:---|:---|
| US-09 | AC-1 | 退款到原支付渠道 |

与现有工具链的集成

NL覆盖率工具不是孤立的工具,它与整个req工具链集成:

与req-refresh集成

  • 在刷新需求文档后,自动运行NL覆盖率分析
  • 发现冲突时,提示用户修复

与req-catalog集成

  • 在生成需求目录时,嵌入NL覆盖率指标
  • 提供全局的NL健康度视图

与req pipeline集成

  • 在justify阶段,检查NL覆盖率
  • 如果冲突数量过多,标记为"NL gap"
Figure 20 **Figure 6.2 — NL覆盖率工具与工具链集成**

实际使用场景

场景一:新功能开发

  1. 写User Story和Acceptance Criteria
  2. 用AI生成代码
  3. 运行NL覆盖率工具检查NL落地情况
  4. 修复冲突,改进模糊的NL
  5. 确认NL和代码一致后,提交
Figure 21 **Figure 6.3 — 新功能开发流程**

场景二:代码重构

  1. 运行NL覆盖率工具检查现有NL覆盖率
  2. 发现冲突:NL说X,代码做Y
  3. 决定:是改NL还是改代码
  4. 重构代码
  5. 再次运行NL覆盖率工具确认一致性
Figure 22 **Figure 6.4 — 代码重构流程**

场景三:需求评审

  1. 运行NL覆盖率工具生成报告
  2. 查看backlog:这是待办事项
  3. 查看模糊的NL:这是需要细化的需求
  4. 查看冲突的NL:这是需要立即修复的问题
  5. 基于报告进行需求评审
Figure 23 **Figure 6.5 — 需求评审流程**

测试的真相:AI写测试的问题

NL胶水层不仅改变了开发流程,也暴露了AI写单元测试的一个根本性错误。

AI写测试的陷阱

当前AI写单元测试的方式是这样的:

  • 读取代码实现
  • 根据代码逻辑生成测试用例
  • 测试100%通过

这看起来很完美,但实际上是循环论证

  • 代码说X,测试验证X → 通过
  • 代码说Y,测试验证Y → 通过
  • 代码错了,测试也跟着错 → 还是通过
Figure 24 **Figure 7.1 — AI写测试的循环论证**

这种测试的价值是零:它只能证明"代码做了代码做的事",不能证明"代码做了应该做的事"。

测试应该从User Story来

正确的测试生成路径:

  • 从User Story的Acceptance Criteria生成测试
  • 测试描述的是"应该是什么",不是"现在是什么"
  • 如果代码错了,测试会失败 → 发现冲突
Figure 25 **Figure 7.2 — 从User Story生成测试**

这个区别是本质性的:

  • 基于代码的测试:验证代码的自洽性(代码和自己一致吗?)
  • 基于NL的测试:验证代码的正确性(代码和需求一致吗?)

一个具体的例子

假设User Story说:

1
2
3
US-08: As a 财务, I want to 审批退款申请

AC-1: Given 退款金额≤1000元, When 主管审批通过, Then 自动进入财务审批

AI基于代码生成的测试(代码中阈值写死为500元):

1
2
3
4
def test_refund_approval_auto_forward():
    refund = create_refund(amount=500)
    approve_by_supervisor(refund)
    assert refund.status == "finance_review"  # ✅ 通过,但验证的是错误的阈值

基于User Story生成的测试

1
2
3
4
def test_refund_approval_auto_forward():
    refund = create_refund(amount=1000)
    approve_by_supervisor(refund)
    assert refund.status == "finance_review"  # ❌ 失败!代码阈值是500,NL说1000

第二种测试才能发现问题。

NL作为测试的真相源

这进一步强化了NL胶水层的定位:

Figure 26 **Figure 7.3 — NL作为测试的真相源**

NL胶水层的完整价值链:

  • NL → 代码:指导实现
  • NL → 测试:生成验证标准
  • 测试 → 代码:验证一致性
  • 冲突检测:发现NL和代码的矛盾

当测试从NL来,而不是从代码来,测试才真正有了价值——它验证的是"代码做了应该做的事",而不是"代码做了代码做的事"。

更大的图景:NL作为万能胶水层

当我们把NL胶水层的思维从"代码和代码之间"推广出去,会发现一个更大的图景:AI本身就是胶水层,而NL是它的粘合剂

代码与代码之间的胶水

这是最直接的层面,也是本文前面讨论的核心:

  • 模块A和模块B之间的依赖关系,用NL描述比用代码注释更清晰
  • 函数之间的契约,用User Story表达比用类型签名更完整
  • 重构时的意图保持,用NL锚定比用代码推断更可靠

AI在这里的角色是翻译器:把NL翻译成代码,把代码翻译成测试,把测试翻译成报告。NL是粘合剂,AI是执行者。

程序与程序之间的胶水

当你有多个系统需要集成时,NL的价值更加明显:

  • 系统A的API说"返回用户信息",系统B期望"返回客户档案"——这是同一个东西吗?
  • 微服务之间的数据流,用代码定义只能看到格式,用NL定义能看到语义
  • 第三方集成时,对方文档说"支持批量操作"——批量是多少?100?10000?
Figure 27 **Figure 8.1 — NL作为系统间胶水**

传统的做法是用IDL(Interface Definition Language)或OpenAPI Spec来定义接口。这些是精确的,但缺乏语义。NL补充了语义层——不仅定义"格式是什么",还定义"这意味着什么"。

人与人之间的胶水

这是最容易被忽视的层面。在软件开发中,人与人之间的沟通成本往往高于代码编写成本:

  • 产品经理说"用户体验要好",开发理解为"响应要快",测试理解为"不能有bug"
  • 前端说"这个组件要可复用",后端理解为"要抽象",设计理解为"要一致"
  • 新成员问"这个功能为什么这么做",老成员说"当时就这么定的"

NL作为胶水层的价值:

  • 消除歧义:把"好"定义成"< 200ms响应时间"
  • 对齐理解:把"可复用"定义成"支持3种以上场景"
  • 传递上下文:把"为什么"写进User Story的value子句
Figure 28 **Figure 8.2 — NL作为团队间胶水**

AI在这里的角色是对齐器:把不同角色的NL表述统一成同一个真相源,把模糊的共识转化为精确的验收标准。

团队与团队之间的胶水

当组织规模扩大,团队之间的协作成为瓶颈:

  • 平台团队说"我们提供了用户服务",业务团队说"我需要的是客户管理"
  • 数据团队说"数据仓库已就绪",分析团队说"我找不到我需要的指标"
  • 安全团队说"符合合规要求",产品团队说"用户注册流程太复杂了"

传统的解决方案是架构评审会跨团队文档API网关。这些都是有效的,但它们有一个共同的问题:更新成本高,容易过时

NL胶水层的优势:

  • 低成本更新:改一条User Story比改一份架构文档快10倍
  • 高保真传递:NL比代码更容易被非技术人员理解
  • 可验证性:NL可以通过AI自动检查一致性
Figure 29 **Figure 8.3 — NL作为跨团队胶水**

AI作为胶水层的本质

回到最根本的问题:AI在软件开发中的角色是什么?

传统观点:AI是加速器——让代码写得更快。

我的观点:AI是胶水层——让不同的事物粘合在一起。

Figure 30 **Figure 8.4 — AI作为胶水层的全景**

而NL(自然语言)是这个胶水层的粘合剂

  • 没有NL,AI只是快速生成代码的工具
  • 有了NL,AI成为连接意图、代码、测试、团队的枢纽

这就是为什么NL覆盖率如此重要——它度量的不是"代码写得好不好",而是**“胶水粘得牢不牢”**。

当NL覆盖率高且准确时:

  • 代码改动有迹可循
  • 测试失败有因可查
  • 团队协作有据可依
  • 系统演化有向可追

当NL覆盖率低或不准确时:

  • 代码成为黑盒
  • 测试成为装饰
  • 团队协作靠猜
  • 系统演化靠运气

总结:可靠的胶水

Vibe-Coding让代码生成变得极其快速,但这种速度需要被约束引导

Figure 31 **Figure 9.1 — NL胶水层全景**

NL胶水层的建立,本质上是工作范式的转变

  • 从"处理代码"变成"处理User Story"
  • 从"代码覆盖率"变成"NL落地率"
  • 从"测试通过"变成"NL和代码一致"

这个转变的意义:

  • 代码是细节:AI可以快速生成,但NL是真相源
  • NL是中心:它粘合了用户意图、代码实现、测试用例
  • 一致性是目标:NL和代码必须一致,否则胶水就断了

三个核心指标(实现率、准确率、代码NL率)和三个核心检测(冲突、模糊、未实现)提供了度量和改进的框架。

最终的目标不是"完美的文档",而是可靠的胶水——让NL能够有效地粘合上下游,让Vibe-Coding在速度的同时保持可控。