首页 > 资讯 > 什么时候值得采用规范驱动开发

什么时候值得采用规范驱动开发

InfoQ 2026-09-21 12:04 5 阅读 查看原文

验证成了新的瓶颈

AI 编程助手已经不是什么新鲜事,而是逐渐变成了软件开发的基础设施。在 2026 年的今天,大多数工程团队每周都在用编程助手。AI 生成的代码开始组成了越来越多的生产环境代码,并且覆盖了全部的软件开发生命周期。但问题也随之而来:AI 让代码产出越来越多,但没能让人们更有把握地确认这些代码是否正确、安全,以及是否真正符合最初的意图。 一些受控研究显示,AI 确实带来了实际的生产力提升,但在真实生产环境中也出现了开发速度减缓和质量问题。与此同时,越来越多的实证研究发现,进入生产环境的 AI 生成代码中,依然存在安全漏洞、常见的 Bug 类型,以及一些不易察觉的行为偏离。

于是,瓶颈变了。过去瓶颈是在“写代码”,现在瓶颈成了“验证代码”。不过这也让真正棘手的问题从“模型能力够不够”变成了治理问题:谁应该对 AI 生成代码的行为负责?如何发现代码已经偏离了原本的意图?人和模型又该如何分担监督这项工作?

这个问题在 2026 年第二季度已经不再只是纸上谈兵,因为相关要求正在陆续落地。EU AI Act 针对高风险 AI 提出了风险管理、记录保存以及有效人工监督等要求。ISO/IEC 42001 要求建立有文档记录的 AI 管理体系,并配套控制措施和审计轨迹。NIST AI 风险管理框架则将类似要求归纳为 Govern、Map、Measure 和 Manage 四个部分。如果越来越多的生产代码由 AI 生成,那么仅仅说“我们会认真审核 AI 的输出”,其实很难说明风险管理、记录保存和人工监督这些控制措施究竟是怎么落实的。

目前比较常见的一种做法,是不仅审查代码,也审查规范。也就是说,把规范当成 AI 必须遵守的契约,在生成代码之前先进行审查,再要求生成的代码符合这份规范。我认同这种思路,但我也想知道,它到底能不能经得起实际测量。于是,我围绕其中最核心的一个环节做了一项研究:由人来对照一份经过批准的基线,审查 AI 生成的代码。

研究结果让我重新思考了自己该如何论证这整套方法,这也是我写下这篇文章的原因。规范基线并没有让评审人员发现更多 Bug。它真正改变的是:评审人员发现的问题,有了明确的依据,也因此能够追溯和问责。搞清楚这两者之间的区别很重要,因为只有这样,你才能判断:使用 AI 所付出的这些额外成本,到底什么时候值得。下面的结论来自我参与的一项研究,该研究已经被 GAISS 2026 收录 。需要说明的是,这些结果仍属于初步、方向性的发现,样本量较小,涉及的任务也不多。后文我会逐一说明这些结论的局限性。

将规范视作治理的基线

规范(Specification)是在开始构建软件之前,对软件应该做什么形成的书面、共同认可的描述,其中包括需求、接口以及可测试的行为。在这项研究中,一份规范分为三层:规范本身,也就是业务需求和规则;高层设计(HLD),用于定义组件及其接口;以及低层设计(LLD),用于明确每个方法必须满足的具体不变量。举个例子,一个资金转账服务的规范会制定这样一条业务规则:“转账绝不能让账户余额变成负数。”

其中,规范的 HLD 会定义一个 TransferService 接口,其中包含 transfer 操作,也就是指定转出账户、转入账户和金额。LLD 则进一步把这条要求变成可以测试的不变量:如果转账金额超过可用余额,就必须拒绝转账,同时不能产生任何账本记录。之后审查 AI 生成的代码时,依据的就是这些具体规则。

为了让这个过程更直观,我们来看研究中实际使用的一条基线,它贯穿上述三个层次。

在规范层,一条业务需求规定:转账必须以原子方式在账户之间移动资金;同一个幂等 Key 最多只能执行一次;并且绝不能凭空产生或销毁资金。

到了 HLD 层,这些要求被转化为一个契约:Bank 组件提供一个转账操作,包含 from_idto_idamountidempotency_key;转账、验证和有序审计历史则被定义为相互独立的子系统。

