理解 OpenSpec 与规范驱动开发

image-800-1782528813566554045.webp

每个代码库都有两套叙事。

第一套很容易找到,它就藏在代码库里。每一笔 commit、每一段 diff、每一次重命名、每一行被删掉的代码,只要你找对地方,都还在。

第二套则很难复原:它是系统为什么会变成这样的历史。

为什么这个接口要这样设计?为什么服务层要强制执行这条规则?为什么当时放弃了更简单的方案?为什么会有这个约束存在?

这些答案通常不在代码里,而在工单、Slack 线程、会议纪要、PR 评论里,以及在场的某个人脑子里。

OpenSpec 的出发点很简单:

变更背后的意图,应该和代码本身一样被认真版本化。

它让每个系统都有一份“活的规范”,每个提议中的变更都有一个可评审的形状,每次上线的变更都能在项目历史里找到永久落点。写第一行代码之前,你就已经定义清楚:改什么、为什么改、什么不能动、怎样算完成

这也让 OpenSpec 在 AI coding agent 进入工作流后尤其有价值。一旦意图被明确写下来,实现就变成了一件容易跟进的事。

有三件事经常失控:

  • Unstable context(上下文飘忽不定)。 Agent 忘了你三句提示之前说过的话,或者每次运行都幻觉出一个不同的项目结构。
  • Uncontrollable output(输出不可控)。 你说要个小重构,它给你改了二十个文件。
  • No engineering hooks(没有工程接口)。 没法把这个流程接进 CI、接进 Code Review、交给队友。它只是一段聊天记录,不是一个系统。

OpenSpec 是个小型开源工具,专门解决这三个问题。它不会取代你的 AI agent,而是在 agent 前面加一层有纪律的规范层,让 agent 有东西可以锚定工作,也让你有可以review、可以推回重做的东西。

一句话总结:OpenSpec 设定边界,agent 负责干活。

OpenSpec 到底是什么

OpenSpec 是一个规范驱动开发(SDD)的实现工具。它不是框架,不是 agent,不是运行时。它是一种工作流,也是一种目录结构,把规范变成系统的single source of truth(唯一事实来源),记录系统现在是什么、正在变成什么。

规范是有人写的,只是写得七零八落。散在 Notion、Confluence、Jira ticket 底部、六个月没人打开的 README 里。常见问题是规范过时,进而引发一堆麻烦:

  • Overall intent gets lost(整体意图丢失)。 新贡献者看不清系统的形状。
  • Feature overlaps go undetected(功能重叠没人发现)。 两个独立看都没问题的 spec,合在一起会打架。
  • Validation is impossible(验证无从谈起)。 线上系统没法检查规范是否还跟现实一致。
  • No shared map(没有共享地图)。 没人——不管人类还是 AI——能一致地推理整个系统。

OpenSpec 的做法是把规范收敛成每个系统一份活的文档,再加一个定义清晰的变更队列流经其中。换句话说,它同时解决两件事:让“当前系统是什么”有一个单一入口,也让“系统正在变成什么”有一个可审阅、可回溯的流程。Agent 读规范、Agent 改规范、Agent 实现规范。每一步都可检查。

image-openspec-800-1782528813877653631.webp

心智模型

理解 OpenSpec 最直观的方式,是把它看作一份契约。Agent 是承包商。

空着手、没带契约来的承包商,会按照他认为你想要的样子去盖楼。有蓝图的承包商,才知道该交付什么、什么不能碰、以及“完成”长什么样。OpenSpec 就是那份契约。Agent是承包商。

这份契约放在一个结构化的规范文件里,agent 动手之前必须先读。这是一个简化的例子,针对一个 Java/Spring Boot 服务:


project:
  name: user-service
  language: Java
  framework: Spring Boot 3.x
rules:
- "Services cannot access the database directly. Use the Repository layer only."
- "Controllers are responsible for parameter validation only. No business logic."
- "All public interfaces return Result<T> for consistent error handling."
- "No new third-party dependencies without an ADR."
- "Forbidden: storing PII in logs, synchronous calls to external services, raw SQL outside the Repository layer."

这个文件就是 agent 的工程手册。它定义技术栈、架构、边界、agent 能做什么、不能做什么。agent 每次执行任务前都会读一遍。如果它建议的方案违反了规则,违规会出现在规范里,而不是埋在聊天记录里。

