GitHub Spec Kit 实战(四):读懂和干预 /speckit.plan——AI 最自由发挥的一步

GitHub Spec Kit 实战(四):读懂和干预 /speckit.plan------AI 最自由发挥的一步

前两篇讲了 specify 怎么写不偏、constitution 怎么定规矩。/speckit.plan 这一步,AI 最容易"自由发挥",也是人最该把关的地方。

为什么?

  • specify 阶段有"不许写技术栈"的强约束,AI 戴着手铐写
  • constitution 是项目级硬规则,AI 读到会遵守
  • plan 阶段是第一次要 AI 选型 ------技术栈、依赖、架构、项目结构,全靠 AI 推断。完全没有"不许发挥"的限制

我前前后后看了 30 多份 AI 出的 plan.md,翻车方式高度雷同:

  • 选型"看起来很合理"但和项目现状冲突(明明项目用 Spring Boot,AI 给规划了一套 Express + Prisma)
  • "一次性做了 8 个功能模块"------P1 之外的也顺手规划了
  • 选型决策没记录 rationale------3 个月后没人记得"为什么用 X"
  • Constitution Check 流于形式------AI 自己写的、自检的、自通过的

这篇文章把 /speckit.plan 怎么读、怎么改、怎么卡住 AI 讲透。

plan.md 的骨架:6 个必填节

仓库的 plan-template.md 给的骨架:

