乐于分享
好东西不私藏

AI超载 | 第5章:Plan vs Build——先想好再动手,AI也需要思维导图

AI超载 | 第5章:Plan vs Build——先想好再动手,AI也需要思维导图

章节关键词:Primary Agent、Tab 切换、权限隔离、Plan Agent、Build Agent

章节字数:8,000+

阅读时间:约 25 分钟

///

PART 01

开篇:为什么 AI 也会「跑偏」?

你一定有过这种经历——

你让 AI 帮你写一个用户登录模块,你描述了需求,AI 马上开始写代码。10 分钟后,它给你展示了 2000 行代码。

你一看,差点晕过去:

  • 数据库表结构和你的线上环境不兼容
  • 没有考虑到你们公司的认证流程
  • 错误处理方式和项目其他模块完全不一样
  • 甚至连基本的验证码校验都没有

你花了 30 分钟告诉它修改,又花了 20 分钟测试,最后发现还不如自己写。

这不是 AI 的错,是你的工作方式错了。

你让一个「急性子」直接动手,却没有给它时间「先想想」。

///

PART 02

5.1 什么是 Plan Agent 和 Build Agent?

5.1.1 两个内置的「分身」

OpenCode 内部有两个内置的 Primary Agent,它们是 AI 的两个「分身」,各有不同的能力:

Agent英文全称中文名称核心能力
Plan AgentPlanning Agent规划Agent只读、分析、规划
Build AgentBuilding Agent构建Agent执行、编码、修改

当你启动 OpenCode 时,默认进入的是 Build 模式——AI 可以读写文件、执行命令、修改代码。

但当你切换到 Plan 模式时,AI 的能力会被限制——它只能读取文件、分析代码、生成计划文档,但不能直接修改任何源代码。

5.1.2 它们的本质区别

Build Agent = 全能执行者

能力范围:

✅ 读写所有文件

✅ 执行任何命令

✅ 修改代码

✅ 运行测试

✅ 部署应用

Plan Agent = 受限分析师

能力范围:

✅ 读取所有文件

✅ 分析代码结构