工作流

这个循环是 OpenSpec 的核心。它把写代码拆成三个阶段,和资深工程师的工作方式一模一样:先对齐要做什么,再动手做,然后把决策永久固化。

每次变更都要经过工具注入到你 agent 里的三个斜杠命令:

  • /openspec:proposal <change-name>** — 描述你想做什么。agent 写入一个变更文件夹。现在不写代码。**
  • /openspec:apply <change-name>** — 规范通过评审后,agent 才开始写代码。严格按规范执行。**
  • /openspec:archive <change-name>** — 合并后,变更被折叠进主规范。规范与现实保持同步。**

proposal 阶段会在 openspec/changes/<change-name>/ 下生成四个文件:


openspec/changes/<id>/
├── proposal.md   ← 为什么(问题、动机、范围)
├── design.md     ← 技术决策与方案
├── tasks.md      ← 实现检查清单
└── specs/        ← 规范增量(不是单个 spec.md)
    └── <capability>/
        └── spec.md

一个实际例子

我们给服务加一个“列出用户”的接口。

第一步:Propose(提议)。

/openspec:proposal Add user list API

agent 先读现有规范,然后在写任何文件之前回来提问。认证用 cookie 还是 bearer,还是都要?分页用 cursor 还是 offset?排序字段有哪些,无效值怎么返回?空结果集返回 200 加 [] 还是 404?任何会改变公开接口的事,agent 不会瞎猜。歧义解决后,它在 openspec/changes/add-user-list-api/ 下生成 proposal.mddesign.mdtasks.mdspecs/<capability>/spec.md

仅此而已。这个接口被描述成一个接口契约,不是一个 Python 函数。

如果你熟悉 /grill-me 这个 skill,工作方式一样。

第二步:Review(评审)。


openspec list
openspec show add-user-list-api
openspec validate add-user-list-api

你读规范的方式,就像读 PR 描述。接口清不清晰。你发现缺了分页、缺了排序参数、错误场景没说清楚。你打回:

加上基于 cursor 的分页,默认 20 条,最大 100。加上 sortorder 查询参数。sort 无效时返回带结构化错误的 400。

agent 只更新规范。不动代码,不动 tasks,不会“提前开始实现”。这就是纪律。

第三步:Apply(应用)。

/openspec:apply add-user-list-api

agent 开始写代码,严格按 tasks.md 执行。每个任务是一个 checkbox。每完成一个就更新状态。你可以在任意任务后暂停,检查 diff,再恢复。

第四步:Archive(归档)。

/openspec:archive add-user-list-api

变更被合并进 openspec/specs/ 下的主规范。变更文件夹移到 openspec/archive/。规范现在反映了系统实际的样子。六个月后,当有人问“为什么 user list API 长这样?”,归档的变更文件夹就是答案。

提议和应用之间的分割,正是你随便跟 AI agent 聊时会跳过的步骤。第一天会觉得慢。但它是系统在第二百天还能保持 coherent 的唯一原因。

Delta specs 与唯一事实来源

一个微妙的设计选择让这个循环同时适用于人类和 AI:变更过程中,规范永远不会被直接编辑。OpenSpec 用的是 delta specs

delta 是一份小文档,用 plain language 说明改了什么:

  • ADDED Requirements。系统新增的行为。
  • MODIFIED Requirements。行为改了形态。
  • REMOVED Requirements。被拿掉的行为。

Deltas 对人类一目了然,对 LLM 解析起来 trivial。你不需要 diff 整份规范来看某个变更做了什么。归档时,deltas 合并进 openspec/specs/ 下的主规范,变更文件夹移到 openspec/archive/ 作为永久记录。

这样你同时得到两样东西:

  • 一份活的唯一事实来源,始终反映当前系统。
  • 一份完整的审计追踪,记录系统怎么变成今天这样。每个架构选择、每个 API 契约变更、每个“以前做 X 后来改成 Y”都能在 archive/ 里搜到。

对 AI agent 来说,deltas 是一种强制约束:它把“要改什么”缩小成一份可读的增量,让上下文更稳定;把“做到什么程度算完成”落到可验证的检查点上,让输出更可控;同时也为校验与自动化(例如接进 CI)提供了工程接口。模型不需要去推理一大份文档然后猜什么是新的。它只需要读一份几百词的 delta,然后合并。这是更小、更可靠的任务。

