Claude Code Plan Mode:多阶段规划系统深度解析

4 阅读26分钟

摘要

Claude Code 的 Plan Mode(计划模式)是一个 多阶段规划系统,通过"先规划,再实施"的工作流,在实际编码前确保实现方案与用户意图对齐。本文深入剖析 Plan Mode 的核心机制:Agent Loop 架构、提示词注入策略、并行执行优化,以及如何通过软引导和硬约束保障流程执行。

核心价值:

  • 前置验证:在写代码前确认实现方向,避免方向性错误
  • 并行探索:通过多个 Explore Agent 并行搜索,3× 加速理解复杂代码库
  • 多视角设计:通过多个 Plan Agent 从不同角度设计方案,避免方案局限
  • 渐进式指导:通过 Attachment 机制渐进式注入提示词,节省 Context
  • 用户把关:通过明确的批准机制确保方案符合预期

适用场景:

  • 新功能实现(架构选型不明确)
  • 多文件重构(影响范围大)
  • 需求不明确(需要探索和澄清)
  • 多种实现方案(需要权衡取舍)

1. 为什么需要 Plan Mode

1.1 问题场景

在 AI 辅助编程中,有一类任务特别容易出错:

场景 1:架构选型不明确

User: "给 API 加上缓存功能"

Agent: (直接开始写代码)
       → 选择了 Redis 方案
       → 添加了依赖和配置
       → 写了 200 行代码

User:  "等等,我们不想引入 Redis,用内存缓存就行"
       → 全部推倒重来 ❌

场景 2:需求理解偏差

User: "优化登录流程的性能"

Agent: (误以为是前端性能问题)
       → 优化了前端渲染
       → 添加了懒加载

User:  "我是说后端数据库查询太慢"
       → 方向完全错误 ❌

场景 3:影响范围预估不足

User: "修改用户认证机制"

Agent: (只看了 auth.ts)
       → 修改了认证逻辑
       → 部署后发现 10 个其他模块也依赖旧逻辑
       → 全部挂掉 ❌

1.2 Plan Mode 的解决方案

Plan Mode 引入了 "先规划,再实施" 的工作流:

传统流程:
  User 提需求  Agent 直接写代码  发现问题  返工

Plan Mode 流程:
  User 提需求  Agent 进入计划模式
              
              Phase 1: 探索代码库(并行 Explore Agents)
              
              Phase 2: 设计方案(并行 Plan Agents)
              
              Phase 3: 审查和提问(对齐用户意图)
              
              Phase 4:  plan 文件(详细步骤)
              
              Phase 5: 请求用户批准(ExitPlanMode)
              
  User 审查计划  批准/修改  Agent 按计划实施

关键优势:

  1. 前置探索:在决策前充分理解代码库
  2. 方案对比:可并行设计多个方案
  3. 全局视角:一次性给出完整实现路径
  4. 批准机制:用户可在写代码前把关

2. Agent Loop 架构:单一循环 + 多层嵌套

2.1 核心问题:五个阶段如何执行?

Plan Mode 的 5 个阶段是在 一个 Main Agent Loop 中顺序执行,而非每个阶段各自独立的 loop。但 Phase 1 和 Phase 2 启动的子 Agents(Explore 和 Plan)各自有独立的 loops。

2.2 执行架构图

┌─────────────────────────────────────────────────────────────┐
  Main Agent Loop (单个 loop,多个 turns)                     
  每个 turn = 1  API round-trip                             
├─────────────────────────────────────────────────────────────┤
                                                              
  Turn 1: 接收 Plan Mode 完整指导 (Full Attachment)          
                                                             
  Turn 2: Phase 1 - 并行启动 3  Explore Agents             
         ┌────────────────────────────────────┐              
           Explore Agent 1 (独立 Loop)                     
           - Turn 1: Grep "auth"                           
           - Turn 2: Read auth.ts                          
           - Turn 3: 返回结果                               
         ├────────────────────────────────────┤              
           Explore Agent 2 (独立 Loop)                     
           - Turn 1: Read package.json                     
           - Turn 2: 返回结果                               
         ├────────────────────────────────────┤              
           Explore Agent 3 (独立 Loop)                     
           - Turn 1: Grep "User model"                     
           - Turn 2: Read models/User.ts                   
           - Turn 3: 返回结果                               
         └────────────────────────────────────┘              
          (等待所有子 Agent 完成)                            
                                                              
  Turn 3: Phase 2 - 并行启动 3  Plan Agents                
         ┌────────────────────────────────────┐              
           Plan Agent 1 (独立 Loop)                        
           - Turn 1: 分析探索结果                           
           - Turn 2: 设计简洁方案                           
           - Turn 3: 返回方案                               
         ├────────────────────────────────────┤              
           Plan Agent 2 (独立 Loop)                        
           - Turn 1: 设计安全方案                           
           - Turn 2: 返回方案                               
         ├────────────────────────────────────┤              
           Plan Agent 3 (独立 Loop)                        
           - Turn 1: 设计性能方案                           
           - Turn 2: 返回方案                               
         └────────────────────────────────────┘              
          (等待所有子 Agent 完成)                            
                                                              
  Turn 4: Phase 3 - 审查方案,询问用户                        
          (调用 AskUserQuestion)                            
                                                              
  Turn 5: 收到用户答复                                        
                                                             
                                                              
  Turn 6: Phase 4 -  plan 文件 (clever-pancake.md)        
          (调用 Write 工具)                                  
         接收 Sparse Attachment (每 5 turns 提醒一次)         
                                                              
  Turn 7: Phase 5 - 调用 ExitPlanMode                        
          (退出循环)                                         
                                                              
└─────────────────────────────────────────────────────────────┘
         
   Plan Mode 结束

2.3 代码实现:query.ts 的 Loop 机制

