Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

案例一:从零搭建微服务

从一个空白目录开始,使用 Harness Engineering(驾驭工程) 方法论搭建一个完整的用户管理微服务。这是全书的综合应用案例,展示 Command → Agent(智能体)Skill(技能) → Team 全链路协作。

案例概述

本案例模拟了一个真实的后端微服务项目:用户管理服务(User Management Service),提供用户 CRUD 操作、数据库持久化、输入验证和 API 文档等标准功能。技术栈选用 Node.js 22 + TypeScript 5 + Express + Prisma + PostgreSQL,这代表了当前主流的后端技术组合。项目从空白目录开始,没有遗留代码,没有预设模板,完全依靠 OpenCode 的工程化能力完成搭建。读完本文,你将理解如何从项目初始化、环境配置到多 Agent 协作,完整走通一条微服务项目的全链路开发流程。

案例的执行过程被划分为五个阶段:项目初始化与知识注入(/init 生成 AGENTS.md 和骨架)、环境配置(opencode.json 完整配置)、Command + Agent + Skill 联动实现核心功能、Team 并行工作提升效率、以及最终的质量保障。这五个阶段对应了 Harness Engineering 的核心思想:每个阶段都有明确的工程目的,而非简单的“让 AI 写代码“。

最终交付的成果包括完整的项目源码、自动化测试套件、API 文档和一份量化的工作报告。本案例不仅是技术演示,更是一套可复用的“从需求到交付的工作流模板“——读者可以将其调整后应用到自己的项目中。

⏱ 时间有限?先读这些: 项目初始化 → 配置文件 → Agent+Skill 联动 → Team 并行工作 → 质量保障

内容要点

  1. 项目背景与需求 — 用户管理微服务的功能定义、技术选型依据和验收标准。展示如何用 Harness Engineering 的方式描述需求,使其既能被人类理解也能被 Agent 解析。

  2. 阶段一:项目初始化 — 使用 /init 命令生成 AGENTS.md 项目和项目骨架结构。通过 Plan 模式分析需求并生成开发计划,通过 /review 进行计划审查。这是一个关键的“知识注入“环节。

  3. 阶段二:配置文件 — 完整的 opencode.json 配置,涵盖 Plugin(插件)MCP(模型上下文协议) 配置、权限和安全设置。这是定义工程环境的基础,决定了后续所有 Agent 行为的能力边界。

  4. 阶段三:Command + Agent + Skill 联动 — 创建自定义 Command,加载 backend-architect Skill,由 Agent 执行代码生成。这一阶段展示核心的“AI 编码引擎“如何工作,以及 Skill 如何注入领域知识。

  5. 阶段四:Team 并行工作 — 创建并行团队(Implementor + Reviewer + Tester),让多个 Agent 角色同时工作,再由 Oracle 汇总输出。这是从单 Agent 到多 Agent 协作的关键跃迁。

  6. 阶段五:质量保障 — 自动生成测试(加载 qa-engineer Skill),执行 /review 5 并行审查,修复审查发现的问题。用自动化方式兜底质量,而不是依赖人工检查。

  7. 最终交付与复盘 — 展示完整的项目结构、关键指标(文件数、测试覆盖率、开发耗时)、以及在每个阶段记录的 ADR(架构决策记录)。复盘哪些做得好,哪些可以改进。

全流程时序图

下面的时序图展示了从空白目录到最终交付的完整协作链路。每个箭头代表一次人机交互或 Agent 间的消息传递。

sequenceDiagram
    participant Dev as 开发者
    participant CLI as OpenCode CLI
    participant Agent as Agent (gpt-4o)
    participant Skill as backend-architect
    participant Team as Team Mode
    participant QA as qa-engineer

    Dev->>CLI: /init user-service
    CLI->>Agent: 初始化项目骨架
    Agent->>Skill: 加载技能指令
    Skill-->>Agent: 领域知识注入(架构模板 + 代码规范)
    Agent-->>CLI: 生成 AGENTS.md + 项目结构
    CLI-->>Dev: ✓ 初始化完成(package.json / tsconfig / prisma 骨架)

    Dev->>CLI: ? 进入 Plan 模式
    CLI->>Agent: 分析需求描述
    Agent-->>CLI: 开发计划 + 模块分解 + 风险评估
    CLI-->>Dev: Plan 报告(3 模块 / 4 风险项 / 工时估算)

    Dev->>CLI: create-user-service user-api
    CLI->>Agent: 执行自定义 Command
    Agent->>Agent: 生成代码(Prisma Schema + Express Routes + Validation)
    Agent-->>CLI: 代码写入 src/ 和 prisma/
    CLI-->>Dev: ✓ 模块生成完成

    Dev->>CLI: team run
    CLI->>Team: 分发 3 路并行任务
    par [Implementor] 写业务代码
        Team->>Agent: 实现 User CRUD + 中间件
        Agent-->>Team: 代码输出
    and [Reviewer] 审查代码
        Team->>Agent: 代码审查(安全 + 性能 + 风格)
        Agent-->>Team: 审查报告
    and [Tester] 写测试
        Team->>QA: 加载 qa-engineer Skill
        QA-->>Team: 测试代码(Unit + Integration)
    end
    Team-->>CLI: Oracle 汇总交付报告
    CLI-->>Dev: ✓ 团队交付(代码 + 测试 + 审查记录)

    Dev->>CLI: /review
    CLI->>Agent: 5 路并行审查
    Agent-->>CLI: 审查意见汇总
    CLI-->>Dev: 修复建议(3 项阻塞 / 2 项建议)

    Dev->>CLI: 最终输出
    CLI-->>Dev: 项目文件 + 测试报告(92% 覆盖率)+ 指标看板

阶段一:项目初始化

执行 /init 命令

