惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

S
SegmentFault 最新的问题
博客园 - 三生石上(FineUI控件)
WordPress大学
WordPress大学
博客园 - 【当耐特】
月光博客
月光博客
Vercel News
Vercel News
D
Docker
I
InfoQ
Apple Machine Learning Research
Apple Machine Learning Research
博客园 - 叶小钗
MongoDB | Blog
MongoDB | Blog
GbyAI
GbyAI
有赞技术团队
有赞技术团队
雷峰网
雷峰网
博客园 - 聂微东
小众软件
小众软件
Y
Y Combinator Blog
腾讯CDC
L
LangChain Blog
The GitHub Blog
The GitHub Blog
宝玉的分享
宝玉的分享
Stack Overflow Blog
Stack Overflow Blog
大猫的无限游戏
大猫的无限游戏
T
The Blog of Author Tim Ferriss

博客园 - 咸着的鱼25

环境才是 Agent 的核心基础设施 OpenCode + OpenSpec + Oh-My-OpenCode 联合 SDD/ATDD 开发指南 在线服务数据压缩算法比较 延迟深度链接 搭建wiki系统后端存储-来自大模型 广告投放名词 java spring IoC原理 面试题1 c++ 代码技巧 c++ 性能分析 粗排治理之性能优化 MMR 算法优化 core 基本操作 聊天室开发心得 Docker 学习笔记 Airflow 使用简介 lua转换etcd应答 修改系统参数 https学习笔记 openresty: nginx worker不同请求之间共享数据
AI 驱动开发工作流:OpenCode + Oh-My-OpenCode + SDD + ATDD
咸着的鱼25 · 2026-03-26 · via 博客园 - 咸着的鱼25

目录

  1. 背景与动机
  2. 工具链概览
  3. OpenCode 核心使用
  4. Oh-My-OpenCode 增强
  5. SDD:规格驱动开发
  6. ATDD:验收测试驱动开发
  7. OpenSpec:规格阶段的守门员
  8. 五者协同工作流
  9. 完整案例:用户订单服务
  10. 团队规范建议
  11. 常见问题

1. 背景与动机

为什么要改变?

传统开发流程中,我们面临几个核心痛点:

  • 需求到代码的鸿沟:产品文档到可运行代码之间存在大量人工翻译成本
  • 测试滞后:测试往往在代码写完后才补,而不是指导开发
  • 上下文丢失:AI 辅助编码时,AI 不了解项目规范、架构约束,产出物良莠不齐
  • 并行能力受限:单人单线程工作,无法充分利用 AI 的并行能力

新工作流的目标

需求文档 → 规格 → 测试 → 实现 → 验证
    ↑                              |
    └──────── AI 全程参与 ──────────┘
  • SDD(Specification-Driven Development):先写规格,再让 AI 生成代码
  • ATDD(Acceptance Test-Driven Development):先写验收测试,代码必须让测试通过
  • OpenCode:AI 编码主力,执行具体代码修改
  • Oh-My-OpenCode:增强 OpenCode,提供多模型编排、并行子 agent、更强工具链

2. 工具链概览

各工具职责

工具 职责 阶段
Oh-My-OpenCode 多模型编排,Sisyphus 主 agent 协调规划 全程
OpenCode (Plan 模式) 分析需求、生成规格、制定实现计划 规格阶段
OpenCode (Build 模式) 编写代码、重构、修复 bug 实现阶段
SDD 将需求转化为机器可读的 API 规格(OpenAPI/ADR) 设计阶段
ATDD 将验收条件转化为可执行测试(Cucumber/JUnit) 测试阶段
OpenSpec 规格阶段的流程框架,物理隔离规格与代码,防止 AI 提前写代码 规格阶段

工具安装

# 安装 OpenCode
curl -fsSL https://opencode.ai/install | bash
# 或
npm install -g opencode-ai

# 安装 Oh-My-OpenCode
npm install -g oh-my-opencode

# 在 opencode.json 中启用插件
# ~/.config/opencode/opencode.json
{
  "$schema": "https://opencode.ai/config.json",
  "model": "anthropic/claude-sonnet-4-20250514",
  "plugin": ["oh-my-opencode"]
}

3. OpenCode 核心使用

3.1 两种核心模式

┌─────────────────────────────────────────────────┐
│  Tab 键切换                                      │
│                                                  │
│  [Plan 模式]          [Build 模式]               │
│  - 只分析,不修改     - 完整工具权限              │
│  - 生成规格/计划      - 执行实际代码变更           │
│  - 安全探索代码库     - 运行命令/测试              │
└─────────────────────────────────────────────────┘