更进一步:worktrees、subagents 和 ADRs

基本循环就位后,如果你只想落地最小闭环,上面三步已经够用。下面两个扩展主要面向并行 shipping 的团队,以及需要长期沉淀决策背景的场景。

Git Worktrees** + ****subagents****。** 模式很简单。先在 main 上提议所有即将到来的变更,保持规范一致。然后在各自的 worktree 里 apply 每个变更,通过 subagent 执行。每个 subagent 合并前先跑验证。变更按顺序回来、合并、归档。唯一事实来源的规范只在尘埃落定后更新一次。多条变更流同时推进,规范不会在团队脚下漂移。

ADRs** 与 specs 并列。** Specs 描述系统**做什么**。它们不说为什么选了这个架构。OpenSpec 支持 spec-driven-with-adr schema,在主规范旁边加入架构决策记录。每份 ADR 记录背景、考虑过的选项、决策、以及后果,一直保留到归档。一年后,ADR 回答“为什么这个服务长这样?”,规范回答“这个服务做什么?”

这两个扩展让决策可被发现,而不是埋在 Slack 线程里,或存在某个恰好记得这件事的人的脑子里。

和 CLAUDE.md 的区别

这是我在团队里引入 OpenSpec 时收到的第一个问题,而且很合理。仓库根目录下的 CLAUDE.md.cursorrules 文件,是给 agent 提供上下文最直接的第一步尝试。OpenSpec 做的是不同的事。

一个上下文文件是静态且只读的。你写一次,agent 读一次,时间一长就会漂移,不再反映系统实际行为。没有验证步骤,也没有“这条规则只适用于变更 X”的概念。就是一段模型每次都读的大段文字。

OpenSpec 是结构化的、可验证的、不断累积的。规范和实现会做 diff。变更流经一个你可以 review 的循环,像代码一样。决策在 archive/ 里积累。契约会随着时间越用越锋利,而不是越来越模糊。

它和 GitHub Spec Kit 或 Kiro 的位置也不同,那些是更重、更 opinionated 的框架。OpenSpec 更像一种工作流,不是一个框架。它和 Claude Code、Cursor、OpenCode,或任何能读文件夹、跑 CLI 的 agent 都能配合。一个下午就能落地,现有工具全保留。

Drawbacks(局限)

OpenSpec 会让你慢一点。规范层增加了开销。写代码之前要写东西、review、维护。问题是这个成本什么时候回本。

以下情况回本:

  • The project is brownfield(项目是 brownfield)。 一份新规范能捕捉当前系统状态,不需要强制重写。新变更干净地流入。
  • The change is real(变更是真的存在)。 多文件工作、新端点、schema 迁移,任何初级工程师都需要设计文档的工作。这正是 AI agent 最容易跑偏的地方。
  • The team is large enough to need coordination(团队大到需要协调)。 当两个人没法把整个系统装进脑子里时,一份活的规范给了他们一张共享地图。
  • You are using AI agents heavily(你重度使用 AI agent)。 独立创始人、只有一个资深工程师的小团队、任何 agent 负责 30% 以上代码的场景。Agent 比人类更需要一份契约,因为 agent 在 session 之间没有记忆。

以下情况不回本:

  • 单行 bug 修复。
  • 一周内就会被删掉的临时原型。
  • 小到你可以把整个系统装进脑子、从来不需要对它们用 AI agent 的个人项目。

老实说:价值随变更规模、团队规模、以及对 AI 的依赖程度缩放。对一个三文件的周末脚本,OpenSpec 是杀鸡用牛刀。对一个三个工程师每天上线的服务,再加上 agent 在循环里,它就是一个系统漂移和一个系统复利之间的区别。

从chatbot到contractor

OpenSpec 有趣的地方不在于工具本身,而在于心智能的转变。

你不再把 agent 当一个聊天机器人,碰巧可能给你一个能用的答案。

你开始把它当一个需要蓝图、需要 review、需要归档步骤的 contractor。工作从“问 AI 然后复制答案”变成“对齐规范、看着 agent 按规范执行、完成后更新规范”。

这就是我们和初级工程师、和外包承包商、和自己一直用的循环。Spec、build、archive。唯一的新鲜事是执行者是个模型而不是人,而且这个循环现在快到来一天可以跑几十次。

瓶颈从来都不是敲代码。