在 LLD 层,同样的要求进一步落实成可测试的方法契约:查询两个账户,账户不存在则抛出异常;如果幂等 Key 已经执行过,则直接返回此前的结果,不得再次转移资金;接下来检查余额是否充足,如果不足,则终止操作,并且不能改变任何一个账户的状态;然后将源账户扣款和目标账户入账作为一个整体执行,同时分别追加历史记录。

这些要求都对应着一个有名字的不变量,代码审查时可以直接引用,例如 transfer_idempotenttransfer_atomic_on_failtransfer_moves_fundsassets_conserved。完整的基线在核心资金操作、转账、利息、每日限额、账单和账户生命周期等方面,共定义了 20 个这样的不变量。这份编号后的不变量列表,就是代码审查用来检查生成代码是否发生偏离的契约。

在展示研究结果之前,我们先看看这套测试中的治理模型。核心思路是:不要把规范当作是提示词的装饰品,而要把它当成治理工件——一份经过审查、批准并版本化的契约,所有控制点和责任归属都应以它为依据。

它建立在三个原则之上:在输出之前先治理输入,因为审查规范的成本低于修改已经生成的代码;让基线明确且可审计,把经过批准基线、高层设计和低层设计作为一个统一的参考基线;以及让人在关键判断环节参与,而不是让人承担大量机械检查工作。自动化负责发现偏离,再由专人来判断什么才是正确的。

这种方法会形成五个生命周期控制点,每个控制点都会产生一个工件,供下一环节继续使用,如表 1 所示。

表 1:规范治理的五个生命周期控制点,每个控制点都会产生供下一环节使用的工件。

关键不只是存在这五个控制点,而是每个控制点都会留下具体记录。已批准的基线记录了人类认可的系统应该如何运行;代码生成记录将代码与特定版本的基线关联起来;偏离日志记录实现在哪些地方偏离了基线;核查记录则记录人类如何处理这些偏离。这些工件组合在一起,就把“我们审查过 AI 的输出”变成了一个真正可以追溯、解释和审计的流程。

如图 1 所示,最终形成的架构不是单向的 Prompt,而是一个迭代循环:先编写并批准基线,模型根据特定版本生成代码,再针对同一版本检测偏离,由人来处理每一项有意义的偏离。如果设计发生变化,就回到评审关卡;如果发现代码缺陷,则回到生成或实现环节。整个过程产生的记录共同构成审计轨迹。

图 1:面向 AI 生成代码的规范驱动治理循环(来源:作者制作)。

这套设计的意义在于,治理体系通常要求的审计轨迹,并不需要额外“外挂”一套流程,而是自然地从整个循环中产生。每一项 AI 生成的行为,都可以追溯到一条经过批准的需求,以及一个承担责任的人类决策。

这里也明确了责任归属:按照 RACI 模型,模型对应代码生成承担 Responsible(负责执行)的角色,但永远不会承担 Accountable(最终负责)的角色。RACI 的另外两个角色是 Consulted(被咨询)和 Informed(知会)。最终责任始终在人类手中。

这就是“有效的人类监督”在实际操作中的含义,也是概念性的治理框架通常没有明确说明的部分。

偏离审查的研究:召回率与归因

这套方法的核心,是让人来检查 AI 生成的代码有没有偏离已经确定的设计。因此,我针对这一环节开展了一项受控的组内研究。

我从两个维度评估这项审查工作:召回率,也就是评审人员成功发现了代码中多少实际存在的偏离;以及归因,也就是评审人员能否把每一项发现追溯到某条已经批准的具体要求或明确的不变量,而不是只说“这里看起来不对”。

研究对象是一套支持多账户的银行服务。它的规模被刻意控制得比较小,但属于一个真实受到监管约束的金融领域,因此正确性、输入校验、支出限额、利息计算、账户对账单以及完整的审计记录都非常重要。系统提供了一组真实核心银行系统都会有的 API,包括 open_accountdepositwithdrawbalancetransferhistoryset_daily_withdraw_limitaccrue_intereststatementtotal_assetsclose_account

研究中分别生成了两套独立实现,即服务 A 和服务 B,以避免研究结果受某一个代码库本身的影响。之所以选择这个领域,是因为它集中体现了 AI 生成代码容易出问题的那些特性:资金必须守恒,转账必须具备原子性和幂等性,透支和每日限额必须得到控制,每一次余额变化都必须能够审计。对于这种约束众多、风险又高的业务逻辑来说,代码即使看起来合理,只要某个细节出了问题,也可能造成实际损失。

