Files
cs-note/claude-code-best/docs/safety/plan-mode.md
T

155 lines
6.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
tags: [Plan-Mode, 计划模式, 权限模式, Claude-Code, 安全]
create time: 2026-06-09 22:30
---
# 计划模式 - Plan Mode 先看后做的安全机制
## 概述
Plan Mode 为复杂任务提供了一个"只读探索"阶段,通过 EnterPlanModeTool 和 ExitPlanModeV2Tool 两个工具实现闭环。AI 先在只读模式下充分理解代码库,形成计划方案后提交用户审阅,批准后才恢复全部权限执行。这解决了"AI 匆忙行动"的问题。
## 正文
### 问题场景
你说"重构这个模块",AI 立刻开始改代码——但你还没搞清楚它打算怎么改。等改了一半发现方向不对,已经来不及了。
### Plan Mode 的解决方案
计划模式给对话加了一个"只读阶段",通过两个工具实现闭环:
```mermaid
flowchart TD
A["EnterPlanMode\n用户审批进入"] --> B["探索阶段\n只读工具集 Read/Grep/Glob"]
B --> C["ExitPlanMode\n提交方案审批"]
C --> D["恢复执行\n全部工具权限"]
```
1. **EnterPlanMode — 进入计划模式**:AI 自主判断(或用户触发)任务需要规划,调用 `EnterPlanModeTool`(`packages/builtin-tools/src/tools/EnterPlanModeTool/EnterPlanModeTool.ts:36`)。该工具需要**用户审批**(`checkPermissions` 返回 `ask`)。
2. **探索阶段 — 只读工具集**:权限模式切换为 `'plan'`,AI 只能使用 `isReadOnly()` 为 true 的工具(Read、Grep、Glob、Agent 等)。写操作被自动拒绝。
3. **ExitPlanMode — 提交方案审批**:AI 完成探索后,调用 `ExitPlanModeV2Tool`(`packages/builtin-tools/src/tools/ExitPlanModeTool/ExitPlanModeV2Tool.ts:147`),将计划文件提交给用户审阅。这是第二个**需要用户审批**的节点。
4. **恢复执行 — 全部工具权限**:用户批准后,权限模式恢复为进入前的状态,AI 按计划执行。
### 权限的自动收窄与恢复
#### 进入:prepareContextForPlanMode()
`EnterPlanModeTool.call()`(第 77 行)的核心逻辑:
```typescript
// 1. 记录转换状态(保存之前的模式)
handlePlanModeTransition(currentMode, 'plan')
// 2. 切换权限上下文为 plan 模式
context.setAppState(prev => ({
...prev,
toolPermissionContext: applyPermissionUpdate(
prepareContextForPlanMode(prev.toolPermissionContext),
{ type: 'setMode', mode: 'plan', destination: 'session' },
),
}))
```
`prepareContextForPlanMode()`(`src/utils/permissions/permissionSetup.ts`)做了什么:
- 创建新的 `ToolPermissionContext`,`mode` 设为 `'plan'`
- 在 plan 模式下,工具的 `isReadOnly()` 检查成为唯一准入条件
- 如果用户的默认模式是 `'auto'`,还会激活 classifier 的副作用
#### 退出:权限恢复 + Prompt-based 权限
`ExitPlanModeV2Tool` 的退出逻辑做了两件关键的事:
**1. 恢复权限模式**
通过 `handlePlanModeTransition()` 和 `applyPermissionUpdate()` 恢复到进入前的模式。
**2. 注入 Prompt-based 权限**
这是 Plan Mode 最精妙的设计——AI 可以在计划中声明它需要执行的命令类别:
```typescript
// ExitPlanModeV2Tool 的 inputSchema
allowedPrompts: z.array(z.object({
tool: z.enum(['Bash']),
prompt: z.string().describe('Semantic description, e.g. "run tests"'),
})).optional()
```
当 AI 提交计划时,如果声明了 `allowedPrompts: [{ tool: 'Bash', prompt: 'run tests' }]`,用户批准后,"run tests" 这类 Bash 命令会被自动放行——不再需要逐个确认。
> [!tip] Prompt-based 权限的价值
> 这个设计让 AI 可以"预告"它将要执行的操作类别,用户在审批计划时一并授权,避免了执行阶段的频繁打断。
### 计划文件的持久化
计划内容被写入磁盘文件(由 `getPlanFilePath()` 确定路径),这与简单的"AI 说一段话然后开始执行"有本质区别:
1. `ExitPlanModeV2Tool` 的 `normalizeToolInput` 从磁盘读取计划内容,注入到 `input.plan` 和 `input.planFilePath`
2. 计划文件是用户**可编辑**的——用户可以在审批前修改 AI 的方案
3. `planWasEdited` 字段标记用户是否修改了计划,影响后续的 tool_result 回显
4. `persistFileSnapshotIfRemote()` 在远程场景下保存文件快照
### Teammate 场景下的计划审批
在 Agent Swarms(`isAgentSwarmsEnabled()`)模式下,计划审批有额外的协作流程:
```typescript
// 如果是 Teammate 角色
if (isTeammate()) {
// 发送计划到 Team Leader 的 mailbox 等待审批
const requestId = generateRequestId()
writeToMailbox(getTeamName(), {
type: 'plan_approval_request',
plan, requestId, ...
})
// 返回 awaitingLeaderApproval: true
// Team Leader 审批后通过 mailbox 通知 Teammate
}
```
这意味着在蜂群模式下,计划可能不是由直接用户审批,而是由 Team Leader 审批。
### 什么时候该用计划模式
`EnterPlanModeTool` 的 Prompt(`packages/builtin-tools/src/tools/EnterPlanModeTool/prompt.ts`)定义了两套触发标准——外部版本更积极(鼓励规划),内部版本更克制(仅在真正模糊时使用):
| 场景 | 外部版本 | 内部版本 |
|------|---------|---------|
| 修复 typo | 跳过 | 跳过 |
| 添加删除按钮 | **进入**(涉及多个文件) | **跳过**(路径明确) |
| 重构认证系统 | **进入** | **进入**(高影响重构) |
| "开始做 X" | — | **跳过**(直接开始) |
| 架构决策(Redis vs 内存缓存) | **进入** | **进入**(真正模糊) |
### 计划模式 + 任务系统
计划模式通常与任务系统配合使用:
1. 在计划模式中,AI 把实施步骤创建为任务列表(`TodoWrite`)
2. 用户审批计划(包含任务列表)
3. 退出计划模式后,AI 按任务列表逐项执行
4. 用户可以通过任务列表追踪进度
### 完整生命周期
```mermaid
flowchart TD
U["用户: 重构这个模块"] --> AI1["AI 判断需要规划\n调用 EnterPlanModeTool"]
AI1 -->|"用户审批 Ask 对话框"| TRANS["handlePlanModeTransition(default, 'plan')\nprepareContextForPlanMode()"]
TRANS --> EXPLORE["AI 使用 Read/Grep/Glob/Agent 探索代码库\n可能 10+ 轮只读工具调用"]
EXPLORE --> EXIT["AI 形成方案\n调用 ExitPlanModeV2Tool\nallowedPrompts: run tests, install deps"]
EXIT -->|"用户审批计划\n可编辑计划文件"| RESTORE["恢复权限模式\n注入 prompt-based 权限"]
RESTORE --> EXEC["AI 使用全部工具执行计划\nrun tests 等命令自动放行"]
```
## 关联笔记
- [[why-safety-matters|AI 安全至关重要]]
- [[permission-model|权限模型]]
- [[auto-mode|Auto Mode]]
- [[sandbox|沙箱机制]]