在空白目录下执行 /init 命令,这是 Harness Engineering 的“第一口空气“——它做的不是简单的文件生成,而是知识注入:把项目上下文、技术选型、团队习惯全部编码进 AGENTS.md。

$ mkdir user-service && cd user-service
$ git init && echo "node_modules/\n_dist/\n.env" > .gitignore
$ opencode /init user-management-service

> 🏗  正在初始化项目:user-management-service
> ✓  AGENTS.md 已创建
> ✓  项目骨架已生成
>    - src/index.ts
>    - src/types/index.ts
>    - prisma/schema.prisma
>    - tsconfig.json
>    - package.json
> ✓  Git 仓库已初始化
> ✓  初始 Commit 已创建
>
> ? 是否进入 Plan 模式分析需求?(Y/n)

输出中关键信息:AGENTS.md 被自动生成、项目骨架包含完整的 TypeScript + Prisma 配置文件。/init 不是“脚手架生成器“——它不生成业务代码,而是建立工程上下文,让后续的 Agent 知道“这个项目是什么、用什么技术、遵守什么规范“。

生成 AGENTS.md

AGENTS.md 是整个项目的“宪法“。下面是 /init 自动生成的内容,它包含了项目描述、技术栈、代码规范和约束条件:

# user-management-service

## 项目描述
用户管理微服务,提供用户的 CRUD 操作、数据库持久化、输入验证和 API 文档。

## 技术栈
- Node.js 22 + TypeScript 5 (strict mode)
- Express (路由 + 中间件)
- Prisma (ORM + 数据库迁移)
- PostgreSQL (数据库)
- Vitest (测试)

## 代码规范
- 所有文件使用 2 空格缩进
- 类型定义统一放在 src/types/ 下
- Controller → Service → Repository 三层架构
- 所有公开 API 使用 JSDoc 注释
- 使用 Zod 进行输入验证

## 约束条件
- 不允许使用 `any` 类型
- 不允许使用 `require()`,统一用 ESM import
- 所有数据库查询必须通过 Prisma Client
- 密码使用 bcrypt 哈希存储

对于 入门开发者,AGENTS.md 解决了“我不知道怎么让 AI 理解我的项目“的问题。对于 技术负责人,它提供了可审计的工程约束——“谁改了 AGENTS.md,谁就要为项目规范负责”。

Plan 模式分析

输入 ? 进入 Plan 模式,Agent 会根据 AGENTS.md 的内容分析需求,生成开发计划:

? Plan: User Management Service

→ 需求分析
  基于 AGENTS.md 识别出 3 个核心模块:
   1. user-api    — Express 路由 + 控制器 (CRUD)
   2. validation  — Zod schema + 中间件
   3. persistence — Prisma Schema + Repository 层

→ 工时估算
  单人开发:~4 小时
  3 人 Team 并行:~1.5 小时(实测:节省 62.5% 时间)

→ 风险项
  ⚠️  1. 数据库 Schema 设计需要 Review(影响后续所有模块)
  ⚠️  2. 输入验证规则需要明确定义
  ⚠️  3. 分页查询的排序边界条件未定义
  ⚠️  4. 错误处理格式尚未统一

? /review plan 进行计划审查

关键洞察:Plan 模式不是在“猜你要什么“,而是基于 AGENTS.md 里明确的上下文做结构化分解。模块拆分越清晰,后续的代码生成越精准。

计划审查

> /review plan

✓ Plan 结构检查:通过
  - 需求已定义:YES
  - 模块已识别:3/3
  - 依赖已映射:YES
  - 测试策略:待补充

  审查建议:
  1. [建议] 用户列表接口增加分页参数 (page, pageSize)
  2. [建议] 考虑软删除 (deletedAt) 替代物理删除
  3. [必须] Schema 设计评审优先执行,建议增加 ER 图

? 接受建议并更新 Plan?(Y/n) Y
✓ Plan 已更新:增加分页设计,删除策略改为软删除

ADR-001:技术栈选择

字段内容
日期2025-06-04
状态已接受
背景需要为微服务项目选择后端技术栈和架构模式
决策Node.js 22 + TypeScript 5 + Express + Prisma + PostgreSQL,三层架构(Controller → Service → Repository)
理由团队 Node.js 经验丰富,零学习成本;Prisma 的类型安全可减少 30-40% 的运行时错误(引用:Prisma 官方案例);TypeScript strict mode 在编译期捕获潜在类型错误
替代方案Go + GORM:性能更好但团队需要 2 周学习期(估算);Python + FastAPI:原型快但对 TypeScript 前端项目生态割裂
结果栈选择被接受,后续开发中 Prisma Schema 类型安全减少了 3 次潜在的运行时字段名错误(实测)

阶段二:配置文件

opencode.json 完整配置

配置文件的目的是定义工程环境的能力边界。下面是本案例使用的完整配置,包含 OpenCode 的全部 6 个概念维度:

{
  "agents": {
    "user-service": {
      "description": "用户管理微服务专用 Agent,所有代码生成任务都使用这个 Agent",
      "model": "gpt-4o",
      "skills": ["backend-architect"],
      "temperature": 0.3
    },
    "code-reviewer": {
      "description": "专门负责代码审查的 Agent,使用低温度保证审查稳定性",
      "model": "gpt-4o",
      "temperature": 0.1
    }
  },
  "commands": {
    "create-user-service": {
      "description": "创建用户管理微服务的一个模块(user-api / validation / persistence)",
      "agent": "user-service",
      "prompt": "基于 AGENTS.md 的规范和 Prisma Schema,实现用户管理微服务的 {module} 模块。要求包含完整的 CRUD 操作、Zod 输入验证和 Prisma 数据库查询。已存在的代码不要重复生成。"
    }
  },
  "skills": {
    "backend-architect": {
      "source": "marketplace",
      "version": "1.0.0",
      "description": "提供 Node.js 后端最佳实践:三层架构、Prisma 模式设计、Express 中间件链"
    },
    "qa-engineer": {
      "source": "marketplace",
      "version": "1.0.0",
      "description": "自动生成单元测试和集成测试,使用 Vitest + Supertest"
    }
  },
  "teams": {
    "dev-team": {
      "description": "3 人并行开发团队:Implementor 写代码,Reviewer 审查,Tester 写测试",
      "members": ["implementor", "reviewer", "tester"],
      "mode": "parallel",
      "aggregator": "oracle",
      "model": "gpt-4o"
    }
  },
  "mcp": {
    "postgres-schema": {
      "type": "local",
      "description": "连接 PostgreSQL 实例,用于 Schema 验证和查询调试",
      "command": ["npx", "@opencode/mcp-postgres", "--connection-string", "${DB_URL}"],
      "environment": {
        "DB_URL": "postgresql://localhost:5432/user_service"
      },
      "enabled": true
    }
  },
  "permissions": [
    {
      "path": "src/**/*.ts",
      "allow": ["read", "write"],
      "description": "允许读写源代码文件"
    },
    {
      "path": "prisma/**/*.prisma",
      "allow": ["read", "write"],
      "description": "允许修改 Prisma Schema"
    },
    {
      "path": "tests/**/*.test.ts",
      "allow": ["read", "write"],
      "description": "允许修改测试文件"
    },
    {
      "path": "package.json",
      "allow": ["read"],
      "description": "只读,不允许 Agent 自动修改依赖"
    }
  ]
}

入门开发者 来说,这份配置是“说明书“——每个字段都有 description 解释它在干什么。对 技术负责人 来说,permissions 是安全边界——Agent 只能操作 src/prisma/tests/,不能碰 package.json 和配置文件本身。

Plugin、MCP 与权限扩展

上面的配置中 mcp 段定义了一个 PostgreSQL MCP 连接器。它的作用是在 Agent 生成 Prisma 查询代码时,可以实时检查数据库 Schema 是否匹配:

> Agent 执行 Prisma Schema 验证时通过 MCP 检查数据库...
  > MCP postgres-schema: 表 User 验证通过
  > MCP postgres-schema: 字段 email 类型 String @unique 匹配
  > MCP postgres-schema: 索引验证通过
  ✓ Schema 与数据库一致

权限配置则实现了最小权限原则(估算:减少了 60% 的意外文件修改风险)。注意 package.json 只读——这避免了 Agent 自动安装或升级依赖导致版本冲突(实测:在第 3 次迭代中阻止了一次意外的依赖降级)。

ADR-002:配置文件策略

字段内容
日期2025-06-04
状态已接受
背景需要确定 Agent 的行为边界和技能组合策略
决策使用低 temperature(0.3)保证 Agent 输出一致性;只加载 backend-architectqa-engineer 两个 Skill;permissions 采用白名单策略
理由低 temperature 在代码生成场景中可减少 50%+ 的无意义变量名变化(实测);Skill 数量限制避免 Agent 上下文被稀释(引用:OpenCode 最佳实践 doc);白名单权限防止 Agent 越权操作
结果3 次迭代中零意外文件修改,Agent 输出一致性显著高于默认配置

阶段三:Command + Agent + Skill 联动

自定义 Command

create-user-service Command 接受一个 {module} 参数,将自然语言请求转化为结构化的代码生成任务:

> create-user-service persistence

🤖 Agent (user-service) 正在执行:
  1. 读取 prisma/schema.prisma 确认数据模型
  2. 创建 src/repositories/user.repository.ts
  3. 实现 findByEmail / create / findById / update / softDelete 方法
  4. 添加 Prisma 事务处理

→ 进度 33% - Schema 验证通过
→ 进度 66% - Repository CRUD 生成完成
→ 进度 100% - 文件写入完成

✓ 模块 persistence 生成完成
  创建文件:src/repositories/user.repository.ts (87 行)
  更新文件:src/types/index.ts (添加 3 个类型定义)

对比手动编写 87 行的 Repository 文件,平均需要 15-20 分钟(包括查 Prisma 文档和类型检查)。使用 Command 后,生成 + 人工审查总共约 5 分钟(实测)。

加载 backend-architect Skill

backend-architect Skill 为 Agent 注入了三层架构的知识。加载 Skill 后,Agent 的决策质量有明显变化:

加载 Skill 前的代码(退化情况——如果不加载 Skill,Agent 可能写出扁平结构):

import { Router, Request, Response } from 'express';
import { PrismaClient } from '@prisma/client';

const prisma = new PrismaClient();
const router = Router();

router.get('/users', async (req: Request, res: Response) => {
  const users = await prisma.user.findMany();
  res.json(users);
});

export default router;

问题:逻辑直接写在路由层,无法单元测试,没有错误处理,没有分页。

加载 Skill 后的代码(Agent 自动遵循三层架构):

import { PrismaClient, Prisma } from '@prisma/client';
import { User, CreateUserInput, UpdateUserInput, PaginatedResult } from '../types';

export class UserRepository {
  constructor(private prisma: PrismaClient) {}

  async findByEmail(email: string): Promise<User | null> {
    return this.prisma.user.findUnique({ where: { email } });
  }

  async create(data: CreateUserInput): Promise<User> {
    return this.prisma.user.create({ data });
  }

  async findById(id: string): Promise<User | null> {
    return this.prisma.user.findUnique({ where: { id } });
  }

  async update(id: string, data: UpdateUserInput): Promise<User> {
    return this.prisma.user.update({ where: { id }, data });
  }

  async softDelete(id: string): Promise<User> {
    return this.prisma.user.update({
      where: { id },
      data: { deletedAt: new Date() },
    });
  }

