OpenSpec实战教程:用需求规格锁死AI编程,附踩坑记录
如果你最近在关注 AI 辅助编程大概率刷到过一个叫 OpenSpec 的词。它不是一个框架也不是又一个代码生成器而是一套“先把需求锁死再让 AI 写代码”的工程方法。我前后折腾了几个周末在个人项目和一个小团队内部各试了一轮终于把它的完整用法理清了。这篇文章就是我基于实际体验写下的 OpenSpec 使用教程与踩坑记录。OpenSpec 解决的是个很现实的问题AI 生成代码的能力越来越强但它拿到模糊不清的需求时也会一本正经地自由发挥。你让它做一个“用户登录”功能它可能会自己决定加不加验证码、密码错误几次锁定、锁定后要不要发邮件通知。而 OpenSpec 的思路是把这些决策全部前置到一份结构化的 markdown 规格说明里再通过命令行工具做校验让 AI 在明确的约束范围内写实现。简单说它给 AI 编程加了一层“验收标准先行”的契约。这篇文章适合两类人一是每天在用 Cursor、Copilot、CLI 工具生成代码但总觉得结果不可控的开发者二是团队里想引入规范化的需求流转却不想被沉重的文档流程拖垮的人。看完你至少能搞清楚 OpenSpec 的目录结构、核心命令、一条需求从提出到落地的完整路径以及我在实际使用中遇到的坑和应对方法。1. OpenSpec 到底是什么需求与代码之间的“契约层”1.1 一句话理解 OpenSpec把 OpenSpec 想象成一份“可执行的产品需求合同”。传统需求文档写完之后懂行的开发还得自己转译成任务、再拆解成代码而 OpenSpec 的规格文件specifications本身就是结构化的、可机器校验的Change Proposal 则精确描述了这次要改什么、验收标准是什么。我最初接触它时最大的感受是它不帮你写代码它帮你把“该写什么代码”这件事定义清楚然后让 AI 或人工开发者照着执行。它的核心模块包括Specifications领域内能力的功能规格说明持续维护是项目长期行为的“契约文件”。Changes针对某一批需求的变更提案包含需求来源、任务拆分、验证清单。CLI 校验工具用openspec validate检查文档是否符合规范是否遗漏验收场景。这三个东西配合起来形成了一个“先描述、再提案、后实现、最终校验”的闭环。1.2 与传统需求文档的本质区别很多人第一反应是这不就是把 Word 文档换成了 Markdown 吗区别还真不小。我整理了一张对比表对比项传统需求文档OpenSpec存储形态Word/Confluence经常过期仓库内openspec/目录与代码同源描述方式大段叙述自然语言结构化 markdown带唯一编号的 requirement验收可测性“响应要迅速”这类模糊表达每个需求带明确的场景描述变更记录文档版本号基于文件的 Change Proposal可 diff、可 review机器可读性差AI 只能当上下文好CLI 可校验AI 可严格消费本质上OpenSpec 把“需求”变成了仓库里的一等公民。任何人改需求都要像改代码一样提交、审查、合并。AI 拿到规格文件时读取到的是带编号、带验收场景的精确说明而不是一坨“我感觉应该做到大概这样”。1.3 为什么我在 AI 编程半年后倒回来研究 OpenSpec我之前用 AI 工具写了不少功能越往后越觉得不对劲单个需求生成代码的速度确实快但功能之间的边界混乱、“加个功能结果破坏了原有逻辑”的情况开始变多。原因不是 AI 傻而是我给它的输入不完整。有两次印象特别深一次是让 AI 做一个“用户邀请”功能它自己添加了“被邀请人需要验证邮箱”的逻辑而我原本的计划里根本没有这一步导致后面联调时白折腾。另一次是让 AI 改一个订单状态字段它把所有出现状态的地方都改了一遍结果改崩了定时任务里的状态判断。OpenSpec 的价值就是把这类决策风险前置。写规格说明时把边界条件、异常场景、验收标准全部写清楚AI 生成代码时才有据可依人工审查时先看规格再看代码也更容易发现偏离点。1.4 OpenSpec 的目录结构与核心命令初始化之后仓库里会多出类似这样的结构openspec/ project.md # 项目级说明 specs/ capabilities/ user-auth/ spec.md # 能力规格说明 changes/ 2024-01-login-lockout/ proposal.md # 变更提案 tasks.md # 任务拆分 README.md # 变更说明常用命令我列一下后面实操部分会展开openspec init # 初始化 OpenSpec 目录 openspec validate # 校验规格和变更是否合法 openspec list # 列出所有变更提案 openspec plan # 基于变更生成实现计划 openspec propose # 创建变更提案 openspec implement # 标记/生成实现任务 openspec apply # 将变更应用到能力规格中注意这些命令在不同版本间差异较大我记录的以我当时实际操作的版本为准。后续使用时建议先跑openspec --help看文档。1.5 什么项目适合用 OpenSpec什么不适合个人小 Demo、两三天就能写完的一次性脚本说实话没必要上 OpenSpec那点结构负担还比不上写规格的时间成本。但凡是三个月以上的项目、多人协作、或者依赖 AI 生成大量代码的场景就值得引入。尤其适合以下情况团队里大量使用 AI 编码需要给 AI 更精确的任务描述业务需求经常变化需要一个“变更记录”和“需求基线”你希望代码审查不只是看代码还对照“当初到底想要什么效果”来审。2. OpenSpec 的核心理念规格优先变更驱动2.1 规格文件不是模板而是长期契约第一次看 OpenSpec 生成的规格文件模板时我觉得它有点啰嗦。一个“登录”功能要写场景、要写 constraints还要给每个需求编号。但等我用了一段时间之后意识到这才是它最值钱的地方。编号是重中之重。比如USR-AUTH-LOGIN-001这样的编号会从规格文件一直延伸到变更提案、代码注释、提交信息。排查 bug 时你能直接通过编号定位到最初的业务预期而不需要反复追问“这个逻辑本来是谁定的”。这比 JIRA ticket 还直接因为它在仓库里跟着代码走。2.2 规格文件里的 requirement 到底长什么样我实践的规格文件核心就是一个个带编号的 requirement。每个 requirement 包含描述、场景、约束三部分。场景部分我通常会再细分成“正常流程”和“异常流程”异常流程这条业界可验证的准则在 OpenSpec 里写起来非常自然。以“用户登录失败次数限制”为例我最后沉淀下来的规格长这样## Requirement: USR-AUTH-LOGIN-001 **Description** 系统应限制用户登录失败次数防止暴力破解。 **Scenario: 连续失败 5 次后锁定** - Given 用户“alice”已经连续输入错误密码 4 次 - When 该用户第 5 次输入错误密码 - Then 系统返回“登录失败次数过多”错误 - And 该用户账号进入 15 分钟的锁定状态 - And 锁定期间即使输入正确密码也不允许登录 **Scenario: 锁定后自动解锁** - Given 用户“alice”账号已被锁定 - When 锁定时间达到 15 分钟 - Then 系统自动解除锁定 - And 该用户可以再次尝试登录 **Constraints** - 锁定状态应记录在服务端不依赖客户端时间这种写法的好处是任何人在实现前就能确认哦原来锁定时长是 15 分钟锁定期间正确密码也不能登录。这两个点如果靠口头沟通几乎一定会翻车。2.3 变更提案一次改动只做一件事在 OpenSpec 里“变更提案”Change Proposal是我最喜欢的设计。它不像传统需求变更那样依赖口头确认或者聊天记录而是把一次改动的前因后果全部落到文件里。我创建的 proposal.md 里会强调三块内容Why为什么有这个变更来自哪个 issue、哪条业务反馈。What要新增哪些需求、修改哪些需求、废弃哪些需求指向具体编号。Validation如何验证这次变更做对了包括测试场景和手工验证步骤。写“What”时有玄机尽量引用规格里的需求编号而不是重新描述一遍。这样变更提案和规格文件就形成了引用关系不会出现两分文档内容打架的情况。2.4 validate 究竟在校验什么openspec validate表面上是检查格式实际用途比我想象中硬核。它会检查规格文件里的 requirement 是否有唯一编号编号命名是否规范每个 requirement 是否至少包含一个验收场景场景是否包含核心要素变更提案引用的需求编号是否真实存在目录结构是否符合 OpenSpec 约定。这一点对 AI 编程特别重要。你给 AI 一堆来源不明的文档它生成的代码很大概率是“看起来合理但缺边界”而经过校验的规格文件至少从结构上保证了边界条件和验收标准是完整的AI 就不容易自由发挥。2.5 OpenSpec 与 TDD、BDD 的关系很多人会把 OpenSpec 和 BDD行为驱动开发混在一起。其实它们不是一回事但可以很好地配合。BDD 关心的是“用 Given/When/Then 描述行为”OpenSpec 的验收场景也是 Given/When/Then 风格但它更关心这件事在需求和文档层面的表达。我现在的习惯是在 OpenSpec 里写业务需求和验收场景再由 AI 生成对应的测试用例然后通过测试来驱动实现。规格相当于“上游契约”测试是“实现层保障”。这个过程一旦跑通AI 生成的代码目标就非常明确让那些验收场景全部通过。3. OpenSpec 实操从零开始创建一个登录锁定功能3.1 环境准备与初始化OpenSpec 的 CLI 基于 Node.js 生态。我建议使用 Node.js 18 或更高的 LTS 版本然后直接通过 npx 初始化node -v npx openspec init没有全局安装依赖的话npx 会临时拉取包并执行。我在公司内网环境遇到过下载缓慢的情况可以先把命令包全局安装好npm install -g openspec openspec initinit跑完后仓库里会多出openspec/目录同时 README 里会追加一段说明告诉你这个目录是干吗的。如果项目已经有规范文档目录也可以在初始化时手动指定位置但我不推荐乱改路径因为后续所有命令和工具都按默认约定走路径一变反而容易出问题。初始化阶段最值得做的动作是修改project.md用两三句话说明项目是干什么的、核心领域有哪些。这个文件虽然短但它是 AI 理解项目背景的最重要入口不要留模板原文。3.2 规划能力规格从哪里开始写OpenSpec 的理念是先有“能力规格”specifications再有“变更提案”changes。如果你的项目还没有规格第一步就是为当前要做的功能建一个 capability。我的建议是不要一开始追求大而全。先把当前迭代要做的功能拆成能力点比如用户认证、订单管理、通知服务每个能力点单独一个 spec 文件。每个 spec 里先写 3~5 个你最确定的需求再慢慢补充。一口气写 20 个需求只会让你放弃维护。这里有一个关键判断能力规格是“稳定契约”变更提案是“这次要动的软件包”。稳定契约描述的是系统应有的长期能力不会因为一个迭代就频繁重写变更提案则记录这一次改动具体碰了哪些契约。二者不能混在一起。3.3 实例为“用户登录失败次数限制”创建 spec以登录模块为例我先在openspec/specs/capabilities/下新建目录user-auth/然后创建spec.md。文件开头用一段话描述该能力的边界# User Auth Capability 本能力描述用户认证相关功能包括登录、登出、登录安全策略。 本规格不描述用户管理、权限角色分配等外围功能。然后逐条写需求。写需求时我强烈建议直接写 Given/When/Then 场景因为你自己写着写着就会发现边界条件。比如“登录失败次数限制”这个需求只写“应该限制登录失败次数”太含糊落实到场景里才能暴露出 5 次、15 分钟、锁定期间不能登录这些细节。写完后跑一下校验openspec validate --capability user-auth如果校验失败通常是因为 requirement 编号冲突、场景格式不规范、或者缺少约束字段。根据报错信息逐项修掉就行。我的经验是校验器比人工 review 严格得多但它能抓出很多“你以为写了但实际没写完整”的漏网之鱼。3.4 创建变更提案把需求变成可执行计划规格文件定义的是“长期能力”但这次迭代只做登录锁定这一小部分。在 OpenSpec 模式里我为这个迭代新建一个变更提案openspec propose --name login-lockout --title Add login lockout policy这个命令会在openspec/specs/changes/login-lockout/下生成proposal.md、tasks.md等文件。我在proposal.md里记录变更动机、影响范围、以及要新增或修改的需求编号然后参考规格文件里的需求把验收条件同样写清楚。这个提案不需要完整复制规格内容关键是建立引用。这样 review 的人一眼就能看出“这个提案动了哪些契约”。3.5 让 AI 按规格生成实现OpenSpec 本身不是一个 AI Agent它输出的是结构性文档。但你可以把这些文档作为上下文喂给 AI 编码工具。我实际用的方法很直接在项目根路径下把spec.md中对应需求的片段、proposal.md、tasks.md一起贴给 AI给出明确指令“请按以上需求和验收场景完成实现不要新增规格中未描述的功能”AI 生成代码后我跑一遍测试和openspec validate再人工 review 不确定的边界。这种方式的生成质量比单纯丢一句“写个登录功能”要稳定得多。说实话有一次 AI 生成的代码里多了一段“连续失败 3 次需要输入验证码”的逻辑但规格里没有这个要求我几乎是扫一眼就发现了——因为你有了明确的规格基准偏离变得极其显眼。3.6 实现落地与状态流转OpenSpec 的提案状态流转大致是proposed - in-progress - implemented - applied。实现完成不代表提案生命结束当代码合并并验证通过后我会用如下命令将这次变更“应用”到能力规格中openspec apply login-lockout这个动作意味着变更提案中的需求正式成为能力规格的基线。之后如果再有人要改登录锁定逻辑他们会基于已经应用后的协议进行迭代整个过程清晰可追溯。如果团队有 CI我还会在流水线里加一步openspec validate防止文档结构与真实逻辑脱节。3.7 多人协作时的 review 重点多人协作场景下我最关注的是 review 变更提案而不是直接 review 大段代码。代码 review 往往陷入细节而提案 review 能先把方向对齐。我的团队现在要求每个迭代必须有一个独立提案review 时重点关注三点变更动机是否从业务问题出发而不是“顺手改一下”新增需求编号是否在规格中引人注目避免产生重复或冲突需求验证方式是否明确能不能在合并前跑出结果。这样一改我发现代码 review 的效率反而提高了因为大部分争议在提案阶段就已经解决。提案通过后代码实现基本不会出现框架级偏差剩下的是小修小补。4. 实操路上的坑与排查技巧实录4.1 初始化失败和网络问题npx 拉包失败是我遇到的第一个坑。公司内网镜像源配置不一总是超时。我的解决办法是固定 npm 镜像源或者全局安装。另外一个隐蔽问题项目仓库里之前已经存在一个空目录openspec/此时init会提示冲突不会覆盖既有内容。如果你确定里面没东西先移走再说别硬删免得误删其他文件。4.2 需求编号命名混乱OpenSpec 对编号要求唯一但并不限制你用什么命名体系。一开始我用了R1、R2这种序号结果需求一多就乱了。后来改成能力-子功能-三位数字的格式可读性瞬间上来了。比如USR-AUTH-LOGIN-001。如果你已经用简单序号铺开了改动成本会有些高建议尽早统一规范越晚越难改。4.3 规格写太细反而影响开发效率过度设计是另一个极端。有的需求真的没多少边界条件比如“修改用户昵称长度限制 50 字符”这种一条 requirement 配一个正常场景就够了。有的需求很复杂比如支付回调那场景就得写全重复回调、金额不一致、订单已关闭、签名错误。我的判断标准是这个需求写错边界是否会导致返工如果会写细如果不会能从简就从简。OpenSpec 的模板不会逼你写一堆废话别自我加压。4.4 变更提案与规格文件不同步最大的坑是提案里改了需求但规格文件没有更新或者反过来。导致后期 AI 读到两份自相矛盾的文件表现就是“这次生成的结果和上次不一样”。我在实践中的对策是约定合并代码的同时必须把对应提案标记为 implemented并在同一个 PR 里完成openspec apply。如果做不到就加一条 CI 规则提案状态和对应需求存在不匹配时禁止合并。4.5 CLI 校验失败时的排查顺序遇事不决先看报错但有几条我实际踩过的规则可以帮你少走弯路文件路径中不要使用空格OpenSpec 对目录名的解析比你想的严格spec.md中 requirement 的 ID 必须全局唯一跨 capability 也不行proposal 中引用的 requirement ID 必须在 capability spec 中真实存在引用不存在也会报错场景里的 Given/When/Then 关键字是强约定我试过写成中文“当/并且”校验直接失败。常见问题可能原因解决方式Validate 报 ID 重复不同 capability 下使用了相同编号在编号中增加能力前缀场景格式报错关键字不标准或缩进不对严格使用 Given/When/Then/AndApply 报需求不存在proposal 引用了已删除的 spec 文件统一在变更提案中先改 specInit 卡住不动npx 下载慢或源有问题全局安装后重试命令版本不一致OpenSpec 更新较快以本仓库package.json锁版本4.6 给 AI 的上下文别只给规格文件有一段时间我偷懒直接把spec.md丢给 AI让它生成代码。结果发现 AI 对项目里的现有架构一无所知生成的代码风格和项目现状不一致。后来我调整为把规格文件、相关模块的现有代码片段、项目技术栈说明一起作为上下文。OpenSpec 能帮你定义“做什么”但“怎么做”仍然需要结合项目实际来约束 AI。5. 个人体会与后续可以怎么扩展5.1 单人使用与团队使用的心态差异我自己单人项目上使用 OpenSpec它带来的最大好处是隔了一个月再回去改功能我能快速回忆起当时的业务预期不需要靠聊天记录考古。而在团队里它最大的价值是“减少需求口口相传的失真”。但前提是团队里至少有一个人愿意强制维护规格文件否则很容易变成应付差事。5.2 结合 CI/CD 做自动化质量门禁后续我计划在 CI 里加入两步第一步openspec validate作为格式门禁第二步检查变更提案是否包含了至少一个测试场景并与测试用例建立关联。这相当于把“需求完整性”的检查前置到构建管道里。如果你用的 CI 是 GitHub Actions甚至可以写一个简单 job专门在 PR 时跑openspec validate。5.3 让 AI Agent 直接消费 OpenSpecOpenSpec 的文件是结构化的这意味着它天然适合被 AI 当作上下文。我更看好的一种扩展是让一个 AI Agent 在后台监听新的变更提案自动根据提案生成实现计划、代码和测试然后人工 review。规则前置 验收驱动的组合能让 AI 编程从“随机模式”切换到“契约模式”这正是我正在测试的方向。最后再分享一个小技巧无论你是自己写还是让 AI 写规格写完之后都自己默读一遍场景看能不能在脑内演一遍用户操作。能完整走通一遍说明这需求是真清楚了走不通早改规格比返工代码划算得多。