章节 填什么
Summary 一句话讲明白做什么 + 怎么实现
Technical Context 6 个字段:语言/版本、主依赖、存储、测试、目标平台、项目类型
Constitution Check 双重门禁------Phase 0 前 + Phase 1 后各查一次
Project Structure 文档 + 源码目录树(3 选 1,未选的要删
Complexity Tracking 违反宪法时的"罪状表"(多数 plan 留空)
Phase 0/1 产物 research.md / data-model.md / contracts/ / quickstart.md

plan.md 命令源码里写明:

Phase 0: Generate research.md (resolve all NEEDS CLARIFICATION)

Phase 1: Generate data-model.md, contracts/, quickstart.md

Re-evaluate Constitution Check post-design

两阶段不是装饰------Phase 0 解决"用什么",Phase 1 解决"怎么用"。AI 必须先做完 Phase 0 才有 Phase 1。

第一个关卡:Technical Context 别让 AI 瞎填

Technical Context 有 6 个字段,每个都可能 NEEDS CLARIFICATION

复制代码
**Language/Version**: [e.g., Python 3.11, Swift 5.9, Rust 1.75 or NEEDS CLARIFICATION]
**Primary Dependencies**: [e.g., FastAPI, UIKit, LLVM or NEEDS CLARIFICATION]
**Storage**: [if applicable, e.g., PostgreSQL, CoreData, files or N/A]
**Testing**: [e.g., pytest, XCTest, cargo test or NEEDS CLARIFICATION]
**Target Platform**: [e.g., Linux server, iOS 15+, WASM or NEEDS CLARIFICATION]
**Project Type**: [e.g., library/cli/web-service/mobile-app/compiler/desktop-app or NEEDS CLARIFICATION]
**Performance Goals**: [domain-specific, e.g., 1000 req/s, 10k lines/sec, 60 fps or NEEDS CLARIFICATION]
**Constraints**: [domain-specific, e.g., <200ms p95, <100MB memory, offline-capable or NEEDS CLARIFICATION]
**Scale/Scope**: [domain-specific, e.g., 10k users, 1M LOC, 50 screens or NEEDS CLARIFICATION]

AI 翻车点 1:直接把 NEEDS CLARIFICATION 全填上"合理默认"

我看到的 plan 经常长这样:

Language/Version : TypeScript 5.6

Primary Dependencies : React 18, Express 4, Prisma

Storage: PostgreSQL 16

AI 自己拍脑袋填的。没有问你,没有写 rationale。

plan.md 命令源码里的原话是"mark unknowns as NEEDS CLARIFICATION"------AI 应该不知道时标不知道,不是 AI 替用户决定。

正确做法: 给 plan 命令加约束(写在 constitution 的 Additional Constraints 里):

markdown 复制代码
### VI. Plan Stage Discipline
- Technical Context 字段中,AI 推断出的选型必须用 [INFERRED: 原因] 标注
- 任何 [INFERRED] 项必须在 Phase 0 research.md 中给出 Decision/Rationale/Alternatives
- 用户未在 spec 中明确提到的技术选型,必须保留 NEEDS CLARIFICATION 让用户确认

读 plan 的第一步:打开 Technical Context,逐项检查------

  • 有没有 NEEDS CLARIFICATION 留下没解决的?(不该有,Phase 0 必须解决)
  • 有没有 AI 推断但没标 INFERRED 的?(项目里搜"Primary Dependencies"等关键字段,看 AI 是否偷偷填了)
  • 推断是否和 codebase 现状冲突?(项目用 Spring Boot,AI 规划 Express------这就是冲突)

第二个关卡:Constitution Check 双重门禁

plan-template.md 顶部明文写:

GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.

两重门禁。Phase 0 前查一次------技术选型是否违反宪法;Phase 1 后查一次------数据模型、接口契约是否违反宪法。

AI 翻车点 2:Constitution Check 自写自审

我见过太多 plan 的 Constitution Check 段是:

Constitution Check

  • ✅ Library-First: 本 feature 设计为独立模块
  • ✅ CLI Interface: 暴露 CLI
  • ✅ Test-First: 测试先行
  • ✅ Observability: 结构化日志

All gates pass.

AI 自己写的、自检的、全部通过。这等于没有检查。

正确做法 :把 Constitution Check 改造成机械检查而不是 AI 自评。比如改成:

markdown 复制代码
## Constitution Check

| Principle | Gate | Status | Evidence |
|-----------|------|--------|----------|
| I. Library-First | 是否独立 library? | ✅ | `packages/photo-album/` 独立目录,含独立 package.json |
| II. CLI Interface | 是否暴露 CLI? | ⚠️ | CLI 计划在 v1.1,v1 仅提供 HTTP API |
| III. Test-First (NON-NEGOTIABLE) | 测试是否先写? | ✅ | tasks.md 中 [TEST] 任务排在 [IMPL] 之前 |
| IV. Integration Testing | 是否规划集成测试? | ✅ | tasks.md 含 `tests/integration/` 任务 |
| V. Observability | 是否带结构化日志? | ✅ | data-model.md 含 audit_log 实体 |

**Violations requiring justification**: II. CLI Interface 推迟到 v1.1
(见 Complexity Tracking 第 1 条)

关键差异

  • 每条原则都问具体问题("是否独立 library?"),不是抽象的"是否符合"
  • Status 用 ✅ / ⚠️ / ❌ 三档,不是非黑即白
  • Evidence 列指向具体证据(文件路径、章节),不是"已规划"
  • 违反项必须进 Complexity Tracking

第三个关卡:Project Structure 别让 AI 留 3 套选项

plan-template.md 给的源码段是这样:

markdown 复制代码
### Source Code (repository root)

```text
# [REMOVE IF UNUSED] Option 1: Single project (DEFAULT)
src/
├── models/
...

# [REMOVE IF UNUSED] Option 2: Web application (when "frontend" + "backend" detected)
backend/
...
frontend/
...

# [REMOVE IF UNUSED] Option 3: Mobile + API (when "iOS/Android" detected)
api/
...
ios/ or android/
...

plan-template.md 注释里明文要求:

ACTION REQUIRED: Replace the placeholder tree below with the concrete layout for this feature. Delete unused options and expand the chosen structure with real paths (e.g., apps/admin, packages/something). The delivered plan must not include Option labels.

AI 翻车点 3:3 个 Option 全部保留

我看过一个 plan,三套结构全留着,最后加一句"Selected: Option 1"。这是偷懒------AI 没做选型决策。

正确读法 :plan 里如果出现 Option 1 / Option 2 / Option 3 文字,直接打回让 AI 改。要求 AI:

  1. 选 1 个 Option
  2. 删掉另外 2 个的代码块
  3. 选中的 Option 展开成真实路径 (不是 src/ models/ 这种占位)
  4. Structure Decision 段写为什么选这个

相册例子改后:

markdown 复制代码
### Source Code (repository root)

```text
packages/
├── photo-album-core/        # 核心库,Library-First 原则
│   ├── src/
│   │   ├── models/
│   │   ├── services/
│   │   └── lib/
│   └── tests/
│       ├── unit/
│       └── integration/
└── photo-album-cli/         # CLI 入口,CLI Interface 原则
    └── src/

apps/
└── web/                     # Web 界面,v1.1
    ├── src/
    └── tests/

Structure Decision : 选 Option 1 变体 ------把源码拆成 packages/(核心库 + CLI)和 apps/(前端),因为宪法 Principle I 要求每个 feature 起步为独立 library,Principle II 要求暴露 CLI。

复制代码
## 第四个关卡:读 research.md 看 AI 怎么选型

Phase 0 产出的 `research.md` 是 plan 里**最值得读的一份**。`plan.md` 命令源码里规定的格式:

> - Decision: [what was chosen]
> - Rationale: [why chosen]
> - Alternatives considered: [what else evaluated]

**AI 翻车点 4:Decision + Rationale 齐全,但 Alternatives 是凑数的**

我看过的 research.md 一半长这样:

> ### Question 1: 数据库选型
>
> **Decision**: PostgreSQL
> **Rationale**: 团队熟悉
> **Alternatives considered**: MySQL, MongoDB

**"团队熟悉"不是 rationale**。Alternatives 凑了 2 个名字、没讲为什么 rejected------3 个月后没人能回溯决策。

**正确版:**

```markdown
### Question 1: 主数据库选型

**Decision**: PostgreSQL 16

**Rationale**:
1. 团队 12 个工程师中 10 个有 PG 生产经验
2. ACID 强一致,FR-005(相册嵌套限制)需事务约束
3. EXIF 元数据(FR-001)用 JSONB 存储可减少表数量
4. TimescaleDB 扩展可应对 SC-001 性能要求

**Alternatives considered**:
- MySQL 8: 团队经验相同,但 JSON 支持弱于 PG;事务隔离级别对嵌套相册场景不够
- MongoDB: 无强事务,与 FR-005 冲突;运维成本高(团队无 NoSQL 经验,见 constitution Principle VI)
- SQLite: 单机性能优秀,但 SC-002 要求 5000 张照片 5 秒内检索,多用户并发不可行

**Trade-offs**:
- PG 学习曲线低(团队熟)
- 但 PG 写性能弱于 MongoDB 大批量插入------本场景是读多写少,可接受

每条 Alternative 都要讲为什么 rejected ------这才叫选型。读 plan 阶段最快的"过稿"方法:扫一眼 research.md 的 Alternatives 字段,全是敷衍描述的 plan 风险高,全部详细比较的 plan 风险低。

第五个关卡:data-model.md 别让 AI 替你做领域决策

Phase 1 产出的 data-model.md,由 plan.md 命令源码规定:

  1. Extract entities from feature specdata-model.md:
    • Entity name, fields, relationships
    • Validation rules from requirements
    • State transitions if applicable

AI 翻车点 5:字段过多,把数据库 schema 写进 spec

我看过的 data-model.md

markdown 复制代码
### Photo
- id: BIGSERIAL PRIMARY KEY
- file_path: VARCHAR(500) NOT NULL
- exif_date: TIMESTAMP WITH TIME ZONE
- file_size: BIGINT
- mime_type: VARCHAR(50)
- upload_time: TIMESTAMP DEFAULT NOW()
- album_id: BIGINT REFERENCES albums(id)
- user_id: BIGINT REFERENCES users(id) NOT NULL
- created_at: TIMESTAMP DEFAULT NOW()
- updated_at: TIMESTAMP DEFAULT NOW()
- deleted_at: TIMESTAMP

这是 DDL,不是 data model。 字段类型、大小、DEFAULTREFERENCES------全是 schema 决策。plan 阶段不该决定这些。

正确版:

markdown 复制代码
### Photo
- **id**: 系统生成的唯一标识
- **file_path**: 照片存储路径(本地或对象存储,由 storage 层决定)
- **exif_date**: 拍摄日期(来自 EXIF,FR-001)
- **file_size**: 文件大小(用于 UI 显示)
- **mime_type**: 文件类型(用于前端展示)
- **album_id**: 所属相册(强约束:每张照片属且仅属一个相册,FR-005)
- **uploaded_at**: 上传时间(用于 UI 排序)

**Validation rules**:
- exif_date 缺失时使用 uploaded_at 作为 fallback(FR-001 的兜底)
- file_size > 0
- mime_type 必须在白名单 [image/jpeg, image/png, image/heic]

**State transitions**:
- 上传中 → 已归档(exif_date 解析完成)
- 已归档 → 已重分组(用户拖拽,FR-004)
- 已归档 → 已删除(v2 引入软删,v1 物理删除)

关键差异

  • 不写 SQL 类型(BIGSERIAL / TIMESTAMP
  • 不写 schema 细节(REFERENCES / DEFAULT
  • 业务约束("每张照片属且仅属一个相册"------这条直接来自 FR-005)
  • 验证规则(白名单、兜底策略)
  • 状态机(v1 没有但预留扩展)

读 plan 阶段最快的"过稿"方法 2data-model.md 里出现 SQL DDL(CREATE TABLE、PRIMARY KEY、FOREIGN KEY、DEFAULT)------直接打回

第六个关卡:contracts/ 和 quickstart.md

plan.md 命令源码对这两个的说明:

Define interface contracts (if project has external interfaces) → /contracts/:

  • Identify what interfaces the project exposes to users or other systems

  • Document the contract format appropriate for the project type

  • Examples: public APIs for libraries, command schemas for CLI tools, endpoints for web services, grammars for parsers, UI contracts for applications

  • Skip if project is purely internal (build scripts, one-off tools, etc.)
    Create quickstart validation guidequickstart.md:

  • Document runnable validation scenarios that prove the feature works end-to-end

  • Include prerequisites, setup commands, test/run commands, and expected outcomes

AI 翻车点 6:contracts 写成 API 文档,quickstart 写成 README

markdown 复制代码
# contracts/api.md
## POST /api/photos
Body: multipart/form-data
Response: 200 OK
...

# quickstart.md
## Run the server
npm install
npm start

contracts 不是 API 文档 ------是契约。Web 服务的契约是 OpenAPI/GraphQL schema,CLI 工具是命令 schema(argparse 定义、exit code 列表、stdout 格式),库的契约是 public API 接口签名。

quickstart 不是 README ------是可执行的端到端验证场景plan.md 命令明文要求"runnable validation scenarios"------每个场景要能跑、跑完有明确预期结果。

相册例子的 quickstart:

markdown 复制代码
## 验证场景 1: 单张照片自动归类

**Prerequisites**:
- PostgreSQL 已启动且空库
- 编译产物已生成
- 测试目录有 `fixtures/photo_2024_03_15.jpg`(EXIF 含 2024-03-15)

**Steps**:
```bash
$ pbm-cli upload fixtures/photo_2024_03_15.jpg

Expected output:

复制代码
[INFO] Uploading photo...
[INFO] EXIF date detected: 2024-03-15
[INFO] Created album "2024 年 3 月" (id=1)
[INFO] Photo added to album 1
[OK] Upload complete

Validation:

sql 复制代码
SELECT COUNT(*) FROM albums WHERE title = '2024 年 3 月';  -- 应返回 1
SELECT COUNT(*) FROM photos WHERE album_id = 1;             -- 应返回 1
复制代码
每个场景都包含:**前置条件**、**可执行命令**、**预期输出**、**验证 SQL**------**可以直接在 CI 里跑**。

## Complexity Tracking:违反宪法时的"罪状表"

`plan-template.md` 最后一段:

> **Fill ONLY if Constitution Check has violations that must be justified**
>
> | Violation | Why Needed | Simpler Alternative Rejected Because |
> |-----------|------------|-------------------------------------|
> | [e.g., 4th project] | [current need] | [why 3 projects insufficient] |
> | [e.g., Repository pattern] | [specific problem] | [why direct DB access insufficient] |

**关键三列**:

1. **Violation**: 违反了哪条
2. **Why Needed**: 当前什么需求必须违反
3. **Simpler Alternative Rejected Because**: 为什么简单的方案不行

**AI 翻车点 7:Complexity Tracking 写"我觉得需要"**

我看过的:

> | Violation | Why Needed | Simpler Alternative Rejected Because |
> |-----------|------------|-------------------------------------|
> | 引入 Kafka | 当前需求 | 简单方案不行 |

**等于没写**。`Why Needed` 是当前需求的具体痛点,不是"当前需求"。`Simpler Alternative Rejected Because` 要讲清楚试过哪个简单方案、卡在哪。

**正确版:**

> | Violation | Why Needed | Simpler Alternative Rejected Because |
> |-----------|------------|-------------------------------------|
> | 引入 Redis 缓存(违反 Principle VII Simplicity 倾向) | SC-001 要求 5000 张照片 5 秒内检索,PG 单表扫描 P95 850ms 不达标 | 1) 试过加 PG 索引:P95 降到 220ms,仍不达标<br>2) 试过分区表:维护成本高,小团队不划算<br>3) 试过只查最近 90 天照片:UX 部门拒绝(用户期望翻历史照片) |
> | 拆 4 个微服务(违反 Principle I 单一 library 倾向) | 用户上传链路涉及 EXIF 解析、AI 标签、缩略图、CDN 推送,单进程无法满足 SC-001 的 30s 要求 | 1) 试过单进程异步队列:P95 仍超时<br>2) 试过拆 2 个服务(上传/处理分离):处理服务仍是瓶颈<br>3) 折中方案:保留 2 服务,AI 标签和缩略图作为上传服务的 worker 进程 |

