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