使用原则

  • 不确定怎么做时,先用 Plan 模式 探索,确认方向后切换 Build 模式
  • 复杂任务一定先 Plan,再 Build,避免 AI 走弯路

3.2 AGENTS.md:给 AI 的项目说明书

在项目根目录创建 AGENTS.md,这是 AI 了解项目的核心入口:

# 初始化(AI 自动扫描项目生成)
/init

# 然后手工完善
# 项目名:订单服务 (order-service)

## 技术栈
- Java 17 + Spring Boot 3.x
- Maven 构建
- MySQL 8.0(通过 JPA/Hibernate 访问)
- Redis(缓存层)
- Cucumber + JUnit 5(验收测试)

## 项目结构
- `src/main/java/com/example/order/` - 主代码
  - `controller/` - REST 控制器
  - `service/` - 业务逻辑
  - `repository/` - 数据访问
  - `domain/` - 领域对象/实体
  - `dto/` - 请求/响应 DTO
- `src/test/` - 测试代码
  - `java/.../acceptance/` - Cucumber 验收测试
  - `resources/features/` - .feature 文件

## 代码规范
- 所有公开方法必须有 Javadoc
- Service 层统一抛出自定义 BusinessException,不允许直接抛 RuntimeException
- Controller 层只做参数校验和 DTO 转换,不含业务逻辑
- 字段验证使用 Jakarta Validation 注解
- 禁止在 Service 层直接使用 HttpServletRequest

## 接口规范
- 统一响应格式:`{ "code": 200, "message": "success", "data": {} }`
- 错误码定义在 `ErrorCode` 枚举中
- 分页接口使用 `PageRequest` 参数

## 测试规范
- 每个新功能必须有对应的 .feature 文件(ATDD)
- 验收测试覆盖正常流程 + 至少 2 个边界/异常场景
- 单元测试使用 Mockito,不依赖真实数据库

## Git 规范
- 分支命名:feature/xxx、fix/xxx、refactor/xxx
- commit message 格式:`type(scope): description`

提交到 Git,让团队所有成员的 OpenCode 都读到相同的上下文。

3.3 子 agent 调用

# 在消息中 @mention 子 agent
@explore 帮我找所有实现了 OrderService 接口的类

@general 并行检查以下三个文件的代码规范符合情况:
  - OrderController.java
  - OrderService.java  
  - OrderRepository.java

4. Oh-My-OpenCode 增强

4.1 核心能力

Oh-My-OpenCode(npm 包:oh-my-opencode,GitHub:code-yeongyu/oh-my-openagent)是 OpenCode 的增强插件,提供:

能力 说明
Sisyphus 主 agent 智能编排器,自动分解任务、并行调度子 agent
Prometheus 规划师 采访模式,在动手前深度规划,适合 SDD 阶段
ultrawork 命令 一键启动全量 agent,推进任务直到完成
Hash 锚定编辑 每行代码有内容 hash,杜绝过期行错误
Background Agents 同时运行 5+ 个专项 agent
LSP + AST-Grep IDE 级别的代码重构和搜索

4.2 关键命令

# 启动后在 OpenCode TUI 中输入:

/start-work      # 调用 Prometheus,采访式规划,适合 SDD 阶段
ultrawork        # 或 ulw,一键全力推进任务
/init-deep       # 为项目所有子目录生成层级 AGENTS.md

4.3 多模型编排策略

Oh-My-OpenCode 会自动根据任务类型路由到最合适的模型:

任务类型           → 模型
─────────────────────────────────────
架构设计/复杂推理  → claude-opus 或 kimi-k2.5
代码实现/重构     → gpt-5.x-codex
前端/UI           → visual-engineering 专项模型
快速单文件修改    → 轻量快速模型

无需手动切换模型,Sisyphus 自动判断。

4.4 安装配置

安装后,~/.config/opencode/opencode.json 会自动更新。项目级配置放在 .opencode/oh-my-opencode.jsonc

{
  // 项目级 Oh-My-OpenCode 配置
  "agents": {
    "sisyphus": {
      // 覆盖默认模型(可选)
      "model": "anthropic/claude-opus-4-6"
    }
  },
  "background_tasks": {
    "max_concurrent": 3  // 最多同时运行 3 个后台 agent
  }
}

5. SDD:规格驱动开发

5.1 什么是 SDD?

SDD(Specification-Driven Development)的核心理念:

代码是规格的实现,而不是需求的直接翻译

工作流:

产品需求文档
    ↓  (Plan 模式 + Prometheus)
API 规格(OpenAPI YAML)+ 架构决策记录(ADR)
    ↓  (Build 模式)
代码实现
    ↓
与规格对齐验证

5.2 AI 辅助生成规格