**关键特征**:

- 每个 Violation 都**试过简单方案**
- `Rejected Because` 列出**至少 2 个被否的方案**
- 数字、命令、版本号都给------可复现的拒绝理由

## plan 阶段过稿清单

拿到 plan.md 后,按这个清单走一遍:

Technical Context

  • 没有未解决的 NEEDS CLARIFICATION

  • AI 推断项都标了 INFERRED 并有 research.md 对应

  • 选型与 codebase 现状不冲突

Constitution Check

  • 每条原则问具体问题,不是抽象陈述

  • Evidence 列指向文件/章节

  • 违反项进了 Complexity Tracking

Project Structure

  • 只保留 1 个 Option

  • 展开成真实路径

  • Structure Decision 写为什么选

research.md

  • 每个 Decision 有 Rationale

  • 每个 Decision 有 Alternatives considered(≥2 个)

  • Alternatives 都写为什么 rejected

data-model.md

  • 没有 SQL DDL(CREATE TABLE / PRIMARY KEY / FOREIGN KEY)

  • 字段都是业务术语

  • Validation rules 引用具体 FR

  • State transitions 标明 v1/v2 范围

contracts/

  • 是契约不是文档(OpenAPI/argparse/exit code list)

  • 跳过项有说明("纯内部工具,跳过")

