文章

怎样写出 AI 代理能够验证的验收标准

验收标准(AC,acceptance criteria)是一项变更要算作完成之前必须为真的条件。Given/When/Then 是 Gherkin 里的步骤形态。Cucumber 用这种语言做行为驱动开发(BDD,behaviour-driven development):Given 是起始状态,When 是发生一个动作,Then 是随后出现的可观察结果。产品需求文档(PRD,product requirements document)是一段散文,点出问题和打算做的产品。规范驱动开发(SDD,spec-driven development)把结构化的规格,而不只是那段散文,当作核对实现的来源。EARS(Easy Approach to Requirements Syntax)用来约束一条需求句子,让其中的子句保持固定顺序。持续集成(CI,continuous integration)是对每一次提议的变更所做的自动检查;本站在 验证器页面 上说明 SpecScore 的两项检查。本页说明如何写出人工智能(AI)编程代理能够验证的验收标准,并给出验收标准示例,以及 SpecScore 命令行界面(CLI,command-line interface)实际检查什么。

更新于 2026年11月3日。

验收标准是什么

一条验收标准回答的是“必须满足什么”。它点出一个审阅者、一项测试或一个代理能够接受或拒绝的条件。它不点出要调用的函数,也不点出要改的文件。那些选择属于实现。

需求是规则。在 SpecScore 的 需求规格 里,需求是功能中的一个 #### REQ: 标题:一项义务,自带标识符。验收标准把相关需求收成一个条件。场景是一个具体案例,用来表明该条件成立。验收标准规格 是这样说的:标准回答“必须满足什么”,场景回答“如何证明”。

PRD 可以把同样的意图写进段落。段落帮得上人。代理仍然得猜测哪一句才是通过或失败的检验。把标准写成独立的一句,猜测就没有了。

是什么让验收标准可被验证

AI 编程代理能够照着一条标准做,前提是后来的读者能指出证据,并说出通过或失败。五种习惯让这件事成为可能。没有一种是句子模板。它们是可以套到任何模板上的检查,包括 Given/When/Then 和一份清单。

可观察

结果是局外人能看见的东西:页面上的一条消息、一条存下来或没存下来的记录、一次重定向、一份被拒绝的表单。“系统理解购物者”不可观察。“页面在邮箱字段旁边显示 Enter a valid email address”可以观察。

具体

点出行为者、输入和结果。“坏输入会被处理”没有说是哪一种输入、哪一种处理。“购物者提交邮箱 not-an-email”说了。具体并不是把每一个生产环境的值都贴上来。它是说,读者可以自己准备一份测试数据,并且知道应该发生什么。

一个行为

一条标准,一个行为。“购物者可以登录、重置密码,并看到锁定消息”是一句话里的三条标准。代理实现了第一条、跳过第三条,仍然可以说这句话做完了。把它们拆开。

通过或失败

可验证的验收标准有两种结局。“表单应该感觉很快”是程度,不是一个结果。如果需要一条界限,就把界限写出来:“结账页显示错误,并且不再向服务器发出进一步的请求。”如果说不出失败长什么样,这句话还不是一条标准。

去掉含糊的词

去掉把结果藏起来的词:优雅、直观、健壮、用户友好、无缝、快、应该、适当。把每一个换成你愿意接受的证据。“优雅地处理错误”变成“在字段旁边显示 Enter a valid email address,并且不要下单”。

Given/When/Then 和一份清单

团队真正留得住的验收标准示例,大多落在两种形态里。Given/When/Then 是一个场景。清单是一串结果。状态、动作、结果的顺序要紧时,用场景。要列出同一个动作各自独立的结果时,用清单。

Cucumber 的 Gherkin 参考 给步骤配上关键字 Given、When、Then、And 和 But。这份参考用英文;Gherkin 也有这些关键字的译文。关键字后面的文字,会在 Cucumber 运行文件时对上称为步骤定义(step definition)的代码。SpecScore 不用那一层。下面的例子是 Gherkin,好让这种形态看起来眼熟。它不是 SpecScore 文件。

Given/When/Then
Scenario: Reject an email that is not a valid address
  Given the shopper is on the checkout form
  When the shopper submits the email "not-an-email"
  Then the page shows "Enter a valid email address" beside the email field
  And the order is not placed
清单
- Submitting the email "not-an-email" shows "Enter a valid email address" beside the email field.
- The order is not placed.

When 只留一个。第二个动作就是第二个场景。And 延续同一个关键字:另一个 Given,或另一个 Then。不要用 And 夹带第二个行为。

清单是同一份约定,只是没有关键字。每一行仍然是一个可观察的结果。上面的清单就是场景的 Then,写成一项项检查。

这和 EARS 需求的关系

这些句式写在EARS 需求语法详解与示例。