// src/query.ts:241-279
async function* queryLoop(params: QueryParams): AsyncGenerator<...> {
  const { maxTurns, ... } = params

  // 初始化状态
  let state: State = {
    messages: params.messages,
    toolUseContext: params.toolUseContext,
    turnCount: 1,  // 从 1 开始
    ...
  }

  // 主循环:while(true)
  while (true) {
    // 1. 调用 API
    const response = await callClaudeAPI(state.messages)

    // 2. 处理响应(工具调用、文本输出等)
    const { assistantMessages, toolResults } = await processResponse(response)

    // 3. 检查是否达到 maxTurns
    const nextTurnCount = state.turnCount + 1
    if (maxTurns && nextTurnCount > maxTurns) {
      yield createAttachmentMessage({ type: 'max_turns_reached', maxTurns })
      return { reason: 'max_turns', turnCount: nextTurnCount }
    }

    // 4. 更新状态,进入下一轮
    state = {
      messages: [...state.messages, ...assistantMessages, ...toolResults],
      turnCount: nextTurnCount,
      ...
    }
  }
}

2.4 关键特性

2.4.1 Turn 的定义

Turn = 1 次 API Round-trip

  • 发送消息给 Claude API
  • 接收响应(可能包含工具调用)
  • 执行工具(如果有)
  • 将结果追加到消息历史

2.4.2 子 Agent 的独立性

// Phase 1: 启动 Explore Agent
Task({
  description: "Search auth code",
  prompt: "搜索现有认证代码",
  subagent_type: "Explore"
})

// 这会启动一个新的 query() 调用:
// - 有独立的 messages 数组
// - 有独立的 turnCount(从 1 开始)
// - 可以有自己的 maxTurns 限制
// - 完成后返回结果给 Main Agent

子 Agent 执行期间,Main Agent 的 Loop 在等待:

// Main Agent 的 Turn 2
const exploreResults = await Task(...) // 阻塞,等待子 Agent 完成
// 子 Agent 完成后,Main Agent 继续下一个 turn

2.4.3 并行执行的实现

同一个 turn 中调用多个工具:

// messages.ts:3240 - Phase 1 指导
`Launch up to 3 Explore agents IN PARALLEL (single message, multiple tool calls)`

// 实际执行:Main Agent 在一个 turn 中返回
[
  { type: 'tool_use', name: 'Task', input: { subagent_type: 'Explore', ... } },
  { type: 'tool_use', name: 'Task', input: { subagent_type: 'Explore', ... } },
  { type: 'tool_use', name: 'Task', input: { subagent_type: 'Explore', ... } }
]

// 系统并行执行这 3 个 Task,等待全部完成后再进入 Main Agent 的下一个 turn

3. 提示词注入策略:渐进式披露 + 定期提醒

3.1 核心问题:如何保障 Agent 按流程执行?

Plan Mode 的 5 个阶段指导不是一次性注入的,而是通过 Attachment 机制 渐进式披露。

3.2 Attachment 机制

3.2.1 什么是 Attachment?

Attachment 是 Claude Code 在特定时机自动注入到消息历史中的 系统提示词

// src/utils/attachments.ts:881-882
// 在每个 turn 开始前,检查是否需要注入 plan_mode attachment
maybe('plan_mode', () => getPlanModeAttachments(messages, toolUseContext))

3.2.2 注入时机:每 5 个 Turn 一次

// src/utils/attachments.ts:1186-1208
async function getPlanModeAttachments(messages, toolUseContext) {
  // 检查是否在 Plan Mode
  if (permissionContext.mode !== 'plan') return []

  // 统计距离上次注入的 turn 数
  const { turnCount, foundPlanModeAttachment } =
    getPlanModeAttachmentTurnCount(messages)

  // 限流:每 5 个 turn 注入一次
  if (foundPlanModeAttachment && turnCount < 5) {
    return []  // ⏭️ 跳过注入
  }

  // 确定注入类型:Full or Sparse
  const attachmentCount = countPlanModeAttachmentsSinceLastExit(messages) + 1
  const reminderType = attachmentCount % 5 === 1 ? 'full' : 'sparse'

  return [{ type: 'plan_mode', reminderType, ... }]
}

配置参数:

// src/utils/attachments.ts:259-262
export const PLAN_MODE_ATTACHMENT_CONFIG = {
  TURNS_BETWEEN_ATTACHMENTS: 5,        // 每 5 个 turn 注入一次
  FULL_REMINDER_EVERY_N_ATTACHMENTS: 5  // 每 5 次注入 1 次完整版
}

3.3 两种提示词版本

3.3.1 Full 版(完整指导,~2000 tokens)

何时注入:

  • 首次进入 Plan Mode(Turn 1)
  • 第 26, 51, 76... 次注入时(每 25 次重置)

内容:

// src/utils/messages.ts:3207-3292
function getPlanModeV2Instructions(attachment) {
  return `Plan mode is active. The user indicated that they do not want you to execute yet.

## Plan File Info:
${attachment.planFilePath}
You should build your plan incrementally by writing to or editing this file.

## Plan Workflow

### Phase 1: Initial Understanding
Goal: Gain comprehensive understanding of the user's request.
Critical: Only use the Explore subagent type.

1. Focus on understanding the user's request
2. **Launch up to 3 Explore agents IN PARALLEL**
   - Use 1 agent when the task is isolated
   - Use multiple agents when scope is uncertain
3. Provide each agent with specific search focus

### Phase 2: Design
Goal: Design an implementation approach.

Launch Plan agent(s) to design the implementation based on Phase 1 results.
You can launch up to ${agentCount} agent(s) in parallel.

### Phase 3: Review
Goal: Review the plan(s) from Phase 2.

1. Read the critical files identified by agents
2. Ensure that the plans align with the user's request
3. Use AskUserQuestion to clarify remaining questions

### Phase 4: Final Plan
Goal: Write your final plan to the plan file.

- Begin with a Context section
- Include only your recommended approach
- Include the paths of critical files
- Reference existing functions with their file paths
- Include a verification section

### Phase 5: Call ExitPlanModeV2Tool
At the very end of your turn, call ExitPlanModeV2Tool.

Your turn should only end with either:
- Using AskUserQuestion tool (for clarifications)
- Calling ExitPlanModeV2Tool (for plan approval)
`
}