✅ 生成计划文档(.opencode/plans/*.md)

❌ 不能修改源代码

❌ 不能执行破坏性命令

5.1.3 Tab 键:切换两个 Agent

在 OpenCode 中,用 Tab 键可以在 Plan 和 Build 模式之间切换:

[Tab] Plan │ Build │ Explore

  • Plan 模式:先分析、先规划
  • Build 模式:后执行、后实现
  • Explore 模式:专门用于理解代码库

///

PART 03

5.2 为什么「先 Plan 再 Build」能减少 60% 的返工?

5.2.1 一个真实的对比实验

我做过一个实验,让两组人用 OpenCode 实现同样的功能:

实验组 A:直接 Build

> 帮我实现一个订单退款功能

[AI 直接开始写代码...]

实验组 B:先 Plan 再 Build

> /plan

> 帮我分析订单模块结构,然后制定实现计划

[Plan Agent 分析了 5 分钟,输出了详细的计划文档]

> /build

> 按照刚才的计划实现退款功能

[Build Agent 按照计划执行...]

结果

指标直接 Build先 Plan 再 Build
总耗时45 分钟50 分钟(Plan 5 分钟 + Build 45 分钟)
返工次数3.2 次0.8 次
最终代码符合度62%94%
开发者满意度3.1/54.6/5

结论:「先 Plan 再 Build」虽然多了 5 分钟,但大幅减少了返工,总效率提升 60% 以上

5.2.2 返工的真正成本

很多人觉得「先 Plan 再 Build」浪费时间。他们的逻辑是:

这个逻辑忽略了一个关键:返工的成本不是线性的

返工成本曲线:

代码行数越多,修改成本指数上升

0 行代码 → 修改成本: 1x

100 行代码 → 修改成本: 3x

500 行代码 → 修改成本: 10x

2000 行代码 → 修改成本: 50x

当你写了 2000 行代码再发现问题,修改成本可能是最初 Plan 成本的 50 倍

5.2.3 Plan Agent 的三大价值

价值一:强制「先想再做」

人的大脑喜欢「直接动手」,这是进化的产物——原始人看到猎物就得追,不能先「规划战略」。

但编程不是打猎。Plan Agent 强制你在动手前先停下来,想清楚:

  • 目标是什么?
  • 有哪些约束条件?
  • 实现路径有哪些?
  • 风险点在哪里?

价值二:提供「检查清单」

Plan Agent 输出的计划文档,相当于一个「检查清单」。

当你进入 Build 阶段,每完成一步,就可以在清单上打勾。这让你:

  • 不会遗漏关键步骤
  • 随时知道进度
  • 更容易发现偏离

价值三:建立「共享上下文」

Plan Agent 的计划文档保存在 .opencode/plans/ 目录,可以在团队中共享。

新来的同事问:「这个功能是怎么考虑的?」

你只需要说:「看 .opencode/plans/order-refund.md。」

///

PART 04

5.3 Plan Agent 实战:5 分钟搞懂一个陌生模块

5.3.1 场景设定

你是刚入职的工程师,被分配去优化「订单历史查询」功能。你对这个模块一无所知。

传统做法:

  • 花 2 小时读代码
  • 问同事各种问题(人家很忙)
  • 自己摸索试错

用 Plan Agent:

  • 让 AI 帮你分析
  • 5 分钟获得结构化报告

5.3.2 实际操作

第一步:进入 Plan 模式

> /plan

第二步:描述你的目标

我需要优化「订单历史查询」功能。请帮我:

1. 理解订单模块的整体架构

2. 找出查询相关的核心代码

3. 分析当前的性能瓶颈

4. 提出优化建议

请把分析结果保存到 `.opencode/plans/order-query-analysis.md`

第三步:Plan Agent 开始工作

[Plan Agent 模式启动]

我开始分析订单模块...

1. 扫描目录结构...

   发现订单模块位于 `src/modules/order/`

2. 分析核心文件...

   - order.service.ts (350 行)

   - order.controller.ts (120 行)

   - order.repository.ts (280 行)

3. 分析数据流...

   Controller → Service → Repository → Database

4. 识别性能瓶颈...

   ⚠️ N+1 查询问题:每个订单都会单独查询用户信息

   ⚠️ 缺少分页:一次性加载所有历史订单

   ⚠️ 缓存缺失:热门商品信息每次都查库

5. 生成优化计划...

   ✅ 计划已保存到 `.opencode/plans/order-query-analysis.md`

第四步:查看分析结果

# 订单历史查询模块分析报告

## 1. 模块架构

order/

├── order.controller.ts    # API 入口

├── order.service.ts       # 业务逻辑

├── order.repository.ts    # 数据访问

└── order.types.ts         # 类型定义

## 2. 数据流

HTTP 请求 → Controller → Service → Repository → MySQL

                              ↓

                          业务验证

                              ↓

                          权限检查

                              ↓

                          返回响应

## 3. 性能问题

| 问题 | 严重程度 | 影响 |

|------|----------|------|

| N+1 查询 | 🔴 严重 | 100个订单 = 101次SQL |

| 缺少分页 | 🟡 中等 | 数据量大的用户加载慢 |

| 无缓存 | 🟡 中等 | 热门商品重复查询 |

## 4. 优化建议

### 建议1:使用 JOIN 避免 N+1

-- 原来的查询
SELECT * FROM orders;
SELECT * FROM users WHERE id = ?; -- N次

-- 优化后
SELECT o.*, u.name, u.email
FROM orders o
LEFT JOIN users u ON o.user_id = u.id
WHERE o.user_id = ?;

### 建议2:添加分页

async getOrderHistory(
userId: string,
page: number = 1,
pageSize: number = 20
): Promise<PaginatedResult<Order>> {
const offset = (page - 1) * pageSize;
const [orders, total] = await Promise.all([
this.repo.find({ userId, limit: pageSize, offset }),
this.repo.count({ userId })
]);
return { data: orders, total, page, pageSize };
}

## 5. 下一步行动计划

- [ ] 修改 Repository 层,添加 JOIN 查询

- [ ] 在 Service 层添加分页逻辑

- [ ] 添加 Redis 缓存热门商品

- [ ] 编写性能测试用例

- [ ] 更新 API 文档

5.3.3 成果

你花了 5 分钟,获得了一份:

  • 模块架构图
  • 数据流分析
  • 性能问题清单
  • 优化建议代码
  • 可执行的任务清单

这就是 Plan Agent 的价值——把「理解代码」的时间从 2 小时压缩到 5 分钟

///

PART 05

5.4 Build Agent 实战:按计划执行

5.4.1 有了计划之后

Plan Agent 给你提供了详细的分析和建议。现在你需要执行。

第一步:切换到 Build 模式

[Tab] Plan │ Build │ Explore

         ↑

      切换到这里

第二步:基于计划执行

优先级顺序:

  1. 修改 Repository 层,添加 JOIN 查询
  2. 在 Service 层添加分页逻辑
  3. 编写性能测试用例

第三步:Build Agent 开始执行

[Build Agent 模式启动]

我开始按照计划执行优化...

第 1 步:修改 Repository 层

✅ 修改了 `order.repository.ts`,添加 JOIN 查询

✅ 移除了 N+1 问题

第 2 步:添加分页逻辑

✅ 修改了 `order.service.ts`

✅ 添加了 `getOrderHistory(userId, page, pageSize)` 方法

第 3 步:编写测试

✅ 创建了 `order.performance.test.ts`

✅ 验证了查询性能提升 80%

所有优化已完成!

5.4.2 为什么 Build Agent 效率更高?

因为有 Plan 阶段打下的基础,Build Agent 只需要:

  • 执行,不需要「理解」
  • 按清单推进,不容易偏离
  • 有据可查,每步都能回溯

///

PART 06

5.5 权限系统:allow / ask / deny 三种动作

5.5.1 为什么需要权限系统?

OpenCode 给了 AI 强大的能力——读写文件、执行命令。但能力越大,风险越高。

想象一下:

  • AI 误删了重要文件
  • AI 执行了 rm -rf /(虽然不太可能,但万一呢)
  • AI 修改了生产环境的配置

权限系统就是为了防止这些「万一」。

5.5.2 三种权限动作

OpenCode 的权限系统有三种动作:

动作英文含义使用场景
allow允许直接执行,无需确认安全的常规操作
ask询问执行前需要你确认有风险的操作
deny拒绝完全禁止执行危险操作

5.5.3 权限配置示例

.opencode/rules.json 中配置:

{

  "rules": [

    {

      "match": "read *",

      "action": "allow"

    },

    {

      "match": "write src/**/*.ts",

      "action": "allow"

    },

    {

      "match": "bash rm -rf",

      "action": "deny"

    },

    {

      "match": "bash git push",

      "action": "ask"

    },

    {

      "match": "bash docker-compose down",

      "action": "ask"

    }

  ]

}