Alistair Mavin 的指南 把 EARS(Easy Approach to Requirements Syntax)定义为一种温和地约束文字需求的机制。子句保持同一个顺序。指南给出的通用形态是:While <optional pre-condition>, when <optional trigger>, the <system name> shall <system response>。一条需求有零个或多个前置条件、零个或一个触发、一个系统名,以及一个或多个系统响应。

那句话是义务:系统应当满足的规则。验收标准是这项义务要算作已满足时必须为真的条件。一个 Given/When/Then 场景是这个条件的一个具体案例。需求可以写成 EARS 句子,标准仍然可以不含示例输入。

在 SpecScore 的功能里,需求可以是 #### REQ: 标题下的一句 EARS 风格的话。标准保持为抽象条件。场景带着示例。EARS 并不取代这两者。它只约束需求那一句。

验收标准示例:含糊的和可验证的

点出证据

含糊

The checkout form should handle bad input gracefully and feel fast.

可验证

When the shopper submits the email "not-an-email", the checkout page shows "Enter a valid email address" beside that field and does not place the order.

含糊的那一行藏着两项要求(错误和速度),也没有证据。可验证的那一行有一个动作,以及人或代理都能核对的两个结果。示例保持英文,因为格式关键字是英文。

一个行为

含糊

Users can sign in, reset a password, and see an error if the account is locked.

可验证

When a visitor submits a known email and the matching password, the app sets a session cookie and redirects to /dashboard.

登录、重置和锁定是三个行为。可验证的那一行只留下登录。重置和锁定各自得到自己的标准,带着自己的输入和结果。

写结果,不写函数名

含糊

The handler returns 400 from validateEmail().

可验证

When the shopper submits an email that is not a valid address, the checkout page shows "Enter a valid email address" and does not place the order.

函数名和状态码把标准绑在今天的代码上。检查挪到另一个函数,结果仍然成立。当状态码本身就是你承诺的行为时,把它留下来,并且写成可观察的结果。

SpecScore 如何保存它们,以及 CI 检查什么

SpecScore 在 specscore/specscore 仓库发布格式,在 specscore/specscore-cli 发布 CLI。两份文档描述验收标准时,形态并不完全一样。两份都是现行的。这一节先说明哪一份是哪一种,然后说明 specscore spec lint 实际拒绝什么。

这份验收标准规格,也就是该 URL 上提供的功能 README,把标准写在功能 README 里面。每个功能都有一节 ## Acceptance Criteria。每条标准是一个 ### AC: {slug} 标题。slug 使用小写,并用连字符连接。下一行是 **Requirements:**,按下面的形式列出它所收拢的需求:{feature-id}#req:{slug}。后面是散文,也就是合成后的条件。规格说,这段散文保持抽象。它不得包含 Given/When/Then 步骤、示例输入或实现细节。还没有标准时,这一节读作 “Not defined yet.”,Open Questions 一节写 “Acceptance criteria not yet defined for this feature.”。标准是可选的分组:规格说,收拢两条或更多需求时加一条,若只会复述单条需求就省掉。

具体的证明是功能的 _tests/ 目录下的场景文件。场景规格 要求有一个 # Scenario: 标题、一节 ## Steps、大写的 GIVEN、WHEN、THEN 和 AND,并且恰好一个 WHEN。可选的 **Validates:** 行把场景连到 {feature-path}#ac:{slug},或连到一条需求。## Rehearse 一节可以在 rehearse 围栏里放一段可执行脚本,脚本以 shebang 开头。

CLI 也读取精简的标准文件,每条标准一个,位于 _acs/<slug>.ac.md。登录示例 和 Rehearse 指南 记录了这种形态:# AC: <slug>,可选的 **Status:**、**Verifies:** 和 **Applies-to:**(这三项都可选),以及 ## Statement 下面的条件。specscore rehearse acs <feature-dir> 根据这些文件打印一张摘要表。specscore rehearse acs --write 用这张表,外加一条说明该节由生成而来、不要手改的注释,替换功能 README 里的 Acceptance Criteria 一节。没有 # AC: 标题的文件,会被该命令报为错误。

本仓库的 CI 是 dogfood 工作流。它安装 CLI v0.39.1,并在拉取请求上、以及向 main 的推送上运行 specscore spec lint。2026年11月3日核对过,该发行版和 CLI 的默认分支都包含下面这些规则。检查器查的是结构。它不给英文打分。