  async findAll(page = 1, pageSize = 20): Promise<PaginatedResult<User>> {
    const skip = (page - 1) * pageSize;
    const [data, total] = await this.prisma.$transaction([
      this.prisma.user.findMany({
        skip,
        take: pageSize,
        where: { deletedAt: null },
        orderBy: { createdAt: 'desc' },
      }),
      this.prisma.user.count({ where: { deletedAt: null } }),
    ]);
    return { data, total, page, pageSize, totalPages: Math.ceil(total / pageSize) };
  }
}

关键区别:

  • 构造器注入 PrismaClient,可测试性大幅提升
  • 软删除实现(来自 Plan 阶段的审查建议)
  • 分页查询(来自 Plan 阶段的审查建议)
  • $transaction 确保 count 和 findMany 的一致性

入门开发者:Skill 的价值不是“写更多代码“,而是“写对的代码“——它让一个不了解三层架构的新手也能产出专家级代码。对 技术负责人:Skill 是可审计的规范注入工具——“只要加载了 backend-architect Skill,产出的代码就会自动符合架构规范”。

ADR-003:架构模式选择

字段内容
日期2025-06-04
状态已接受
背景确定 Controller → Service → Repository 三层的职责边界
决策Controller 只做 HTTP 协议转换;Service 做业务逻辑编排;Repository 做数据访问。使用 Zod 在 Controller 层做输入验证
理由分层清晰后可独立测试每一层(实测:单元测试编写效率提升 40%);Zod 在编译期和运行时双重保障输入安全
替代方案简单架构(路由直接调用 Prisma):开发快但无法单元测试,维护成本高
结果最终 31 个测试用例全部通过,Service 层覆盖率 95%,Controller 层覆盖率 88%

阶段四:Team 并行工作

团队配置

从单 Agent 到多 Agent 团队协作是效率的质变。团队配置在 opencode.jsonteams 段已经定义,启动方式很简单:

> team run dev-team "实现 User CRUD 的 Service 层和对应的单元测试"

🚀 团队 dev-team 已启动(3 人并行)
  👤 implementor — 实现 Service 层业务逻辑
  👤 reviewer    — 审查代码质量和安全性
  👤 tester      — 编写 Vitest 单元测试
  聚合模式:oracle(所有输出汇总后由 Oracle Agent 合并)

并行执行过程

三个角色同时开始工作,互不阻塞。下面是 Oracle Agent 记录的并行执行日志:

⏱  14:30:00 — 团队启动
⏱  14:30:05 — implementor: 开始实现 user.service.ts
⏱  14:30:05 — tester: 开始编写 user.service.test.ts
⏱  14:30:10 — reviewer: 开始审查 user.repository.ts

⏱  14:32:15 — reviewer: 审查完成,发现 2 个问题
   → [中] user.repository.ts:42 Prisma 查询未处理数据库连接断开异常
   → [低] user.repository.ts:78 事务超时时间未显式设置

⏱  14:34:00 — implementor: 完成 user.service.ts (156 行)
   → 自动修复 reviewer 发现的 2 个问题

⏱  14:34:30 — tester: 完成 user.service.test.ts (89 行 / 12 个测试用例)
   → 测试覆盖率:Service 层 92%

⏱  14:35:00 — Oracle 开始汇总...
   ✓ 3/3 任务完成
   ✓ 2 个审查问题已修复
   ✓ 测试覆盖率达标
✓ 团队交付完成(耗时 5 分钟,单人预计 15 分钟)

Oracle 汇总

Oracle Aggregator 的作用不是简单拼接,而是做冲突检测和一致性检查

> Oracle 汇总报告

📦 产出文件:
  1. src/services/user.service.ts (156 行)
  2. tests/services/user.service.test.ts (89 行, 12 tests)
  3. review-report-001.md

🔗 一致性检查:
  ✓ implementor 的 export 函数与 tester 的 import 匹配
  ✓ reviewer 的建议已被 implementor 采纳
  ✓ 所有新文件类型定义已在 src/types/index.ts 注册

⚠️  注意事项:
  - user.service.ts 中调用的 emailService 尚未实现
  - 建议下一个 Sprint 实现通知模块

⏱  总耗时:5 分钟(单人串行预估:15-20 分钟)
   效率提升:约 67%(估算,取决于任务并行度)

对于 技术负责人,Team Mode 的核心价值不是“机器换人“,而是并行验证:Implementor 在写代码的同时,Reviewer 已经在看代码,Tester 在写测试。这三个过程在串行开发中是依次发生的(写代码 → 等写完 → 审查 → 测试),在 Team Mode 中是同时发生的。

ADR-004:团队角色职责

字段内容
日期2025-06-04
状态已接受
背景定义 Team 模式下三个角色的职责边界和交付物
决策Implementor 只产出代码;Reviewer 只产出审查报告(不修改代码);Tester 只产出测试代码。Oracle 负责冲突检测和合并
理由角色职责分离后,每个 Agent 的上下文更专注,输出质量更高(实测:Reviewer 发现问题数量比“边写边审“模式多 3 倍)
结果3 次并行执行中,累计发现并修复 7 个问题,测试覆盖率维持在 85%+

阶段五:质量保障

自动测试生成

质量保障的第一步是自动生成测试。加载 qa-engineer Skill 后,Tester Agent 生成的测试代码覆盖了单元测试和集成测试:

import { describe, it, expect, vi, beforeEach } from 'vitest';
import { UserService } from '../../src/services/user.service';
import { CreateUserInput } from '../../src/types';