3.3.2 Sparse 版(精简提示,~300 tokens)

何时注入:

  • 第 6, 11, 16, 21... 次注入时

内容:

// src/utils/messages.ts:3385-3397
function getPlanModeV2SparseInstructions(attachment) {
  return `Plan mode still active (see full instructions earlier in conversation).

Read-only except plan file (${attachment.planFilePath}).
Follow 5-phase workflow.
End turns with AskUserQuestion (for clarifications) or ExitPlanModeV2Tool (for plan approval).
Never ask about plan approval via text or AskUserQuestion.`
}

3.4 注入时间线可视化

Turn 1:  [FULL]  📖 完整 5 阶段指导 (2000 tokens)
         "Phase 1: Launch up to 3 Explore agents..."
Turn 2:  -
Turn 3:  -
Turn 4:  -
Turn 5:  -
Turn 6:  [SPARSE] 💡 精简提示 (300 tokens)
         "Follow 5-phase workflow. End with ExitPlanMode."
Turn 7:  -
Turn 8:  -
Turn 9:  -
Turn 10: -
Turn 11: [SPARSE] 💡 精简提示
Turn 12: -
...
Turn 16: [SPARSE] 💡 精简提示
...
Turn 21: [SPARSE] 💡 精简提示
...
Turn 26: [FULL]  📖 完整指导再次注入 (每 25 次重置)

3.5 转换为 UserMessage

Attachment 通过 normalizeAttachmentForAPI() 转换为 UserMessage:

// src/utils/messages.ts:3453-3828
export function normalizeAttachmentForAPI(attachment: Attachment): UserMessage[] {
  switch (attachment.type) {
    case 'plan_mode': {
      return getPlanModeInstructions(attachment)
      // 返回 UserMessage[],追加到消息历史
    }
  }
}

// 这些 UserMessage 会被追加到 messages 数组,发送给 API:
messages = [
  ...existingMessages,
  ...normalizeAttachmentForAPI({ type: 'plan_mode', reminderType: 'full', ... })
]

3.6 为什么渐进式注入?

优势:

  1. 节省 Context

    • 不需要每轮都发送完整指导
    • 节省 ~1700 tokens/turn(Full 2000 → Sparse 300)
  2. 保持记忆

    • Agent 在前面的 turn 已经看到了完整流程
    • 利用 Claude 的长上下文能力
  3. 持续提醒

    • 每 5 轮的 Sparse 提示防止 Agent "遗忘"流程
    • 在长对话中保持对齐
  4. 灵活应对

    • Agent 可以根据实际情况调整节奏
    • 不强制每个 turn 一个 phase

4. 流程保障机制:软引导 + 硬约束

4.1 软引导:详细的阶段指导

提示词对每个 Phase 都给出了明确指导:

// Phase 1 指导(messages.ts:3235-3244)
`### Phase 1: Initial Understanding
Goal: Gain comprehensive understanding
Critical: Only use the Explore subagent type

1. Focus on understanding the user's request
2. **Launch up to 3 Explore agents IN PARALLEL**
   - Use 1 agent when task is isolated
   - Use multiple agents when scope is uncertain
3. Provide each agent with specific search focus`

特点:

  • ✅ 明确目标(Goal)
  • ✅ 操作指导(具体要做什么)
  • ✅ 工具要求(使用哪些工具)
  • ❌ 但不强制(Agent 可以灵活调整)

4.2 硬约束:工具权限限制

4.2.1 Main Agent 权限

在 Plan Mode 中,Main Agent 只能编辑 plan 文件

// 文件修改检查
function canEditFile(filePath: string): boolean {
  const planFilePath = getPlanFilePath()
  return filePath === planFilePath  // ❌ 其他文件无法编辑
}

// 违规示例:
Write({
  file_path: "/Users/user/project/src/auth.ts",  // ❌
  content: "..."
})
// → Error: Plan Mode 中不允许修改非 plan 文件

4.2.2 Explore Agent 权限(完全只读)

// src/tools/AgentTool/built-in/exploreAgent.ts
const EXPLORE_AGENT = {
  disallowedTools: [
    'Agent',        // 不能嵌套调用
    'ExitPlanMode', // 不能退出 Plan Mode
    'FileEdit',     // ❌ 不能编辑任何文件
    'FileWrite',    // ❌ 不能写入任何文件
    'NotebookEdit'  // ❌ 不能修改 Notebook
  ],

  model: 'haiku',  // 使用快速模型

  systemPrompt: `
    === 关键:只读模式 ===
    你被严格禁止:
    - 创建新文件(无 Write, touch 等)
    - 修改现有文件(无 Edit 操作)
    - 删除文件(无 rm)
    - 移动/复制文件(无 mv/cp)
    - 使用重定向操作符(>, >>, |)或 heredoc 写文件

    你的职责仅限于:搜索和分析现有代码。
  `
}

4.2.3 Plan Agent 权限(完全只读)

