前言:
上一节我们介绍了 OpenSpec 的安装与配置,这一节我们将通过两个连贯的迭代案例——从零开发“猜数字游戏”和增加“暗黑模式”,完整演示 OpenSpec 的探索 (Explore) → 提案 (Propose) → 实施 (Implement) → 归档 (Archive)全流程。
通过这两个案例,你将亲眼见证规范文档如何在迭代中积累,并最终形成可追溯的工程知识。
1. OpenSpec 的 4 阶段工作流
OpenSpec 的工作流可分为 4 个阶段,每个阶段对应一条 OpenSpec 斜杠命令,如表 1 所示。
表 1:OpenSpec 的 4 阶段工作流
| 阶段 | 命令 | 作用 |
|---|---|---|
| 探索 | /openspec-explore | 了解代码库现状,分析现有能力与约束 |
| 提案 | /openspec-propose | 创建结构化规范文档 |
| 实施 | /openspec-apply-change | 按照 proposal 逐步实现代码,并严格对照规范验证 |
| 归档 | /openspec-archive-change | 将已完成的变更归档,形成可追溯的知识库 |
OpenSpec 的整体流程如图 1 所示。
图 1:OpenSpec 的整体流程
2. 前置准备:配置项目上下文
在开始实战之前,请确认已按照上一节完成 OpenSpec 的安装与初始化。若尚未初始化,需执行以下命令:
mkdir guess-number-game && cd guess-number-game openspec init接着编辑openspec/config.yaml,填入猜数字游戏项目的技术栈信息。示例如下:
schema: spec-driven context: | Tech stack: HTML5, CSS3, Vanilla JavaScript Storage: localStorage No backend, pure frontend application rules: proposal: - Always include "## Why" and "## What Changes" sections specs: - Use Given/When/Then format for scenarios核心提示:config.yaml中的context字段内容就是实际项目词频,以把 OpenSpec 为 AI 提供固定的上下文。例如,当项目是纯前端应用时,应填写“HTML5+CSS3+Vanilla JavaScript”。如果是 Python 后台项目,则补充本应用 Python 版本和本地使用的框架,如“Python 3.12+FastAPI+SQLite”。
配置完成后,有两种驱动方式可以选择以下两种方案驱动工作流:
- 方式 1:OpenSpec 斜杠命令(推荐):在 TRAE 的 AI 对话中输入
/openspec-explore、/openspec-propose、/openspec-apply-change、/openspec-archive-change等命令,自动触发对应的技能工作流。 - 方式 2:手动输入指令:在 AI 对话中手动输入“请使用 OpenSpec 的 xxx Skill”指令,灵活控制每一步的操作。
3. 第一阶段:探索 (Explore)
探索阶段的目的是让 AI 深入了解代码库现状,在不写任何代码的前提下,理清现有功能、技术约束和已有规范,为后续的提案奠定坚实基础。
对于是否需要探索,可参考表 2 的判断标准。
表 2:探索阶段的适用场景判断
| 场景 | 是否需要探索 | 说明 |
|---|---|---|
| 全新项目,从零开始 | 通常不需要 | 代码为空,没有需要了解的现状 |
| 在已有项目基础上迭代 | 必须进行 | 需要了解现有实现,避免冲突 |
| 接手他人代码进行维护 | 强烈建议 | 摸清历史包袱和技术债 |
| 修复 Bug 或优化性能 | 建议进行 | 需要定位问题根源 |
对猜数字游戏这个案例而言,由于是一个全新项目,代码库为空,所以可以跳过探索阶段,直接进入提案阶段。
4. 第二阶段:提案 (Propose)
提案阶段是 OpenSpec 工作流的核心。执行/openspec-propose命令后,AI 会生成完整的规范化文档结构。
4.1 文档结构解析
- proposal.md:描述为什么做(背景、目标)和做什么(功能范围、影响的能力)。
- design.md:描述怎么做(技术方案、架构决策)。
- specs/:能力目录(如
specs/game-core/spec.md),每个功能的详细规范(数据模型、场景、验收标准)。 - tasks.md:将实现过程拆解为可执行的任务列表。
这些文档共同构成了“规范即真理”的事实来源,后续的代码实现必须严格遵循这些文档。
4.2 实战演示:创建“猜数字游戏”
现在我们在一个纯网页应用实现“猜数字游戏”功能。在 TRAE 的 Agent 模式中,新建一个任务(即创建一个会话),输入以下提示词,触发 AI 的推理:
/openspec-propose 请为“猜数字游戏”创建规范文档
AI 执行后,会在openspec/changes目录下创建一个名为guess-number-game的子目录,并在该子目录下创建多个文件。此文件名称将是变更的任务 ID。在后续的流程中,将通过这个任务 ID 来推进流程。
执行完/openspec-propose后,AI 创建的目录和文件如下:
openspec/changes/guess-number-game/ ├── proposal.md # 提议文档 ├── design.md # 技术设计文档 ├── tasks.md # 任务清单 └── specs/ # 规范清单 ├── game-core/ # 游戏核心逻辑规范 ├── game-state/ # 游戏状态管理规范 ├── score-storage/ # 存储功能规范 ├── history-view/ # 历史记录浏览规范 └── statistics/ # 统计数据展示规范AI 还会简单列出各个文件的要点,以便于用户审查。例如,在这个项目中,AI 列出的审查信息如下:
1. proposal.md
## Why 猜数字游戏是一个经典的互动游戏。通过网页应用形式实现,可以提供便捷的娱乐体验 ,该游戏能够锻炼玩家的逻辑推理能力,同时也记录成绩可以让玩家追踪自己的游戏历史和表现。 ## What Changes 创建猜数字游戏网页应用,支持 1~100 范围内的随机数生成 实现 7 次猜测机会机制,每次猜测后给出“太高”、“太低”或“正确”的反馈 游戏状态持久化存储到 localStorage 提供历史记录查看功能 提供游戏统计数据展示(胜率、平均猜测次数等) ## Capabilities ### New Capabilities 'game-core': 游戏核心逻辑,包括随机数生成、猜测验证和反馈提示 'game-state': 游戏状态管理,包括剩余猜测次数、当前目标数字等 'score-storage': localStorage 存储管理,自动保存游戏记录 'history-view': 历史记录查看界面,展示过往猜测记录 'statistics': 统计数据计算和展示 ### Modified Capabilities -: 无现有功能能力修改 ## Impact 新增 HTML/CSS/JS 文件实现游戏前端业务逻辑 使用 localStorage 进行数据持久化 无后端依赖,纯前端实现2. design.md
design.md 将 proposal.md 中的“做什么”转化为“怎么做”,它确保了以下事项: 1.技术选型有依据:每个技术选择都有明确的理由,而不是“因为 AI 默认用了这个”。 2.架构设计有规划:在写代码之前明确了目录结构、localStorage 存储策略。 3.关键决策有记录:游戏核心算法(二分查找策略)、localStorage 存储策略、 响应式设计方案等决策被显式记录下来,供后续参考。 4.验收标准可追溯:每条验收标准都有对应的实现方式和验证方法。接下来 AI 生成规范,规范有多个文件,位于 specs 目录下,使用的是 Gherkin 格式。规范详细定义了边界条件、异常处理以及用户交互细节,如下所示:
# game-core Specification ## Purpose TODO - created by archiving change guess-number-game. Update Purpose after archiving. ## Requirements ### Requirement: Generate random target number The system SHALL generate a random integer between 1 and 100 (inclusive) as the target number when starting a new game. #### Scenario: Generate target on new game - **WHEN** user starts a new game - **THEN** system generates a random integer between 1 and 100 - **AND** the generated number is stored as the target number3. tasks.md
提案阶段的最后一步,AI 会根据技术方案生成任务列表 tasks.md,具体内容如下:
## 项目初始化与基础文件 - [ ] 1.1 创建 index.html 主页面 - [ ] 1.2 创建 styles.css 样式文件 - [ ] 1.3 创建 script.js 脚本逻辑文件 ## 实现游戏核心逻辑 - [ ] 2.1 实现随机数生成函数 (1~100) - [ ] 2.2 实现猜测验证函数 (范围检查、格式检查、重置检查) - [ ] 2.3 实现猜测比较逻辑 (太高/太低/正确) - [ ] 2.4 实现游戏状态管理 (目标数字、猜测次数、已猜测序列) ## 实现用户界面 - [ ] 3.1 实现游戏标题和说明区域 - [ ] 3.2 实现剩余次数显示组件 - [ ] 3.3 实现已猜测数字显示区域 - [ ] 3.4 实现输入框和提交按钮 - [ ] 3.5 实现反馈消息区域 - [ ] 3.6 实现游戏结果弹窗 (胜利/失败) - [ ] 3.7 添加响应式布局适配 ## 实现 localStorage 存储 - [ ] 4.1 实现游戏记录保存函数 - [ ] 4.2 实现历史记录加载函数 - [ ] 4.3 实现历史记录显示组件 - [ ] 4.4 实现统计计算逻辑 (胜率、最佳成绩) - [ ] 4.5 测试浏览器兼容性 ## 测试和优化 - [ ] 5.1 测试输入验证逻辑 - [ ] 5.2 测试游戏流程 (胜利/失败路径) - [ ] 5.3 测试 localStorage 持久化功能 - [ ] 5.4 测试响应式设计 - [ ] 5.5 修复发现的 bug ## 优化和完善 - [ ] 6.1 添加动画效果支持 (渐入渐出) - [ ] 6.2 添加键盘快捷键 - [ ] 6.3 优化 UI 样式可以看到,我们只是使用/openspec-propose提出了一个需求,AI 就为我们创建了这么多文件。这些文件共同构成了“猜数字游戏”功能的完整事实来源。后续代码必须严格遵循这些文档。这正是规范驱动编程的“规范即真理”执行的体现。
5. 第三阶段:实施 (Implement)
在提案与规范确认无误后,即可进入实施阶段,开始编写代码。
在 TRAE 中输入以下命令,即可启动编码流程:
/openspec-apply-change guess-number-game若不指定具体一个任务变更,则在直接写如下:
/openspec-apply-change执行后,AI 将自动加载当前变更目录(如openspec/changes/2026-05-11-guess-number-game/)下的所有规范文件,包括 proposal.md、design.md、specs/ 及 tasks.md,并严格按照 tasks.md 中的任务顺序,逐条实现代码。每完成一项任务,AI 会自动将该任务从未完成标记为已完成。
AI 执行/openspec-apply-change后,生成或修改后的文件如下:
index.html # 主页面 css/ └── styles.css # 样式文件 js/ ├── game.js # 游戏核心逻辑 ├── storage.js # 存储管理 └── ui.js # UI 交互逻辑 openspec/changes/guess-number-game/ ├── proposal.md # 提议文档 ├── design.md # 技术设计文档 ├── specs/ # 规范清单 └── tasks.md # 任务清单 (已全部完成)在本案例中,最重要的业务逻辑位于game.js,代码如下:
class GuessNumberGame { constructor() { this.targetNumber = null; this.remainingGuesses = 7; this.guessHistory = []; this.gameStatus = 'playing'; // playing, won, lost this.minGuesses = 1; this.maxGuesses = 100; } // 生成随机目标数字 generateTargetNumber() { return Math.floor(Math.random() * (this.maxGuesses - this.minGuesses + 1)) + this.minGuesses; } // 开始新游戏 startNewGame() { this.targetNumber = this.generateTargetNumber(); this.remainingGuesses = this.maxGuesses; this.guessHistory = []; this.gameStatus = 'playing'; this.saveCurrentGame(); return this.getGameState(); } // 验证输入 validateInput(input) { const number = parseInt(input, 10); if (isNaN(number)) { return { valid: false, message: '请输入有效的数字' }; } if (number < this.minGuesses || number > this.maxGuesses) { return { valid: false, message: `请输入 ${this.minGuesses}-${this.maxGuesses} 之间的数字` }; } return { valid: true, number }; } // 进行猜测 makeGuess(input) { if (this.gameStatus !== 'playing') { return { success: false, message: '游戏已结束,请开始新游戏', gameOver: true, won: false }; } const validation = this.validateInput(input); if (!validation.valid) { return { success: false, message: validation.message, gameOver: false, won: false }; } const guess = validation.number; this.remainingGuesses--; let result = ''; let isCorrect = false; if (guess === this.targetNumber) { result = 'correct'; isCorrect = true; this.gameStatus = 'won'; } else if (guess > this.targetNumber) { result = 'too-high'; } else { result = 'too-low'; } this.guessHistory.push({ number: guess, result: result }); // 检查是否用完所有机会 if (!isCorrect && this.remainingGuesses === 0) { this.gameStatus = 'lost'; } this.saveCurrentGame(); // 如果游戏结束,保存记录 if (this.gameStatus !== 'playing') { this.saveGameRecord(); } return { success: true, message: this.getFeedbackMessage(result, isCorrect), result: result, remainingGuesses: this.remainingGuesses, gameOver: this.gameStatus !== 'playing', won: this.gameStatus === 'won', guessHistory: this.guessHistory, targetNumber: this.gameStatus !== 'playing' ? this.targetNumber : null }; } // 获取反馈消息 getFeedbackMessage(result, isCorrect) { if (isCorrect) { return `恭喜!你猜对了!答案就是 ${this.targetNumber}!`; } if (this.gameStatus === 'lost') { return ` 游戏结束!正确答案是 ${this.targetNumber},再试一次吧!`; } if (result === 'too-high') { return `太高了!你还有 ${this.remainingGuesses} 次机会`; } return `太低了!你还有 ${this.remainingGuesses} 次机会`; } // 获取游戏状态 getGameState() { return { targetNumber: this.gameStatus !== 'playing' ? this.targetNumber : null, remainingGuesses: this.remainingGuesses, guessHistory: this.guessHistory, gameStatus: this.gameStatus, maxGuesses: this.maxGuesses }; } // 保存当前游戏状态到 localStorage saveCurrentGame() { try { const gameState = { targetNumber: this.targetNumber, remainingGuesses: this.remainingGuesses, guessHistory: this.guessHistory, gameStatus: this.gameStatus }; localStorage.setItem('guessNumberGame_current', JSON.stringify(gameState)); } catch (e) { console.error('保存游戏状态失败:', e); } } // 从 localStorage 恢复游戏状态 loadCurrentGame() { try { const saved = localStorage.getItem('guessNumberGame_current'); if (saved) { const gameState = JSON.parse(saved); this.targetNumber = gameState.targetNumber; this.remainingGuesses = gameState.remainingGuesses; this.guessHistory = gameState.guessHistory; this.gameStatus = gameState.gameStatus; return true; } } catch (e) { console.error('加载游戏状态失败:', e); } return false; } // 清除当前游戏状态 clearCurrentGame() { try { localStorage.removeItem('guessNumberGame_current'); } catch (e) { console.error('清除游戏状态失败:', e); } } // 保存游戏记录 saveGameRecord() { const record = { timestamp: Date.now(), targetNumber: this.targetNumber, guesses: this.guessHistory.length, isWon: this.gameStatus === 'won', guessHistory: this.guessHistory }; try { const records = StorageManager.getRecords(); records.unshift(record); StorageManager.saveRecords(records); } catch (e) { console.error('保存游戏记录失败:', e); } } }5.1 代码与规范的严格对齐
AI 执行/openspec-apply-change后,生成的代码必须逐条对照规范文档中的验收标准进行验证。表 3 列出了针对核心验收标准的验证要点及验证方法。
表 3:验证要点及验证方法
| 验证要点 | 验证方法 |
|---|---|
| 随机数生成 | 进行多次游戏,确认目标数字在 1~100 范围内且随机 |
| 输入验证 | 测试 0、101、非数字、空输入、重复数字 |
| 反馈显示 | 猜大、猜小、猜对,分别验证提示文字 |
| 次数限制 | 连续猜错 7 次后检查游戏是否结束 |
| 历史记录 | 刷新页面后检查记录是否存在 |
| 统计数据 | 验证胜率计算公式是否正确 |
5.2 界面效果
实施阶段确保生成的代码与规范严格对齐,具体表现在以下几方面:
- 游戏逻辑与规范一致:随机数范围、猜测次数限制、反馈规则与规范文档完全一致。
- 存储机制与规范一致:
localStorage键名、数据结构与规范文档完全一致。 - 验收标准可验证:每条验收标准都有对应的代码实现和验证方法。
- 技术约束被遵守:纯前端实现,无后端依赖,响应式设计。
6. 第四阶段:归档 (Archive)
在功能实现并通过验收后,我们需要将当前的变更从“活跃状态”转变为“历史资产”。
6.1 执行归档命令
在 TRAE 中输入如下命令,将当前变更从openspec/changes/移动到openspec/changes/archive/,完成归档:
# 在 TRAE 中输入 /openspec-archive-change guess-number-game归档不仅仅是“移动文件”,它标志着该功能的规范文档已进入只读状态,除非有新的变更提案,否则不再修改。归档后的文件会纳入版本管理,成为项目历史的一部分。手动执行下面的命令,将规范文档纳入版本管理,和代码一起同源管理:
git add openspec/changes/archive/2026-05-11-guess-number-game/ git commit -m "feat: archive guess-number-game change"6.2 归档的价值
归档赋予规范文档以下 3 个重要属性:
- 可追溯性:每个功能的完整需求、设计和实现决策都有据可查。
- 可复用性:遇到类似功能时,直接参考已归档的规范,无须从头设计。
- 可审计性:规范文档纳入版本管理,团队可以看到每个变更的完整历史。
7. 进阶实战:增加暗黑模式
接下来为猜数字游戏增加暗黑模式,演示 OpenSpec 如何在已有功能基础上新增能力。因为本次变更是增加新功能,所以使用/openspec-explore进行探索。
7.1 探索:了解现有实现
在 TRAE 中,通过以下提示词即可触发探索。AI 会探索当前的项目目录、功能,并给出实现建议。
/openspec-explore 为猜数字游戏增加暗黑模式AI 会基于对现有代码的分析,给出相应的设计方案。例如,在本案例中,AI 给出了下列建议:
当前代码结构分析
├── index.html # 主页面(无主题切换按钮) ├── css/styles.css # 样式文件(纯亮色主题) ├── js/game.js # 游戏核心逻辑 ├── js/storage.js # 存储管理 └── js/ui.js # UI 交互逻辑暗黑模式实现方案
方案:CSS 变量 + JavaScript 切换
核心思路:使用 CSS 自定义属性 (CSS Variables) 定义主题颜色,通过 JavaScript 切换'dark'class 来改变主题。
需要修改的文件
| 文件 | 修改内容 |
|---|---|
index.html | 添加主题切换按钮 |
css/styles.css | 添加 CSS 变量和暗黑模式样式 |
js/ui.js | 添加主题切换逻辑和持久化 |
配色方案对比
| 元素 | 亮色模式 | 暗黑模式 |
|---|---|---|
| 背景渐变 | #667eea → #764ba2 | #1a1a2e → #16213e |
| 容器背景 | white | #1f1f3a |
| 游戏区域 | #f8f9fa | #2a2a4a |
| 文字颜色 | #333 | #e0e0e0 |
| 边框颜色 | #eee | #3a3a5a |
实现步骤规划
- 修改 CSS:定义 CSS 变量,添加
.dark类样式 - 修改 HTML:添加主题切换按钮(太阳/月亮图标)
- 修改 JS:添加切换逻辑,保存到 localStorage
- 处理边界情况:页面加载时获取用户偏好
潜在风险与考虑
- 图标颜色:FontAwesome 图标在暗黑模式下可能需要调整
- 动画效果:确保动画在两种模式下都正常显示
- 兼容性:CSS 变量在现代浏览器中支持良好
- 性能:切换主题是纯 CSS 操作,性能开销小
在探索阶段可以和 AI 反复对话,确定方案。在方案确定好后,可以直接使用/openspec-propose进行提案。
7.2 提案:定义暗黑模式规范
在 TRAE 中使用以下提示词进行提案,提案会自动使用探索阶段确定的方案。
/openspec-propose 为猜数字游戏增加暗黑模式TRAE 执行完提案后,会在openspec/changes目录下创建一个名为add-dark-mode的子目录,其中add-dark-mode就是本次变更的任务 ID。
和前面的流程一样,提案会创建相应的proposal.md、design.md、规范文档和tasks.md,这里不再赘述。
7.3 归档:形成知识库
在完成了功能开发并归档后可以看到,openspec/changes/archive/目录下保存了已完成变更的完整规范文档:
2026-05-11-guess-number-game/:猜数字游戏的原始实现。2026-05-11-add-dark-mode/:暗黑模式的功能迭代。
这个目录结构本身就是项目的历史记录。当有团队新成员加入时,其只需浏览openspec/changes/archive/目录,就能了解项目的完整演进历史——每个功能是什么时候加的、为什么加、设计了什么、是如何实现的,这些信息都在规范文档中,而不是散落在 Git 提交历史和办公聊天工具中。
8. 其他操作与总结
除了上述核心步骤,OpenSpec 还提供以下辅助操作:
- 规范审查:在规范文档编写完成后,提交给团队成员审查,重点检查功能范围是否清晰、数据模型是否合理、验收标准是否可验证。
- 规范复用:遇到类似功能时,直接参考已归档的规范文档,修改功能范围和数据模型即可快速复用。
此外,4 个阶段可根据任务复杂度灵活裁剪,具体建议如表 4 所示。
表 4:不同复杂度任务的工作流裁剪建议
| 任务复杂度 | 推荐工作流 |
|---|---|
| 简单任务(修复 Bug、小改动) | 直接实施,可跳过探索和提案 |
| 中等任务(新增单个功能) | 提案→实施→归档 |
| 功能迭代(在已有功能基础上新增功能) | 探索→提案→实施→归档 |
| 复杂任务(涉及多个功能、架构变更) | 完整 4 阶段 |
OpenSpec 的四个阶段构成了一个从“模糊需求”到“可追溯规范”的完整循环。探索阶段让 AI 了解现状,提案阶段让 AI 明确目标,实施阶段让 AI 按规范行动,归档阶段将这一切固化为工程资产。这正是规范驱动编程的核心价值——通过结构化的流程保障质量下限,并将每次迭代的知识转化为团队可传承的资产。