
Plan模式的完整流程摘要Plan模式是當前主流AI編程助手如Cursor、Claude Code、Cline等中一種非常重要的工作機制在真正動手修改代碼之前先讓AI產出一份可審閱的執(zhí)行方案由人類確認或調整后再落地執(zhí)行。本文將圍繞 Plan 模式的完整生命周期——開啟 → 補充信息 → 審閱方案 → 確認執(zhí)行或調整計劃——展開深入剖析結合可運行的代碼示例手把手帶你理解并實現(xiàn)一個最小可用的 Plan 模式工作流。無論你是想理解AI Agent的交互設計還是想在自己的工具中落地類似機制本文都能提供可復用的思路與代碼。引言在直接對話式改代碼的模式下我們常常遇到這樣的問題AI 一上來就大改特改改完才發(fā)現(xiàn)方向完全錯了需求含糊時AI 自行腦補產出的代碼與預期南轅北轍涉及多文件、多步驟的重構AI 邊想邊改中途邏輯斷裂人類失去了對改動范圍的把控review 成本極高。Plan 模式正是為了解決這些痛點而誕生的。它的核心思想是把思考和執(zhí)行分離。先產出一份結構化的計劃Plan讓人來審閱、補充、調整確認無誤后再進入執(zhí)行階段。這本質上是一種人在回路Human-in-the-loop的工程設計。下面我們按完整流程逐步拆解。一、開啟 Plan 模式從直接執(zhí)行切換到先規(guī)劃1.1 為什么要有一個顯式的開啟動作Plan 模式不應該默認開啟也不應該悄悄開啟。它需要用戶明確表達先給我方案別急著改。原因有三成本考量規(guī)劃本身要消耗 token 和時間簡單任務不值得預期管理用戶需要知道當前處于只讀/規(guī)劃狀態(tài)避免誤以為代碼已被修改權限邊界Plan 階段通常應禁用寫文件、執(zhí)行命令等有副作用的工具。1.2 用狀態(tài)機建模一個清晰的實現(xiàn)方式是把整個流程建模為有限狀態(tài)機fromenumimportEnum,autoclassPlanState(Enum):IDLEauto()# 普通對話模式PLANNINGauto()# Plan 模式正在生成/補充計劃REVIEWINGauto()# 計劃已生成等待用戶審閱EXECUTINGauto()# 用戶已確認正在執(zhí)行DONEauto()# 執(zhí)行完成1.3 開啟時的工具約束在 Plan 狀態(tài)下必須對 Agent 可用的工具做白名單限制只允許只讀工具READ_ONLY_TOOLS{read_file,list_dir,search_code,grep}WRITE_TOOLS{write_file,edit_file,run_command,delete_file}defget_allowed_tools(state:PlanState)-set[str]:根據當前狀態(tài)返回允許使用的工具集合ifstatein(PlanState.PLANNING,PlanState.REVIEWING):# 規(guī)劃/審閱階段只讀禁止任何副作用操作returnREAD_ONLY_TOOLSifstatePlanState.EXECUTING:returnREAD_ONLY_TOOLS|WRITE_TOOLSreturnREAD_ONLY_TOOLS|WRITE_TOOLS這樣一來即便模型想去改文件工具層也會直接攔截從機制上保證 Plan 階段是安全的。1.4 觸發(fā)開啟的幾種常見方式顯式指令用戶輸入先給方案、“plan first”、“別改代碼先說說思路”快捷鍵/開關IDE 中的 Plan 模式按鈕如 Cursor 的 Plan Mode自動升級當檢測到任務涉及多文件或高風險操作時Agent 主動建議進入 Plan 模式。二、補充信息讓計劃建立在充分上下文之上2.1 為什么補充信息是獨立的一步一個高質量的計劃前提是信息充分。如果需求含糊、代碼庫上下文缺失模型只能靠猜產出的計劃必然空洞。因此 Plan 模式在生成計劃前或生成初稿后應主動向用戶澄清關鍵信息。這一步的產物通常包括兩類向用戶提問范圍、約束、優(yōu)先級、驗收標準主動檢索代碼庫讀取相關文件、搜索調用鏈把事實性上下文補齊。2.2 結構化澄清把問題變成數(shù)據與其讓模型自由發(fā)揮地問不如定義一個結構化的澄清對象fromdataclassesimportdataclass,fielddataclassclassClarification:question:str# 要問用戶的問題reason:str# 為什么需要這個信息options:list[str]field(default_factorylist)# 可選項可選blocking:boolTrue# 是否阻塞計劃生成# 示例模型在規(guī)劃前提出的澄清項clarifications[Clarification(question這次重構是否需要保持對外 API 完全兼容,reason決定能否修改函數(shù)簽名影響改動范圍,options[必須兼容,可以破壞性變更],blockingTrue,),Clarification(question是否需要同步補充單元測試,reason影響計劃中的步驟數(shù)量與工作量,options[需要,不需要],blockingFalse,),]2.3 主動檢索用只讀工具補齊上下文在等待用戶回答的同時Agent 可以并行地用只讀工具收集事實defgather_context(agent,target_symbol:str)-dict:在 Plan 階段收集與目標符號相關的上下文只讀context{}# 1. 找到定義位置context[definition]agent.call_tool(search_code,{query:fdef{target_symbol}})# 2. 找到所有調用點評估影響面context[callers]agent.call_tool(grep,{pattern:f{target_symbol}(,exclude:tests/})# 3. 讀取相關測試文件context[tests]agent.call_tool(search_code,{query:target_symbol,path:tests/})returncontext2.4 補充信息的收斂判斷不能無限地問下去。一個實用的收斂策略是只對blockingTrue的問題強制等待回答非阻塞問題給出默認假設并在計劃中顯式標注假設……當關鍵上下文定義、調用點、測試都已獲取即可進入下一步。三、審閱方案把計劃變成可讀、可評估的產物3.1 計劃的推薦結構一份好的計劃不是一段散文而是結構化、可逐步核對的清單。推薦包含以下字段fromdataclassesimportdataclass,fielddataclassclassPlanStep:id:inttitle:strdescription:strfiles:list[str]# 預計涉及的文件risk:str# low / medium / highdone_when:str# 驗收標準如何判斷這步完成dataclassclassPlan:goal:str# 一句話目標assumptions:list[str]# 明確列出的假設steps:list[PlanStep]out_of_scope:list[str]# 明確不做什么防止范圍蔓延open_questions:list[str]# 仍需確認的問題3.2 生成計劃的提示詞骨架PLAN_PROMPT你正在 Plan 模式禁止修改任何文件。請基于以下上下文產出執(zhí)行計劃。 ## 目標 {goal} ## 已收集的上下文 {context} ## 已確認的信息 {confirmed} ## 輸出要求 1. 用結構化步驟描述改動每步標注涉及文件、風險等級、驗收標準 2. 明確列出你的假設assumptions與不做的事out_of_scope 3. 若仍有阻塞性問題放入 open_questions不要臆測。 3.3 審閱時人類關注什么審閱階段人類主要評估四件事我們可以在 UI 上對應地高亮關注點對應字段典型問題方向對不對goal這是我要解決的問題嗎范圍合不合理steps[].files、out_of_scope改動面是否過大風險可不可接受steps[].risk有沒有破壞性變更假設成不成立assumptions這些假設我認可嗎3.4 一個渲染示例把 Plan 渲染成人類易讀的 Markdowndefrender_plan(plan:Plan)-str:lines[f## 目標\n{plan.goal}\n]lines.append(## 假設)lines[f-{a}forainplan.assumptions]or[- 無]lines.append(\n## 執(zhí)行步驟)forsinplan.steps:lines.append(f{s.id}. **{s.title}**風險:{s.risk}\nf - 涉及文件:{, .join(s.files)}\nf - 完成標準:{s.done_when})lines.append(\n## 不在本次范圍)lines[f-{x}forxinplan.out_of_scope]or[- 無]ifplan.open_questions:lines.append(\n## 待確認問題)lines[f-{q}forqinplan.open_questions]return\n.join(lines)輸出的效果大致如下## 目標 將 UserService 中的同步查詢改為異步保持對外 API 兼容。 ## 假設 - 調用方均通過 await 調用無同步調用殘留 - 數(shù)據庫驅動支持 async ## 執(zhí)行步驟 1. **抽取數(shù)據訪問層**風險: medium - 涉及文件: services/user_service.py, db/repo.py - 完成標準: 新 repo 通過現(xiàn)有單測 2. **改造 UserService 為 async**風險: high - 涉及文件: services/user_service.py - 完成標準: 所有方法標注 async 且調用鏈更新 ## 不在本次范圍 - 不修改 HTTP 層四、確認執(zhí)行從計劃到落地的受控切換4.1 確認動作本身要有契約用戶的確認不應只是一個模糊的好的而應是一個明確的信號最好帶上是否允許偏離計劃的授權范圍dataclassclassApproval:approved:boolallow_deviation:boolFalse# 執(zhí)行中是否允許偏離計劃max_files_changed:int|NoneNone# 可選的硬性約束4.2 執(zhí)行器按步驟推進進入EXECUTING狀態(tài)后執(zhí)行器應當逐步推進并在每步完成后對照done_when自檢defexecute_plan(agent,plan:Plan,approval:Approval)-dict:ifnotapproval.approved:raiseRuntimeError(計劃未獲批準禁止執(zhí)行)results[]forstepinplan.steps:# 1. 執(zhí)行前再次確認工具權限此時已放開寫權限agent.set_allowed_tools(get_allowed_tools(PlanState.EXECUTING))# 2. 執(zhí)行該步驟outputagent.run_step(step)# 3. 自檢對照完成標準checkagent.verify(step.done_when,output)results.append({step:step.id,title:step.title,passed:check.passed,detail:check.detail,})# 4. 若不允許偏離且自檢失敗立即中止回到審閱ifnotcheck.passedandnotapproval.allow_deviation:agent.set_state(PlanState.REVIEWING)return{aborted_at:step.id,results:results}agent.set_state(PlanState.DONE)return{results:results}4.3 偏差檢測執(zhí)行時守護計劃邊界即使批準了執(zhí)行也應監(jiān)控實際改動是否越界比如改了計劃外的文件defcheck_deviation(step:PlanStep,changed_files:list[str])-list[str]:返回計劃外被修改的文件列表plannedset(step.files)return[fforfinchanged_filesiffnotinplanned]一旦發(fā)現(xiàn)越界就應暫停并向用戶報告而不是默默改完——這正是 Plan 模式價值的關鍵體現(xiàn)。五、調整計劃審閱不通過時的回退與迭代5.1 調整不是重新來一遍用戶審閱后可能給出三類反饋微調某步拆分/合并、調整順序、改風險描述范圍變更增刪步驟、修改out_of_scope方向否定目標本身理解錯了需要重來。前兩類應當在保留已有上下文的基礎上做增量修改避免每次都從頭生成。5.2 用 diff 方式修改計劃defapply_feedback(plan:Plan,feedback:str)-Plan:根據用戶反饋增量修改計劃示意# 實際實現(xiàn)中可再次調用模型僅要求輸出變更部分delta_promptf現(xiàn)有計劃{render_plan(plan)}用戶反饋{feedback}請僅輸出需要變更的步驟新增/刪除/修改保持其余步驟不變。 deltacall_model(delta_prompt)returnmerge_delta(plan,delta)# 將增量合并回原計劃5.3 調整后的再確認調整完成后狀態(tài)回到REVIEWING再次請用戶確認。整個流程形成一個可循環(huán)的閉環(huán)PLANNING ──? REVIEWING ──┬──? EXECUTING ──? DONE ▲ │ └──── 調整計劃 ?──────┘5.4 完整驅動循環(huán)示例把前面各部分串起來得到一個可運行的骨架defrun_plan_mode(agent,goal:str):# 1. 開啟agent.set_state(PlanState.PLANNING)agent.set_allowed_tools(get_allowed_tools(PlanState.PLANNING))# 2. 補充信息contextgather_context(agent,target_symbolextract_symbol(goal))clarificationsagent.propose_clarifications(goal,context)confirmedask_user(clarifications)# 阻塞式收集回答# 3. 生成并審閱方案planagent.generate_plan(goal,context,confirmed)agent.set_state(PlanState.REVIEWING)whileTrue:print(render_plan(plan))feedbackinput(確認執(zhí)行(y/調整內容): )iffeedback.strip().lower()y:approvalApproval(approvedTrue)break# 5. 調整計劃planapply_feedback(plan,feedback)# 4. 確認執(zhí)行agent.set_state(PlanState.EXECUTING)returnexecute_plan(agent,plan,approval)六、工程實踐中的幾個關鍵細節(jié)6.1 計劃要可驗證每個步驟都必須有done_when否則執(zhí)行階段無法自檢也無法判斷是否需要回退。模糊的完成改造是壞計劃所有調用點編譯通過且單測綠才是好計劃。6.2 顯式記錄假設把模型的隱含假設擺到臺面上是 Plan 模式最有價值的部分之一。用戶在審閱時看到假設調用方無同步殘留往往能立刻發(fā)現(xiàn)模型理解是否有偏差。6.3 權限與狀態(tài)強綁定安全邊界必須由代碼保證而非依賴模型自覺。前面get_allowed_tools的設計就是這一原則的體現(xiàn)Plan 階段物理上無法寫文件。6.4 日志與可追溯建議對每個狀態(tài)遷移、每次計劃版本都留痕便于事后復盤為什么走了這條路徑importlogging logging.basicConfig(levellogging.INFO)deftransition(agent,from_state,to_state,reason):logging.info(f[PlanMode]{from_state.name}-{to_state.name}|{reason})agent.set_state(to_state)總結Plan 模式的本質是把 AI 的思考與執(zhí)行解耦通過人在回路的方式在動手之前先對齊目標、范圍與風險。本文完整梳理了它的四個核心環(huán)節(jié)開啟用狀態(tài)機顯式建模并在規(guī)劃階段用只讀工具白名單從機制上禁止副作用補充信息通過結構化澄清 主動檢索讓計劃建立在充分上下文之上并明確收斂條件審閱方案把計劃結構化為目標、假設、步驟、范圍、待確認問題讓人能高效評估方向與風險確認執(zhí)行或調整計劃以明確的審批契約進入執(zhí)行逐步推進并檢測偏差審閱不通過時以增量 diff 方式迭代最終形成閉環(huán)。一句話概括好的 Plan 模式讓 AI 先說清楚要做什么再在人類的確認下只做說好的事。掌握了這套流程你不僅能更好地使用現(xiàn)有的 AI 編程工具也能在自己的 Agent 系統(tǒng)中落地一套安全、可控、可追溯的規(guī)劃工作流。