// src/tools/AgentTool/built-in/planAgent.ts
const PLAN_AGENT = {
  disallowedTools: [
    'Agent',        // 不能嵌套调用
    'ExitPlanMode', // 不能退出 Plan Mode
    'FileEdit',     // ❌ 不能编辑文件
    'FileWrite',    // ❌ 不能写入文件
    'NotebookEdit'  // ❌ 不能修改 Notebook
  ],

  model: 'inherit',  // 继承 Main Agent 的模型

  systemPrompt: `
    === 关键:只读模式 - 不可修改文件 ===
    这是只读规划任务。你被严格禁止修改任何文件。

    你的职责专属于:探索代码库并设计实现计划。
  `
}

4.3 硬约束:强制 Exit 机制

提示词强制要求 Agent 的 turn 只能以两种方式结束:

// messages.ts:3287-3290
`Your turn should only end with either:
- Using AskUserQuestion tool (for clarifications)
- Calling ExitPlanModeV2Tool (for plan approval)

Do NOT ask about plan approval via text or AskUserQuestion.`

验证机制:

// tools/ExitPlanModeTool
const planExists = getPlan(toolUseContext.agentId) !== null
if (!planExists) {
  return Error("Cannot exit plan mode without a plan file")
}

4.4 软引导 vs 硬约束对比

维度软引导硬约束
实现方式提示词指导工具权限限制 + Exit 验证
可绕过性✅ Agent 可以灵活调整❌ 无法绕过
作用引导 Agent 按流程执行防止 Agent 修改代码或跳过步骤
示例"Launch up to 3 Explore agents"canEditFile() === false

保障效果:

软引导(提示词):建议 Agent 执行 Phase 1-5
    ↓
Agent 按建议执行(大多数情况)
    ↓
硬约束(工具限制):防止 Agent 跳过流程或修改代码
    ↓
即使 Agent "想"跳过某些步骤,也无法:
- ❌ 无法直接修改代码(只能编辑 plan 文件)
- ❌ 无法不调用 ExitPlanMode 就结束(提示词强制要求)
- ❌ 无法在没有 plan 文件的情况下退出(ExitPlanMode 验证)

5. 五阶段工作流详解

5.1 阶段概览

Phase 1: Initial Understanding(初步理解)
         - 启动最多 3  Explore Agent 并行搜索
         - 理解用户需求和代码库现状
         
Phase 2: Design(设计方案)
         - 启动 1-3  Plan Agent 并行设计
         - 从不同视角设计实现方案
         
Phase 3: Review(审查完善)
         - 审查 Plan Agent 的方案
         - 使用 AskUserQuestion 澄清疑问
         
Phase 4: Final Plan(最终计划)
         - 将最终方案写入 plan 文件
         - 包含上下文、步骤、验证等
         
Phase 5: Exit(请求批准)
         - 调用 ExitPlanMode 工具
         - 等待用户批准/编辑/拒绝

5.2 Phase 1: Initial Understanding

目标

充分理解用户需求和代码库现状

方法

通过 Explore Agent 并行探索代码

实例:认证功能实现

Main Agent (Turn 2):
  用户要求:添加 JWT 认证

  调用 3  Task 工具(并行):

  ┌─────────────────────────────────────────┐
   Task 1: Explore Agent                   
   Prompt: "搜索现有的认证相关代码"         
                                           
   Agent Loop:                             
     Turn 1: Grep("authentication|auth")   
     Turn 2: Read(src/middleware/auth.ts)  
     Turn 3: Return "当前使用 session"     
  └─────────────────────────────────────────┘

  ┌─────────────────────────────────────────┐
   Task 2: Explore Agent                   
   Prompt: "查找 JWT 相关依赖和配置"        
                                           
   Agent Loop:                             
     Turn 1: Grep("jsonwebtoken|jwt")      
     Turn 2: Read(package.json)            
     Turn 3: Return "未安装 JWT 库"        
  └─────────────────────────────────────────┘

  ┌─────────────────────────────────────────┐
   Task 3: Explore Agent                   
   Prompt: "探索用户模型和数据库交互"       
                                           
   Agent Loop:                             
     Turn 1: Grep("User model")            
     Turn 2: Read(models/User.ts)          
     Turn 3: Return "用户模型有 password"  
  └─────────────────────────────────────────┘

   (等待所有 Agent 完成)

Main Agent (Turn 3):
  接收到 3  Agent 的结果
   进入 Phase 2

Explore Agent 配置

const EXPLORE_AGENT = {
  disallowedTools: ['Agent', 'ExitPlanMode', 'FileEdit', 'FileWrite', 'NotebookEdit'],
  model: 'haiku',  // 快速 + 低成本
  systemPrompt: `
    你是文件搜索专家。

    === 只读模式 ===
    严格禁止文件修改。

    你的职责:
    - 快速查找文件(Glob)
    - 搜索代码(Grep)
    - 读取和分析文件内容(Read)

    尽可能并行调用多个工具。
  `
}

5.3 Phase 2: Design

目标

基于探索结果设计实现方案

方法

通过 Plan Agent 并行设计(1-3 个,取决于订阅等级)

实例:多视角设计

Main Agent (Turn 3):
  基于 Phase 1 的探索结果

  调用 3  Task 工具(并行):

  ┌─────────────────────────────────────────┐
   Task 1: Plan Agent                      
   视角:简洁性                             
                                           
   Prompt: "基于探索结果,设计 JWT 认证。   │
  │         视角:最小化代码改动,复用现有   │
  │         结构。                           │
  │                                         │
  │         背景:                           │
  │         - 现有:session 认证             │
  │         - 用户模型:models/User.ts       │
  │         - 需添加:jsonwebtoken 依赖"    
                                           
   Agent Loop:                             
     Turn 1: Read(src/middleware/auth.ts)  
     Turn 2: 设计方案                       
     Turn 3: Return "方案 1: 最小改动"     
  └─────────────────────────────────────────┘

  ┌─────────────────────────────────────────┐
   Task 2: Plan Agent                      
   视角:安全性                             
                                           
   Prompt: "基于探索结果,设计 JWT 认证。   │
  │         视角:最大化安全性,考虑刷新令   │
  │         牌、令牌撤销。"                   
                                           
   Agent Loop:                             
     Turn 1: 设计双令牌机制                 
     Turn 2: Return "方案 2: 双令牌+黑名单" 
  └─────────────────────────────────────────┘

  ┌─────────────────────────────────────────┐
   Task 3: Plan Agent                      
   视角:性能                               
                                           
   Prompt: "基于探索结果,设计 JWT 认证。   │
  │         视角:优化性能,减少数据库查询。"
                                           
   Agent Loop:                             
     Turn 1: 设计缓存方案                   
     Turn 2: Return "方案 3: JWT+缓存"     
  └─────────────────────────────────────────┘

   (等待所有 Agent 完成)