Step 1:用 Plan 模式 + /start-work 生成规格草稿

# 切换到 Plan 模式(Tab 键)
# 输入:

我需要设计一个订单创建接口。业务需求:
- 用户可以提交包含多个商品的订单
- 需要校验库存是否充足
- 订单金额需要计算折扣
- 支持多种支付方式(微信、支付宝、银行卡)
- 订单提交成功后异步发送确认邮件

请用 /start-work 采访我,然后生成 OpenAPI 规格。

Prometheus 会问你:

  • 是否有幂等性要求?
  • 库存不足时是部分成功还是全部失败?
  • 折扣逻辑的优先级规则是什么?
  • ……

Step 2:规格文件落地

AI 生成后保存为 docs/api/order-api.yaml

openapi: 3.0.0
info:
  title: Order Service API
  version: 1.0.0

paths:
  /api/v1/orders:
    post:
      summary: 创建订单
      operationId: createOrder
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateOrderRequest'
      responses:
        '201':
          description: 订单创建成功
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OrderResponse'
        '400':
          description: 参数校验失败
        '409':
          description: 库存不足

components:
  schemas:
    CreateOrderRequest:
      type: object
      required: [userId, items, paymentMethod]
      properties:
        userId:
          type: string
          description: 用户 ID
        items:
          type: array
          items:
            $ref: '#/components/schemas/OrderItem'
          minItems: 1
        paymentMethod:
          type: string
          enum: [WECHAT, ALIPAY, BANK_CARD]
        couponCode:
          type: string
          description: 优惠券码(可选)
    # ... 其他 schema 定义

5.3 用规格驱动 AI 生成代码

有了规格,对 AI 的指令变得精确:

# 切换到 Build 模式
# 输入:

根据 @docs/api/order-api.yaml 中的规格,
实现 POST /api/v1/orders 接口,包括:
1. CreateOrderRequest DTO(带 Jakarta Validation 注解)
2. OrderController(只做参数校验,业务逻辑委托给 Service)
3. OrderService 接口及实现(包含库存校验逻辑)
4. 统一响应格式封装

遵循 @AGENTS.md 中的代码规范。

6. ATDD:验收测试驱动开发

6.1 什么是 ATDD?

ATDD(Acceptance Test-Driven Development):

先写验收测试,再写实现代码,以测试通过作为完成标志

使用 Gherkin 语言(.feature 文件)描述业务场景,让业务人员、测试、开发共同理解需求:

Feature: 创建订单
  作为一个已登录用户
  我想要提交订单
  以便购买商品

  Scenario: 正常创建订单
    Given 用户 "user001" 已登录
    And 商品 "SKU001" 库存为 10
    When 用户提交包含 2 件 "SKU001" 的订单
    Then 返回状态码 201
    And 订单状态为 "PENDING_PAYMENT"
    And 商品 "SKU001" 库存减少为 8

  Scenario: 库存不足时拒绝创建
    Given 用户 "user001" 已登录
    And 商品 "SKU001" 库存为 1
    When 用户提交包含 5 件 "SKU001" 的订单
    Then 返回状态码 409
    And 错误信息包含 "库存不足"

6.2 AI 辅助生成 Feature 文件

# Plan 模式下输入:

基于 @docs/api/order-api.yaml 的 createOrder 接口,
帮我生成完整的 Cucumber feature 文件,要覆盖:
1. 正常流程(单商品、多商品)
2. 库存不足场景
3. 无效优惠券场景
4. 无效支付方式场景
5. 并发下单场景(幂等性)

保存到 src/test/resources/features/order/create-order.feature

6.3 AI 辅助生成 Step Definitions

有了 .feature 文件,让 AI 生成对应的 Step 实现:

# Build 模式下输入:

根据 @src/test/resources/features/order/create-order.feature,
生成对应的 Cucumber Step Definitions,要求:
- 使用 Spring Boot Test + MockMvc
- 使用 @MockBean 模拟 OrderService
- 每个 Step 方法要简洁,复杂逻辑抽取到 helper 方法
- 保存到 src/test/java/com/example/order/acceptance/OrderStepDefs.java

6.4 红绿循环

写 .feature 文件(红)
    ↓
生成 Step Definitions(红)
    ↓
AI 生成业务代码(绿)
    ↓
运行验收测试
    ↓
全部通过 → 完成
失败 → 让 AI 修复(迭代)

运行测试的提示词

运行 mvn test -Dtest=AcceptanceTest,
如果有失败的用例,分析原因并修复,直到全部通过。

7. OpenSpec:规格阶段的守门员

7.1 为什么需要 OpenSpec?