5 名评审人员参与了研究,每个人都有 3~10 年的工作经验。他们分别对真实的 AI 生成银行服务进行了两次独立评审,采用的是平衡交叉的 2×2 实验设计。

在“基线”条件下,评审人员会拿到经过批准的规范、高层设计(HLD)和低层设计(LLD),然后根据这些内容检查代码。在“仅代码”条件下,他们只能看到公开 API,需要自行判断代码是否正确。这正是目前 AI 代码评审最常见的方式:没有一份经过批准的契约,人直接对 AI 生成的代码做判断。

两套服务都会在两种条件下出现,因此不同评审人员以及不同服务本身造成的影响可以相互抵消。评审人员独立完成工作,不运行代码,不使用 AI 助手,也不能上网查询。经过裁定后确定的实际偏离数量是:一套服务有 11 个,另一套有 10 个。

研究结果是通过检查源代码确定的,因为单靠一套测试用例,会漏掉一些评审人员能够正确发现的真实缺陷。参与评审的银行服务分别使用 JavaPython 实现,测试则使用 JUnitMockito,这些测试用于帮助确定实际存在的缺陷。

代码生成和自动化评审复现实验使用了多种前沿模型,包括 Anthropic Claude Opus 4.8OpenAI GPT-5.2DeepSeek V4Google Gemini 3.1。规范、高层设计和低层设计则作为一份统一的、带版本管理的 Markdown 基线编写。统计分析使用的 Wilcoxon 符号秩检验、Mann-Whitney U 检验和 Fleiss' kappa,也全部使用 Python 标准库实现。

再补充一些实验过程的细节。研究人员通过逐个检查源代码,将每套服务与包含 20 个不变量的基线进行比对,从而确定实际缺陷。服务 A 有 11 个真实偏离,服务 B 有 10 个。之所以采用源代码检查,是因为单独运行一套可执行测试时,会漏掉一些评审人员能够正确发现的缺陷。

每份评审报告都会从两个维度进行评分:召回率,即评审人员报告出的不同实际偏离数量,除以该服务经过裁定确认的实际偏离总数;以及归因,即每项发现是否能够对应到一个明确命名的不变量,而不是笼统地说“这里好像有问题”。

评审人员之间的一致性使用 Fleiss' kappa,根据每个偏离“是否被发现”的结果进行计算。由于每位评审人员都分别完成了一次基线评审和一次仅代码评审,因此两种条件之间的配对比较采用 Wilcoxon 符号秩检验。

自动化复现实验沿用了完全相同的 2×2 设计和同一套经过裁定的实际结果,让大语言模型代替评审人员完成 90 次自动评审,并使用相同的召回率和归因标准进行评分,再通过 Mann-Whitney U 检验进行比较。

代码生成方面的实验则是另一组独立实验,用来研究不同交付方式以及思维链带来的结果差异。实验比较了几种方式:直接生成代码;先进行推理、不写规范;在同一个 Prompt 中先要求写规范、再写代码;以及分阶段进行——先单独生成 SPEC.md,再在新的生成步骤中根据它实现代码。测试任务从单函数任务一直到完整的、包含 20 个不变量的银行服务,同时使用能力较强和刻意削弱的模型,以便将规范本身的影响与模型能力的影响区分开来。

下面就是最值得关注的结果。正如表 2 所示,这并不是单纯的“赢了”,而是一种取舍。

表 2:基线与仅代码条件下的偏离审查结果(5 名评审人员,组内实验;配对 Wilcoxon 检验)

召回率并没有提高

无论有没有基线,评审人员发现的偏离数量在统计上都没有明显差别(0.525 对 0.518,p=0.69)。这一点值得直接说清楚,因为人们通常认为,规范驱动的治理方法最大的价值就是让评审人员更擅长发现 Bug,而在这项任务中,它并没有做到这一点。

真正产生变化的是归因

这里所说的“归因”,是指评审人员能否把发现的问题对应到某一条明确被违反的要求或某个已经命名的不变量,而不是笼统地说“这里看起来有问题”。在有基线的情况下,81% 的问题都能追溯到具体条款。而在仅看代码的情况下,由于没有一份可以引用的契约,归因率是 0%(p=0.043)。评审人员发现的是同一批缺陷,但只有在存在基线时,这些问题才真正有了明确的依据,可以指出它违反了哪一项已经批准的要求。仅看代码的评审人员反复会说:“无法判断这种行为是不是原本就有意设计成这样的。”这句话其实把整个治理上的缺口都说清楚了。