Main Agent (Turn 4):
  接收到 3 个方案
   进入 Phase 3

Plan Agent 配置

const PLAN_AGENT = {
  disallowedTools: ['Agent', 'ExitPlanMode', 'FileEdit', 'FileWrite', 'NotebookEdit'],
  model: 'inherit',  // 继承 Main Agent 的模型(Opus/Sonnet)
  omitClaudeMd: true,  // 节省 token
  systemPrompt: `
    你是软件架构师和规划专家。

    === 只读模式 ===
    严格禁止文件修改。

    你的职责:
    1. 理解需求和指定的视角
    2. 彻底探索代码库
    3. 设计实现方案(应用指定视角)
    4. 提供详细的实施步骤

    必需输出:
    ### 关键文件清单
    列出 3-5 个最关键的文件
  `
}

并发控制

// src/utils/planModeV2.ts:5-29
function getPlanModeV2AgentCount(): number {
  const subscriptionType = getSubscriptionType()

  if (subscriptionType === 'max' ||
      subscriptionType === 'enterprise' ||
      subscriptionType === 'team') {
    return 3  // 高级订阅:3 个并行 Plan Agent
  }

  return 1  // 免费/基础:1 个 Plan Agent
}

function getPlanModeV2ExploreAgentCount(): number {
  return 3  // 所有用户:3 个 Explore Agent
}

5.4 Phase 3: Review

目标

审查方案并确保对齐用户意图

操作

// messages.ts:3278-3282
`Phase 3: Review
Goal: Review the plan(s) from Phase 2 and ensure alignment.

1. Read the critical files identified by agents to deepen understanding
2. Ensure that the plans align with the user's original request
3. Use AskUserQuestion to clarify any remaining questions`

实例:对比方案并询问用户

Main Agent (Turn 4):
  审查 3 个方案:

  方案 1(简洁性):
  ✅ 改动最小,风险低
  ❌ 安全性一般,无刷新令牌

  方案 2(安全性):
  ✅ 安全性最高,支持令牌撤销
  ❌ 复杂度高,需要 Redis

  方案 3(性能):
  ✅ 性能最优,减少数据库查询
  ❌ Token 体积大,包含敏感信息

  调用 AskUserQuestion 工具:

  AskUserQuestion({
    questions: [{
      question: "JWT 认证的实现方向?",
      header: "方案选择",
      multiSelect: false,
      options: [
        {
          label: "简洁方案(推荐)",
          description: "最小改动,保留现有结构,适合快速上线"
        },
        {
          label: "安全方案",
          description: "双令牌 + 黑名单,需引入 Redis"
        },
        {
          label: "性能方案",
          description: "JWT 包含用户信息,减少数据库查询"
        }
      ]
    }]
  })

  ↓ (等待用户响应)

User (Turn 5):
  选择 "简洁方案"

Main Agent (Turn 5):
  确认方向,进入 Phase 4

5.5 Phase 4: Final Plan

目标

将最终方案写入 plan 文件

plan 文件结构

# 实现 JWT 认证

## Context(上下文)
当前认证系统使用 session,存在以下问题:
- 服务器需要维护 session 状态
- 水平扩展困难(需要 session 共享)
- 移动端集成不便

目标:切换到 JWT(无状态认证)

## Implementation(实现步骤)

### 1. 添加依赖
- 安装 jsonwebtoken: `npm install jsonwebtoken @types/jsonwebtoken`

### 2. 修改 src/middleware/auth.ts
- 添加 generateToken(userId: string) 函数
  - 使用 jwt.sign() 生成 token
  - 过期时间:24 小时
- 添加 verifyToken(token: string) 函数
  - 使用 jwt.verify() 验证 token
  - 返回 userId 或抛出异常
- 修改 authenticateUser 中间件
  - 从 req.headers.authorization 读取 token
  - 调用 verifyToken() 验证
  - 将 userId 附加到 req.user

### 3. 修改 src/routes/auth.ts
- 登录接口(POST /api/login)
  - 验证用户名密码后调用 generateToken()
  - 返回 token 给客户端
- 删除 logout 接口(JWT 无状态,无需服务端登出)

### 4. 更新 models/User.ts
- 保持不变(已有 password 字段)

### 5. 更新测试
- 修改 tests/auth.test.ts
  - 更新登录测试,检查返回 token
  - 更新受保护路由测试,使用 token 而非 session

## Reusable Utilities(复用现有代码)
- models/User.ts:comparePassword()(密码验证)
- utils/env.ts:getEnvVar()(读取 JWT 密钥)

## Verification(验证步骤)
1. `npm install` - 安装依赖
2. `npm test` - 运行测试
3. `curl -X POST http://localhost:3000/api/login -d '{"username":"test","password":"pass"}'` - 应返回 JWT token
4. `curl http://localhost:3000/api/protected -H "Authorization: Bearer <token>"` - 应返回用户数据

写入方式