在实践 SDD 时,一个常见问题是:AI 不等规格确认就开始写代码。Plan 模式的 ask 权限只是弹出确认框,并不强制阻止。当团队成员有时候直接在 Build 模式下开始工作,规格文档就很容易被跳过。

OpenSpec 解决这个问题的方式是:用目录结构和 agent 权限,在物理层面隔离规格阶段和实现阶段

OpenSpec 不是 OpenCode 的竞争者,而是 SDD 的流程框架,与 OpenCode 互补。

7.2 核心概念

OpenSpec 为每个功能维护一个独立目录:

openspec/
└── changes/
    └── create-order/           # 每个功能一个目录
        ├── proposal.md         # 功能提案(需求描述)
        ├── design.md           # 架构/设计决策
        ├── tasks.md            # 拆解的实施任务
        └── specs/
            ├── api.yaml        # OpenAPI 规格
            └── events.yaml     # 领域事件规格(如需要)

三个核心命令:

命令 作用
/opsx:propose 开启新功能规格流程,AI 采访后生成 proposal.md
/opsx:apply 规格确认后,解锁代码文件权限,开始实现
/opsx:archive 功能完成后,归档规格文档

7.3 opencode-plugin-openspec:与 OpenCode 的集成

opencode-plugin-openspec(GitHub: Octane0411/opencode-plugin-openspec)是专门为 OpenCode 打造的 OpenSpec 插件,提供一个名为 openspec-plan 的专用 agent。

与 Plan 模式的关键区别

对比项 Plan 模式 openspec-plan agent
对文件写入的控制 ask 权限(弹确认框) deny 权限(物理禁止写代码文件)
强制性 低,人可以绕过 高,连 AI 自己也无法绕过
适用场景 个人探索、快速迭代 团队协作、有规范要求的项目

7.4 安装与配置

# 安装 OpenSpec CLI
npm install -g @fission-ai/openspec

# 安装 OpenCode 插件(可选,但推荐)
# 在 opencode.json 的 plugin 数组中添加:
{
  "$schema": "https://opencode.ai/config.json",
  "model": "anthropic/claude-sonnet-4-20250514",
  "plugin": ["oh-my-opencode", "opencode-plugin-openspec"]
}

初始化项目:

# 在项目根目录
openspec init

这会创建 openspec/ 目录和基础配置。

7.5 典型工作流:以"创建订单"为例

阶段一:提案(Propose)

# 在 OpenCode 中,使用 openspec-plan agent
# 此时 AI 无法修改任何业务代码

/opsx:propose create-order

我要实现创建订单功能:
- 用户提交多商品订单
- 校验库存
- 支持优惠券
- 异步发送确认邮件

AI 采访并生成 openspec/changes/create-order/proposal.md

# Proposal: 创建订单功能

## 背景
用户需要能够一次性提交包含多个商品的订单。

## 业务规则
- **原子性**:多商品订单中任一商品库存不足,整单取消
- **优惠券**:每单限用一张,固定金额减免
- **邮件**:异步发送,失败不影响订单主流程

## 接口设计决策
- 幂等键:通过 `X-Idempotency-Key` 请求头实现
- 错误策略:409 表示库存不足,404 表示商品/优惠券不存在

## 未解决问题
- [ ] 并发下单时的库存锁定策略(乐观锁 vs 悲观锁)?

人工 Review proposal.md,确认无误后继续。

阶段二:生成规格

# 仍在 openspec-plan agent 中(仍无法写业务代码)

根据 proposal.md,生成:
1. OpenAPI 规格到 openspec/changes/create-order/specs/api.yaml
2. 架构决策记录到 openspec/changes/create-order/design.md
3. 任务拆解到 openspec/changes/create-order/tasks.md

人工 Review,若 Review 通过:

/opsx:apply create-order

这一步将规格文件复制到 docs/api/同时解锁业务代码文件的写入权限,后续可切换回普通 Build 模式继续工作。

阶段三:实现(接续 ATDD 流程)

/opsx:apply 之后,流程与不使用 OpenSpec 的 ATDD 流程完全相同:

  1. AI 根据 specs/api.yaml 生成 Cucumber .feature 文件
  2. 生成 Step Definitions 骨架
  3. Build 模式实现业务代码
  4. 运行测试直到全绿

7.6 是否需要引入 OpenSpec?

情况 建议
团队 ≤ 2 人,文档纪律好 可以不用,Plan 模式 + 人工自律即可
团队 3+ 人,需要流程保障 推荐引入,防止规格被跳过
AI 经常在规格未确认时就开始写代码 强烈推荐,openspec-plan agent 物理阻断
需要规格文档可追溯、可归档 推荐,/opsx:archive 提供完整归档机制

8. 五者协同工作流

完整工作流图

