OpenSpec 实战指南:从工作流到落地
为什么需要 OpenSpec
在 AI 编程时代,真正的难点往往不是“AI 会不会写代码”,而是“AI 能不能稳定写出你真正想要的代码”。问题往往不在模型能力,而在于需求、边界、约束和验收标准没有被稳定地表达出来。当意图没有沉淀为可复用的工程事实,AI 就只能在模糊上下文里“猜”。
OpenSpec 解决的正是这个问题。它的核心思想可以概括成一句话:先对齐规范,再生成代码(align before code)。与其把 AI 当成一个只看提示词的即时执行器,不如把它放进一套可追溯、可迭代、可沉淀的规范工作流里。
OpenSpec 既不是重量级流程平台,也不是传统瀑布式文档系统。从实践上看,它更像一套轻量的仓库内协议:
- 用
specs/保存系统当前已经成立的事实; - 用
changes/保存本次准备引入的未来变化; - 用 proposal、spec、design、tasks 把“为什么改、改成什么、怎么实现”拆开表达;
- 用 sync 和 archive 把一次变更逐步沉淀为下一次变更的上下文。
它的设计哲学,基本可以概括为四点:
- Fluid not rigid:规范是活文档,不是一次性文书。
- Iterative not waterfall:支持边做边校正,而不是阶段锁死。
- Easy not complex:只保留真正影响实现质量的文档。
- Brownfield-first:优先服务已有项目,而非只适配全新仓库。
这也是 OpenSpec 最值得重视的地方:它不要求团队先建立一套庞大的流程,再允许 AI 参与;它的目标恰恰相反——用最小的文档负担,换取更稳定的人机对齐质量。
命令分工与推荐工作流
如果只先记一条主线,记住下面这条工作流就够了:
1 | |
这条主线概括了 OpenSpec 中较常见的一种节奏:
- 先建立工作目录。
- 再为一次变更生成工件。
- 在实现前完成人类审查。
- 随后按任务清单实现代码。
- 最后按需校验、沉淀并归档。
安装与初始化
1 | |
初始化后,项目里通常会出现一个 openspec/ 工作目录:
1 | |
有些 AI 工具还会在仓库根目录维护自己的配置文件,例如 AGENTS.md 或 CLAUDE.md。这类文件更适合视为AI 工具层配置,而不是 OpenSpec 自身的流程工件。它们可以和 openspec/ 共存,但职责不同,后文会单独说明。
核心命令怎么分工
| 命令 | 作用 | 何时使用 | 典型产出 |
|---|---|---|---|
openspec init |
初始化工作目录 | 第一次接入 OpenSpec | openspec/ 基础骨架 |
/opsx:new |
创建 change 容器并给出首个工件模板 | 想逐步推进时 | 一个新的 change 目录 |
/opsx:continue |
按依赖顺序继续生成下一个工件 | new 之后继续补齐工件 |
单个新增工件 |
/opsx:propose |
一次生成到可实现状态 | 需求比较清楚,想快速进入审查 | proposal.md、specs/、design.md、tasks.md |
/opsx:ff |
快速生成到 apply-ready | 与 propose 类似,强调更快进入实现 | 同上 |
/opsx:explore |
通常用于调研、澄清、比较方案,可按需回写工件 | 需求不清、方案摇摆、边界不明 | 分析结论,可选的工件更新 |
/opsx:apply |
按任务清单实现代码 | 工件已审查通过后 | 代码变更、任务勾选 |
/opsx:verify |
扩展工作流中的一致性核对动作 | 希望在归档前补充核查时 | completeness / correctness / coherence 报告 |
/opsx:sync |
扩展工作流中的 spec 合并动作 | 想在归档前单独预览或执行 spec 合并时 | 更新后的主 Specs |
/opsx:archive |
默认工作流里的收口动作:验证变更、更新主 Specs 并归档 change | 一个变更收口时 | 更新后的主 Specs + 归档目录 |
更稳妥的理解:命令是动作,不是关卡
OpenSpec 的强项不在于把你锁进固定阶段,而在于把一次变更拆成几个可回退、可重做、可校正的动作:
propose负责形成可讨论、可审查的变更表达;explore通常用于澄清不确定性、调查问题和比较方案;apply负责把已确认的规范转成实现;verify负责检查“做出来的东西”是否真的对齐“说清楚的东西”;archive负责完成一次变更的收口;在扩展工作流里,sync可以作为单独的 spec 合并动作提前执行。
因此,更稳妥的理解是:默认主流程通常围绕 propose → explore/apply → archive 展开;如果你希望在归档前补充核查或单独处理 spec 合并,则可以再引入 verify 与 sync。如果你把这条主线记住,后面的大部分细节都会自然落位。
核心概念
要真正把 OpenSpec 用顺,需要抓住的其实只有三组概念:当前事实与未来变化、变更工件、沉淀动作。
1. specs/:系统当前事实
openspec/specs/ 存放的是系统已经成立的行为契约。它回答的问题是:
这个系统现在已经具备什么能力?
典型写法会使用 SHALL / MUST 一类语义约束词,再配合 Gherkin 风格场景描述行为边界:
1 | |
这里描述的不是“打算做什么”,而是“已经应该成立的行为”。因此,specs/ 更像长期知识库,而不是临时草稿区。
2. changes/:本次准备引入的未来变化
openspec/changes/ 不是单个文件,而是一组变更目录。每一个 change 目录代表一次独立的需求、重构或能力调整。对应地,它回答的问题是:
这次准备把系统改成什么样?
可以把两者记成一句话:
specs/= current truthchanges/= proposed future state
这也是 OpenSpec 区别于很多“需求文档 + 代码实现”松散流程的关键:它把当前事实和未来变更明确分层了。
3. change 目录里的四类工件
一个典型的 change 目录通常长这样:
1 | |
四类工件的职责分别是:
| 工件 | 作用 | 重点问题 |
|---|---|---|
proposal.md |
说明为什么改、改什么、影响什么 | Why / What / Impact |
specs/.../spec.md |
把需求翻译成可验证的行为契约 | 系统应该如何表现 |
design.md |
记录实现方案、技术权衡、关键设计 | 准备怎么做 |
tasks.md |
把实现拆成 AI 可执行任务清单 | 先做什么,后做什么 |
一个常见误解,是把 proposal、spec、tasks 当成彼此重复的文档。实际上,它们承担的是不同语义:
1 | |
OpenSpec 之所以通常先产出 proposal,再产出 spec 和 tasks,不是因为 spec 不重要,而是因为没有先对齐方向,再精确描述行为,往往只会得到“方向错误但表达严谨”的产物。
4. Delta Specs:只描述这次变更的增量
change 目录里的 specs/ 不是主 Specs 的完整拷贝,而是增量规范。它通常围绕三类动作展开:
ADDED:新增需求MODIFIED:修改需求REMOVED:删除需求
这种设计有两个直接好处:
- 一次变更的边界更清楚;
- AI 更容易理解“这次到底改哪里”。
5. sync 与 archive 的关系
这是全文里最容易混淆的一组动作。
/opsx:sync:在扩展工作流中,单独把 change 中的 Delta Specs 合并进主specs/。/opsx:archive:在默认收口流程中,验证变更、更新主 Specs,并把 change 目录归档为历史记录。
因此,较准确的理解是:sync 可以被理解为把 spec 合并动作单独拿出来;而 archive 则是默认收口动作,既处理归档,也会把 future-state specs 合并进主 specs/。归档之后,proposal、design、tasks、delta specs 也会随整个 change 目录一起进入 archive,而不是只剩下一部分。
6. config.yaml:可选的项目配置入口
openspec/config.yaml 不是强制前置条件,但它通常值得填写。官方文档里,它不只是项目上下文入口,还常用于补充 schema 与工件规则。常见用途包括:
context:项目背景、技术栈、架构模式、领域信息;rules:团队规则、接口约束、错误处理约定等;schema相关配置:为生成工件提供默认结构或约束。
例如:
1 | |
它的作用并不神秘:为 OpenSpec 工作流提供结构化、稳定、可复用的项目配置。如果项目简单,AI 也许能从代码库自行推断出足够信息;如果项目复杂,config.yaml 往往能明显改善 proposal 或其他工件草案的质量。
7. 目录结构应该怎么读
把前面的概念合起来,OpenSpec 目录可以这样理解:
1 | |
如果只用一句话总结:specs/ 是沉淀层,changes/ 是工作层,config.yaml 是上下文层。
完整实操步骤(以“新增下单接口”为例)
把前面的概念放进一次具体变更里,整条链路就会清楚很多。下面用一个常见的后端需求演示一遍:新增 POST /orders 接口,下单时同步扣减库存,库存不足返回 409。
步骤一:初始化并补充上下文
先执行:
1 | |
如果项目已经比较复杂,建议补充 config.yaml。你可以让 AI 先生成初稿,再手动审查:
1 | |
这一步的目标不是“写文档”,而是减少后续 proposal 的理解偏差。
步骤二:创建 change
如果你想一步到位生成主要工件,可以直接使用 /opsx:propose:
1 | |
生成后的 change 目录可能类似这样:
1 | |
如果你更想逐步控制,也可以先 /opsx:new,再通过 /opsx:continue 一件件补齐。
步骤三:审查工件
这是整条链路中最关键的人类审查节点。在这里不要急着进入实现,而是先问四个问题:
proposal.md是否把范围和影响说清楚了?spec.md是否覆盖了关键场景和边界情况?design.md是否解释了关键技术选择?tasks.md是否足够具体,能被 AI 顺序执行?
例如,一个简洁的 proposal.md 可以长这样:
1 | |
对应的增量 spec 可以这样表达:
1 | |
而 tasks.md 则应该把实现拆成可执行步骤:
1 | |
如果这里发现问题,先修工件,再进入实现。不要把这一步省略掉。
步骤四:执行实现
确认工件可接受之后,再进入 apply:
1 | |
或者直接用自然语言告诉 AI:
1 | |
这时 AI 的工作重点,不再是“理解需求”,而是“按已确认的任务清单落地实现”。这也正是前面先补齐 proposal、spec、tasks 的价值。
步骤五:在需要时补充实现对账
如果你希望在归档前额外做一次结构化核对,可以执行:
1 | |
verify 更适合被理解为扩展工作流里的核对动作。它关注的不是单纯“测试跑没跑”,而是至少三个维度:
- Completeness:任务与需求是否完整落地;
- Correctness:实现是否偏离 spec;
- Coherence:实现是否违背设计或项目既有模式。
如果报告中还有关键问题,先修掉,再进入下一步。
步骤六:在需要时单独同步主 Specs
1 | |
这一步会把 changes/add-create-order/specs/ 中的 Delta Specs 合并进主 openspec/specs/。它更适合在你希望不归档、只先处理 spec 合并时使用。重点不是整文件覆盖,而是按意图更新主规范。
步骤七:归档本次变更
1 | |
归档意味着这次变更结束了,但它不会抹去上下文。相反,整个 change 目录会进入 changes/archive/,成为以后回溯“为什么这么改”的历史依据;与此同时,future-state specs 也会在归档过程中并入主 specs/,成为新的 current truth。
到这里,一个完整的 change 才算真正闭环。若采用默认主流程,可以理解为:
1 | |
如果团队需要更强的中间检查,也可以在归档前补充:
1 | |
人机交互边界与修正工作流
要把 OpenSpec 用稳,关键不在于第一次 propose 是否成功,而在于发现问题后能否按正确顺序回退和修正。最常见的问题不是命令不会用,而是:
- proposal / spec 有问题时该改哪里;
- apply 之后发现结果不对,应该先改代码还是先改 spec;
- 人到底能不能直接改这些 Markdown 工件。
原则一:人负责意图与判断,AI 负责结构化与执行
在 propose 阶段,更合理的协作边界是:
- 人类提供目标、上下文、约束、边界和取舍;
- AI把这些信息整理成 proposal、spec、design、tasks;
- 人类再审查这些工件是否真的表达了自己的意图。
因此,proposal、spec、design、tasks 都不是神圣不可改的中间产物,而是可迭代的人机协作载体。
原则二:发现问题时,先分清是“实现问题”还是“规范问题”
apply 之后如果出现缺陷,根因通常只有两类:
1 | |
这个区分非常重要,因为它直接决定修复顺序。
三种常见修正路径
1. 用 /opsx:explore 先澄清,再决定是否回写工件
当问题本质上是“需求没想清楚”或“方案还在摇摆”时,/opsx:explore 是很自然的入口。官方文档对它的强调重点是思考、调研、澄清和收敛问题,因此更稳妥的理解是:它通常先服务于分析与澄清,而不是把它当作默认实现入口。
典型场景包括:
- 需求范围需要收缩或扩展;
- 某个边界场景在 proposal / spec 中表达得不够准确;
- 技术方案需要先比较几种实现路径;
- apply 前审查发现工件之间存在不一致。
如果 explore 的结论稳定了,再让 AI 把结论落实到 proposal、spec、design、tasks 中。
2. 用自然语言直接更新特定工件
如果你已经很明确知道问题在哪里,也可以直接告诉 AI 修改具体文件,例如:
1 | |
这种方式的优点是精确,缺点是你需要主动关注跨文件一致性。
3. 人工直接编辑 Markdown
这也是允许的。OpenSpec 的工件本质上就是普通 Markdown 文件,你完全可以手工修改它们。更稳妥的做法是:
- 先明确你改动了哪些工件;
- 再让 AI 检查 proposal / spec / design / tasks 是否仍然一致;
- 在实现后用
verify再做一次对账。
Apply 后的正确修复顺序
如果问题来自实现偏离 spec,那就直接修代码即可。
但如果问题来自spec 本身有缺陷,顺序必须反过来:
1 | |
例如,下单接口最初只定义了“库存充足”和“库存不足”两个场景。后来测试发现:库存服务超时时,系统仍然创建了订单。此时真正的问题,不一定是代码写错,而可能是 spec 根本没有定义“库存服务超时”这个行为。
这类情况下,先补 spec 才有意义:
1 | |
然后再去更新 design、tasks 和实现代码。原因很简单:spec 是 AI 后续工作的标准答案。如果你只改了代码,却不改 spec,那么下次 AI 再读规范时,仍然会基于错误前提继续工作。这就是典型的 spec drift。
一条更实用的修正循环
把上面的原则浓缩起来,更实用的修正循环其实很简单:
1 | |
OpenSpec 的价值,不只是“帮助 AI 开工”,更在于帮助你在问题暴露后,仍然用同一套规范体系把事情收回来。
什么时候看哪些文件,什么时候改哪些文件
这一节可以当作速查表使用。
生命周期速查表
| 阶段 | 重点文件 | 你要做什么 |
|---|---|---|
| 初始化 | openspec/config.yaml |
可选地补充项目上下文与规则 |
| 理解现状 | openspec/specs/*/spec.md |
了解系统当前已经承诺的行为 |
| 创建提案 | changes/<id>/proposal.md |
审查变更范围、动机与影响 |
| 审查需求 | changes/<id>/specs/**/spec.md |
审查新增或修改的行为场景 |
| 审查方案 | changes/<id>/design.md |
审查关键设计和实现权衡 |
| 审查执行计划 | changes/<id>/tasks.md |
确认任务拆分是否足够具体 |
| 实现 | 代码文件 + tasks.md |
让 AI 按任务顺序落地并更新勾选状态 |
| 对账 | verify 报告 |
判断实现是否完整、正确、一致 |
| 沉淀 | 主 specs/ + changes/archive/ |
sync 更新事实,archive 保留历史 |
决策速查
1 | |
注意事项与最佳实践
1. 不要跳过人工审查
OpenSpec 的核心收益来自“在编码前对齐”。如果 proposal 和 spec 没看就直接 apply,本质上仍然是在让 AI 自由发挥,只是多绕了一层目录结构。
2. 不要把 sync 和 archive 混为一谈
更稳妥的理解是:archive 本身就是默认收口动作;如果你希望在归档前先单独核查或单独合并 specs,再引入 verify 与 sync。这样既能适配默认工作流,也能覆盖扩展工作流。
3. 不要混用不同工件的时态
specs/讲的是当前事实;proposal.md讲的是本次准备做什么;tasks.md讲的是实现步骤。
一旦把“未来工作项”写进主 spec,或者把“系统现状”写进 tasks,AI 和人都会很难读懂上下文。
4. config.yaml 很重要,但不是绝对前提
它常用于补充项目上下文、默认 schema 和工件规则,在复杂项目中尤其有价值;但 OpenSpec 的工作流不应被误解为“没有 config.yaml 就无法启动”。
5. 用渐进式方式接入 OpenSpec
没有必要一夜之间把整个项目重写成 Spec。更现实的方式,是从新功能、关键接口、经常变动的模块开始,让 specs/ 随着一次次 sync 慢慢沉淀起来。
6. 警惕 spec drift
只修代码、不修规范,短期看起来省事,长期会不断制造重复劳动。每当 AI 下一次再读取过时的 spec,旧问题就会重新出现。
7. 把 archive 当成历史资产,而不是回收站
归档目录里保存的是一次变更完整的上下文:为什么改、怎么设计、怎么拆任务、最后沉淀了哪些规范。它对回溯设计决策非常有价值。
验收标准与 ATDD:让 Spec 真正可测试
OpenSpec 的 spec 非常适合描述行为,但要把规范真正变成可执行验收标准,仍然需要进一步思考一个问题:
“可读的场景”如何变成“可验证的标准”?
Gherkin 场景擅长行为,不天然擅长量化指标
例如,下面这种写法表达了目标,但仍然不够可测:
1 | |
问题在于:
- 没说明是单次请求、平均值,还是 P95 / P99;
- 没说明并发与负载条件;
- AI 也很难直接据此生成精确断言。
更实用的做法:分层表达验收标准
比较稳妥的实践是把验收标准拆成三层:
例如:
1 | |
然后在 design.md 中说明测试策略,例如使用 Gatling 做压测、用 JUnit 覆盖功能场景。这样做的好处是:
- 行为契约仍然留在 spec;
- 量化目标有明确归属;
- AI 在 apply 阶段更容易同时生成功能代码与测试代码。
ATDD 在 OpenSpec 里的落点
如果把验收测试驱动开发(ATDD)放进 OpenSpec 语境里,可以把它理解成:
1 | |
也就是说,OpenSpec 最适合作为 ATDD 的上游规范层。它不替代测试框架,但它能让测试目标在编码前就变得清晰。
OpenSpec Commands / Skills:如何理解这套生态
在支持 AI 命令和技能机制的工具里,OpenSpec 常常会以命令、skills 或两者结合的方式交付:
- 显式命令:例如
/opsx:propose、/opsx:apply、/opsx:archive; - 自动激活的 skills:当你用自然语言描述意图时,AI 工具可按其集成方式匹配对应能力。
更稳妥的理解是:它们常常服务于同一套工作流,但具体呈现方式取决于工具集成与交付配置:
- 命令更适合显式控制、重复执行和标准化步骤;
- skills 更适合自然语言驱动和低摩擦交互。
一个很实用的理解方式是:
- 新手阶段优先用显式命令,因为边界更清楚;
- 熟练阶段可以更多使用自然语言,让 AI 自动匹配;
- 复杂场景常常是两者混用:先用自然语言澄清问题,再用显式命令推进关键步骤。
三个最容易讲错的点
/opsx:new 不是“一次生成所有工件”
它更像“建立 change 容器并开始第一步”,适合想自己掌控生成节奏的人。
/opsx:explore 不是“提前开始实现”
它的价值在于思考、调研、比较、澄清,以及在必要时把结论回写到工件,而不是直接产出业务代码。
/opsx:archive 不是“只做历史归档”的别名
archive 的意义是收口与保留历史,同时在默认流程里也会更新主 Specs;sync 更适合在需要时把 spec 合并动作单独拿出来执行。
项目配置搭配建议
把 OpenSpec 放进真实项目时,最容易困惑的一点是:AGENTS.md、CLAUDE.md、规则文件、openspec/ 到底是什么关系。
最简单的理解方式是把它们分成三层:
1. AI 工具层
例如 AGENTS.md、CLAUDE.md 或某些工具自己的 rules 文件。它们主要负责:
- 项目级提示;
- Agent 行为约束;
- 编码规范、协作规则、调用策略。
它们回答的问题更像是:
AI 在这个仓库里应当如何工作?
2. OpenSpec 上下文层
主要是 openspec/config.yaml。它常用于:
- 提供项目背景;
- 提供规则与约束;
- 提供默认 schema 或工件级规则,并改善 proposal 等工件草案的质量。
它回答的问题是:
这个项目是什么样的?
3. OpenSpec 规范层
主要是 openspec/specs/ 与 openspec/changes/。它们负责:
- 描述当前事实;
- 表达本次变更;
- 沉淀历史与知识。
它回答的问题是:
系统现在是什么,以及这次准备把它改成什么?
推荐搭配方式
如果你只想用一句话记住:
- AI 工具层负责“怎么协作”;
- OpenSpec 上下文层负责“项目是什么”;
- OpenSpec 规范层负责“系统行为与变更是什么”。
三者并不冲突,反而是互补关系。对复杂项目来说,这种分层往往比把所有信息都塞进同一个 Markdown 文件里更稳定。