// Main Agent (Turn 6)
Write({
  file_path: "/Users/user/.claude/plans/clever-pancake.md",
  content: "[上述 plan 内容]"
})

plan 文件命名

// src/utils/plans.ts
function getPlanSlug(sessionId?: SessionId): string {
  const slug = generateWordSlug()
  // 生成例子:clever-pancake, brave-tiger, happy-moon

  return slug
}

// 主对话 plan 文件:
//   ~/.claude/plans/clever-pancake.md

// 子 Agent plan 文件:
//   ~/.claude/plans/clever-pancake-agent-abc123.md

为什么用 word-slug:

  • ✅ 可读性:clever-pancakeplan-1742095823 更友好
  • ✅ 唯一性:词汇组合空间足够大(冲突概率低)
  • ✅ 可恢复性:resume 时通过 slug 恢复 plan 文件

plan 文件长度优化

Claude Code 通过 A/B 测试优化 plan 文件长度:

// src/utils/planModeV2.ts:64-95
// tengu_pewter_ledger 实验
// 控制 Phase 4 的指导内容,优化 plan 文件长度

// Baseline(控制组,14天数据,N=26.3M):
const baseline = {
  p50: 4906,   // 字符数
  p90: 11617,
  mean: 6207,
  rejectRate: {
    under2K: '20%',    // 过短 → 信息不足
    '2K-5K': '30%',
    '5K-10K': '35%',
    '10K-20K': '40%',
    over20K: '50%'     // 过长 → 冗余信息
  }
}

// 最佳长度:4K-6K(拒绝率最低)

// 实验组:trim, cut, cap
// 逐步收紧 Phase 4 的指导,要求更简洁的 plan

5.6 Phase 5: Exit

目标

通知用户计划已完成,请求批准

操作

// Main Agent (Turn 7)
ExitPlanMode()

// 系统行为:
// 1. 验证 plan 文件存在
const planExists = getPlan(toolUseContext.agentId) !== null
if (!planExists) {
  return Error("Cannot exit plan mode without a plan file")
}

// 2. 读取 plan 文件内容
const planContent = readFile(planFilePath)

// 3. 展示给用户
// 4. 等待用户决策

用户界面

┌─────────────────────────────────────────┐
│ Plan Ready for Review                   │
├─────────────────────────────────────────┤
│ [clever-pancake.md 内容预览]            │
│                                         │
│ # 实现 JWT 认证                         │
│                                         │
│ ## Context                              │
│ 当前认证系统使用 session...             │
│                                         │
│ ## Implementation                       │
│ 1. 添加依赖...                          │
│ 2. 修改 auth.ts...                      │
│ ...                                     │
├─────────────────────────────────────────┤
│ [Approve]  [Edit]  [Reject]             │
└─────────────────────────────────────────┘

用户决策

  1. Approve(批准)

    → Plan Mode 结束
    → Agent 切换到实施模式
    → 开始按 plan 文件执行
    
  2. Edit(编辑)

    User: "不要用 Redis,用内存缓存"
    
    → Plan Mode 继续
    → Agent 根据反馈修改 plan
    → 再次调用 ExitPlanMode
    
  3. Reject(拒绝)

    → 放弃当前 plan
    → 返回正常对话模式
    

6. 性能优化:并行执行

6.1 性能对比

执行方式Phase 1 耗时Phase 2 耗时总耗时
串行执行15s + 15s + 15s = 45s30s + 30s + 30s = 90s135s
并行执行max(15s, 15s, 15s) = 15smax(30s, 30s, 30s) = 30s45s
加速比

6.2 并行实现机制

// messages.ts:3240
`Launch up to 3 Explore agents IN PARALLEL (single message, multiple tool calls)`

// Main Agent 在一个 turn 中返回多个 tool_use:
{
  type: 'assistant',
  content: [
    { type: 'tool_use', name: 'Task', input: { subagent_type: 'Explore', ... } },
    { type: 'tool_use', name: 'Task', input: { subagent_type: 'Explore', ... } },
    { type: 'tool_use', name: 'Task', input: { subagent_type: 'Explore', ... } }
  ]
}

// 系统并行执行这 3 个 Task:
Promise.all([
  executeTask(agent1),
  executeTask(agent2),
  executeTask(agent3)
])

// 等待全部完成后,将结果追加到 Main Agent 的消息历史
// Main Agent 进入下一个 turn

6.3 并发控制

// src/utils/planModeV2.ts
function getPlanModeV2AgentCount(): number {
  const subscriptionType = getSubscriptionType()

  if (subscriptionType === 'max' ||
      subscriptionType === 'enterprise' ||
      subscriptionType === 'team') {
    return 3  // 高级订阅:3 个并行 Plan Agent
  }

  return 1  // 免费/基础:1 个 Plan Agent
}

function getPlanModeV2ExploreAgentCount(): number {
  return 3  // 所有用户:3 个 Explore Agent
}

7. 对比:Plan Mode vs 其他方案

7.1 vs 直接询问用户

维度Plan Mode直接询问
信息基础基于代码库探索基于用户回答
决策质量Agent 已理解现状,提供具体建议Agent 缺乏上下文,建议可能不适用
用户负担审查完整 plan(1次)回答碎片化问题(N次)
返工风险低(前置验证)高(可能误解需求)

示例对比:

场景:添加缓存功能

===== 直接询问 =====
Agent: "要用 Redis 还是内存缓存?"
User:  "Redis"
Agent: (开始写代码)
       → 发现项目已有 memcached
       → 引入 Redis 会有重复
       → 返工 ❌

===== Plan Mode =====
Agent: (Phase 1 探索)
       → 发现项目已有 memcached 配置
       (Phase 2 设计)
       → 方案 1:复用 memcached(推荐)
       → 方案 2:迁移到 Redis(成本高)
       (Phase 4 写 plan)
       → 建议复用 memcached,理由:...
