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