最近博客停更两周,没摸鱼,是把所有时间都砸在了一个新项目 voice-shopping 上 —— 一个基于多智能体的语音购物助手。但比项目本身更值得说的是:整个项目我没写一行"自由发挥"的代码,全程用 OpenSpec 的 Spec Coding 流程驱动 Claude Code 来做。这篇就不展开聊技术栈了,重点说说 OpenSpec 这套玩法在真实项目里到底怎么跑、踩了哪些坑、什么场景下值。
项目基本情况
voice-shopping 是个语音购物助手,用户通过语音和 AI 助手自然交互,系统自动完成意图理解、需求澄清、商品推荐、情感应答。技术栈一句话:
- Java 21 + Spring Boot 4.0.5 多模块工程(common / infrastructure / ai / business / web 五层)
- AgentScope Java 1.0.11 编排 4 个 Worker Agent + 1 个手写 Orchestrator 状态机
- qwen-max / qwen-turbo / qwen-plus 三档模型分场景使用,text-embedding-v3 1024 维向量
- PostgreSQL + pgvector 关系数据 + HNSW 向量检索 + JSONB 属性过滤
- Redis 7.x 三层记忆架构(短期记忆 List、会话状态 Hash、scope 缓存)
- 阿里云 NLS 流式 ASR(Paraformer 实时版)+ TTS(CosyVoice)
- Sa-Token 1.45.0 多商家认证 + 行级数据隔离
- WebSocket 全双工,二进制帧传 PCM、文本帧传控制 JSON
亮点是 多视角点评团(Perspective Team) 机制 —— 同一批候选商品由"价格敏感型 / 专业极客型 / 新手小白型"三个并行 Agent 各自点评,通过事件总线合流,给到 EmotionAgent 包装成最终话术,比单一推荐链路更接近真实导购场景。
整体链路大致长这样:
flowchart LR U([用户语音]) -->|PCM| ASR[ASR
Paraformer] ASR -->|文本| ORCH{Orchestrator
状态机} ORCH --> CL[ClarifyAgent] ORCH --> REC[RecAgent] ORCH --> EM[EmotionAgent] REC -.候选.-> P1[价格敏感] REC -.候选.-> P2[专业极客] REC -.候选.-> P3[新手小白] P1 & P2 & P3 -->|事件总线| EM EM -->|文本| TTS[TTS
CosyVoice] TTS -->|流式音频| U
意图分支路由:
| 意图 | 链路 | 备注 |
|---|---|---|
PRODUCT_RECOMMENDATION |
Clarify → Rec → (Perspective?) → Emotion | 主链路 |
PRODUCT_COMPARE |
Rec(基于上轮重排) → Emotion | 用 last_recommendations 改写过滤条件 |
CLARIFY_NEEDED |
Clarify | 循环直到 READY |
ORDER_CONFIRM |
OrderService 走原子扣库存 | AFTER_COMMIT 行为回流 |
CHITCHAT |
ChitchatReplyPool(不调 LLM) | 模板池随机抽 |
OUT_OF_SCOPE |
EmotionAgent 礼貌拒绝 |
但说实话,项目本身的技术选型不是这篇的重点。重点是:这种规模的项目如果让我自己手撸提示词驱动 Claude Code 一把梭,大概率写到一半就跑偏。OpenSpec 是我目前找到的最稳的协作范式。
为什么选择 OpenSpec / Spec Coding
写过几个 AI 协作项目就会发现一个问题:你和 Claude Code 之间的"理解差"是项目失败的最大变量。
- 你说"加个用户画像",模型给你来个 7 张表的复杂建模
- 你说"做个简单的推荐",模型给你接了 3 个 RPC + Redis 多级缓存
- 你说"先把这个小功能跑起来",模型顺手把 5 个相关文件全重构了
每次返工不仅心累,还会让你怀疑 AI 协作到底有没有意义。Spec Coding 的本质是把"你脑子里那个版本"先落到文档里,让模型在动手之前先和你对齐。
OpenSpec 是一套围绕这个理念的工具,核心是三份文档 + 一份主 spec:
proposal.md:本次变更的 Why / What / Impact —— 给"决策者"看的design.md:技术设计、关键决策、备选方案、权衡 —— 给"架构师"看的tasks.md:拆解到可勾选粒度的任务清单 —— 给"执行者"看的openspec/specs/<capability>/:各个 capability 的当前形态 —— 项目的"知识快照"
文档对齐之后再开始写代码,跑偏概率断崖式下降。
特别强调一下 主 spec 这个概念 —— 它是项目能力的最新形态,每次 archive 时由 OpenSpec 自动把 change 里的增量合并进去。新一轮 explore/propose 时,模型会先读主 spec 了解项目当前长什么样,所以它不会"忘记"上一版做过什么。这一点比你给一个新 ChatGPT 会话从头讲项目要靠谱得多。
OpenSpec 实战流程
我现在每个版本都按下面这个固定流程跑,对应 4 个 slash command:
flowchart LR A["/opsx:explore
聊方案"] --> B["/opsx:propose
生成提案"] B --> C{"人工
逐字读完
三份文档"} C -- 不满意 --> A C -- 通过 --> D["/opsx:apply
按 tasks 执行"] D --> E{"人工验证
功能正常?"} E -- 否 --> F["让 Claude 调试
调不出自己 debug"] F --> E E -- 是 --> G[git commit] G --> H["/opsx:archive
归档版本"]
Step 1:/opsx:explore 先聊清楚
这一步不写任何文档,纯讨论。我会把这一版想做什么、为什么做、有什么约束抛给模型,让它反过来问我细节。
比如做"成本控制 + 指标埋点"那一版,我开头只说了"想砍 LLM 成本,加点埋点",然后 explore 阶段我们来回聊了:
- 砍哪些?默认开启的 Perspective 团队要不要关掉?(关 → 单轮成本下降 ~46%)
- CHITCHAT 闲聊兜底分支白调一次 EmotionAgent 才被替换成模板,浪费要不要止损?(彻底跳 LLM)
- Clarify 单字段缺失场景能不能直接用模板问句?(4 类常用 slot 走模板,多字段才走 LLM)
- 埋点用 logfmt 还是 JSON?写到主日志还是独立文件?(logfmt + 独立
cost-metrics.log文件) - 埋点放哪一层?要不要改 Service 签名?(不要 —— 用 MDC 拿 sessionId)
- 灰度策略本期做不做?(不做 —— 写进 proposal 的"非目标")
explore 阶段是反向 PUA 模型的最佳时机:让它把你没想到的边界、风险、设计权衡全暴露出来。我经常聊到一半发现"原来这个改动会牵动那个模块",赶紧调整范围。
我自己的小 trick:让模型每聊几轮就主动列一遍"目前我理解的本期范围 / 非目标 / 待澄清",等它把这三栏列得跟我心里想的一样了,就可以收尾去 propose 了。这样 propose 出来的 proposal.md 就不会突然冒出某个我们没聊过的"额外功能"。
Step 2:/opsx:propose 生成提案
聊清楚后跑 /opsx:propose,模型会在 openspec/changes/<change-name>/ 目录下生成一整套文档:
1 | openspec/changes/cost-control-and-metrics-logging/ |
specs/ 是最容易被忽略但最关键的一部分 —— 它是本次变更对主 spec 的"补丁"。OpenSpec 把每个 capability 当成可独立增删改的单元,archive 时这些 spec 增量合并进 openspec/specs/,下次 propose 时模型读到的就是最新形态。
这里有个绝对不能省的步骤:人工逐字读完三份文档。
我自己的经验是,模型生成的提案 80% 时候是对的,但那剩下的 20% —— 比如"顺手"把不该改的接口签名改了、把不该删的 prompt 删了、把"保留作回滚资产"理解成"反正用不上删了"—— 一旦放过去,后面 apply 阶段就是灾难。
具体阅读三份文档的时候我有自己的优先级:
proposal.md的 What Changes 一条条对照心里那份清单:少了什么?多了什么?proposal.md的 Impact 重点看"代码删除"段落:模型敢删的东西,往往是你没想到要删的design.md看关键决策对不对:选了 A 方案没选 B 方案的理由你认不认tasks.md数行数:超过 20 行立刻触发"是不是该拆"的警觉specs/看 capability 名字:新增了哪些、改了哪些、有没有 REMOVED 的
如果发现哪条不符合预期,就直接把对应段落贴出来让模型改,或者再起一轮 explore 重新对齐。确认下一版不做的功能,一定要明确告知模型移出本次范围,否则它会"贴心地"顺手帮你做。
Step 3:/opsx:apply 开始执行
文档锁定后跑 /opsx:apply,模型会按 tasks.md 顺序执行任务,每完成一项就把 checkbox 从 [ ] 改成 [x]。
这一步我做两件事:
- 盯着
tasks.md的进度,每完成几项瞄一眼实现细节,不对就立刻打断纠偏,别等全跑完才回头看 - 跑完后人工验证功能,发现不对让 Claude Code 调试直到正常,调不出来自己上手 debug
第二点很关键:有些功能在模型看来是"正常"的,实现却不符合你的预期。
举两个真实翻车的例子:
翻车 1:ASR 埋点签名污染
成本控制那版我让它给 ASRService 加埋点,proposal 里只写了"在 onComplete/stop 时记录 audioMs"。它直接改了 ASRService.startStream 的方法签名往里塞 sessionId/userId 参数,跑起来确实能埋点,但污染了几乎所有上层调用 —— OrchestratorService、WebSocketHandler、控制器全得改。我自己 debug 才发现完全可以用 SLF4J 的 MDC 拿 sessionId(WebSocket onOpen 时 MDC.put("sessionId", id)),ASR 那边 MDC.get("sessionId") 直接取,签名一行不用改。
后来我把这条经验抽成 design.md 里的一个固定决策:“业务字段透传一律用 MDC,禁止侵入 Service 签名”,下个 change 模型就再也没犯过这个错。
翻车 2:删过头
cost-control 这版 design.md 里写了"删除已废弃的 RecommendReasonService",我没仔细看放过去了,结果 apply 阶段它顺手把 prompts/rec.txt 也删了 —— 虽然确实废弃,但保留作回滚资产更稳。还好提前在 proposal.md 里写了"保留 prompts/emotion.txt 不删"的明确边界,否则可能误删的更多。
翻车 3:tests 写得太深
Orchestrator 那一版让它顺手补单测,结果它写了一堆 mock 满天飞的"白盒测试",把 SessionService 内部调用顺序都校验了。后来一改实现细节单测就挂一片。我现在 proposal 里会明确写"单测只校验对外契约 + 关键 fail-fast 路径",把测试粒度先框死。
经验总结一句话:调不动的时候自己 debug,往往能发现模型理解偏差。让模型重试 2 次还不对,立刻自己上 —— 5 分钟 debug 出来的根因,比模型自己再调 5 轮都准。
Step 4:/opsx:archive 归档版本
功能验证 OK + tasks.md 全勾选 + 一个或多个独立 git commit 提交完成后,跑 /opsx:archive 把当前 change 移到 openspec/changes/archive/<日期>-<change-name>/,同时把 specs 增量合并到主 spec 里。
archive 这一步看起来只是文件搬家,实际上它在做三件重要的事:
- change 不再被 propose 干扰:归档后下次 explore/propose 时模型只能看到 archive 里的快照(用于回顾),不会把它当"待办"
- 主 spec 更新:
openspec/specs/<capability>/spec.md是项目能力的最新形态,archive 时增量被 merge 进去,下个 change explore 时模型读到的就是最新版 - change 历史可追溯:archive 目录里保留完整的 proposal/design/tasks,code review 时翻回来对照 git diff 一目了然
我现在 archive 之前还会顺便扫一眼 openspec/specs/ 下有没有 capability 是 REMOVED 的,确认一下知识库的"瘦身"。
两个真实案例:从 explore 到归档
光说流程太抽象,挑两个 change 完整讲一下。
案例 1:add-perspective-team —— 旁路并行的设计
这是 6/13 那批 4 个 Agent change 里最有意思的一个。需求很简单:在主推荐链路给出 Top-K 商品后,启动"价格顾问 / 专业跑者 / 入门买家"三个 Agent 各自点评一句,给用户更立体的导购感。
explore 阶段聊出来的关键决策:
| 问题 | 选择 | 理由 |
|---|---|---|
| 串在主链路里还是旁路 | 旁路 | 3 次 LLM 调用 1.2-1.8s,串进主链路会割裂语音体验 |
| 三个 Agent 并行还是顺序 | MsgHub 内顺序广播 | AgentScope 自带 MsgHub 能让后发言的 Agent 看到前者的发言,更像真讨论 |
| Profile 加载和召回两条腿要不要并行 | 要 | 互不依赖,串行白白等 ~200ms,用 CompletableFuture.thenCombine 合流 |
| 失败如何降级 | 空字符串 | 不阻塞主链路,主推荐照常 |
| 用户开口这种横切信号怎么处理 | Spring ApplicationEvent |
预热缓存、内容审计跟主链路解耦,@Async @EventListener |
propose 出来的 proposal 节选(What Changes 部分):
1 | - 新增 `perspective-team` 能力:基于 io.agentscope.core.pipeline.MsgHub |
apply 时的小翻车:模型一开始把 MsgHub 的 try-with-resources 写漏了,导致连续跑两次第二次会卡住 —— 我跑功能验证发现的,让它修。这种细节 propose 阶段是看不出来的,只能靠跑出来才知道。
最终 commit:555c9ed feat(perspective): 实现多视角点评团 + 推荐并行合流 + 事件总线,一个 change 一个 commit,干净。
案例 2:merchant-data-isolation —— BREAKING 改造怎么落
这是 6/15 的一个破坏性变更:把 WebSocket 鉴权从 query ?userId= 改成 Sa-Token 的 Sec-WebSocket-Protocol: Bearer-{token} 头校验,整条链路同步加 merchant_id 行级过滤。
为什么单独拎出来讲:BREAKING + 跨多个模块(auth、session、scope、rec、ws)+ 需要 Flyway 迁移 + 涉及 RFC 6455 协议细节 —— 这种 change 最容易写崩。
explore 阶段重点确认的事情:
- 是否兼容旧 query userId?→ 不兼容,前端配合改一次到位(不留兼容代码 = 不留技术债)
- WebSocket 子协议响应头要回写吗?→ 必须(RFC 6455 要求,不回写 Chrome 会报错)
- 商品检索全平台兜底要不要做?→ 要,Redis scope 缓存 miss 时降级为"仅该用户、全平台",记 WARN 日志
- session.channel 加新枚举值需要 Flyway 迁移吗?→ 需要,V6 扩 CHECK 约束
- 单元测试覆盖到哪一层?→ scope filter 合流的关键路径 + 握手鉴权 401 路径
proposal 里写的 Impact 段(节选):
1 | 代码: |
这个 proposal 给我的感觉就是"清单已经齐了,剩下的纯体力活"。apply 阶段几乎一口气跑完,中间只调了一个 bug:模型一开始忘了在握手失败时回写 sub-protocol 响应头,浏览器握手卡死,我让它对照 RFC 6455 修了一下。
关键是这种 BREAKING 改造,proposal 里"回滚方案"段必须明写。万一上线发现握手失败率飙了,知道怎么撤回是命根子。
最终 commit:a10b5d0 feat(merchant-isolation): 实现多商家数据隔离与权限模型。
真实开发流程:从 git log 看 OpenSpec 节奏
voice-shopping 项目目前一共归档了 15 个 change,对应 git log 上每条独立 feature commit:
1 | 2026-06-11 maven-project-scaffold 初始化 Maven 多模块骨架 |
每一行都对应一个完整的 OpenSpec 闭环:explore → propose → 看文档 → apply → 验证 → archive → commit。
按主题分类一下,看得更清楚:
timeline title voice-shopping 15 个 change 时间线 06-11 : 项目骨架 (1) 06-12 : 基础能力 (4)
agent工厂 / 画像记忆 / 向量检索 / 语音通道 06-13 : Agent实现 (4)
Emotion / Perspective / Clarify / Rec 06-14 : 编排与记忆 (2)
Orchestrator / 长期记忆 06-15 : 多商家与订单 (3)
数据隔离 / 下单 / 订单主体过滤 06-16 : 性能优化 (1)
延迟与流式
可以看到每个 change 都很小:
- 6/12 一天连做了 4 个 change:agent 工厂、画像记忆、向量检索、语音通道,每个都是独立可交付的小步。这一天平均每个 change 从 explore 到 archive 大约 1.5-2 小时
- 6/13 拆成 4 个 Agent 各自一个 change:没把"做完所有 Agent"打包成一个大版本。事后看这是最聪明的决策 —— 4 个 Agent 各有 4 套 prompt,混着提交根本对不上 review
- 6/14 把 orchestrator 和长期记忆拆开:虽然记忆系统会被 orchestrator 调用,但二者职责完全不同。先做 orchestrator 骨架(用 stub memory),再把记忆系统单独迭代
- 6/15 把"商家隔离"和"订单主体过滤"拆成两个 change:都是数据隔离主题,但前者建权限模型,后者收口订单查询,关注点不同。订单主体过滤还引入了一条很重要的工程规范:“Repository 派生查询必须显式带主体维度”,单独一个 change 做这事比夹在订单流程里好
如果哪个版本跑偏了,git revert 一个 commit 就完事,不用心疼,因为它本来就只代表一个独立的 change。
心得体会
跑了 15 个 change 下来,几条最重要的经验:
1. 尽可能拆小,小步快跑
最大的坑是想"一次性吃个大胖子"。
我刚开始做 Orchestrator 那一版差点犯错,本来想一个 change 把"状态机 + 4 个 Agent 接入 + WebSocket 改造"全做了,explore 阶段聊到一半发现 tasks.md 列了 30 多项,立刻拆。最后拆成"orchestrator 状态机骨架"和"WebSocket 全双工接入"两个独立 change,实施起来心态稳很多。
判断标准很简单:
tasks.md超过 20 项 → 考虑拆- 涉及 3 个以上模块的代码改动 → 考虑拆
- 既有 BREAKING 又有非 breaking 的特性 → 必须拆
- 既改 spec 又改实现 → 看能不能把 spec 改单独提一个 change
调试阶段一旦出问题,change 越大越难定位。
2. 每个 change 一个独立 git commit(或几个)
每完成一个 change 就独立 commit,不要混着提交。原因有三:
- 跑偏了
git revert直接回滚,干净利落 - 不能保证你认真看过 OpenSpec 生成的所有文档,万一漏了,回滚成本极低
- code review 时一个 commit 对应一份 proposal,前后呼应,很容易看清意图
我的 commit message 也按一个固定模板:feat(<scope>): 一句话能力描述,scope 用 change 名字简化版(perspective / orchestrator / merchant-isolation),跟 archive 目录名对得上,一眼能反查到对应的 proposal。
3. 三份文档必须认真看
proposal.md / design.md / tasks.md 不是给模型看的,是给你看的。
最容易踩的坑是模型"顺带"做的修改。除了前面说的"删过头",还有几种我吃过亏的:
- 顺手统一命名:模型觉得
RecommendOrchestrator和ParallelRecommendService命名不一致,悄悄把前者改名 —— 但前者已经在 README 里写过了,引用全断 - 顺手补防御性代码:你说"加 ASR 埋点",它顺手把
ASRService里所有null检查都补了一遍,PR diff 大三倍 - 顺手给 record 加方法:明明只是数据载体,被加了
validate()toMap()一堆"看起来有用"的方法
Tip:用 diff 工具把两次 propose 的文档比一比,能快速看出本轮新增/变化的边界。如果第二次 propose 比第一次多出来一段你没要求的内容,立刻揪它出来。
4. 明确告知不做的事情
如果 explore 阶段聊到的某个能力本版本不做,一定要明确写进 proposal.md 的"不影响项"或"非目标"段落。
模型默认会把所有讨论过的内容尽量塞进当前版本,写一句"灰度策略本期不做"比反复让它从 tasks.md 里删要省事得多。
我自己甚至会主动让模型在 proposal 末尾加一段"非目标 / Out of Scope"列表,把所有讨论过但本期不做的事情明文写出来。这样下次 explore 想做这事的时候,搜一下就能知道"上次为什么没做、当时怎么权衡的"。
5. 仔细看模型推荐的方案,调不对就自己 debug
有些场景调一两轮不通,大概率不是模型笨,是它理解和你不一样。
前面说的 ASR 埋点签名污染就是典型例子:模型觉得"传 sessionId 进来很合理",我觉得"用 MDC 拿就行不要改签名",光让它"再调一遍"调不出来,自己看 5 分钟代码就知道关键分歧在哪。
判断准则:让模型重试 2 次还不对,立刻自己 debug。不要陷入"我再让它试一次说不定就好了"的赌徒心态。
debug 出根因后还有一个动作:把这次的分歧写成一条决策放进下次的 design.md 里。比如那次之后我在仓库 CLAUDE.md 加了一条:“业务字段透传一律用 MDC,禁止侵入 Service 签名”,后续 change 模型就再没犯过同样的错 —— spec 在帮你"教育"模型。
6. 主 spec 是地图,change 是脚印
跑了几次之后我才意识到:openspec/specs/ 下的主 spec 才是这套体系真正的资产,change 只是过程产物。
主 spec 记录的是"项目当前长什么样":哪些 capability 存在、各自的契约是什么、哪些已经 REMOVED。新人接手项目,先读主 spec 比读代码快 10 倍 —— 因为它是用人话写的"接口承诺"。
每次 explore 阶段,模型读的也是主 spec。如果你发现模型频繁"忘记"上一版做过的设计,大概率是主 spec 里那段写得不够清楚,应该回头补。
7. CLAUDE.md 是"会话级 spec"
OpenSpec 管的是"项目能力 spec",但有些规范是整个项目通用的(命名约定、错误处理风格、测试策略),这些放进每个 change 的 design.md 都太啰嗦。我的做法是把它们沉淀到仓库根的 CLAUDE.md 里 —— 每次 Claude Code 启动会自动读取,相当于"会话级 spec"。
举几条我现在 CLAUDE.md 里的硬规矩:
- 接口请求/响应禁止用
Map<String, Object>,必须定义专用 record - 所有接口返回
ApiResult<T>,不用ResponseEntity - Repository 涉及主体(user/merchant)的查询必须显式带主体维度,如
findByIdAndUserId - 业务字段透传走 MDC,禁止侵入 Service 签名
- Flyway 已执行的迁移脚本禁止修改
这些规矩进了 CLAUDE.md 之后,几乎每个 change 都能省掉一次"模型又写错了被我改回去"的循环。
适用场景
OpenSpec 不是银弹,我个人感觉它最值的场景是:
- ✅ 从 0 到 1 的项目:每一步都需要设计对齐,文档驱动收益最大
- ✅ 复杂业务功能:涉及多模块、多 Agent、多边界条件
- ✅ 重构老代码:proposal 阶段就能把"改动范围 / 不改动范围"框死
- ✅ 跨多人 / 多 session 协作:spec 是共享语言,下次接着干不用从头讲
- ✅ BREAKING 变更:proposal 强制你写"回滚方案",相当于上线前体检
不太适合的场景:
- ❌ 改一行代码的小修小补:写 proposal 的时间够你改 10 个 bug 了
- ❌ 纯探索性 demo:还没想清楚要做什么的时候,spec 反而是束缚
- ❌ 完全独立的工具脚本:没有"项目演进"概念,主 spec 没意义
工具清单
我用的具体工具栈,给想试的同学参考:
| 工具 | 用途 |
|---|---|
| Claude Code | 主交互界面,跑所有 slash command |
| OpenSpec slash commands | /opsx:explore /opsx:propose /opsx:apply /opsx:archive |
| Git | 每个 change 独立 commit,archive 后立即提交 |
| CLAUDE.md | 仓库级长期规范 |
openspec/config.yaml |
OpenSpec 配置(capability 模板、spec 风格) |
docs/data/ |
项目独立维护的"数据契约文档"(表字段、Agent DTO、ER 图等),与 spec 互补 |
总结
回到开头那个问题:博客为什么停更两周?
因为我把这两周该写博客的精力,全压到了 OpenSpec 文档上。写 spec 的过程本质上就是在把脑子里模糊的想法用结构化语言写清楚 —— 这本身就是写技术博客的最重要的练习。15 个 change 的 proposal/design/tasks 加起来好几万字,比这两周不写的文章字数多得多,质量还更高(因为每一句都对应一行代码)。
跑下来最大的感受是:Spec Coding 不是给模型用的,是给你自己用的。模型只是个忠实的执行者,它产出的代码质量上限取决于你 spec 写得有多清楚。当你把"加个推荐功能"翻译成 proposal 里 30 行 What Changes、design 里 5 个关键决策、tasks 里 18 个可验证的子任务 —— 你脑子里那个模糊的想法就已经清晰了大半。剩下的事情交给 Claude Code 跑就行。
如果你也在用 Claude Code 做正经项目,强烈推荐试试 OpenSpec 工作流:
/opsx:explore把方案聊到位,让模型替你想清楚边界/opsx:propose生成三份文档,人工逐字读完/opsx:apply按tasks.md推进,盯着进度,不对立刻拦/opsx:archive归档收尾,每个 change 独立 commitCLAUDE.md+openspec/specs/沉淀仓库级 / 项目级长期规范
核心心法一句话:拆得越小,跑得越稳。Spec 写得越清,模型偏得越少。
下一篇会聊聊 voice-shopping 里 Orchestrator 状态机的设计 —— 怎么用一个手写 Service 把 4 个 Agent 串成全双工流式管道,包括 phase 转移、last_recommendations 的快照设计、PRODUCT_COMPARE 怎么基于上轮重排。