User:  "同意方案 1"
       → 避免了重复引入 ✅

7.2 vs 边做边计划

维度Plan Mode边做边计划
全局视角✅ 一次性给出完整路径❌ 碎片化决策
用户打断1 次(批准 plan)N 次(每个分叉点)
代码浪费无(批准后再写)多(可能推倒重来)
适用场景复杂任务、架构决策简单任务、明确需求

示例对比:

场景:重构认证系统

===== 边做边计划 =====
Turn 1: Agent 开始修改 auth.ts
Turn 2: 遇到 session 存储问题  问用户
Turn 3: 继续改,发现需要修改 10 个依赖文件  问用户
Turn 4: 发现性能问题  问用户是否接受
Turn 5: 用户说"太复杂了,回到原方案"
         浪费 4 轮对话和代码 

===== Plan Mode =====
Phase 1: 探索依赖关系,发现 10 个文件依赖 auth.ts
Phase 2: 设计方案,预估性能影响
Phase 3:  plan,列出所有文件修改和风险
Phase 4: 用户审查 plan,提前发现复杂度问题
         调整方案或选择放弃
         未写任何代码,0 浪费 

7.3 vs O1/O1-Pro 的内置推理

维度Plan Mode(Claude Code)O1 内置推理
推理类型外显(多阶段工作流)内隐(思维链)
可观察性✅ 用户看到探索和设计过程❌ 推理过程不可见
中途干预✅ 可在 Phase 3 询问用户❌ 无法中途介入
并行探索✅ 多个 Agent 并行搜索❌ 串行推理
方案对比✅ 多 Plan Agent 设计不同方案❌ 输出单一方案
token 开销高(多次 API 调用)中(单次调用,长推理)

适用场景:

  • Plan Mode:需要用户参与决策、多方案对比、可观察性
  • O1 推理:纯 Agent 自主决策、无需中途介入

8. 设计权衡

8.1 优势

8.1.1 前置验证避免返工

量化收益:

无 Plan Mode 的返工场景:
  - 写代码:30 分钟
  - 发现方向错误
  - 推倒重来:30 分钟
  - 总耗时:60 分钟

有 Plan Mode:
  - 探索 + 规划:10 分钟
  - 用户发现方向错误
  - 调整 plan:5 分钟
  - 按正确 plan 实施:30 分钟
  - 总耗时:45 分钟
  - 节省:15 分钟(25%)

8.1.2 并行探索提升效率

性能数据:

串行探索(1 个 Agent):
  - 搜索认证代码:15s
  - 搜索用户模型:15s
  - 搜索测试模式:15s
  - 总耗时:45s

并行探索(3 个 Agent):
  - 3 个任务同时执行
  - 总耗时:max(15s, 15s, 15s) = 15s
  - 加速:3×

8.1.3 多视角设计避免局限

实例:

任务:优化 API 性能

单一方案(性能视角):
  - 添加缓存层(Redis)
  - 优化数据库查询
  → 忽略了简洁性(引入 Redis 增加复杂度)

多视角方案:
  - 方案 1(简洁性):仅优化查询,无新依赖
  - 方案 2(性能):Redis 缓存,性能最优
  - 方案 3(可维护性):异步队列,解耦业务
  → 用户根据项目阶段选择合适方案

8.2 代价

8.2.1 额外 API 调用

Plan Mode 的 API 调用:
  - Main Agent(Phase 1-5):5-10 次
  - Explore Agent × 3:3 次
  - Plan Agent × 3:3 次
  - 总计:11-16 次 API 调用

无 Plan Mode:
  - Main Agent 直接实施:3-5 次

增加成本:2-3×

权衡理由:

  • 对于简单任务:额外成本不值得(跳过 Plan Mode)
  • 对于复杂任务:避免返工的价值 > API 成本

8.2.2 用户等待时间

时间线:
  0s: 用户提需求
  
  10s: Phase 1 探索完成(3  Explore Agent 并行)
  
  40s: Phase 2 设计完成(3  Plan Agent 并行)
  
  50s: Phase 3-4 审查和写 plan
  
  50s: 等待用户批准
  ───────────────────────────
  用户首次看到 plan:50 

 Plan Mode:
  0s: 用户提需求
  
  5s: Agent 开始写代码
  ───────────────────────────
  用户首次看到代码:5 

缓解措施:

  • 并行执行(Phase 1 和 Phase 2 各节省 2×)
  • 使用 Haiku 执行 Explore Agent(更快)
  • 对简单任务跳过 Plan Mode

8.2.3 plan 文件质量依赖 Agent 能力

观察数据:

plan 文件长度分布(14 天,N=26.3M):
  - p50: 4,906 字符
  - p90: 11,617 字符
  - mean: 6,207 字符

拒绝率与长度的关系:
  - <2K 字符:20% 拒绝率(信息不足)
  - 2K-5K:30%
  - 5K-10K:35%
  - 10K-20K:40%
  - >20K:50% 拒绝率(冗余信息)

最佳长度:4K-6K

优化措施:

  • 实验不同的 Phase 4 指导(trim, cut, cap)
  • 目标:收紧 plan 长度到 20-30 行
  • 方法:禁止 Context 章节、要求子弹点格式

9. 总结

