2025年的AI编程工具,说实话已经卷到了一种离谱的程度。Cursor、Claude Code、Codex CLI、OpenCode,把每一个单独拎出来都活得下去,但它们都共享同一个毛病:你让它改一个bug,它可能顺手把你命名了半年的接口变量全部重命名;你让它加一个筛选功能,它改完以后你根本不知道它动了哪些文件,review起来跟考古一样。我用OpenSpec大概是从2024年底开始的,这个东西解决的恰好就是这个问题——AI写代码之前,先立规矩,再动手。
OpenSpec不是又一个代码生成器,它是一个“规范驱动开发”的流程层。简单说,它让AI Agent在动代码之前,先产出一份人类可读的改动规格说明,明确“当前状态是什么、目标状态是什么、要改哪些文件、哪些是必须需求、哪些是期望需求”,等这份说明书被人工认可之后,AI才被允许进入执行模式。这篇文章我会把OpenSpec的安装、核心概念、工作流、实际案例和我在使用过程中踩过的坑全部分享出来,适合那些已经被AI编程工具搞到破防、但又不愿意放弃AI效率的开发者。
1. 为什么“先立规矩再动手”:AI编程最大的失控点
1.1 让AI直接改代码,改一次崩三处
我先说一个几乎所有人都会遇到的场景。你让AI给待办事项应用加一个“优先级”字段,它改起来特别快,几十秒就给你列了五个文件。你以为这就完了?仔细一看,它把整个表格组件重写了,之前手工调好的样式全丢了,测试用例也改了,甚至顺手把工具函数里的排序逻辑换成了另一种写法。这种“程序员的热情过度发挥”在AI身上体现得淋漓尽致,因为它不知道哪些代码是你精心设计过的、哪些是可以动的,它只知道你的描述里最接近的意图是什么。
我早期用Cursor做项目的时候,因为这类问题回滚过很多次。有一次给客户做一个小型CRM,我让AI优化一下列表查询接口的性能,它花了十分钟把后端ORM模型全部重构了,重构完数据库迁移对不上,前端接口字段也变了,整个项目直接跑不起来。当时我就意识到,问题不在AI的能力,而在于我给它的是“目标”而不是“约束”。AI写代码不是写不好,它是太自由了,自由到不考虑全局的稳定性。
1.2 人类最适合做的是“意图管理”,而不是“代码搬运”
后来我开始琢磨一个很朴素的问题:在AI编程时代,人类开发者真正的价值到底在哪里?代码的具体实现,AI已经写得很好了,尤其是那些重复的模式化代码。但有一件事AI短时间学不会,就是“知道自己手上有哪些雷”。哪些模块是上线了好几年的稳定代码,哪些函数被十多个地方引用,哪些改动会牵连到别的服务,这些信息散落在开发者的脑子里,不在AI的训练数据里。
传统的代码审查,是AI改完代码之后你来review diff,这叫“事后检查”。问题在于AI的diff太发散了,它一口气改了二十个文件,你根本看不过来,也没法判断每一个改动背后的理由。OpenSpec换了一个思路:把审查点从“代码”提前到“意图”。在AI动手之前,先让它把要怎么做、改哪些文件、预期的行为变化写出来,你审查的是一份规格说明书,而不是几百行diff。这本质上是一种“意图管理”的分工:人类管方向和边界,AI管执行和细节。
1.3 OpenSpec的定位:AI Agent的“产品经理”
我经常给朋友打一个比方:让AI直接写代码,就像你请了个装修师傅,告诉他“我要把厨房弄好看点”,然后让他自由发挥。师傅可能给你装了个欧式吊顶,但是水管没动,因为你没说。OpenSpec做的事情,是先让师傅出一张设计图,标注清楚哪里要拆、哪里要留、用什么材料、预算多少,你看了设计图说行,他才开始动工。装修师傅还是那个师傅,但流程变了,翻车概率就大幅下降了。
所以OpenSpec的定位,不是替代Cursor或者Claude Code,而是站在它们上面的一层“流程控制中枢”。如果说AI Agent是执行者,那OpenSpec就是那个逼着AI先做方案的产品经理。它不写代码,它逼着AI把思路写出来,然后等你说“可以动手”。这个概念在整个AI编程工具链里非常稀缺,也是我当时决定深入用它的根本原因。
2. OpenSpec核心机制拆解:plan、run、review三段式
2.1 三个命令,把AI编程变成有状态的工程流程
OpenSpec整个工作流,可以压缩成三个核心命令:openspec plan、openspec run、openspec review。这三个命令对应了“计划、执行、验收”三个阶段,和传统软件工程的瀑布模型有点像,但它不是死板的流程,而是一种轻量级的质量门禁。
先解释一下plan阶段。你给OpenSpec一个模糊或清晰的需求描述,它会调用你配置好的AI模型,去读懂项目现有代码,然后生成一份规格文件。这个文件不是代码,是描述改动的文档,包含当前实现状态、目标实现状态、受影响文件、需要满足的条件列表等等。生成的spec会被放在项目里的一个固定目录下,等着你人工审查。
等你确认spec没问题,再执行openspec run。这个时候AI才会真正去修改代码,按照spec里约定的内容逐个实现。这里有一个很关键的设计:run阶段AI只能围绕spec文件里提到的文件和需求去改,不能自由发挥去动其他代码。最后用review命令对执行结果做核验,看看改动是否真的和约定一致,有没有漏改、错改。整个过程有据可查,AI不是随便改完就交差,而是必须面对一个“验收标准”。
2.2 项目结构:一切有据可查
OpenSpec初始化之后,会在项目根目录生成一个.openspec文件夹,我习惯把它提交到Git仓库里,因为里面的spec其实就是一个轻量级的需求和设计文档库。这个目录下通常会有几个关键部分,我用一个表来说明:
| 目录/文件 | 作用 | 我的使用习惯 |
|---|---|---|
project.md | 记录项目整体约定、技术栈、架构约束 | 每次动大需求前先更新它,让AI有全局上下文 |
specs/ | 存放已经完成并归档的规格说明 | 当作项目文档的一部分,随时能查 |
changes/ | 存放当前正在进行的变更规格 | 每次开工前都来这个目录看一眼状态 |
agent-log.md | 记录AI在每次任务中的执行日志 | 出了问题能回溯AI到底做了什么 |
这个结构最让我舒服的一点,是它天然形成了一种“项目记忆”。以前用AI改代码,这周改完下周就忘了当时为什么这么改。有了spec归档,每次改动的原因、目标、范围都清清楚楚。后来我跟团队配合的时候发现,新成员看一遍specs/目录里的历史文档,比让他自己读三天源码还有效。
2.3 spec文件就是一个“需求规格书”
OpenSpec的每个变更,都对应一个.md格式的spec文件。我用过一段之后,总结出它包含的几块核心内容:首先是变更概述,一句话说清楚这次要干嘛;然后是当前状态和目标状态,这个非常关键,它强迫AI先去看懂现有代码,而不是凭空想象;还有受影响文件清单,这相当于给AI画了一个“安全范围”,文件清单之外的代码不允许动;最后一个部分是需求列表,里面区分need和want,need是必须实现的核心功能,want是有余力再做的优化点。
举个例子,我让AI给一个待办事项应用增加标签功能,spec文件的核心部分大概长这样:
# spec: add-tags-to-todos ## Overview 为待办事项增加标签分类能力。 ## Current State - Todo表只有 id、title、completed 三个字段 - API只支持新增、查询、完成待办 - 前端不展示任何分类与筛选 ## Desired State - Todo新增 tags 字段,类型为 string[] - API支持按标签筛选列表 - 前端在待办项上展示标签,并支持按标签过滤 ## Files to Change - src/models/todo.ts - src/routes/todos.ts - src/pages/TodoList.tsx ## Requirements **Need(必须完成)** 1. todo模型增加 tags 字段,默认空数组 2. create/update 接口接收 tags 参数,并做格式校验 3. TodoList组件展示标签,并支持点击标签筛选 **Want(期望实现)** 1. 标签支持自定义颜色 2. 标签支持模糊搜索这个文件一旦生成,就相当于你和AI之间签了一份合同。AI在执行阶段如果跑偏了,你只需要把这份spec拿出来对照,就能明确告诉它哪里违反约定。这种“契约感”是直接对话式编程给不了的。
3. 快速上手:安装OpenSpec并跑通第一个AI改造任务
3.1 安装与初始化:两条命令的事
OpenSpec的安装很简单,Node环境正常的机器上,一条npm命令就能装好:
npm install -g openspec装完以后,在你要改造的项目根目录里执行初始化:
openspec initinit过程会引导你选择默认的AI模型和provider,比如Claude Code或者OpenAI兼容接口,还会生成上面说的.openspec目录。第一次跑init的时候,我建议你认真看一下生成的project.md,把项目的技术栈、目录结构、编码约定写进去。这一步很多人会偷懒跳过去,但后面你就发现,project.md写得越清楚,AI在写spec的时候越准确,因为它的思考有了一个“项目世界观”。
初始化完成之后,你就可以开始第一条plan指令了。这里有个小技巧:不要把需求描述得又大又空,最好一句话说清目标、范围、约束。比如“让Todo模型支持标签,保留现有接口兼容”和“给Todo加标签”,前者生成的spec质量会高很多。
3.2 实战:让AI给自己写代码流程加上Todo标签功能
我拿一个真实的Todo项目来走一遍完整流程。先运行plan命令:
openspec plan "为Todo增加标签功能,支持按标签筛选,保留现有API兼容"这一步会唤起AI工具去读项目代码,然后生成spec文件。等命令结束后,我先不急着run,而是打开生成的spec文件,逐条检查它的表述是否准确。有一次AI生成的spec里写“注意删除旧的todo删除接口”,这明显是过度理解了需求,我赶紧在Files to Change里加了一个“禁止删除任何现有路由”的约束,再重新plan一次。
确认spec没问题后,执行:
openspec runOpenSpec会进入执行模式,AI开始按照spec改动代码。整个过程和普通AI编程差不多,但有一个核心差异:它不会开第二个无关文件去“顺便优化”。执行完以后,我会跑一遍测试:
npm test然后再执行review命令:
openspec reviewreview会检查改动是否和spec里的需求一一对应,有没有漏项。如果review发现AI没有完成某个need项,它会直接标记为不通过,你就得重新让AI补上。这一步相当于给AI的产出做了“校验和”。
3.3 在Cursor、Claude Code、OpenCode、Codex里用OpenSpec
我常用的组合,是OpenSpec作为流程管理层,Cursor和Claude Code作为执行引擎。具体操作是:在Cursor的Agent模式或者Claude Code里,直接把openspec plan、openspec run、openspec review当成工具命令来调用,让AI自己去执行命令、读取spec、改动代码。
这里面有个顺序上的讲究:plan阶段和run阶段最好用同一个AI会话,因为plan阶段AI已经读了一遍项目结构,对项目的理解会延续到run阶段,跑起来更顺。但如果你用的是Codex或者OpenCode这类工具,要注意不同工具的上下文管理方式不一样,切换工具时可能会丢掉之前的记忆,所以最稳妥的方式是:每次执行run之前,都让AI重新读一遍spec文件,而不是假设它还记得。
顺便说一句,有一种常见错误是让AI既写spec又自己执行,相当于“自己写需求自己实现”,最后spec往往写得很敷衍。我的经验是:在plan阶段和run阶段之间,至少要“人工停顿一下”,你亲手看过一眼spec,再放AI进去跑。这一步花不了多少时间,但能省掉后面至少一半的返工。
4. 案例拆解:一次完整的接口重构是如何落地的
4.1 需求描述:把错误处理逻辑从Controller抽到Middleware
我拿一个稍微复杂一点的案例来说明OpenSpec在高风险重构里的价值。有个老项目的API接口,每个Controller里都写了一段重复的try/catch错误处理,代码互相拷贝,改一处要同步改好几处。我想把错误处理逻辑统一收敛到一个全局中间件里,删掉各个Controller里的重复代码。
迁移的时候我给自己提了一个硬性要求:接口行为不能有任何变化,对外部调用方完全透明。这种“不能有任何行为变化”的重构,恰恰是直接让AI改最容易翻车的场景,因为AI会惯性觉得“既然重构,那就顺手优化一下响应格式”。
所以我在plan命令里把约束写得很明确:
openspec plan "将Controller中重复的try-catch错误处理抽到全局中间件,API响应格式严格保持不变,禁止修改任何路由路径"4.2 spec文件里的关键设计:约束比需求更重要
生成的spec里,Desired State部分描述了重构后的代码结构,Files to Change列明了涉及的文件清单,这部分没什么意外。真正有价值的是Requirements里的Need约束,我也写进了“禁止”条款:
**Need(必须完成)** 1. 新增 src/middleware/error-handler.ts,统一捕获异常并返回错误响应 2. 所有Controller中重复的try/catch代码块移除,改用中间件处理 3. 错误响应格式与现有格式完全一致:{ code: number, message: string } **Want(期望实现)** 1. 中间件支持异步错误捕获 2. 增加请求ID字段,方便日志追踪 **Do Not** - 不得修改路径参数和路由定义 - 不得修改HTTP状态码映射关系 - 不得改动数据校验逻辑你发现没有,我专门加了Do Not这一段。这是我在一次糟糕的AI重构经历之后总结出来的做法——AI对“能做什么”的理解往往远超你对它的预期,但“不能做什么”你必须显式告诉它。没有这一段,AI看到Controller里有个变量命名不规范,会顺手改掉,虽然不破坏功能,但让review diff变得巨大,纯粹增加你的工作量。
这个spec提交给AI执行后,改动集中在新增middleware文件和删除各Controller的重复代码。执行完我跑了全量接口测试,所有用例通过,响应体结构对比一致。整个过程最让我满意的是,review阶段的输出能明确列出哪些代码被删除、哪些文件没被触碰,方便我做二次确认。
4.3 AI执行后的验收环节:不是跑通就完事了
openspec review这个命令的验收意识,很多开发者可能觉得多余。但我想说,它其实是整个流程里最被低估的环节。很多情况下AI执行完毕后代码能跑,但它可能用了“伪装实现”——比如为了保留一个方法签名,内部直接抛异常而不是真正实现逻辑。这部分静态看代码很难发现。
我现在的做法是:review通过后,我还会跑一遍静态检查工具和测试覆盖率,用工具去兜底AI的“软性偷懒”。然后我会抽查一下关键文件的diff,看有没有和spec无关的改动。如果一切正常,才把这次变更归档到specs/目录里,变成项目的正式文档资产。
还有一个小细节:审查的时候不要只看代码,还要看agent-log.md。这份日志会记录AI在执行过程中做了哪些决策、遇到过什么问题。有一次我发现AI在日志里写“为避免破坏现有测试,跳过了一个文件的修改”,就立刻意识到有个功能实际上没实现。所以,日志是用来抓AI“自作主张”的重要证据链。
5. 常见问题与避坑实录
5.1 AI总是改超范围代码,怎么破?
这个问题几乎每个用OpenSpec的人都会遇到,尤其是在最开始几周。AI的Files to Change通常也被约束了,但它还是会想办法“越界”。比如它可能在改动schema时,顺手给另一个无关实体也加了字段;或者为了测试方便,在配置里加了一个硬编码的环境变量。
我的解决办法有两个层面:第一,在spec里用Do Not显式列出禁止改动的内容,这条对AI非常有效;第二,把改动范围控制在更小的“变更单元”里,一次只做一件事。比如一个需求同时涉及后端API和前端页面,我会把它拆成两个独立的change,分别plan和run。这样即使AI跑偏,定位和回滚也容易,不会一个错误牵连整个需求。
5.2 spec写得“差不多”,执行效果就打五折
如果你的spec里都是“优化用户体验”“提升代码质量”这种模糊描述,那AI执行出来的结果基本等于没约束。OpenSpec的价值全在spec的精确度上,need项要具体到可以验证的行为,比如“新增接口GET /api/todos?tag=work,返回带有work标签的待办列表”,而不是“支持按标签查询”。
建议在spec里为每个Need项写一个明确的验收方式,要么是接口返回结构,要么是一个单元测试用例。AI执行完review时,它会去对照这些标准。如果你自己都没想清楚验收标准,AI自然只能靠猜。
5.3 小改动也走完整流程,会不会太重?
有朋友问我,改一个拼写错误、调一个配置项,也要走plan、run、review?我的回答是:完全没必要。我自己判断的标准很简单,如果这次改动影响面小于一个函数、不涉及数据模型、不改变对外接口,直接让AI改完拉倒,不需要开spec流程。但如果改动跨了两个以上文件、涉及数据模型或接口路径,那走OpenSpec就是值得的,因为这类改动的回滚成本太高。
我的经验数据是:在OpenSpec流程下,一次跨文件重构的平均耗时,和直接让AI改差不多,但review时间能显著下降,因为大多数实现问题已经在plan阶段被拦截了。算总账并不亏。
5.4 如何把OpenSpec接入CI/CD和代码质量检查
OpenSpec本身不解决CI/CD的问题,但它的产物可以嵌入到现有流程里。我现在的做法是:在MR的CI流水线中增加一个步骤,先执行openspec review,再跑一遍lint和测试,甚至接上像SonarQube那类静态扫描工具做额外检测。一旦review发现spec中某个Need没有满足,流水线直接中断,MR不能合并。
这种做法实际上把“AI写代码的验收权”从个人经验升级成了自动化门禁。以前是人去逐行看AI的diff,现在是让流程卡住不规范的结果。对于团队协作来说,这是一层很划算的保障。
6. 实际收益:OpenSpec到底值不值得用
6.1 直接改代码 vs OpenSpec流程,差别有多大
我整理了这段时间实际使用下来的对比,按我自己的体感打分:
| 维度 | 直接让AI改代码 | OpenSpec流程 |
|---|---|---|
| 单次任务耗时 | 快,但返工率高 | 前期慢,后期稳定 |
| 跨文件改动安全度 | 低,容易失控 | 高,有边界约束 |
| Review成本 | 高,diff巨大 | 低,按spec逐条核对 |
| 可追溯性 | 差,改完就忘 | 好,有完整文档归档 |
| 团队协作 | 个人经验主导 | 流程规范驱动 |
| 学习成本 | 基本为零 | 需要适应plan阶段 |
整体来看,OpenSpec更像是一个“纪律工具”。它不会让AI变得更强,但会让AI的产出变得可控。如果项目是临时脚本、一次性工具,完全没必要用;如果是长期维护的产品代码、特别是团队协作,它的价值会随着时间指数上升。
6.2 什么项目最适合用OpenSpec
我总结了三类最适合OpenSpec的场景。第一类是数据模型和业务逻辑复杂的项目,这类项目改动一个字段往往牵一发动全身,spec里的“影响范围”描述能避免很多连锁事故。第二类是客户项目,需求变更是家常便饭,每次变更都有spec归档,后续追溯和报价都有依据。第三类是团队里有多个开发者在不同AI工具之间切换的项目,OpenSpec提供了一个统一的流程框架,避免每个人各写各的。
反过来,如果项目还在非常早期的原型验证阶段,需求一天变八次,那么每次变更都走plan流程会很痛苦,这时候更适合跑得快的玩法,等需求稳定了再引入OpenSpec也不迟。
6.3 我的使用建议:从小任务开始
如果你想尝试OpenSpec,我的建议是别一上来就拿核心模块做实验。先挑一个两周后的重构任务,或者一个独立的新增小功能,用OpenSpec走一遍完整的plan-run-review流程,体会一下“先立规矩再动手”的感觉。在用完第一次之后,你大概率会形成一个新习惯:不管用什么AI工具,都习惯先把约束写清楚,再让AI去执行。这个习惯本身,比OpenSpec这个工具更值钱。
另外一个小技巧:spec文件写完后,先放二十分钟再看一眼,再去跑run命令。这个“冷静期”能让你发现很多第一遍写的时候没注意的漏洞,比如缺失的边界条件、过宽的改动范围。等你在plan阶段把能想到的坑都填平,run阶段的AI几乎不会让你失望。