┌──────────────────────────────────────────────────────────────┐
│                     一个功能的完整流程                         │
└──────────────────────────────────────────────────────────────┘

1. 需求澄清(Oh-My-OpenCode Prometheus + Plan 模式)
   ├─ /start-work 采访模式,明确需求边界
   ├─ 生成功能说明文档
   └─ 确定接口契约

2. 规格设计(SDD + OpenSpec + openspec-plan agent)
   ├─ /opsx:propose 开启规格流程(物理禁止写业务代码)
   ├─ AI 采访生成 proposal.md
   ├─ AI 生成 OpenAPI YAML 到 openspec/changes/<feature>/specs/
   ├─ 人工 Review 规格,确认无误
   ├─ /opsx:apply 解锁代码权限,规格复制到 docs/api/
   └─ 提交规格文件到 Git

3. 验收测试先行(ATDD + Build 模式)
   ├─ AI 生成 .feature 文件
   ├─ 人工 Review 场景覆盖度
   ├─ AI 生成 Step Definitions 骨架
   └─ 此时测试全部为红(失败)

4. 并行实现(Oh-My-OpenCode ultrawork)
   ├─ Sisyphus 分解任务
   ├─ 多个 Background Agent 并行工作:
   │   ├─ Agent A:实现 Controller + DTO
   │   ├─ Agent B:实现 Service + 业务逻辑
   │   └─ Agent C:实现 Repository + 数据层
   └─ 各 Agent 完成后合并

5. 验证(Build 模式)
   ├─ 运行验收测试(Cucumber)
   ├─ AI 修复失败用例
   └─ 测试全绿 → 功能完成

6. 代码 Review(explore subagent)
   └─ @explore 检查是否符合 AGENTS.md 规范

7. 归档(OpenSpec)
   └─ /opsx:archive 归档规格文档,保留可追溯记录

关键原则

  1. 规格先于代码:没有 OpenAPI 规格,不开始写代码
  2. 测试先于实现:没有 .feature 文件,不开始写 Service
  3. Plan 后再 Build:复杂功能必须先在 Plan 模式中确认方向
  4. AGENTS.md 是唯一真相:所有规范都在这里,AI 和人都遵循
  5. OpenSpec 守门:3+ 人团队中,用 openspec-plan agent 物理隔离规格与代码阶段

9. 完整案例:用户订单服务

场景:为电商平台实现"创建订单"功能

Step 1:需求澄清(10 分钟)

打开 OpenCode,切换到 Plan 模式,输入:

我要实现一个订单创建功能。
请用 /start-work 采访我,完成后给出功能规格摘要。

基本需求:
- 用户选择商品和数量,提交订单
- 要检查库存
- 要支持优惠券
- 成功后发邮件通知

Prometheus 会问:

  • Q: 如果多个商品中有一个库存不足,是全部取消还是部分成功?
  • A: 全部取消,保持原子性
  • Q: 优惠券是按金额减免还是按折扣?同一订单可以用多张吗?
  • A: 固定金额减免,每单只能用一张
  • Q: 邮件发送失败是否影响订单创建成功?
  • A: 不影响,邮件异步发送,失败自动重试

Step 2:生成 OpenAPI 规格

# Plan 模式继续:

根据以上澄清,生成完整的 OpenAPI 3.0 规格,
保存到 docs/api/create-order.yaml

AI 生成的 docs/api/create-order.yaml

openapi: 3.0.0
info:
  title: Order Service
  version: 1.0.0

paths:
  /api/v1/orders:
    post:
      summary: 创建订单
      operationId: createOrder
      tags: [orders]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateOrderRequest'
            example:
              userId: "user_123"
              items:
                - skuId: "SKU_001"
                  quantity: 2
              paymentMethod: "ALIPAY"
              couponCode: "SAVE10"
      responses:
        '201':
          description: 订单创建成功
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiResponse'
        '400':
          description: 请求参数不合法
        '404':
          description: 商品不存在或优惠券不存在
        '409':
          description: 库存不足

components:
  schemas:
    CreateOrderRequest:
      type: object
      required: [userId, items, paymentMethod]
      properties:
        userId:
          type: string
          minLength: 1
        items:
          type: array
          minItems: 1
          items:
            type: object
            required: [skuId, quantity]
            properties:
              skuId:
                type: string
              quantity:
                type: integer
                minimum: 1
                maximum: 99
        paymentMethod:
          type: string
          enum: [WECHAT, ALIPAY, BANK_CARD]
        couponCode:
          type: string

    OrderResponse:
      type: object
      properties:
        orderId:
          type: string
        status:
          type: string
          enum: [PENDING_PAYMENT, PAID, CANCELLED]
        totalAmount:
          type: number
        discountAmount:
          type: number
        finalAmount:
          type: number
        createdAt:
          type: string
          format: date-time

    ApiResponse:
      type: object
      properties:
        code:
          type: integer
        message:
          type: string
        data:
          $ref: '#/components/schemas/OrderResponse'