9.1 核心要点

  1. 单一 Loop + 多层嵌套

    • Main Agent 在一个 Loop 中顺序执行 5 个阶段
    • Phase 1/2 启动的子 Agents 各自有独立 Loops
    • 子 Agents 并行执行,Main Agent 等待结果
  2. 渐进式提示词注入

    • 首次注入完整版(~2000 tokens)
    • 每 5 turns 注入精简版(~300 tokens)
    • 节省 Context,保持记忆
  3. 软引导 + 硬约束

    • 软引导:详细的阶段指导(可灵活调整)
    • 硬约束:工具权限限制 + Exit 验证(无法绕过)
  4. 并行执行优化

    • 最多 3 个 Explore Agent 并行搜索(3× 加速)
    • 最多 3 个 Plan Agent 并行设计(3× 加速)
  5. 批准机制

    • 通过 ExitPlanMode 请求用户批准
    • 用户可批准/编辑/拒绝
    • 确保实现方案符合预期

9.2 适用场景判断

使用 Plan Mode 当:

  • ✅ 架构决策不明确(多种实现方案)
  • ✅ 多文件修改(影响范围大)
  • ✅ 需求不明确(需要探索)
  • ✅ 用户偏好重要(方案选择影响大)

跳过 Plan Mode 当:

  • ❌ 简单修改(拼写错误、单行改动)
  • ❌ 明确需求(用户已给出详细说明)
  • ❌ 研究任务(不是实现任务)

9.3 设计理念

Plan Mode 体现了 Claude Code 的 "Measure Twice, Cut Once" 哲学:

传统木工原则:
  "Measure Twice, Cut Once"
  (测量两次,切割一次)

Plan Mode 原则:
  "Explore Thoroughly, Plan Carefully, Code Once"
  (充分探索,仔细规划,一次编码)

核心理念:

  • 在不确定的情况下,探索 比盲目行动更高效
  • 在复杂的任务中,规划 比直接实施更可靠
  • 在关键的决策上,用户把关 比 Agent 独断更安全

附录 A:关键数据

A.1 性能基线

指标数值数据来源
plan 文件长度(p50)4,906 字符14 天基线数据
plan 文件长度(p90)11,617 字符同上
平均长度6,207 字符同上
最佳拒绝率20-30%<2K 和 4K-6K 区间
最差拒绝率50%>20K 区间

A.2 并发配置

订阅等级Explore AgentPlan Agent
Free/Basic31
Team33
Enterprise33
Max33

A.3 模型选择

Agent 类型模型理由
Main AgentOpus/Sonnet需要高级推理能力
Explore AgentHaiku快速搜索,低成本
Plan AgentOpus/Sonnet(继承)需要架构设计能力

A.4 提示词注入配置

参数说明
TURNS_BETWEEN_ATTACHMENTS5每 5 个 turn 注入一次
FULL_REMINDER_EVERY_N_ATTACHMENTS5每 5 次注入 1 次完整版
Full 版 token 开销~2000完整 5 阶段指导
Sparse 版 token 开销~300精简提示

附录 B:相关代码文件

文件说明
src/query.ts:241-279Main Agent Loop 实现
src/query.ts:1704-1711maxTurns 限制检查
src/utils/attachments.ts:259-262Plan Mode Attachment 配置
src/utils/attachments.ts:1186-1241Plan Mode Attachment 注入逻辑
src/utils/messages.ts:3136-3148Plan Mode 指导路由
src/utils/messages.ts:3207-3292完整版 5 阶段指导
src/utils/messages.ts:3385-3397精简版提示
src/utils/messages.ts:3453-3828Attachment → UserMessage 转换
src/utils/planModeV2.ts:5-62Plan Mode 配置和实验
src/utils/plans.tsplan 文件管理
src/tools/AgentTool/built-in/exploreAgent.tsExplore Agent 定义
src/tools/AgentTool/built-in/planAgent.tsPlan Agent 定义
src/tools/EnterPlanModeTool/EnterPlanMode 工具
src/tools/ExitPlanModeTool/ExitPlanMode 工具

附录 C:常见问题

Q1: 五个阶段是各自独立的 Agent Loop 吗?

A: 不是。五个阶段在 同一个 Main Agent Loop 中顺序执行。但 Phase 1 和 Phase 2 启动的子 Agents(Explore 和 Plan)各自有独立的 Loops。

Q2: 提示词是一次性注入还是渐进式披露?

A: 渐进式披露。首次进入 Plan Mode 时注入完整版(~2000 tokens),之后每 5 个 turns 注入精简版(~300 tokens),节省 Context。

Q3: 如何保障 Agent 按流程执行?

A: 通过 软引导 + 硬约束

  • 软引导:详细的阶段指导(提示词)
  • 硬约束:工具权限限制(只能编辑 plan 文件)+ Exit 验证(必须有 plan 文件才能退出)

Q4: Plan Mode 会自动触发吗?

A: 是的。Agent 根据任务特征自动判断是否需要进入 Plan Mode。用户也可以明确拒绝。

Q5: 如何跳过 Plan Mode?

A:

  1. 用户在 Agent 请求进入时点击 "Decline"
  2. 给出明确详细的需求,减少不确定性
  3. 对于简单任务,Agent 会自动跳过

Q6: plan 文件可以在实施时修改吗?

A: 可以。实施时如果发现 plan 需要调整,Agent 会先更新 plan 文件再继续。

Q7: Explore Agent 和 Plan Agent 的区别?

A:

  • Explore Agent:专注于搜索和读取代码,使用 Haiku 模型(快速、低成本)
  • Plan Agent:专注于设计实现方案,使用 Sonnet/Opus(高级推理)

Q8: 为什么用 word-slug 命名 plan 文件?

A:

  • 可读性好(clever-pancake 比 UUID 友好)
  • 支持 resume(通过 slug 恢复 plan)
  • 唯一性足够(词汇组合空间大)

Q9: Plan Mode 适合小团队吗?

A: 适合。对于缺乏经验的开发者,Plan Mode 提供了架构指导和最佳实践参考。


文档版本: 2.0 最后更新: 2026-04-14 适用版本: Claude Code >= 1.5.0 作者: Based on code analysis and discussions

相关文档: