AI Coding 用得越多,我越能感觉到两个问题。
第一个问题是:开发过程没有留下足够的中间记录。
Agent 可以很快生成大量代码,但方案不对时,常常只能整段推倒重来;某个架构决策是在对话里做的,过两天就不记得当时为什么这样选;一次任务被打断,换一个会话后,Agent 又要从头理解需求和已经做过的工作。真正丢失的不只是上下文,还有上下文中包含的判断。
第二个问题是:AI Coding Agent 没有跟着我和项目一起成长。
今天解决过的问题,下个月还可能重新踩坑;Agent 参与同一个项目十次,第十一次也不一定比第一次更熟悉。每次会话都很聪明,长期看却像一个不断失忆的新同事。
我开始学习 OpenSpec,就是希望先解决第一个问题,再看看它能为第二个问题提供多少基础。
OpenSpec 是什么
OpenSpec 不是一个新的 Coding Agent,也不是一种更神奇的 Prompt。它更像一套面向 AI Coding 的变更管理协议:在写代码之前,先把问题、要求、设计和任务写成文件;实现时按任务执行;完成后再把已经落地的行为合并到项目的长期规格中。
它的核心不是“多写几份文档”,而是把 AI Coding 从一次性对话,变成一个有明确状态的流程。
OpenSpec 中有两类关键信息:
openspec/changes/保存正在进行的变更,回答“这一次为什么改、改什么、怎么改”;openspec/specs/保存系统当前已经实现的行为,回答“这个系统现在应该怎样工作”。
前者是变更日志,后者是随代码演进的活文档。当一次变更被 Archive,其中的需求会进入主 Spec,变更目录则被移入 openspec/changes/archive/。
安装和初始化
OpenSpec 需要 Node.js 20.19.0 或更高版本。可以使用 npm 安装:
| |
macOS 也可以直接使用 Homebrew:
| |
然后在项目根目录执行:
| |
init 会询问正在使用哪些 AI 工具,为 Claude Code、Codex、Cursor 等工具安装对应的 Skill 或命令,并在项目中生成:
| |
这些都是普通文本文件,应该和代码一起提交到 Git,而不是仅保存在某一次会话中。初始化或 OpenSpec 升级后,可以用 openspec update 刷新项目中的 Skills 和命令。
config.yaml 值得单独提一下。它的 context 会进入每个 OpenSpec 工作流,适合放置会影响所有变更的少量事实;rules 可以约束某类产物;operations 则能约束 Apply 和 Archive 如何执行。例如:
| |
配置要尽量短。能从代码读出来的信息没必要重复塞进去,否则只会占用 Agent 的上下文。
一条完整的 OpenSpec 工作流
OpenSpec 官方的核心流程是 Explore → Propose → Review → Apply → Archive。如果把“发现问题后如何回退”也画进去,完整的开发过程更像下面这样:
这条流程最重要的是:**代码不是一开始就出现,而是在规格经过确认后才出现。**修改几段 Markdown 的成本,远低于让 Agent 重写几千行代码。
放进一个真实需求中怎么用
以“接入 RNNoise 降噪模型”为例。最初的需求往往只有一句话:
在现有实时音频处理链路中接入 RNNoise,降低环境噪声。
这句话还不能直接变成代码。按 OpenSpec 流程,可以这样做。
1. Explore:先把问题想清楚
在 AI 对话中说:
| |
Explore 阶段应该回答这些问题:
- 当前输入音频的采样率、通道数、数据类型和每次回调帧长是什么?
- RNNoise 放在采集、重采样、AEC、AGC 和编码之间的哪个位置?
- 使用 RNNoise 原生 C 实现,还是将模型导出为 ONNX 并通过 ORT 推理?
- 上游回调长度不固定时,如何组成 RNNoise 需要的 480 个采样点、10 ms 帧?
- 单声道、立体声和多路并发音频如何处理,每条流的模型状态由谁持有?
- 可接受的额外延迟、CPU 占用和内存上限是多少,初始化失败时是中断链路还是旁路原始音频?
如果这些结论没有想清楚,Agent 越快,只会越快地实现一个错误方案。
2. Propose:把讨论变成可审查的文件
方向确认后,继续说:
| |
Agent 会创建类似这样的目录:
| |
spec.md 不应该只写“接入 RNNoise”,而应该写成可验收的系统行为。例如:
| |
design.md 则应该记录“为什么”:为什么选原生 RNNoise 或 ONNX Runtime,为什么在重采样之后、编码之前执行降噪,如何管理每条流的隐状态,多声道是独立降噪还是先混音,曾经考虑过哪些方案又为什么放弃。这些内容比最后那几行推理调用更值得保留。
3. Review:把最贵的错误留在纸面上
Propose 完成后不要立即点头让 Agent 写代码。建议按下面的顺序审查:
- 看
proposal.md:解决的是不是正确问题,范围是不是过大; - 重点看
specs/:每条要求能否验收,边界和异常是否完整; - 有架构取舍时看
design.md:假设、备选方案和风险是否讲清楚; - 最后看
tasks.md:任务是否覆盖所有 Spec,有没有夹带与需求无关的重构。
发现“现有采集输出其实是 44.1 kHz 立体声”或“多路音频不能共用同一个 RNNoise 状态”,可以直接编辑 Markdown,也可以让 Agent 更新变更文件。审查完成前,不产生业务代码。
4. Apply:换一个干净上下文开始实现
OpenSpec 官方建议在新的会话中执行 Apply,把上一阶段的讨论噪声留在规格外面。可以直接说:
| |
Agent 会先读取这次变更的文件,再按 tasks.md 逐项实现。如果中途被打断,在新会话中再次要求 Apply 即可,新 Agent 会从第一个未勾选的任务继续。
如果实现过程发现原方案有问题,不要只在代码中偷偷改口径。先修正 Spec、Design 或 Tasks,再继续 Apply。否则文档与代码会从这里开始分叉。
5. Verify 和 Archive:用事实关闭变更
验收不能依赖 Agent 的一句“已完成”。至少要根据 Spec 执行:
- 单元测试和集成测试;
- 不同回调长度下的 480 采样点组帧、缓冲和输出时序测试;
- 多路音频的状态隔离,以及停止、重启和不连续跳转时的状态重置;
- 使用固定 WAV 样本与 RNNoise 参考实现做输出对比,同时检查 NaN、削波和样本数变化;
- 在目标设备上测量额外延迟、实时线程 CPU 占用、内存分配和长时间运行稳定性;
- 模型初始化失败、禁用降噪和不支持输入格式时,旁路策略符合 Spec。
OpenSpec 还提供可选的 Verify 工作流,可用来检查实现是否与变更产物一致。默认配置不一定安装它,没有 Verify 时,也可以让另一个新会话根据 Spec 做独立 Review。
所有任务、测试和人工验收全部通过后,再说:
| |
Archive 会把这次变更的行为要求合并到 openspec/specs/,并把变更目录移入 openspec/changes/archive/。Git 仍然是独立的事:规格、代码和测试应该在同一条变更链上提交和审查。
OpenSpec 如何解决“没有中间记录”
回到开头的第一个问题,OpenSpec 的作用非常直接。
| AI Coding 中的问题 | OpenSpec 的对应机制 |
|---|---|
| Agent 方案不符合预期,代码需要重写 | 在 Propose 和 Review 阶段先修正文本,延迟代码生成 |
| 不记得某个决策为什么做 | proposal.md 和 design.md 保存背景、备选方案与取舍 |
| 中途打断或上下文用完 | 变更文件提供上下文,tasks.md 勾选项提供显式进度 |
| 不知道代码是否真的完成 | Spec 把“完成”改写为可验收的场景 |
| 对话结束后信息消失 | 文件进入 Git,Archive 保留变更历史并更新主 Spec |
其中我认为最重要的一点是:**进度不再是 Agent 脑中的隐藏状态。**只要仓库还在,任何 Agent 都能通过读取文件重建任务状态。会话可以丢,工作不应该一起丢。
OpenSpec 能否让 Agent 跟着项目成长
答案是:能解决一部分,但还不是真正的“成长”。
OpenSpec 确实会让后来的 Agent 比以前更容易理解项目:
specs/持续积累系统已经承诺的行为;changes/archive/保留每一次变更的原因和过程;config.yaml把长期有效的项目约束注入每次工作流;- 决策与代码共同版本化,可以被审查、回滚和追溯。
这些解决的是“项目记忆”:Agent 进场后有一套更可靠的外部记忆可以读。
但历史记录不等于学习。OpenSpec 不会自动判断哪个过去的经验值得在新任务中复用,不会把一次线上故障自动提炼成新的审查规则,也不会记住使用者跨项目的偏好。如果没有整理,归档目录只会越来越大,而不一定越来越有用。
所以 OpenSpec 更像是一层必要的基础设施:它先把经验从会话中捞出来,但还需要更强的 Agent 把经验变成能够被下一次任务精准调用的知识。
我期待的下一代 AI Coding Agent
我理想中的 Agent 不是“永远不忘记任何事”。那只会制造更大的上下文和更多过期信息。真正的成长,应该包含三层记忆:
- 项目记忆:系统行为、架构边界、关键决策、当前任务和已知风险;
- 经验记忆:哪些方案曾经失败、为什么失败、后来如何修复、怎样提前发现;
- 个人记忆:使用者偏好的实现方式、可接受的风险、审查习惯和解释粒度。
这些记忆不能只管写入,还要有一个可审查的学习闭环:
这样的 Agent 才不是把历史聊天全部塞进 Prompt,而是能够做四件事:
- 知道什么值得记住,什么只是当时的噪声;
- 在正确的任务中取回正确的经验,而不是无差别加载所有过去;
- 当代码、Spec 和现实冲突时,会质疑旧知识,而不是把它当成永久真理;
- 让使用者知道 Agent 记住了什么,并可以删除、修正、回滚和限制范围。
我希望未来每次解决问题都不是一次性的 Token 消耗,而是给下一次开发降低一点成本。第一次需要我提醒 Agent 项目中的并发约束,第二次它应该主动检查,第三次这条经验应该已经变成 Spec、测试或工具中的自动化护栏。
使用 OpenSpec 的几个建议
一次 Change 只解决一个可验收目标
“重写账户系统”太大,“修改一个变量名”又不需要完整流程。更合适的粒度是:完成后能独立验收、必要时能独立回滚的一个行为变更。
Spec 写行为,Design 写取舍
Spec 描述外部可观察的行为和验收条件,不要把实现细节全部复制进去。Design 记录需要长期保留的技术决策,尤其是被放弃的方案和取舍原因。
不要让文档与代码分叉
实现中改了方案,先更新变更产物;合并代码时同时合并 Spec;验收未完成时不要急着 Archive。文档一旦失真,Agent 就会在错误的记忆上继续工作。
不是所有修改都需要仪式
拼写修正、明显的小 Bug 和无行为变化的重构,未必值得写完整 Proposal。OpenSpec 是用来降低歧义和变更风险的,不是把每一行代码都变成审批流程。
最后
AI Coding 放大了代码生成速度,也放大了错误方案的扩散速度。在这个阶段,仅仅追求“让 Agent 写得更快”还不够,我们需要的是一套让人和 Agent 都能知道正在做什么、为什么这样做、做到哪一步的工作流。
OpenSpec 的价值,就是把“聊过”变成“写下来”,把“Agent 说做完了”变成“按 Spec 验证过了”,把“换个会话就重来”变成“从下一个未完成任务继续”。
它还没有让 Agent 真正学会长期成长,但已经先为成长准备了可信的原料:规格、决策、任务、验证和变更历史。我期待下一步,AI Coding Agent 能在这些原料上形成可审查、可纠正、可遗忘的长期记忆。到那时,它才不只是一个每次都很聪明的临时工,而是一个真正会随着使用者和项目共同进化的协作者。