对于负责监督和治理的人来说,召回率没有变化本身就是一个应该如实接受的结果,而归因能力的提升才是这项方法真正带来的价值。规范基线并没有让评审人员发现更多 Bug。它改变的是偏离审查本身:审查有了明确的契约依据,发现的问题能够追溯到具体要求,评审人员也更有把握,但代价是实实在在的时间投入。而这恰恰是治理机制应该带来的东西——不是发现更多问题,而是让发现的问题能够由明确的责任人对应到已经批准的要求,有依据地说明、处理,并交接给下一环节。

模型来做评审时,这套方法还成立吗?90 次评审的复现实验

5 名人工评审人员虽然能提供一定的信号,但样本确实很小。为了确认这一模式是否具有普遍性,而不是仅仅因为这 5 个人恰好如此,我让大语言模型代替评审人员,按照完全相同的 2×2 实验设计进行了复现:使用 3 个能力较强的助手,共完成 90 次机器评审,并采用相同的、经过裁定确认的实际结果,以及相同的归因定义进行评分。

这是在另一类对象上的一次趋同复现,而不是为了增加人工评审结论的统计效力。这里测量的是自动化评审人员,而不是人类评审人员,两者不能混为一谈。它所能说明的是,同样的模式再次出现了,如表 3 所示。

表 3:自动化评审复现实验(3 个模型,90 次机器评审;Mann-Whitney U 检验)

两种条件下的召回率没有变化。归因率则从有基线时的 0.67 降到了没有基线时的 0.00,而且这一结果在 3 个模型中都一致。这个 0.00 是结构性结果,而不是评分方式造成的。没有经过批准的契约,就没有可以引用的具体要求;无论是人还是模型,都是如此。和人类评审人员一样,LLM 评审人员也需要一份经过批准的规范,才能让发现的问题有明确的归属。

这一点也说明了目前越来越常见的 LLM 代码评审工具应该处在什么位置:它们很适合作为整个评审流程中的前置筛查环节,但如果没有经过治理的基线,它们即使能够标出问题,也无法把这些问题对应到具体要求上,更无法形成可追溯的责任链。

规范如何交付,比有没有规范更重要

如果说代码评审部分的结果表明,基线能够让问题有据可查,那么代码生成部分的发现同样有些反直觉:规范以什么方式交付,比有没有规范更重要。

在一个包含 20 项不变量的银行服务任务中,一组实验要求模型在同一个 Prompt 中“先写规范,再写代码”。结果与直接要求模型写代码没有明显区别:在较弱的模型上,两种方式的通过率都只有 23.8%。另一组实验则采用分阶段的方式:先编写规范,再在一个全新的生成步骤中依据规范实现代码。这种方式将通过率提高到近乎原来的两倍,达到 45%,同时也将构建失败、无法完成编译的次数减少了一半。

两种方式使用的指令内容相同,区别只在于交付方式。只有将规范作为整个开发过程中的治理依据,而不是直接塞进 Prompt 的一段文本,才真正改变了最终结果。(不过,在样本量仅为 4、p≈0.18 的情况下,这一趋势仍受到下文所述“第二轮尝试”这一混杂因素的影响。)

接下来是很多相关研究都会忽略的一点:当“先写规范”在简单任务上看起来效果惊人时,我们必须追问,究竟是规范发挥了作用,还是规范促使模型进行了更多推理?

为此,我增加了一组对照实验:先让模型推理边界情况,但不要求它编写任何规范。在两种能力较强的助手上,我针对 10 类单函数任务进行了测试。结果显示,思维链实验组几乎解释了“先写规范”所带来的全部表面收益。例如,直接写代码的通过率为 59%;先推理再写代码,达到 95%;先写规范再写代码,则为 92%。而“先写规范”与“先推理”之间的差异并不显著。这一结论虽然削弱了“先写规范”带来的效果,却同样重要:对于简单任务,人们归因于“先写规范”的显著收益,很大程度上其实是推理带来的效果。如果要声称通过规范编写 Prompt 能够提升代码质量,就必须先控制推理这一因素。

成本与证据的局限

以上这些做法都有成本。把成本说清楚,才是在做治理,而不是流于形式、照搬流程。