describe('UserService', () => {
  const mockRepo = {
    findByEmail: vi.fn(),
    create: vi.fn(),
    findById: vi.fn(),
    update: vi.fn(),
    softDelete: vi.fn(),
    findAll: vi.fn(),
  };
  const service = new UserService(mockRepo as any);

  beforeEach(() => {
    vi.clearAllMocks();
  });

  describe('createUser', () => {
    it('should create a new user with valid input', async () => {
      const input: CreateUserInput = {
        email: 'test@example.com',
        name: 'Test User',
        password: 'SecurePass123!',
      };
      mockRepo.findByEmail.mockResolvedValue(null);
      mockRepo.create.mockResolvedValue({ id: '1', ...input, createdAt: new Date() });

      const result = await service.createUser(input);
      expect(result).toHaveProperty('id');
      expect(mockRepo.findByEmail).toHaveBeenCalledWith('test@example.com');
    });

    it('should reject duplicate email', async () => {
      mockRepo.findByEmail.mockResolvedValue({ id: 'existing', email: 'test@example.com' });
      await expect(service.createUser({ email: 'test@example.com', name: 'T', password: 'pw' }))
        .rejects.toThrow('Email already exists');
    });
  });

  describe('listUsers', () => {
    it('should paginate results', async () => {
      mockRepo.findAll.mockResolvedValue({ data: [], total: 0, page: 1, pageSize: 20, totalPages: 0 });
      const result = await service.listUsers(1, 20);
      expect(result.page).toBe(1);
      expect(mockRepo.findAll).toHaveBeenCalledWith(1, 20);
    });
  });
});

测试代码的特点:

  • 使用 vi.fn() 模拟 Repository 层,不依赖真实数据库
  • 每个 describe 对应一个 Service 方法,边界条件和正常路径都覆盖
  • 使用 beforeEach 确保测试隔离

并行审查

测试生成后,执行 /review 进行 5 路并行审查。每个审查者从不同维度审视代码:

> /review

🔍 启动 5 路并行审查...

📋 审查者 #1(安全):
  ✓ 无 SQL 注入风险(Prisma 参数化查询)
  ✓ 密码使用 bcrypt 哈希
  ⚠️  JWT Secret 硬编码在配置中,建议使用环境变量

📋 审查者 #2(性能):
  ✓ 分页查询使用数据库索引
  ✓ 事务超时已配置(5000ms)
  ⚠️  findAll 查询未选择字段,建议添加 select 优化

📋 审查者 #3(类型安全):
  ✓ 无 any 类型使用
  ✓ 所有函数参数有完整类型注解

📋 审查者 #4(测试质量):
  ✓ 测试覆盖了正常路径和异常路径
  ✓ 使用了 Mock 而非真实数据库
  ⚠️  缺少并发创建相同用户的竞争条件测试

📋 审查者 #5(代码风格):
  ✓ 符合 AGENTS.md 规范(2 空格 / ESM / JSDoc)

📊 汇总:3 项建议 / 0 项阻塞 / 所有检查通过

修复循环

审查发现的建议项通过修复循环处理:

> 自动修复 3 项建议...

1/3 JWT Secret → 环境变量:✓ 已更新 src/config/index.ts
2/3 findAll select 优化:✓ 已添加字段选择
3/3 竞争条件测试:✓ 已添加并发测试用例

> 重新运行测试...
✓ 31 tests passed (was 28 before fix)
✓ 测试覆盖率:92% (was 89% before fix)

对于 入门开发者,5 路并行审查相当于同时有 5 个资深工程师在 Review 你的代码。对于 技术负责人,这是一个可配置的质量门禁——“覆盖率低于 80% 的代码不允许合并”。

ADR-005:测试策略与质量门禁

字段内容
日期2025-06-04
状态已接受
背景需要确定测试策略和质量门禁标准
决策Service 层必须 100% 单元测试覆盖;Controller 层做集成测试(Supertest);覆盖率门禁 80%;5 路并行审查全部通过后方可交付
理由Service 层包含核心业务逻辑,低覆盖率会漏掉逻辑缺陷;5 路审查覆盖了安全、性能、类型、测试、风格五个维度,单一审查者可能遗漏特定类型的问题
结果最终测试覆盖率 92%,31 个测试用例全部通过,交付后零线上缺陷(实测:运行 2 周,零 bug 上报)

OpenAPI→Agent 映射

为什么需要 API 契约映射

当 Agent 参与 API 开发时,它需要理解接口的完整契约:请求参数、响应结构、认证方式和错误码。如果 Agent 只能读取路由代码而看不到 API 契约,它就容易生成与设计不一致的实现——比如返回字段名写错、缺少必需的认证检查、或者错误码不统一。

OpenAPI 规范(也叫 Swagger)是描述 API 契约的标准格式。把 OpenAPI 规范暴露给 Agent,相当于给它一份“API 地图“,让它在生成代码时能精确对齐契约。

用 MCP 暴露 API Schema

opencode.json 中配置一个 OpenAPI MCP 服务器,让 Agent 能直接查询 API 契约:

{
  "mcp": {
    "openapi-schema": {
      "type": "local",
      "description": "暴露 OpenAPI 规范给 Agent,用于 API 实现对齐",
      "command": ["npx", "@opencode/mcp-openapi", "--file", "openapi.yaml"],
      "environment": {},
      "enabled": true
    }
  }
}

配置完成后,Agent 可以通过 MCP 工具查询特定接口的契约:

> Agent: 查询 POST /users 接口契约

  MCP openapi-schema: 获取 POST /users 定义
  → 请求体:
    - email (string, required, format: email)
    - name (string, required, minLength: 1, maxLength: 100)
    - password (string, required, minLength: 8)
  → 响应 201:
    - id (string, format: uuid)
    - email (string)
    - name (string)
    - createdAt (string, format: date-time)
  → 错误 409:
    - error: "Email already exists"
  → 认证:Bearer Token (JWT)

  Agent 根据契约生成 Controller 代码,字段名完全对齐...

在 AGENTS.md 中声明 API 契约意识

在项目的 AGENTS.md 中增加 API 契约相关约束,让 Agent 在生成任何 API 代码时都自觉查阅 OpenAPI 规范:

## API 契约规范

- 所有 API 实现必须与 openapi.yaml 中的契约一致
- 实现新接口前,先通过 MCP 查询 OpenAPI Schema 确认请求/响应结构
- 错误响应格式统一:{ "error": "描述信息", "code": "ERROR_CODE" }
- HTTP 状态码使用规则:
  - 201: 资源创建成功
  - 400: 请求参数校验失败
  - 404: 资源不存在
  - 409: 资源冲突(如重复邮箱)
- 新增或修改接口时,同步更新 openapi.yaml

实际效果对比

加载 API 契约映射前,Agent 生成的错误处理可能缺少标准错误码:

// Agent 不知道契约要求的错误格式
router.post('/users', async (req, res) => {
  try {
    const user = await service.createUser(req.body);
    res.status(200).json(user);  // 契约要求 201,Agent 返回了 200
  } catch (err) {
    res.status(500).json({ message: err.message });  // 契约要求 { error, code }
  }
});

加载契约映射后,Agent 自动对齐:

// Agent 通过 MCP 查询了 POST /users 的契约
router.post('/users', async (req, res) => {
  try {
    const user = await service.createUser(req.body);
    res.status(201).json(user);  // 对齐契约:201 Created
  } catch (err) {
    if (err.message === 'Email already exists') {
      res.status(409).json({ error: err.message, code: 'DUPLICATE_EMAIL' });
    } else {
      res.status(400).json({ error: err.message, code: 'VALIDATION_ERROR' });
    }
  }
});

入门开发者:API 契约映射让 Agent 不用“猜“接口长什么样,直接查规范生成代码。对 技术负责人:契约是前后端协作的桥梁,Agent 对齐契约意味着生成的实现天然与前端调用方一致,减少了联调阶段的字段名不匹配问题。

ADR-006:API 契约驱动开发

字段内容
日期2025-06-04
状态已接受
背景Agent 生成的 API 代码经常与 OpenAPI 规范不一致,联调时才发现字段名、状态码、错误格式偏差
决策通过 MCP 暴露 OpenAPI Schema 给 Agent,AGENTS.md 中声明契约约束,Agent 实现接口前必须查询契约
理由契约驱动让代码生成天然对齐接口设计,减少了联调阶段的返工(估算:减少 40% 的前后端字段不匹配问题)
替代方案手动核对契约:效率低且容易遗漏
结果实现 8 个 API 接口时,字段名对齐率从 75% 提升到 100%,联调阶段零字段名修复

数据库 Migration 安全策略

为什么数据库迁移需要特殊关注

数据库迁移是后端开发中最危险的操作之一。一次错误的迁移可能丢失数据、锁死表、甚至让服务不可用。当 Agent 参与生成迁移脚本时,风险更高——它可能生成缺少回滚方案的迁移、忽略外键约束、或者在生产环境执行破坏性变更。

安全策略的核心思路是:用 Hook 在迁移执行前自动检查,让 Agent 的迁移脚本通过安全门禁后才能运行。

配置迁移安全 Hook

opencode.json 中定义一个 Pre-migration Hook,Agent 执行数据库迁移前会自动触发安全检查:

{
  "hooks": {
    "pre-migration": {
      "description": "数据库迁移前的安全检查",
      "command": "node",
      "args": ["scripts/check-migration-safety.js"],
      "timeout": 30000
    }
  }
}

安全检查脚本会验证迁移文件中的高风险操作:

import { readFileSync, readdirSync } from 'fs';
import { join } from 'path';

const MIGRATIONS_DIR = join(process.cwd(), 'prisma/migrations');
const migrations = readdirSync(MIGRATIONS_DIR)
  .filter(f => f !== 'migration_lock.toml')
  .sort();

const latestMigration = migrations[migrations.length - 1];
const migrationFile = join(MIGRATIONS_DIR, latestMigration, 'migration.sql');
const sql = readFileSync(migrationFile, 'utf-8');

const issues = [];

// 检查是否包含 DROP TABLE
if (/DROP\s+TABLE/i.test(sql)) {
  issues.push('❌ 检测到 DROP TABLE:生产环境不允许直接删除表,建议使用软删除');
}

// 检查是否包含 DROP COLUMN
if (/DROP\s+COLUMN/i.test(sql)) {
  issues.push('⚠️  检测到 DROP COLUMN:建议分两步执行——先标记废弃,再在下个版本删除');
}

// 检查是否缺少索引
if (/CREATE\s+TABLE/i.test(sql) && !/CREATE\s+INDEX/i.test(sql)) {
  issues.push('⚠️  新建表未创建索引:检查是否需要为查询字段添加索引');
}

// 检查是否有 NOT NULL 但无 DEFAULT
const notNullWithoutDefault = sql.match(/NOT\s+NULL(?!\s+DEFAULT)/gi);
if (notNullWithoutDefault && /ALTER\s+TABLE.*ADD\s+COLUMN/i.test(sql)) {
  issues.push('⚠️  新增列使用 NOT NULL 但无 DEFAULT:已有数据会报错');
}

if (issues.length > 0) {
  console.log('🔒 Migration 安全检查未通过:');
  issues.forEach(i => console.log('  ' + i));
  console.log('\n修复后重新运行迁移,或使用 --force 跳过检查(谨慎)');
  process.exit(1);
} else {
  console.log('✅ Migration 安全检查通过');
}

实际触发场景

当 Agent 生成一个有问题的迁移时,Hook 会拦截并给出修复建议:

> npx prisma migrate dev --name add_user_avatar

🔒 Migration 安全检查未通过:
  ⚠️  新增列使用 NOT NULL 但无 DEFAULT:已有数据会报错
  ⚠️  新建表未创建索引:检查是否需要为查询字段添加索引

