跳到主要内容
别急着让 agent 写代码:先签三份文件,再用 EARS 搭一个文字冒险

别急着让 agent 写代码:先签三份文件,再用 EARS 搭一个文字冒险

Kevin
Kevin

· 阅读约 8 分钟

这篇咱们从零搭一个东西:一套让 Claude Code 按"需求文档→设计文档→任务清单→人工签字→实现→验收"顺序开工的 SDD 工作流,外加一个能真正跑起来的文字冒险小游戏。全程大概四十分钟,跑完你手上会多三份已签好字的文档、一份能复用的规格模板,和一个丢进任意小项目都能照搬的验证回路。

开搞之前先确认这几件事都齐了:

  • Node 18+,本地装好了
  • Claude Code CLI 已登录,账户里有可用额度
  • 能接受这次的游戏是终端文本版,没有画面——画面的事情后面单说

都齐了咱们就开搞。

Step 1:为什么拿游戏当试验田

有人在一个讲 SDD 的帖子底下说了一句我觉得特别准的话:游戏是最适合规格驱动开发的场景,因为游戏本质上是由一堆小契约组成的——输入、状态、计时、计分、失败、反馈,每一条都能单独验证。这句话不是修辞,是真的。你在游戏里输入的每个命令、看到的每个反馈、触发的每个解锁条件,都是可以写进需求文档并且逐条验收的条款。

反过来,很多业务功能的问题是"需求不可观察"——你很难定义"用户觉得这个页面不别扭"到底怎么验收。游戏没有这个问题:按了 A 键就该发生 A 事件,没发生就是 bug,没什么好辩解的。

所以这篇咱们用一个迷你文字冒险来走 SDD 流程。规则很简单:玩家在三个房间之间移动,每个房间代表一段里程碑记忆;三个房间全访问过之后,解锁一个奖励房间。这个设定是从 Hanchett 给他孩子做的一岁生日游戏缩过来的——他那个版本是每个建筑一个里程碑,全部走完开 bonus,咱们用终端版把这个骨架跑通。

Step 2:先用 EARS 写需求文档,不写代码

EARS 格式的需求长什么样?一句话概括:每条需求都长成"当 <触发条件> 时,系统应 <响应>"。它把模糊的"做一个房间系统"拆成了能逐条打勾的行为条款。

给这个迷你游戏列需求,大概是这么五条:

WHEN 玩家输入 go <roomId>
THEN 游戏应把当前房间切换到该房间(仅当该房间已解锁)

WHEN 玩家输入 inspect
THEN 游戏应打印当前房间的描述和里程碑文本

WHEN 玩家进入一个此前未访问过的房间
THEN 游戏应把该房间记入已访问列表

WHEN 已访问列表中的房间数达到 3
THEN 游戏应解锁 exit 房间

WHEN 玩家位于 exit 房间且输入 leave
THEN 游戏应打印结束文本并退出

这就是需求文档的主体。写完你就发现了:这根本不是什么高深的东西,它就是一份你本来应该在脑里过一遍的验收标准,只不过现在被逼着写在了代码前面。

现在轮到你了。打开 Claude Code,把你的项目想法丢进去,要求它按 EARS 格式产出需求草案。注意一句关键要求:只写需求,不写代码

agent 的草稿不会直接通过。你要做的就是审、改、签。这一步很多人会卡在"不知道怎么改"——其实方法特别简单:用 EARS 的句式表达你的不满,别用大白话打补丁。比如你发现需求漏了"玩家随时能回上一个房间",不要说"再加个返回功能",要说"WHEN 玩家输入 back,THEN 游戏应切换到上一个已访问房间"。前者是闲聊,后者是需求条目。养成这个习惯之后,agent 的修改会精准很多,不会越改越偏。

改到所有条款都符合你的预期了,告诉它:

需求文档已确认。不要开始实现,等设计文档完成。

这个签字动作是整个 SDD 的地基。没有这一步,后面所有的"验证"都是空谈——因为规格根本没被固定下来,代码跑了也是跑给你看的,不是跑给需求看的。

### Step 3:写设计文档,把契约翻译成结构

需求文档回答"做什么",设计文档回答"怎么组织"。这一层要交代技术选型、目录结构、数据模型,以及最重要的——每一条 EARS 需求对应到代码里的哪个位置。

先说技术选型。这次咱们用 Node + TypeScript 跑终端交互,不用 Phaser,也不碰 TanStack Start。原因很实在:游戏框架会把教程的注意力全拽到渲染循环上去,而本篇要交付的核心是规格驱动的回路,不是精灵图和动画。渲染层的话题以后单开一篇细讲,先把这个工作流跑通再说。

让 agent 生成设计文档的 prompt 长这样:

根据已批准的需求文档,为 game/ 目录下的终端文字冒险生成设计文档。

覆盖:

  1. 目录结构
  2. 数据模型:Room、GameState 的定义
  3. 契约清单:每一条对应一条 EARS 需求
  4. 验收方式:命令 + 期望输出

只写设计文档,不写实现代码。


生成之后,你的任务是检查它有没有忠实翻译需求。这里有个典型的坑:agent 经常会把"已访问列表达到 3 条时解锁 exit"写成"当所有房间都完成后退出"。看着差不多?差远了。前者是可验证的计数条件,后者是模糊的整体判断。设计文档里出现这种措辞,就得打回去重写。