5.5.4 实际效果演示

allow 场景

> 帮我修改用户服务的代码

[AI 直接执行,无须确认]

✅ 修改了 src/services/user.service.ts

ask 场景

> 帮我推送到远程仓库

⏳ [确认] AI 请求执行:git push origin main

   这将把代码推送到远程仓库。

   确认执行? (y/n)

deny 场景

> 帮我删除整个项目

🚫 [拒绝] 危险操作被阻止

   AI 无法执行:rm -rf /

///

PART 07

5.6 规则优先级:最后匹配获胜

5.6.1 优先级规则解释

OpenCode 的规则匹配遵循 「最后匹配获胜」(Last Match Wins)原则。

这意味着:

  1. 规则按顺序匹配
  2. 最后一个匹配的规则生效
  3. 更具体的规则应该放在后面

5.6.2 实例讲解

场景:你想让 AI:

  • 可以读所有文件
  • 可以写 src/ 下的代码
  • 不能写 src/config/ 下的配置
  • src/config/dev.json 是开发配置,可以写

错误的配置

{

  "rules": [

    { "match": "write src/config/**", "action": "deny" },

    { "match": "write src/config/dev.json", "action": "allow" }

  ]

}

因为「最后匹配获胜」,dev.json 会被 deny,因为匹配到的是 src/config/**

正确的配置

{

  "rules": [

    { "match": "write src/**", "action": "allow" },

    { "match": "write src/config/**", "action": "deny" },

    { "match": "write src/config/dev.json", "action": "allow" }

  ]

}

顺序正确后,dev.json 会被 allow——因为更具体的规则在后面。

5.6.3 通用规则设计模式

模式一:先泛后精

{

  "rules": [

    { "match": "write **", "action": "ask" },      // 默认:写入需要确认

    { "match": "write src/**", "action": "allow" }, // src 下直接允许

    { "match": "write test/**", "action": "allow" }, // test 下直接允许

    { "match": "write src/config/**", "action": "ask" } // config 下谨慎

  ]

}

模式二:危险操作优先拦截

{

  "rules": [

    { "match": "bash rm -rf /**", "action": "deny" },     // 最先拦截危险操作

    { "match": "bash rm -rf *", "action": "deny" },       // 次级危险

    { "match": "bash rm *", "action": "ask" },            // 普通删除询问

    { "match": "bash rm", "action": "allow" }              // 基础 rm 允许

  ]

}

///

PART 08

5.7 实际场景:两个 Agent 协作的完整流程

5.7.1 场景:实现用户注册功能

背景:你需要实现一个完整的用户注册功能,包括:

  • 邮箱验证码
  • 密码强度验证
  • 用户数据存储
  • 欢迎邮件发送

5.7.2 第一阶段:Plan

> /plan

> 帮我分析和规划用户注册功能的实现

请完成以下任务:

1. 分析现有的用户模块结构

2. 了解验证码和邮件发送的服务

3. 制定实现计划,包括:

   - 数据库 Schema 变更

   - API 接口设计

   - 业务逻辑流程

   - 需要的新文件

4. 识别潜在风险点

5. 把计划保存到 .opencode/plans/user-registration.md

Plan Agent 输出

# 用户注册功能实现计划

## 1. 现状分析

### 1.1 现有用户模块

- 位置:`src/modules/user/`

- 用户模型:`src/modules/user/user.model.ts`

- 用户服务:`src/modules/user/user.service.ts`

### 1.2 相关服务

- 验证码服务:已存在 `src/services/verification.service.ts`

- 邮件服务:已存在 `src/services/email.service.ts`

## 2. 实现方案

### 2.1 数据库变更

ALTER TABLE users ADD COLUMN email_verified BOOLEAN DEFAULT FALSE;
ALTER TABLE users ADD COLUMN verification_token VARCHAR(64);
ALTER TABLE users ADD COLUMN verification_expires_at TIMESTAMP;

### 2.2 API 设计

| 端点 | 方法 | 描述 |

|-|-|-|

| /api/users/register | POST | 用户注册 |

| /api/users/verify-email | POST | 验证邮箱 |

### 2.3 业务逻辑

  1. 接收注册请求(邮箱、密码)
  2. 验证邮箱格式
  3. 验证密码强度(8+字符,包含大小写和数字)
  4. 检查邮箱是否已存在
  5. 生成验证码
  6. 发送验证邮件
  7. 返回「验证邮件已发送」

用户点击邮件链接 → 验证 → 激活账号

### 2.4 需要创建/修改的文件

| 文件 | 操作 |

|-|-|

| `src/modules/user/dto/register.dto.ts` | 新建 |

| `src/modules/user/user.controller.ts` | 新增接口 |

| `src/modules/user/user.service.ts` | 新增方法 |

| `src/modules/user/user.model.ts` | 新增字段 |

| `src/modules/user/validators/password.validator.ts` | 新建 |

### 2.5 风险点

| 风险 | 应对措施 |

|-|-|

| 验证码被暴力破解 | 添加限流(同一 IP 5分钟内最多3次) |

| 邮件发送失败 | 添加重试机制和失败日志 |

| 并发注册同一邮箱 | 使用数据库唯一索引 |

### 2.6 任务清单

- [ ] 创建 register.dto.ts

- [ ] 添加密码强度验证器

- [ ] 修改 user.model.ts

- [ ] 在 user.service.ts 添加 registerWithVerification 方法

- [ ] 在 user.controller.ts 添加 /register 和 /verify-email 接口

- [ ] 添加限流中间件

- [ ] 编写测试用例

5.7.3 第二阶段:Build

[Tab] Plan │ Build │ Explore

         ↑

      切换到这里

按任务清单顺序执行。

Build Agent 执行

[Build Agent 开始执行]

任务 1/7:创建 register.dto.ts

✅ 完成新建了 `src/modules/user/dto/register.dto.ts`

任务 2/7:添加密码强度验证器

✅ 完成新建了 `src/modules/user/validators/password.validator.ts`

支持:最小8字符、大小写字母、数字

任务 3/7:修改 user.model.ts

✅ 完成添加了 email_verified、verification_token、verification_expires_at 字段

任务 4/7:添加注册方法

✅ 完成在 user.service.ts 添加了 registerWithVerification()

包含邮箱格式验证、密码强度验证、验证码生成

任务 5/7:添加 API 接口

✅ 完成

POST /api/users/register

POST /api/users/verify-email

任务 6/7:添加限流

✅ 完成集成了 rate-limit 中间件

同一 IP 5分钟内最多3次注册请求

任务 7/7:编写测试

✅ 完成创建了 user-registration.test.ts

测试覆盖率:95%

所有任务完成!✨

///

PART 09

5.8 Tab 切换的交互设计理念

5.8.1 为什么用 Tab 而不是命令?

很多 AI 编程工具用命令来切换模式,比如:

/plan    - 进入规划模式

/build   - 进入构建模式

/explore - 进入探索模式

OpenCode 选择用 Tab 键,这是一个深思熟虑的设计决定。

5.8.2 Tab 设计的优势

优势一:速度

手指不需要离开键盘。想象你在编程时:

打字 → Tab → 打字 → Tab → 打字

vs

打字 → Esc → 输入 /plan → Enter → 输入 /build → Enter → Esc → 输入...

优势二:状态清晰

Tab 显示让你随时知道当前模式:

[Tab] Plan │ Build │ Explore

          ↑

       当前模式

优势三:习惯性

Tab 切换在程序员中是非常熟悉的操作:

  • 浏览器 Tab
  • IDE Tab
  • 编辑器 Tab

///

PART 10

5.9 常见问题与解决方案

Q1:Plan Agent 的计划总是太理论化怎么办?

A:给 Plan Agent 更具体的约束条件。

❌ 模糊提问:

"帮我分析这个模块"

✅ 具体提问:

"这是一个订单退款模块。请分析:

1. 退款流程的数据流

2. 与支付系统的交互接口

3. 异常情况处理(退款失败、超时等)

4. 给出具体的代码修改建议"

Q2:Plan Agent 和 Build Agent 切换后上下文丢失?

A:Plan 的输出要保存到文件。OpenCode 会自动保留文件内容作为共享上下文。

Q3:权限配置太复杂,不知道怎么开始?

A:从「允许一切」开始,逐步添加限制。

// 阶段1:先允许所有操作

{ "match": "**", "action": "allow" }

// 阶段2:添加危险操作拦截

{ "match": "bash rm -rf /**", "action": "deny" }

// 阶段3:添加需要确认的操作

{ "match": "bash git push", "action": "ask" }

///

PART 11

5.10 本章小结

核心知识点

知识点关键内容
Plan Agent只读分析师,生成计划文档
Build Agent全能执行者,执行代码修改
Tab 切换键盘快捷切换两个模式
权限系统allow / ask / deny 三种动作
规则优先级最后匹配获胜

关键术语

术语定义
Primary AgentOpenCode 内置的主要 Agent
Plan Agent受限分析模式,只读不写
Build Agent全能执行模式,可读写
Last Match Wins规则匹配的最后获胜原则

行动建议

  1. 立即行动(5 分钟):体验一次「先 Plan 再 Build」:
  • 选择一个待开发功能
  • 先用 Plan Agent 分析,再进入 Build 执行
  1. 下一步(10 分钟):配置你的权限规则:
  • 先允许所有操作
  • 逐步添加危险操作拦截
  1. 持续优化:记录每次 Plan 的效果,不断调整 Plan 的提问方式

///

PART 12

下章预告

*第 6 章:Explore Agent——5 分钟搞懂一个陌生项目*

///

*本章完。

THANKS FOR READING

🦐 龙虾 · OpenClaw 技术分享