quickstart.md

  • 每个场景有 Prerequisites/Steps/Expected/Validation

  • 命令可直接复制执行

  • 预期输出可断言

Complexity Tracking

  • 每行 Violation 试过 ≥1 个简单方案

  • Rejected Because 含具体数据

    任何一项 ❌ 就打回让 AI 改plan.md 命令是 spec 之后最值得花时间审的------后面 /speckit.tasks 直接消费 plan.md,plan 错了任务清单全错。

    写 plan 时卡住 AI 的 3 个实用技巧

    最后说几个我在 constitution 里加的、专门管 plan 阶段的"硬规则":

    1. INFERRED 标记强制

    markdown 复制代码
    ### VII. Plan Stage: Mark All Inferences
    Technical Context 中任何 AI 推断的技术选型,必须用 [INFERRED] 包裹。
    未标 [INFERRED] 的选型视为用户已确认。
    Phase 0 research.md 中每个 [INFERRED] 必须有对应 Decision/Rationale/Alternatives。

效果:AI 不再"偷偷"填字段,每个推断都暴露在阳光下。

2. Project Structure 单一选项

markdown 复制代码
### VIII. Plan Stage: Single Structure Only
plan.md 的 Project Structure 必须只保留 1 个 Option,
且必须展开为真实路径(不允许 src/、models/ 这种占位)。
保留多个 Option 视为 plan 失败。