人工 Review 后,提交到 Git。

Step 3:生成验收测试

切换回 Plan 模式(或保持 Plan 模式),输入:

根据 @docs/api/create-order.yaml 和以下业务规则:
- 库存不足时全部取消(原子性)
- 每单只能用一张优惠券(固定金额减免)
- 邮件异步发送,不影响主流程

生成 Cucumber feature 文件,覆盖:
1. 正常创建(无优惠券)
2. 使用有效优惠券
3. 商品库存不足(全部回滚)
4. 部分商品库存不足(全部回滚)
5. 优惠券不存在
6. 无效的支付方式

保存到 src/test/resources/features/order/create-order.feature

生成的 create-order.feature

Feature: 创建订单
  作为一个已注册用户
  我希望能够提交包含多个商品的订单
  以便完成购买流程

  Background:
    Given 商品库存数据如下:
      | skuId   | stock |
      | SKU_001 | 10    |
      | SKU_002 | 5     |

  Scenario: 正常创建订单(无优惠券)
    When 用户 "user_123" 提交以下订单:
      | skuId   | quantity |
      | SKU_001 | 2        |
    And 支付方式为 "ALIPAY"
    Then 返回状态码 201
    And 订单状态为 "PENDING_PAYMENT"
    And 商品 "SKU_001" 库存变为 8

  Scenario: 使用有效优惠券创建订单
    Given 优惠券 "SAVE10" 面值为 10 元,状态为可用
    When 用户 "user_123" 提交以下订单:
      | skuId   | quantity |
      | SKU_001 | 1        |
    And 支付方式为 "WECHAT"
    And 使用优惠券 "SAVE10"
    Then 返回状态码 201
    And 响应中 discountAmount 为 10

  Scenario: 单个商品库存不足时全部回滚
    When 用户 "user_123" 提交以下订单:
      | skuId   | quantity |
      | SKU_001 | 20       |
    And 支付方式为 "ALIPAY"
    Then 返回状态码 409
    And 错误信息包含 "库存不足"
    And 商品 "SKU_001" 库存保持为 10

  Scenario: 多商品中有一个库存不足时全部回滚
    When 用户 "user_123" 提交以下订单:
      | skuId   | quantity |
      | SKU_001 | 2        |
      | SKU_002 | 10       |
    And 支付方式为 "ALIPAY"
    Then 返回状态码 409
    And 商品 "SKU_001" 库存保持为 10
    And 商品 "SKU_002" 库存保持为 5

  Scenario: 优惠券不存在
    When 用户 "user_123" 提交以下订单:
      | skuId   | quantity |
      | SKU_001 | 1        |
    And 支付方式为 "ALIPAY"
    And 使用优惠券 "INVALID_CODE"
    Then 返回状态码 404
    And 错误信息包含 "优惠券不存在"

  Scenario: 无效支付方式被拒绝
    When 用户 "user_123" 提交以下订单:
      | skuId   | quantity |
      | SKU_001 | 1        |
    And 支付方式为 "BITCOIN"
    Then 返回状态码 400

Step 4:生成 Step Definitions 骨架

# 切换到 Build 模式

根据 @src/test/resources/features/order/create-order.feature,
生成 Cucumber Step Definitions:
- 使用 Spring Boot Test
- 使用 @SpringBootTest + MockMvc 做集成测试
- 数据用 @Sql 注解初始化
- 保存到 src/test/java/com/example/order/acceptance/CreateOrderStepDefs.java

生成的 Step Definitions 骨架(节选):

@SpringBootTest
@AutoConfigureMockMvc
@CucumberContextConfiguration
public class CreateOrderStepDefs {

    @Autowired
    private MockMvc mockMvc;

    @Autowired
    private InventoryRepository inventoryRepository;

    private ResultActions result;
    private CreateOrderRequest.Builder requestBuilder;

    @Given("商品库存数据如下:")
    public void 商品库存数据如下(DataTable dataTable) {
        List<Map<String, String>> rows = dataTable.asMaps();
        rows.forEach(row -> {
            Inventory inv = new Inventory();
            inv.setSkuId(row.get("skuId"));
            inv.setStock(Integer.parseInt(row.get("stock")));
            inventoryRepository.save(inv);
        });
    }