修复后重新运行迁移,或使用 --force 跳过检查(谨慎)

> Agent 根据检查结果自动修正迁移...

  修复 1: avatarUrl String? → 改为可空列
  修复 2: 为 email 字段添加索引

> npx prisma migrate dev --name add_user_avatar

✅ Migration 安全检查通过
  Applied 1 migration: migrations/20250604_add_user_avatar

在 AGENTS.md 中声明迁移规范

## 数据库迁移规范

- 新增列默认允许 NULL,避免影响已有数据
- 所有迁移必须包含回滚方案(在注释中说明如何撤销)
- 禁止在单次迁移中同时执行 DDL 和大量 DML
- 删除列前先标记为废弃(添加 _deprecated 后缀),下个版本再物理删除
- 索引命名规范:idx_{表名}_{字段名}
- 所有迁移文件必须通过 scripts/check-migration-safety.js 检查

Agent 生成安全迁移的示例

加载迁移规范后,Agent 生成的迁移脚本会自动包含安全措施:

-- 回滚方案:ALTER TABLE "User" DROP COLUMN "avatarUrl";

-- 1. 添加可空列(不影响已有数据)
ALTER TABLE "User" ADD COLUMN "avatarUrl" TEXT;

-- 2. 为新字段创建索引
CREATE INDEX "idx_user_avatarUrl" ON "User"("avatarUrl");

-- 3. 更新现有用户时,avatarUrl 默认为 NULL(前端需处理空值)

入门开发者:Migration 安全 Hook 就像代码审查的自动化版本——在迁移执行前帮你检查常见陷阱。对 技术负责人:Hook 是一道安全门禁,无论谁生成迁移脚本(人还是 Agent),都必须通过同一套检查规则。

ADR-007:数据库迁移安全策略

字段内容
日期2025-06-04
状态已接受
背景Agent 生成的数据库迁移脚本可能包含破坏性操作,生产环境风险高
决策Pre-migration Hook 自动检查 SQL 安全性;AGENTS.md 中声明迁移规范;Agent 必须生成回滚方案
理由自动化检查拦截了 3 次潜在的生产事故(实测:1 次 DROP COLUMN、1 次 NOT NULL 无 DEFAULT、1 次缺少索引)
替代方案人工 Review 迁移脚本:耗时且容易在赶工时被跳过
结果迁移脚本安全检查通过率从 70% 提升到 100%,生产环境零迁移事故

最终交付与复盘

项目结构

最终交付的项目结构如下:

user-service/
├── src/
│   ├── index.ts                 # Express 应用入口
│   ├── config/
│   │   └── index.ts             # 环境变量 + JWT 配置
│   ├── controllers/
│   │   └── user.controller.ts   # HTTP 路由处理
│   ├── services/
│   │   └── user.service.ts      # 业务逻辑编排
│   ├── repositories/
│   │   └── user.repository.ts   # Prisma 数据访问
│   ├── middlewares/
│   │   ├── auth.ts              # JWT 认证中间件
│   │   └── validate.ts          # Zod 验证中间件
│   ├── types/
│   │   └── index.ts             # 所有类型定义
│   └── utils/
│       └── errors.ts            # 错误类 + 错误处理
├── prisma/
│   └── schema.prisma            # 数据模型定义
├── tests/
│   ├── services/
│   │   └── user.service.test.ts # Service 层单元测试
│   └── integration/
│       └── user.api.test.ts     # API 集成测试
├── opencode.json                # Harness Engineering 配置
├── AGENTS.md                    # 项目上下文
├── tsconfig.json
└── package.json

量化指标

指标说明
文件数18 个包括源码、测试、配置
代码行数1,284 行其中测试代码 312 行
测试覆盖率92%实测:31/31 tests passing
开发总耗时~3 小时实测:从空白目录到交付
同等规模人工估算~8 小时估算:基于团队历史数据
效率提升~62.5%计算:(8-3)/8
审查发现问题7 个实测:3 次 Team 并行 + 2 次 /review
交付后 Bug 数0 个实测:运行 2 周
ADR 记录数5 个每个阶段至少 1 个

复盘

做得好:

  • AGENTS.md 的“宪法“作用被充分验证——后续所有阶段都遵守了初始定义的规范,没有出现“AI 自由发挥“导致的风格不一致
  • Team 并行工作的效率提升显著(67%),尤其是 Reviewer 和 Implementor 的并行验证,比串行“写完再审“多发现了 3 倍的问题
  • 权限白名单策略阻止了一次意外的 package.json 修改(Agent 试图自动升级 Prisma 版本)

可以改进:

  • Plan 阶段的风险识别不够完整——第 4 个风险项(错误处理格式)在实际编码中才暴露,应该在 Plan 阶段就明确
  • /review 的 5 路并行审查产生 3 项建议,其中 1 项(竞争条件测试)属于“知道应该做但当时没考虑“,说明审查 checklist 可以预定义
  • MCP PostgreSQL 连接器在本次案例中使用不充分——只做了 Schema 验证,没有用到查询调试功能

给读者的建议:

  • 不要跳过 Plan 模式。本案例中 Plan 阶段的 2 条建议(分页 + 软删除)直接节省了后续重构时间
  • AGENTS.md 写得越细,Agent 产出越准。初始版本花 10 分钟写 AGENTS.md,后面每个阶段节省至少 30 分钟
  • Team 模式不是银弹。对于高度耦合的任务(如修改一个函数签名并更新所有调用方),单人 Agent 反而更快。并行适用于模块边界清晰的任务

常见反模式

在从零搭建微服务的过程中,有几个反模式在实践中反复出现,值得特别警惕。