效果:AI 不再"三选一"敷衍,必须做选型决策。

3. data-model.md 无 DDL

markdown 复制代码
### IX. Plan Stage: data-model.md is Domain, Not Schema
data-model.md 不得出现 SQL DDL 关键字(CREATE/ALTER/DROP/INDEX/REFERENCES/DEFAULT)。
字段类型、大小、约束由 /speckit.tasks 阶段决定。

效果data-model.md 聚焦业务领域,schema 决策留给 tasks。

小结

关卡 核心防翻车点
Technical Context 不让 AI 偷偷填,强制 INFERRED 标注
Constitution Check 双重门禁、机械检查、Evidence 指向文件
Project Structure 只保留 1 个 Option,展开真实路径
research.md 每个 Decision 都有 Alternatives + rejected 理由
data-model.md 没有 SQL DDL,业务约束代替
contracts/ 是契约不是文档
quickstart.md 可执行的端到端场景
Complexity Tracking 试过简单方案再写 Violation

plan 阶段是 spec-kit 工作流里最需要人把关的一步 ------spec 阶段 AI 是戴手铐的,tasks 阶段 AI 是按 plan 走的。只有 plan 阶段 AI 第一次"选型",最容易跑偏。

下一篇会写 /speckit.tasks 怎么拆------task 颗粒度、依赖关系、并行度,都是这个阶段的活。

相关推荐
城事漫游Molly1 小时前
AI辅助实验设计的标准工作流
人工智能·提示词·ai for science·科研论文·实验设计
tianxingjian20191 小时前
科技创新核心工具,TRIZ理论助力技术难题高效突破
大数据·人工智能·科技
wuhen_n1 小时前
RAG 优化实战:检索精准度提升全方案
前端·langchain·ai编程
极客老王说Agent1 小时前
自动化架构演进:2026年有比RPA更加稳定的技术吗?
人工智能·ai·chatgpt·架构·自动化·rpa
独隅1 小时前
IntelliJ IDEA 在 Windows 上的完整安装与使用指南
java·windows·intellij-idea
Misnearch1 小时前
为什么List<int[]> ans = new ArrayList<>()能成功创建
java·object
静Yu1 小时前
从“生成一篇知识点”到“面对面讲清一道题”:我用魔珐星云改造 AI 教育助手的实践
人工智能
陈天伟教授1 小时前
图解人工智能(60)人工智能应用-AI游戏
人工智能·游戏
钱多多_qdd1 小时前
claude code(十一):【企业级应用实战】案例二:会议中的高效编码
ai·claude