    @When("用户 {string} 提交以下订单:")
    public void 用户提交以下订单(String userId, DataTable dataTable) {
        requestBuilder = CreateOrderRequest.builder().userId(userId);
        List<OrderItem> items = dataTable.asMaps().stream()
            .map(row -> OrderItem.builder()
                .skuId(row.get("skuId"))
                .quantity(Integer.parseInt(row.get("quantity")))
                .build())
            .collect(Collectors.toList());
        requestBuilder.items(items);
    }

    @And("支付方式为 {string}")
    public void 支付方式为(String paymentMethod) throws Exception {
        CreateOrderRequest request = requestBuilder
            .paymentMethod(PaymentMethod.valueOf(paymentMethod))
            .build();
        result = mockMvc.perform(
            post("/api/v1/orders")
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(request))
        );
    }

    @Then("返回状态码 {int}")
    public void 返回状态码(int statusCode) throws Exception {
        result.andExpect(status().is(statusCode));
    }

    @Then("订单状态为 {string}")
    public void 订单状态为(String status) throws Exception {
        result.andExpect(jsonPath("$.data.status").value(status));
    }

    // ... 其他 Step 实现
}

此时运行测试,全部为红(因为业务代码还没写)。

Step 5:并行实现业务代码

切换到 Build 模式,使用 ultrawork:

根据 @docs/api/create-order.yaml 和 @AGENTS.md 的规范,
实现创建订单功能。

要求:
1. CreateOrderRequest DTO(带完整 Bean Validation)
2. OrderController(委托给 Service,不含业务逻辑)
3. OrderService 接口 + OrderServiceImpl(包含库存事务性扣减)
4. 邮件异步发送(Spring @Async)
5. 全局异常处理器(BusinessException → 标准响应格式)

测试在 @src/test/resources/features/order/create-order.feature

ultrawork

Sisyphus 自动分解为多个并行子任务,Background Agents 同时工作。

生成的 OrderServiceImpl.java(核心逻辑节选):

@Service
@Slf4j
@RequiredArgsConstructor
public class OrderServiceImpl implements OrderService {

    private final InventoryRepository inventoryRepository;
    private final OrderRepository orderRepository;
    private final CouponService couponService;
    private final EmailService emailService;

    @Override
    @Transactional
    public OrderResponse createOrder(CreateOrderRequest request) {
        // 1. 验证并锁定库存(悲观锁,原子性保证)
        List<InventoryLock> locks = lockInventory(request.getItems());

        // 2. 计算金额
        BigDecimal totalAmount = calculateTotal(request.getItems());
        BigDecimal discountAmount = BigDecimal.ZERO;

        // 3. 处理优惠券
        if (StringUtils.hasText(request.getCouponCode())) {
            Coupon coupon = couponService.validateAndUse(request.getCouponCode());
            discountAmount = coupon.getDiscountAmount();
        }

        // 4. 创建订单
        Order order = Order.builder()
            .userId(request.getUserId())
            .status(OrderStatus.PENDING_PAYMENT)
            .totalAmount(totalAmount)
            .discountAmount(discountAmount)
            .finalAmount(totalAmount.subtract(discountAmount))
            .paymentMethod(request.getPaymentMethod())
            .build();

        order = orderRepository.save(order);

        // 5. 异步发送确认邮件(失败不影响订单)
        emailService.sendOrderConfirmationAsync(order);

        return OrderResponse.from(order);
    }

    /**
     * 锁定库存,任一商品不足则抛出异常触发回滚
     */
    private List<InventoryLock> lockInventory(List<OrderItem> items) {
        return items.stream()
            .map(item -> {
                Inventory inventory = inventoryRepository
                    .findBySkuIdWithLock(item.getSkuId())
                    .orElseThrow(() -> new BusinessException(
                        ErrorCode.PRODUCT_NOT_FOUND,
                        "商品不存在: " + item.getSkuId()
                    ));

                if (inventory.getStock() < item.getQuantity()) {
                    throw new BusinessException(
                        ErrorCode.INSUFFICIENT_STOCK,
                        "库存不足: " + item.getSkuId()
                    );
                }

                inventory.setStock(inventory.getStock() - item.getQuantity());
                inventoryRepository.save(inventory);
                return new InventoryLock(item.getSkuId(), item.getQuantity());
            })
            .collect(Collectors.toList());
    }
}

Step 6:运行验收测试,修复到全绿

运行 mvn test -Dtest="**/acceptance/**"
如果有失败的测试,逐一分析并修复,直到全部通过。

AI 自动运行测试、分析失败原因、修复代码,循环直到全绿。

✓ 正常创建订单(无优惠券)
✓ 使用有效优惠券创建订单
✓ 单个商品库存不足时全部回滚
✓ 多商品中有一个库存不足时全部回滚
✓ 优惠券不存在
✓ 无效支付方式被拒绝

