plan / act 模式:同一套架构下的两个角色
职责
Cline 把「跟用户对话」和「真去改文件」这两件事拆成 plan 和 act 两种模式 (mode),由 Mode = "plan" | "act" 类型显式标注(Mode type:14). 同一个 Task 实例只属于一个 mode,切换模式会重建 Task,所以在单次任务里 mode 是固定不变的。这个 mode 参数从 buildApiHandler 一路传到 ApiHandler,再从 attemptApiRequest 传到 getSystemPrompt 的 context,贯穿到底。
它的两个核心作用:一是影响 ApiHandler 的选择 —— buildApiHandler 根据 mode 从 planModeApiProvider 或 actModeApiProvider 选 provider(mode provider:520),plan 和 act 可以挂不同模型和不同 thinkingBudget;二是影响系统提示里 ACT_VS_PLAN 组件的内容和某些工具的可用性提示(act_vs_plan template:5).
设计动机
- plan / act 两套 ApiConfiguration:
planModeApiProvider / actModeApiProvider是两个独立字段,默认值都是DEFAULT_API_PROVIDER(planModeApiProvider:243),用户可以让 plan 走便宜模型快速讨论方案、act 走更强模型实际改代码。 - mode 贯穿到 handler 构造:
createHandlerForProvider收到 mode 后,从planModeApiModelId / actModeApiModelId、planModeReasoningEffort / actModeReasoningEffort、planModeThinkingBudgetTokens / actModeThinkingBudgetTokens等成对字段里取对应那份(mode-aware options:94),所有「思考预算」「reasoning effort」「model id」都按 mode 隔离。 - plan_mode_respond 工具专属 plan:
plan_mode_respond工具的 description 明确写This tool is only available in PLAN MODE,act 模式虽然没有运行时拦截调这个工具,但系统提示里ACT_VS_PLAN段会告诉模型「ACT MODE 不能用 plan_mode_respond」(ACT MODE exclusion:9). - ACT MODE 主体工具集:generic variant 声明的 18 个工具里,只有
PLAN_MODE = "plan_mode_respond"是 plan 专属,其余execute_command / read_file / write_to_file / replace_in_file / search_files / list_files / attempt_completion等在两个模式下都可用(generic tools:58),act 模式靠这套改文件、跑命令、完成时attempt_completion收尾。 - yoloMode 影响 plan 提示:
getActVsPlanModeTemplateText里检查context.yoloModeToggled !== true,只有非 yolo 模式才在 plan 描述里加一句「可以用 ask_followup_question 问澄清问题」(yolo guard:18),yolo 模式默认不主动问。 - needs_more_exploration 参数:plan_mode_respond 带
needs_more_exploration可选布尔参数(needs_more_exploration:38),让模型在写完响应后还能回头说「我需要再读几个文件」,避免被自己的初始 plan 锁死。
关键文件
Mode type:14—type Mode = "plan" | "act",只有这两个值。buildApiHandler:517— 入口,从planModeApiProvider / actModeApiProvider选 provider,并把 mode 透传给createHandlerForProvider。anthropic case mode-aware:94—case "anthropic"分支按 mode 从成对字段取apiModelId / reasoningEffort / thinkingBudgetTokens。mode provider pick:520—const apiProvider = mode === "plan" ? planModeApiProvider : actModeApiProvider。planModeApiProvider default:243— 两个字段默认都是DEFAULT_API_PROVIDER,用户没显式配置时 plan/act 走同一个 provider。act_vs_plan template:5— 系统提示里 ACT_VS_PLAN 段的模板,定义两种模式各自的工具可用性和行为预期。ACT MODE description:9— ACT MODE 能用除 plan_mode_respond 之外的所有工具,完成时用 attempt_completion。PLAN MODE description:11— PLAN MODE 只能用 plan_mode_respond 回应,目标是先收集信息再出方案。yolo mode guard:18— yolo 模式下删掉「可以问澄清问题」的提示。plan_mode_respond generic:25— plan 模式专属工具的规格,带response / needs_more_exploration / task_progress三个参数。PLAN MODE only constraint:7— description 里写明This tool is only available in PLAN MODE,模型按提示自我约束。act_mode_respond description:29— act 模式专属工具,用来在不打断执行流的情况下报告进度,且不能连续调用。generic tools list:58— generic variant 声明的 18 个工具 id,同时涵盖 plan 和 act 的工具。Task mode field:585— Task 构造时用mode参数调buildApiHandler,这个 mode 来自 TaskParams。
数据流
mode 的传播是单向直线:Task 构造时收到一个 mode,这个 mode 决定 buildApiHandler 走哪条分支,然后 attemptApiRequest 把同一个 mode 通过 SystemPromptContext.providerInfo.mode 传给系统提示层:
// apps/vscode/src/core/api/index.ts
export function buildApiHandler(configuration: ApiConfiguration, mode: Mode): ApiHandler {
const { planModeApiProvider, actModeApiProvider, ...options } = configuration
const apiProvider = mode === "plan" ? planModeApiProvider : actModeApiProvider
// ... thinkingBudget 限额 ...
return createHandlerForProvider(apiProvider, options, mode)
}createHandlerForProvider 的每个 case 都按 mode 选字段,Anthropic 分支很典型:
// apps/vscode/src/core/api/index.ts
case "anthropic":
return new AnthropicHandler({
onRetryAttempt: options.onRetryAttempt,
apiKey: options.apiKey,
anthropicBaseUrl: options.anthropicBaseUrl,
apiModelId: mode === "plan" ? options.planModeApiModelId : options.actModeApiModelId,
reasoningEffort: mode === "plan" ? options.planModeReasoningEffort : options.actModeReasoningEffort,
thinkingBudgetTokens:
mode === "plan" ? options.planModeThinkingBudgetTokens : options.actModeThinkingBudgetTokens,
})mode === "plan" 这个判断在 30 多个 case 里重复出现,看起来啰嗦,但好处是每个分支都明确告诉读者「这里有 plan/act 两份配置」,不用记字段名约定。系统提示层的 ACT_VS_PLAN 段根据 context 渲染:
// apps/vscode/src/core/prompts/system-prompt/components/act_vs_plan_mode.ts
const getActVsPlanModeTemplateText = (context: SystemPromptContext) => `ACT MODE V.S. PLAN MODE
In each user message, the environment_details will specify the current mode. There are two modes:
- ACT MODE: In this mode, you have access to all tools EXCEPT the plan_mode_respond tool.
- In ACT MODE, you use tools to accomplish the user's task. Once you've completed the user's task, you use the attempt_completion tool to present the result of the task to the user.
- PLAN MODE: In this special mode, you have access to the plan_mode_respond tool.
- In PLAN MODE, the goal is to gather information and get context to create a detailed plan for accomplishing the task, which the user will review and approve before they switch you to ACT MODE to implement the solution.
// ...`注意它没有按 mode 渲染不同模板,而是把两种模式的规则都写进同一段提示,让模型自己根据 environment_details.mode 字段决定走哪条路(environment_details mode:7). 真正的「按 mode 选工具」由 ClineToolSet.getEnabledTools 依据 context.providerInfo.mode 过滤工具列表来实现(ApiProviderInfo.mode:75).
边界与失败
- plan 用 act 专属工具没硬拦截:
getEnabledTools是按 variant.tools 列表过滤,没在运行时单独检查 mode 来排除plan_mode_respond(getEnabledTools:87),generic variant 同时声明了PLAN_MODE和ATTEMPT,模型主要靠系统提示约束自己。 - mode 切换重建 Task:Task 构造时存下 mode,运行中切 mode 会触发新 Task 构造而不是改字段,
buildApiHandler只在 Task 构造时调一次(buildApiHandler call:585). - planModeApiProvider 默认值:
DEFAULT_API_PROVIDER是 plan/act 共用的默认值(planModeApiProvider default:243),所以新用户首次启动 Cline 时 plan 和 act 走同一个 provider,行为一致。 - thinkingBudget 在 plan / act 各自限额:
buildApiHandler里取mode === "plan" ? planModeThinkingBudgetTokens : actModeThinkingBudgetTokens单独做 maxTokens 限额(mode budget:525),不会让 plan 的 budget 影响 act 或反之。 - yoloMode 与 plan 冲突:
yoloModeToggled === true时 plan 段会删掉「可以问 ask_followup_question」的提示(yolo guard:18),但 yolo 模式本质是跳过用户确认自动执行,和 plan 的「先讨论后执行」理念有张力,实际上 yolo 用户基本不走 plan。 - act_mode_respond 连续调用拦截:
act_mode_respond工具 description 明确写「不能连续调用,否则会失败」(CRITICAL CONSTRAINT:43),靠运行时拦截。 - needs_more_exploration 自纠错:plan_mode_respond 在写完 plan 后还能回头说「需要更多探索」,通过
needs_more_exploration=true让下一轮走探索工具而不是收尾(needs_more_exploration:38),给模型自我修正的出口。
小结
plan / act 模式是 Cline 在「讨论方案」和「动手改代码」之间画的一条线,工程上就是 Mode = "plan" | "act" 一个类型加一份成对的配置字段。它的设计取舍是:不在运行时动态切换 mode,而是让每次切 mode 重建 Task,换来 handler / 提示 / 工具集都按 mode 整体重建,状态隔离干净。模型实际靠系统提示里的 ACT_VS_PLAN 段自我约束该用哪个工具,运行时拦截极少。
- 工具集怎么按 context 过滤:
/prompts/toolset - 系统提示怎么装配:
/prompts/prompt-builder - mode 怎么影响 handler 选择:
/providers/api-handler