也正是在这一步,你该意识到签字这件事的边界:你签的是"设计是否符合需求",不是"需求是否符合你真正想要的东西"。需求本身写歪了,设计文档再忠实也是把歪的传下去,签字拦不住这个。所以签设计之前,值得回头再读一遍需求文档,确认方向没跑偏——这一步花不了两分钟,但能省掉后面一小时的返工。

### Step 4:生成任务清单,排 MVP 顺序,然后签字

设计文档批准之后,让 agent 产出任务清单。prompt 这样给:

根据已批准的设计文档,生成实现任务清单。

要求:

  • 每个任务带验收标准
  • 按最小可运行版本的顺序排列
  • 只列任务,不写代码

拿到的清单大概长这样:

  • 搭建 TypeScript 项目骨架,npm run dev 能启动
  • 定义 Room 和 GameState 数据模型
  • 实现 go 命令和房间切换
  • 实现 inspect 命令和描述输出
  • 实现已访问列表和 exit 解锁逻辑
  • 实现 leave 命令和结束流程

你的活儿还没完——重排顺序。SDD 对"需求本来就清晰、能拆成小任务"的项目效果最好;反过来,需求还在演进的时候规格会很快过期,清单会变成一种负担。所以排任务的核心原则是:**让第一个可运行的版本尽早出现**。先把骨架和移动命令排前面,哪怕一开始只有一个房间能走,也比憋半天直接上完整版强。

顺序确认了,把签字发给它:

任务清单已批准。按清单顺序开始实现,不要实现清单之外的功能。

这句话是整篇教程里最重要的一条指令。它告诉 agent:计划已锁定,动手的信号已经给了,但不许加戏。SDD 的所有纪律都浓缩在这句话里了。

Step 5:实现——但真正的重点在后面

因为规格已经细化到这个程度,实现阶段反而没什么好讲的了——这正是 SDD 反直觉的地方:代码是最不值钱的那一步。

有个很实际的观察:规格一旦写细了,auto 模式下的普通模型就够用,不必把最贵的那档模型拉出来烧积分。这是个经济学问题——token 应该花在"让模型想清楚做什么"的需求和设计阶段,而不是花在"让模型猜需求"的实现阶段。你与其让实现模型边写边猜,不如前期多花几分钟把每一条 EARS 写到位。这就和"先跑通再优化"是同一个道理:优化的是工作流,不是代码。

实现命令长这样:

claude -p "实现已批准的任务清单,输出到 game/ 目录。不要增加规格之外的任何功能,不要重构代码结构。"

跑完直接进游戏:

cd game && npm run dev

Step 6:验收回路——每条需求都得有活证据

游戏跑起来了,别急着庆祝。现在把需求文档翻出来,逐条过。这才是 SDD 真正值钱的地方:验证用的不是代码本身,而是需求文档。

输入 go bedroom → 输出房间描述
输入 go kitchen → 房间切换
输入 go exit(只访问了 2 个房间)→ 应被拒绝
访问 3 个房间后再输入 go exit → 应被允许
输入 leave → 输出结束文本

对照一下:

  • 每一条 EARS 需求都在游戏里能找到一个对应的操作路径
  • 未满足条件时,游戏确实拒绝了该行为
  • 满 3 个里程碑后,exit 房间确实解锁了
  • 游戏里没有出现需求文档中不存在的功能

如果你发现游戏里多了一个需求里没写过的特性——比如 agent 自作主张加了个背包系统——这就说明实现阶段跑偏了。这时候你手里那份签过字的需求文档就是唯一的裁判:它的条款没提背包,背包就是多余的。你可以选择现在删掉,也可以选择回去改需求文档再补签——反正不能让它不明不白地待在那儿。

对了,插播一句上一步就该交代的事:如果用的是 Claude Code,实现前最好先把任务清单存成项目里的 TASKS.md,让 agent 直接读文件而不是靠对话上下文记任务。这是我写到 Step 5 才想起来必须说的——文件比对话稳,agent 不会聊着聊着把前半段任务忘了。没存的现在回去补一下也不迟。

收尾:三份签名比代码更长久

到这里,这个文字冒险的 SDD 流程就算搭完了。跑起来了吗,照这个清单自查:

  • 三份文件都在:REQUIREMENTS.mdDESIGN.mdTASKS.md
  • 需求文档里每一条 EARS 条款都能在游戏里找到对应行为
  • exit 只在第 3 个房间访问后解锁——这证明验证回路真的和需求绑定了,不是走过场
  • 游戏里没有需求文档之外的多余功能

都打勾了,这篇就算交付。

接下来你可以试着把这个工作流搬回自己的项目里——挑一个需求还算清晰的小功能,走一遍"EARS 需求→设计→任务清单→签字→实现→逐条验收",感受一下先写规格再写代码的节奏。也可以试着一个 EARS 需求对应一条验收脚本,把最后那步验证固化成自动化——这个以后单开一篇细讲。

最后说句掏心窝的:规格驱动不会让你变慢,它只是把"纠结"提前了。代码写完才发现需求理解错了,那才叫真的慢。三份签过字的文档摆在那儿,agent 跑偏了你一眼就能指出来——它写错可以改,你没想清楚就没得改了。签字权在你手里,这是整条流程里最不该外包出去的东西。