Tests run: 6, Failures: 0, Errors: 0

Step 7:代码规范检查

@explore 检查新增的代码是否符合 @AGENTS.md 中的规范,
特别检查:
1. Service 层是否直接抛了 RuntimeException(应该用 BusinessException)
2. Controller 是否包含了业务逻辑
3. 公开方法是否都有 Javadoc

10. 团队规范建议

10.1 文件目录规范

project/
├── AGENTS.md                    # AI 项目说明书(必须提交 Git)
├── .opencode/
│   ├── oh-my-opencode.jsonc     # Oh-My-OpenCode 项目级配置
│   └── skills/                  # 项目自定义 Skills
│       └── java-conventions/
│           └── SKILL.md
├── openspec/                    # OpenSpec 规格目录
│   └── changes/
│       └── create-order/        # 每个功能一个目录
│           ├── proposal.md
│           ├── design.md
│           ├── tasks.md
│           └── specs/
│               └── api.yaml
├── docs/
│   └── api/                     # 已 apply 的 OpenAPI 规格(SDD 产物)
│       ├── order-api.yaml
│       └── user-api.yaml
└── src/
    └── test/
        └── resources/
            └── features/        # Cucumber feature 文件(ATDD 产物)
                └── order/
                    └── create-order.feature

10.2 开发流程检查清单

每个功能开发前:

  • 是否已用 /opsx:propose 开启规格流程(3+ 人团队)?
  • 是否已在 Plan 模式中澄清需求?
  • 是否已生成 OpenAPI 规格并提交?
  • 是否已运行 /opsx:apply 解锁代码权限?
  • 是否已生成 .feature 文件并 Review?

每个功能开发中:

  • Step Definitions 骨架是否已生成?
  • 是否优先让测试驱动实现?

每个功能完成后:

  • 验收测试是否全部通过?
  • 是否用 @explore 做了规范检查?
  • AGENTS.md 是否需要更新?
  • 是否运行 /opsx:archive 归档规格文档?

10.3 AGENTS.md 维护规范

  • 新增架构约束 → 更新 AGENTS.md 的"代码规范"部分
  • 新增公共组件 → 更新"项目结构"部分
  • 每个 Sprint 开始前 Review 一次 AGENTS.md

10.4 禁止事项

  • 禁止在 Build 模式下直接写代码,跳过 Plan 确认(复杂功能)
  • 禁止在未生成 .feature 文件的情况下完成功能开发
  • 禁止让 AI 修改 AGENTS.md 中的规范部分(只有人工修改)
  • 禁止在 3+ 人团队中跳过 /opsx:propose,直接进入 Build 模式

11. 常见问题

Q:OpenSpec 和 Plan 模式有什么本质区别,必须两者都用吗?
A:Plan 模式的文件写入控制是"弹确认框"(ask),人可以绕过。openspec-plan agent 使用 deny 权限,物理禁止写业务代码文件,AI 自身也无法绕过。小团队(1-2 人)靠自律用 Plan 模式即可;3+ 人团队推荐加 OpenSpec 作为流程护栏。两者不是非此即彼,而是深度互补。

Q:OpenSpec 的 /opsx:apply 后,规格还能改吗?
A:可以,/opsx:apply 只是复制规格文件并解锁权限,openspec/changes/<feature>/specs/ 目录仍然存在。改动规格后需要手动同步到 docs/api/,建议通过 PR 记录规格变更。

Q:Plan 模式下 AI 能修改文件吗?
A:默认情况下修改操作会弹出确认框(ask 权限),而非直接禁止。可以在 opencode.json 中将 plan agent 的 edit 权限设为 deny 实现完全只读。

Q:Oh-My-OpenCode 的 ultrawork 命令会不会乱改代码?
A:不会,它仍然受 OpenCode 的权限体系约束。可以在 .opencode/oh-my-opencode.jsonc 中配置 background_tasks.max_concurrent 控制并发度。

Q:AGENTS.md 写多少内容合适?
A:不宜过长(超过 500 行会占用大量 context window),核心规范放 AGENTS.md,细节规范通过 opencode.jsoninstructions 字段引用外部文件。

Q:Cucumber 测试跑得很慢怎么办?
A:验收测试(/acceptance/)和单元测试(/unit/)分开执行。验收测试走真实 Spring 上下文,慢是正常的,可以配 CI 并行执行。

Q:AI 生成的代码质量不稳定怎么办?
A:AGENTS.md 写得越详细,输出越稳定。另外可以创建团队专属的 Skill,把编码规范以 Skill 形式注入:.opencode/skills/java-conventions/SKILL.md


参考资料