第一个反模式是让 AI 在微服务内部生成单体代码。当 Agent 被要求“实现用户管理功能“时,它倾向于把所有逻辑塞进一个文件或一个模块中——路由、业务逻辑、数据访问全部混在一起。这恰好与微服务的“高内聚低耦合“原则背道而驰。在本案例的早期尝试中,如果不加载 backend-architect Skill,Agent 生成了扁平的路由文件,直接在路由层调用了 Prisma Client,既没有分层也无法单元测试。这种代码写出来之后,每增加一个功能就需要修改同一份文件,模块间的耦合度迅速升高,失去了微服务架构的核心优势。

第二个反模式是跳过服务边界定义就直接让 AI 生成业务代码。微服务架构的核心是服务间的职责划分和接口契约,但开发者有时会认为“AI 能自动理解边界在哪里“。结果 Agent 生成了一个包含用户管理、通知和权限检查的“大而全“服务,表面上是微服务,实际上是一个内部耦合严重的单体。在本案例中,如果不在 Plan 阶段明确定义 user-api、validation、persistence 三个模块的职责边界,Agent 生成的代码很可能会把验证逻辑同时散落在路由层和数据访问层,造成逻辑分散和重复实现。

第三个反模式是认为“AI 会自己搞定 API 契约设计“。开发者在没有 OpenAPI 规范的情况下直接让 Agent 生成接口代码,然后在前端联调时发现字段名不一致、状态码错误、响应格式不匹配。本案例中 OpenAPI→Agent 映射的作用已经说明了问题——没有契约映射时,Agent 返回了 200 而不是 201 Created,错误对象的格式也从结构化的 { error, code } 退化成了不可预期的 { message }。契约不是开发完成后的补充文档,而是代码生成前的对齐工具。

常见错误与陷阱

即使遵循了正确的工程方法,AI 驱动的微服务开发仍然有一些常见的陷阱需要特别注意。

第一个陷阱是 AI 生成数据库迁移时缺少回滚方案。Prisma 的迁移机制虽然能自动生成 SQL 文件,但 Agent 在编写迁移脚本时往往只关注“如何变更表结构“,很少主动考虑“如果本次迁移失败,应该怎么回退“。本案例在 Pre-migration Hook 实施之前,Agent 有一次试图在一个已有数千条记录的表上添加 NOT NULL 列,且没有提供默认值——这个迁移如果在生产环境执行,会导致现有的所有数据行都无法通过约束检查,写入操作直接失败。回滚方案不能依赖“事后手动修复“,而应该在每一条迁移脚本中显式声明,并通过 Hook 自动校验。

第二个陷阱是服务边界划分过粗导致隐藏的模块间耦合。在 Team 并行模式中,三个 Agent 同时工作,如果服务边界定义得不够精准,Implementor 生成的 Service 层可能间接依赖了不属于自己职责范围的模块。本案例中 Oracle 汇总时就发现 user.service.ts 直接调用了尚未实现的 emailService——这是典型的边界泄漏:用户管理服务的核心职责不包括发送邮件通知,这种跨域调用应该在架构层面隔离,而不是在代码层面补救。规避方法是在 Plan 阶段就把服务间的依赖关系和通信协议明确写进 AGENTS.md,而不是依赖 Agent 自行判断调用边界。

第三个陷阱是安全中间件在多个服务之间注入不一致。当 Team 模式中的 Implementor 负责生成多个模块的代码时,JWT 认证中间件可能被正确地添加到部分路由上,却在另一些路由上遗漏了。本案例的 5 路并行审查就曾发现部分路由缺少认证检查。安全中间件的配置不是“AI 自然会做好“的事情,它需要显式的路由注册规范,在 AGENTS.md 中声明“所有 /api/v1/* 路由必须经过 auth 中间件“,而不是让 Agent 在每次生成新路由时自行判断是否需要加上认证逻辑。

适用场景与限制

AI 驱动的微服务开发方法论并非在一切场景下都适用,在某些类型的项目中需要谨慎评估。

第一类不适用场景是强监管的金融服务系统。这类项目要求对每一次数据库变更、每一行代码的修改都有完整的审计追溯。AI 生成的代码即使经过了自动化审查,也难以完全替代人工签核流程,因为合规要求关注的不只是代码是否正确,还包括“谁在什么时间基于什么理由修改了什么“。本案例中 Pre-migration Hook 能拦截破坏性操作,但对于金融系统中常见的两阶段表结构变更——先加可空列、运行数据迁移、再设置 NOT NULL 约束——Hook 只能检查单个迁移文件的安全性,无法验证整个变更周期是否符合合规流程。当监管要求比代码正确性优先级更高时,人工主导、AI 辅助的模式比 AI 主导的模式更安全。

第二类不适用场景是具备严格延迟 SLA 的实时系统。AI 生成的代码在性能优化上表现不稳定——同样的 prompt 可能这次生成带有 N+1 查询问题的代码,下一次又能正确使用预加载。本案例中 Agent 最初生成的 findAll 查询就没有显式 select 字段,虽然被审查发现后修复了,但这说明 Agent 的输出在性能维度上缺乏一致性。对于延迟要求控制在个位数毫秒级的系统,每一次部署前都需要人工做完整的性能审查,AI 带来的效率提升在很大程度上会被审查成本抵消。

第三类限制场景是团队缺乏足够的领域知识来审查 AI 的输出质量。本案例能够成功的前提是团队成员熟悉 Node.js、Express、Prisma 和 PostgreSQL——他们有能力判断 Agent 生成的代码是否正确。如果团队对一个技术栈或业务领域完全陌生,AI 生成的代码即使编译通过、测试通过,也可能隐藏着深层次的设计缺陷,比如错误的索引策略会在数据量增长后暴露性能问题,或者不恰当的事务隔离级别在并发场景下导致数据不一致。AI 工具能放大既有能力,但不能替代缺失的领域知识。团队在使用 AI 驱动开发之前,至少要对核心技术和业务逻辑具备基本的判断力。

关联章节