Skip to content

plan / act 模式:同一套架構下的兩個角色

源码版本v4.0.10

職責

Cline 把「跟使用者對話」和「真去改檔案」這兩件事拆成 plan 和 act 兩種模式 (mode),由 Mode = "plan" | "act" 型別顯式標註(Mode type:14). 同一個 Task 實例只屬於一個 mode,切換模式會重建 Task,所以在單次任務裡 mode 是固定不變的。這個 mode 參數從 buildApiHandler 一路傳到 ApiHandler,再從 attemptApiRequest 傳到 getSystemPrompt 的 context,貫穿到底。

它的兩個核心作用:一是影響 ApiHandler 的選擇 —— buildApiHandler 根據 mode 從 planModeApiProvideractModeApiProvider 選 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 / actModeApiModelIdplanModeReasoningEffort / actModeReasoningEffortplanModeThinkingBudgetTokens / 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:14type Mode = "plan" | "act",只有這兩個值。
  • buildApiHandler:517 — 入口,從 planModeApiProvider / actModeApiProvider 選 provider,並把 mode 透傳給 createHandlerForProvider
  • anthropic case mode-aware:94case "anthropic" 分支按 mode 從成對欄位取 apiModelId / reasoningEffort / thinkingBudgetTokens
  • mode provider pick:520const 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 傳給系統提示層:

typescript
// 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 分支很典型:

typescript
// 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 渲染:

typescript
// 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_MODEATTEMPT,模型主要靠系統提示約束自己。
  • 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

對照官方資料:Cline 文件 · README