规则 ac-heading-format 是一条错误。在功能 README(标题为 # Feature: 的文件)里,看起来像验收标准标题的一行必须恰好是 ### AC: <id>,冒号后一个空格,id 为 kebab-case。形如 ### AC: <id> (verifies ...) 的标题也被接受。--fix 只修复空白。带大写字母或下划线的 id 会被报告,并留给人处理。这条规则不要求有 **Requirements:** 行,不要求 “Not defined yet.” 占位,散文里出现 Given、When 或 Then 时也不警告。规格里为那条警告所写的场景,并不是这支 CLI 里的 lint 规则。

当一份计划点名了来源功能时,检查器会读取该功能 Acceptance Criteria 一节里的 ### AC: 标题。规则 P-002 报告任务的 **Verifies:** 值不是这些标题之一所对应的 <feature-slug>#ac:<ac-slug>。规则 P-001 报告没有任何任务覆盖、并且没有列在 ## Deferred AC Coverage 下面的标题。这两条规则不打开 _acs/*.ac.md。rehearse acs --write 产生的摘要表没有 ### AC: 标题,所以计划规则看不到那些精简文件。

同一条 lint 命令不检查场景步骤的关键字。specscore rehearse 运行场景文件:Rehearse 指南在围栏命令上方使用 ## Given 或 ## When 这样的标题,并用 **Use:** 调用可复用的检查。一个文件里有一个 Given 和若干 When 分支时,会作为各自独立的案例运行。specscore rehearse new <feature-slug>#ac:<ac-slug> 读取功能 README,并把该标准里的 Given/When/Then 行抽到 spec/features/<feature-slug>/_tests/<ac-slug>.md。这些都不在 dogfood 工作流里运行。工作流运行的是 specscore spec lint。

内联标准,来自验收标准规格
## Acceptance Criteria

### AC: invalid-email-rejected

**Requirements:** checkout#req:reject-invalid-email

An email that is not a valid address is rejected, and the order is not placed.
精简标准文件,rehearse acs 读取的形态
# AC: invalid-email-rejected

**Status:** accepted
**Verifies:** checkout#req:reject-invalid-email

## Statement

An email that is not a valid address is rejected, and the order is not placed.
场景文件,来自场景规格
# Scenario: Reject an invalid email

**Validates:** [checkout#ac:invalid-email-rejected](../README.md#ac-invalid-email-rejected)

## Steps

GIVEN the shopper is on the checkout form
WHEN the shopper submits the email "not-an-email"
THEN the page shows "Enter a valid email address" beside the email field
AND the order is not placed
Rehearse 场景,Rehearse 指南里的形态
# Rehearse: Reject an invalid email

## Given a submitted email that is not a valid address
```bash
printf 'email=not-an-email\n' > form.txt
```

## When the response is recorded
```bash
printf 'Enter a valid email address\n' > out.txt
```

## Then the message is the invalid-email text
```bash
grep -q 'Enter a valid email address' out.txt
```

这四份样品用来说明那些形态。它们不是一棵完整的功能树,本页也不声称它们单独就能通过 specscore spec lint。功能 README 还需要其他必填小节,那条命令才会干净。

如果想要一份问题清单,用来从含糊的请求里把标准问出来,用 提出好问题。登录演练把一条精简标准 session-hardened 放在其他产物之中。

常见问题

什么是验收标准?

验收标准(AC)是一项变更要算作完成之前必须为真的条件。在 SpecScore 的验收标准规格里,它们是抽象的:说明必须满足什么,把具体步骤留给场景。

什么是 Given/When/Then?

Given/When/Then 是 Gherkin 里的步骤形态:Given 是起始状态,When 是发生一个动作,Then 是随后出现的可观察结果。Cucumber 在 Gherkin 参考里记录这些关键字。SpecScore 的场景规格使用大写关键字 GIVEN、WHEN、THEN 和 AND,每个场景文件恰好一个 WHEN。

怎样为 AI 编程代理写验收标准?

写一个可观察的行为,带上行为者、输入和结果,并且不要含糊的词。示例数据放进场景,不要放进抽象标准。代理就可以对着标准实现,并核对场景。

验收标准和测试用例是一回事吗?

不是。标准陈述条件。测试用例,或 SpecScore 场景,是带具体输入的一次运行,用来表明条件成立或失败。多个场景可以支撑同一条标准。

specscore spec lint 会评判含糊的措辞吗?

不会。在 CLI v0.39.1,也就是本仓库 CI 所安装的发行版里,ac-heading-format 检查 ### AC: 标题。它不为 “graceful” 或 “fast” 这类词打分,标准里出现 Given/When/Then 时也不警告。措辞是写作的事。验证器页面 把那种确定性的 lint 和人工的 AI 审阅分开。

Given/When/Then 步骤在 SpecScore 里放在哪里?

放在场景里,不放在验收标准里。验收标准规格禁止在标准内部写 Given/When/Then 步骤。场景规格把它们放在 _tests/ 下的 ## Steps 里。Rehearse 运行一种相关形态:_tests/ 下的标题和围栏命令,记录在 Rehearse 指南里。检查器不强制这些步骤关键字。

检查你已有的规格。

发现翻译问题或有改进此页面的想法?