最直接的成本是时间。以规范为依据的评审平均需要约 48 分钟,而仅审查代码只需约 27 分钟。这意味着,每一份生成结果都会带来额外的人工成本,而且评审人员需要逐项对照代码检查的不变量越多,成本就越高。不过,这项成本是可以预估的,而不是没有上限的。它也是整个流程中最容易实现自动化的部分。显而易见的下一步,是让 LLM 先行筛查,标出可能偏离规范的地方,并将其送回重新生成,之后再由人工评审。目前,这部分工作仍然是一笔实实在在的额外开销。

此外,还需要在前期投入精力编写规范、高层设计和低层设计,并随着系统演进,持续确保这些内容与实际系统保持同步。这种持续维护的要求,是这套方法落地的主要障碍。只有偏离检测实现自动化后,这一负担才会有所减轻。

现有证据本身也有明确的局限。这是一项有意控制规模的初步研究,仅涉及 5 名评审人员、两套服务和一个业务领域。样本量为 5 时,配对符号秩检验的 P 值最低只能达到 0.043。因此,所谓“显著”结果,只能说明不同评审人员的结果呈现出一致的方向,不能据此校准效应大小。应当把这些结果视为初步研究中较强的信号,而不是确证性证据。

信心提升的结果也只体现出一个趋势。分阶段生成的优势则受到“第二轮尝试”这一混杂因素的影响:效果更好的实验组让模型运行了两次,因此其中一部分收益可能来自额外的生成轮次,而非规范本身。

自动化复现实验在另一类对象上提供了趋同的支持,但不能替代人工监督。此外,它也可能继承模型本身的偏差。我之所以报告这些信息,是因为只有坦诚呈现证据的局限,才能让人有机会据此质疑和检验研究结论。治理工作如果连自身的不确定性都刻意隐瞒,就谈不上真正的治理。

规范治理应该用在什么地方?

把前面的结果放在一起看,得出的结论并不是“任何时候都要先写规范”,而是应该有针对性地决定何时投入这份精力。

规范治理在一种特定场景下最有价值:由能力较强、但仍不够完美的模型来完成复杂且约束众多的任务。 在复杂的银行服务任务中,较弱的模型仍有较大的提升空间,规范约束使其通过率提高了约 21 个百分点。而已经顺利完成任务的强模型,通过规范约束只提高了约 2 个百分点。对于简单任务,表面上的收益则主要来自额外推理。

因此,规范治理应优先用于金融、保险、医疗等受监管、风险高且生命周期长的系统。这类系统需要同时满足大量约束,确保原始意图在长期演进中不被改变,而且通常已经需要按照标准或监管要求留存审计记录。反过来,对于一次性脚本、原型,以及助手能够稳定地一次完成的任务,就没必要采用这套方法。

采用规范治理时,真正让它落地的是明确的责任分工。可以用 RACI 明确各方角色:编写规范的作者、负责审批规范的评审人员、负责生成代码的模型,以及负责处理每一项偏离的协调人员。即使模型负责生成(Responsible),也必须始终由人承担最终责任(Accountable)。这些过程产物可以作为治理、可追溯性、记录留存和人工监督的辅助证据,而这些正是 NIST AI RMFISO/IEC 42001EU AI Act 所强调的控制要求。

表 4 给出了一条分阶段落地的路径,以便控制成本:

表 4:规范治理的分阶段落地路径,将精力集中投入到针对性规则所指明的高价值场景

结论

AI 让代码变得唾手可得,也让治理取代代码编写本身成为新的瓶颈。人们很容易产生一种直觉:强化规范管理,是为了发现更多 Bug。但测量结果并非如此:经过批准的基线并没有提高召回率,却让偏差评审中可归因于基线的部分从 0% 提升到了 81%,同时也带来了真实存在、如今已经可以量化的时间成本。交付比“是否存在”更重要,而简单任务上的收益主要来自推理。至于这套方法的局限,也值得坦率地讲清楚。

因此,应当在输出之前先治理输入,让代码接受经过批准的基线约束,并明确由谁负责决定每一次有意义的偏差该如何处理。然后,根据目标规则,把治理投入到真正值得投入的地方:那些困难、高风险、生命周期长,并且由能力很强但并不完美的模型构建的系统,而不是把这种形式主义式的治理套在其他任何地方。让代码变得充足。只是,不要让计划变得可有可无。也不要假装计划不需要付出成本。

查看英文原文:Article: When Spec-Driven Development Pays Off