戰(zhàn):修改代碼總失敗?從權(quán)限、目錄、依賴(lài)到測(cè)試的8項(xiàng)排查)
第一次用Codex處理真實(shí)項(xiàng)目時(shí)很多人的體驗(yàn)其實(shí)很割裂。你會(huì)發(fā)現(xiàn)它明明已經(jīng)看懂了代碼找到相關(guān)函數(shù)分析出了可能的原因甚至已經(jīng)告訴你準(zhǔn)備修改哪些文件。但任務(wù)真正執(zhí)行下去卻很容易出現(xiàn)另一種情況代碼改了一半停了。Terminal命令執(zhí)行失敗。同一個(gè)錯(cuò)誤連續(xù)修改幾輪。原本只修一個(gè)Bug最后改了十幾個(gè)文件。Codex說(shuō)“已經(jīng)完成”重新運(yùn)行項(xiàng)目問(wèn)題卻還在。于是很多人開(kāi)始把問(wèn)題歸結(jié)為是不是模型不夠強(qiáng)但真正把Codex放進(jìn)工程環(huán)境以后會(huì)發(fā)現(xiàn)一個(gè)很重要的變化模型能力只決定Agent“有沒(méi)有可能知道怎么解決問(wèn)題”工程環(huán)境則決定它“能不能真的把問(wèn)題解決”。現(xiàn)在的Codex并不是一個(gè)只生成代碼片段的聊天窗口。它需要在真實(shí)文件、命令、測(cè)試和項(xiàng)目上下文之間連續(xù)執(zhí)行任務(wù)而OpenAI目前也通過(guò)Sandbox、Approval和權(quán)限規(guī)則限制Agent能夠訪問(wèn)哪些文件、網(wǎng)絡(luò)資源以及哪些動(dòng)作可以直接執(zhí)行。所以當(dāng)Codex修改代碼失敗時(shí)真正需要排查的已經(jīng)不是一個(gè)單點(diǎn)問(wèn)題。而是一整條執(zhí)行鏈理解任務(wù) ↓ 找到正確項(xiàng)目 ↓ 讀取相關(guān)文件 ↓ 獲得必要權(quán)限 ↓ 理解依賴(lài)和環(huán)境 ↓ 定位根因 ↓ 修改代碼 ↓ 運(yùn)行驗(yàn)證 ↓ 判斷是否完成只要其中任何一層出現(xiàn)問(wèn)題最終給人的感覺(jué)都可能是Codex“不好用”。但它們的根因完全不同。下面按照真實(shí)工程執(zhí)行順序把最常見(jiàn)的8類(lèi)問(wèn)題拆開(kāi)。一、第一步不是看Prompt而是確認(rèn)Codex到底站在哪個(gè)目錄很多Codex任務(wù)從一開(kāi)始就已經(jīng)錯(cuò)了。不是代碼錯(cuò)。而是工作目錄錯(cuò)了。假設(shè)真正需要處理的項(xiàng)目是D:\Projects\shop-web但當(dāng)前打開(kāi)的是D:\Projects這個(gè)目錄里還有shop-web shop-api admin-system demo legacy scripts此時(shí)你給Codex一句修復(fù)登錄按鈕點(diǎn)擊沒(méi)有反應(yīng)的問(wèn)題。人類(lèi)知道你正在做shop-web。Agent并不知道。它接下來(lái)只能先建立自己的項(xiàng)目地圖尋找Git倉(cāng)庫(kù) ↓ 查找package.json ↓ 搜索login關(guān)鍵詞 ↓ 判斷前端和后端關(guān)系 ↓ 識(shí)別哪個(gè)目錄才是當(dāng)前項(xiàng)目這就是很多人看到的Codex怎么一直在Search問(wèn)題甚至還沒(méi)有進(jìn)入Bug分析階段。Workspace不是越大越好傳統(tǒng)IDE里我們習(xí)慣直接打開(kāi)整個(gè)倉(cāng)庫(kù)。但對(duì)于Agent來(lái)說(shuō)可見(jiàn)范圍本身就是搜索空間。一個(gè)任務(wù)只和src/features/login相關(guān)卻讓Agent從一個(gè)包含幾萬(wàn)個(gè)文件的大型Monorepo開(kāi)始探索相當(dāng)于人為增加了大量不確定性。所以真正開(kāi)始任務(wù)之前先確認(rèn)三件事當(dāng)前項(xiàng)目是什么任務(wù)真正涉及哪個(gè)模塊哪些目錄根本沒(méi)有必要進(jìn)入上下文這不是Prompt技巧。這是最基礎(chǔ)的Context Boundary。二、第二個(gè)問(wèn)題Codex“知道怎么改”和“能夠改”不是一回事這是Agent和普通聊天模型最明顯的區(qū)別之一。假設(shè)Codex已經(jīng)分析出問(wèn)題位于auth.ts第87行。但接下來(lái)修改失敗。此時(shí)模型推理可能沒(méi)有任何問(wèn)題。真正失敗的是Execution。一個(gè)完整代碼任務(wù)至少可能涉及四種能力Read ↓ Write ↓ Execute ↓ Network而這四個(gè)能力不是天然等價(jià)的。能讀不能寫(xiě)Codex可以讀取文件解釋問(wèn)題給出Patch思路。但不能真正落盤(pán)修改。能寫(xiě)不能執(zhí)行Codex能夠修改auth.ts但不能運(yùn)行pnpm test最終結(jié)果就是代碼寫(xiě)完了但沒(méi)有驗(yàn)證。能執(zhí)行但不能聯(lián)網(wǎng)項(xiàng)目本身缺少某個(gè)依賴(lài)。Agent判斷需要下載。但網(wǎng)絡(luò)訪問(wèn)被限制。任務(wù)又會(huì)停下來(lái)。OpenAI目前把這兩個(gè)層次明確拆成Sandbox與ApprovalSandbox決定命令可以訪問(wèn)哪些文件和網(wǎng)絡(luò)資源而Approval決定某些動(dòng)作是否需要在執(zhí)行之前暫停并獲取進(jìn)一步批準(zhǔn)。因此看到Codex失敗以后最沒(méi)用的問(wèn)題之一就是為什么它沒(méi)做好更有效的問(wèn)法應(yīng)該是具體失敗在哪一個(gè)動(dòng)作ReadWriteExecuteNetwork還是Workspace邊界一旦把“任務(wù)失敗”拆成具體動(dòng)作問(wèn)題才開(kāi)始變得可診斷。三、第三個(gè)問(wèn)題Agent不是突然變笨了而是任務(wù)范圍漂移了這是復(fù)雜項(xiàng)目里非常典型的一種失敗。開(kāi)始時(shí)任務(wù)可能非常簡(jiǎn)單登錄按鈕點(diǎn)擊以后沒(méi)有請(qǐng)求API。第一次分析Codex檢查L(zhǎng)ogin.tsx沒(méi)有明顯問(wèn)題。然后檢查auth-api.ts接著發(fā)現(xiàn)認(rèn)證狀態(tài)可能有關(guān)。于是繼續(xù)讀auth-store.ts隨后看到Token初始化。繼續(xù)進(jìn)入router.ts middleware.ts config.ts到了后面一個(gè)原本只需要修改兩三個(gè)文件的問(wèn)題已經(jīng)演變成整個(gè)認(rèn)證系統(tǒng)分析。這就是Scope Drift。任務(wù)范圍正在自己向外擴(kuò)張。為什么Agent特別容易出現(xiàn)這種問(wèn)題因?yàn)锳gent的目標(biāo)通常是完成任務(wù)。只要它認(rèn)為某個(gè)新文件可能和問(wèn)題有關(guān)就存在繼續(xù)探索的理由。如果任務(wù)沒(méi)有邊界它最合理的行為就是不斷擴(kuò)大搜索范圍直到找到答案。但工程上這并不一定是我們想要的行為。因此一個(gè)成熟任務(wù)不能只有Goal。還應(yīng)該有Scope。例如當(dāng)前問(wèn)題登錄按鈕沒(méi)有發(fā)送請(qǐng)求。優(yōu)先檢查L(zhǎng)ogin.tsx和auth API。不進(jìn)行無(wú)關(guān)重構(gòu)。不升級(jí)依賴(lài)。如果根因位于范圍之外先說(shuō)明證據(jù)再擴(kuò)大檢查范圍。這幾句話真正解決的不是語(yǔ)言表達(dá)。而是限制Agent的決策空間。四、第四個(gè)問(wèn)題Codex正在修代碼但真正壞掉的是依賴(lài)真實(shí)項(xiàng)目里一個(gè)錯(cuò)誤信息通常不等于一個(gè)代碼Bug。例如Module not found看到這句話以后可以有很多可能。可能一import路徑錯(cuò)誤屬于Code Layer。可能二Package沒(méi)有安裝屬于Dependency Layer。可能三當(dāng)前運(yùn)行的不是正確環(huán)境屬于Environment Layer。如果沒(méi)有先區(qū)分這三層Agent非常容易進(jìn)入一個(gè)錯(cuò)誤循環(huán)測(cè)試失敗 ↓ 認(rèn)為源碼有問(wèn)題 ↓ 修改代碼 ↓ 繼續(xù)失敗 ↓ 繼續(xù)修改代碼但真正原因可能只是npm install沒(méi)有成功。或者Python虛擬環(huán)境根本沒(méi)有激活。再比如項(xiàng)目要求Node 22實(shí)際環(huán)境還是Node 18。這種情況下即使Codex重新寫(xiě)十次業(yè)務(wù)邏輯也無(wú)法從根本上解決運(yùn)行環(huán)境問(wèn)題。所以看到錯(cuò)誤以后我更建議先做一個(gè)非常簡(jiǎn)單的判斷代碼問(wèn)題 依賴(lài)問(wèn)題 環(huán)境問(wèn)題不要急著改代碼。一個(gè)非常重要的原則Error發(fā)生在代碼附近不代表Root Cause就在代碼里。這是人類(lèi)排查Bug時(shí)成立的原則。對(duì)Agent同樣成立。五、第五個(gè)問(wèn)題沒(méi)有BaselineCodex甚至無(wú)法證明自己有沒(méi)有修好這是Agent工程里非常容易被低估的一點(diǎn)。假設(shè)你告訴Codex修復(fù)當(dāng)前測(cè)試失敗。它修改代碼以后重新測(cè)試3 failed 126 passedCodex告訴你仍有3個(gè)測(cè)試失敗。問(wèn)題是修改之前是多少如果原來(lái)就是3 failed 126 passed那么至少可以說(shuō)明當(dāng)前修改沒(méi)有引入額外失敗。但如果原來(lái)是1 failed 128 passed那么這次修改實(shí)際上把項(xiàng)目變得更差了。這就是為什么Agent開(kāi)始動(dòng)代碼之前需要建立Baseline。也就是修改前狀態(tài)。例如Tests: 3 failed / 126 passed Lint: 2 warnings Build: success然后Agent執(zhí)行修改。完成以后再次運(yùn)行完全相同的驗(yàn)證Tests: 0 failed / 129 passed Lint: 2 warnings Build: success現(xiàn)在才有了真正意義上的Before / After。這時(shí)我們才能說(shuō)修改改善了項(xiàng)目狀態(tài)。否則“測(cè)試結(jié)果”只是一個(gè)孤立數(shù)字。Agent時(shí)代驗(yàn)證對(duì)象發(fā)生了變化傳統(tǒng)AI編程通常關(guān)注代碼生成得對(duì)不對(duì)Agent開(kāi)發(fā)進(jìn)一步需要關(guān)注系統(tǒng)狀態(tài)有沒(méi)有按照預(yù)期發(fā)生變化所以Baseline并不是測(cè)試流程里的小技巧。它實(shí)際上是Agent Verification的起點(diǎn)。六、第六個(gè)問(wèn)題任務(wù)越模糊Codex需要替你做的決策越多看一個(gè)非常常見(jiàn)的Prompt幫我優(yōu)化登錄模塊。這句話看起來(lái)沒(méi)什么問(wèn)題。但Agent真正執(zhí)行時(shí)會(huì)遇到大量未定義問(wèn)題“優(yōu)化”是指修Bug性能UI代碼結(jié)構(gòu)錯(cuò)誤處理狀態(tài)管理接口設(shè)計(jì)測(cè)試覆蓋如果用戶(hù)沒(méi)有定義Agent只能自己決定。最終很容易出現(xiàn)這種結(jié)果本來(lái)想改Login.tsx最后變成Login.tsx AuthService.ts router.ts store.ts api.ts types.ts package.json這時(shí)候用戶(hù)會(huì)覺(jué)得Codex怎么又亂改東西但從Agent角度看任務(wù)本身就允許它做這種判斷。一個(gè)工程任務(wù)至少應(yīng)該定義四件事Problem到底哪里有問(wèn)題。Scope應(yīng)該重點(diǎn)看哪里。Constraint哪些事情不要做。Done什么結(jié)果算完成。比如問(wèn)題 登錄按鈕點(diǎn)擊后沒(méi)有觸發(fā)API請(qǐng)求。 范圍 優(yōu)先檢查L(zhǎng)ogin.tsx和auth API。 限制 不升級(jí)依賴(lài)。 不修改數(shù)據(jù)庫(kù)。 不進(jìn)行無(wú)關(guān)重構(gòu)。 完成標(biāo)準(zhǔn) 請(qǐng)求恢復(fù)正常 相關(guān)測(cè)試通過(guò) 列出修改文件。它并不是什么“高級(jí)Prompt”。但它解決了一個(gè)非常關(guān)鍵的問(wèn)題把不該由Agent決定的事情提前決定掉。七、第七個(gè)問(wèn)題一個(gè)任務(wù)里塞太多目標(biāo)會(huì)讓因果關(guān)系越來(lái)越混亂Agent能做長(zhǎng)任務(wù)以后很多人自然開(kāi)始追求一次把事情全做完。例如修復(fù)登錄Bug同時(shí)升級(jí)依賴(lài)解決TypeScript錯(cuò)誤優(yōu)化認(rèn)證性能補(bǔ)測(cè)試再重構(gòu)一下公共模塊。表面上看是一個(gè)Task。實(shí)際上里面至少包含Bug Fix Dependency Upgrade Type Fix Performance Optimization Testing Refactor問(wèn)題不是Codex絕對(duì)完成不了。而是這些任務(wù)之間存在大量因果關(guān)系。例如升級(jí)依賴(lài) ↓ 產(chǎn)生新類(lèi)型錯(cuò)誤 ↓ 修改公共類(lèi)型 ↓ 原測(cè)試失效 ↓ 繼續(xù)修改測(cè)試最終當(dāng)項(xiàng)目出現(xiàn)新問(wèn)題時(shí)很難判斷到底是哪一個(gè)修改引入的這會(huì)讓調(diào)試成本急劇增加。正確的長(zhǎng)任務(wù)不是“大任務(wù)”而是階段化任務(wù)。例如階段1 修復(fù)登錄Bug ↓ 驗(yàn)證 ↓ 階段2 處理TypeScript錯(cuò)誤 ↓ 驗(yàn)證 ↓ 階段3 升級(jí)依賴(lài) ↓ 驗(yàn)證每個(gè)階段都形成自己的Input ↓ Change ↓ EvidenceOpenAI目前的Codex App也把不同Agent任務(wù)組織在獨(dú)立線程和項(xiàng)目中并支持直接查看Agent產(chǎn)生的修改和Diff這種產(chǎn)品形態(tài)本身就體現(xiàn)了任務(wù)隔離與審查的重要性。Agent能夠并行并不意味著所有目標(biāo)都應(yīng)該塞進(jìn)一個(gè)上下文。八、第八個(gè)問(wèn)題也是最重要的問(wèn)題Done到底是誰(shuí)定義的Codex最后可能輸出已完成。這句話非常容易讓人產(chǎn)生一個(gè)錯(cuò)覺(jué)任務(wù)已經(jīng)結(jié)束了。但實(shí)際上這里只能證明Agent認(rèn)為自己的執(zhí)行流程已經(jīng)結(jié)束。不能直接證明工程問(wèn)題已經(jīng)解決。這是兩個(gè)完全不同的判斷。一個(gè)可靠的任務(wù)閉環(huán)至少應(yīng)該是Reproduce ↓ Diagnose ↓ Modify ↓ Test ↓ Review Diff ↓ Verify其中任何一層缺失都可能出現(xiàn)“修改完成但任務(wù)沒(méi)有完成。”所以Codex說(shuō)Done以后我更關(guān)注四個(gè)問(wèn)題1. 改了什么具體哪些文件如果原本一個(gè)局部Bug卻修改15個(gè)文件需要重新檢查Scope。2. 為什么這樣改關(guān)鍵Diff必須能夠?qū)?yīng)到Root Cause。否則只是修改以后錯(cuò)誤暫時(shí)消失。這并不等于真正修復(fù)。3. 跑了什么驗(yàn)證不是已完成測(cè)試。而是具體執(zhí)行過(guò)pnpm test pytest pnpm lint npm run build中的哪些。4. 什么沒(méi)有驗(yàn)證例如數(shù)據(jù)庫(kù)沒(méi)有運(yùn)行缺少測(cè)試賬號(hào)第三方服務(wù)不可訪問(wèn)生產(chǎn)配置不可用。這些信息同樣屬于最終結(jié)果。OpenAI目前的Codex工作流支持在線程內(nèi)審查Agent修改、查看Diff以及繼續(xù)進(jìn)入編輯器做人工調(diào)整遠(yuǎn)程工作流中也會(huì)同步Terminal輸出、Diff、測(cè)試結(jié)果和審批狀態(tài)。這說(shuō)明Agent真正的交付物已經(jīng)不應(yīng)該只有Code。還應(yīng)該包括Evidence。把8個(gè)問(wèn)題放在一起會(huì)發(fā)現(xiàn)Codex失敗其實(shí)有四個(gè)層級(jí)如果把前面的排查重新歸類(lèi)會(huì)得到一個(gè)更清楚的結(jié)構(gòu)。第一層Environment包括目錄、Workspace、依賴(lài)、Runtime環(huán)境。它解決的是Agent有沒(méi)有站在正確的地方工作第二層Permission包括Read、Write、Execute、Network。它解決的是Agent有沒(méi)有能力完成需要執(zhí)行的動(dòng)作第三層Task包括目標(biāo)、范圍、限制、任務(wù)拆分。它解決的是Agent到底應(yīng)該做什么以及不應(yīng)該做什么第四層Verification包括Baseline、Test、Diff、Evidence。它解決的是怎么證明Agent真的完成了任務(wù)最終就形成了一條非常清楚的鏈Environment ↓ Permission ↓ Task ↓ Verification很多所謂的Codex能力不夠。其實(shí)真正失敗的可能只是其中某一層。為什么排查順序非常重要假設(shè)目錄本身就錯(cuò)了。你卻開(kāi)始優(yōu)化Prompt。沒(méi)有意義。假設(shè)依賴(lài)沒(méi)有安裝。你卻讓Codex連續(xù)重寫(xiě)業(yè)務(wù)代碼。只會(huì)越改越復(fù)雜。假設(shè)任務(wù)范圍沒(méi)有定義。你卻給它更大的權(quán)限。Agent只會(huì)探索得更遠(yuǎn)。所以我更建議以后直接使用下面這個(gè)順序① 當(dāng)前目錄正確嗎 ↓ ② Workspace范圍合理嗎 ↓ ③ Read / Write / Execute正常嗎 ↓ ④ 依賴(lài)完整嗎 ↓ ⑤ Runtime環(huán)境正常嗎 ↓ ⑥ Task Boundary明確嗎 ↓ ⑦ 修改前有Baseline嗎 ↓ ⑧ 修改后有Evidence嗎這個(gè)順序的價(jià)值就在于先排除基礎(chǔ)層再進(jìn)入智能層。而不是一出現(xiàn)失敗就把所有問(wèn)題歸因于模型。一個(gè)更適合Codex的Bug任務(wù)結(jié)構(gòu)真正使用時(shí)可以把任務(wù)整理成下面這種形式【問(wèn)題】 登錄按鈕點(diǎn)擊后沒(méi)有發(fā)送API請(qǐng)求。 【檢查范圍】 src/login src/api/auth.ts 相關(guān)測(cè)試 【禁止事項(xiàng)】 不要升級(jí)依賴(lài)。 不要修改數(shù)據(jù)庫(kù)Schema。 不要重構(gòu)無(wú)關(guān)模塊。 【執(zhí)行順序】 1. 先復(fù)現(xiàn)問(wèn)題 2. 定位Root Cause 3. 說(shuō)明準(zhǔn)備修改的位置 4. 完成代碼修改 5. 運(yùn)行相關(guān)測(cè)試 6. 檢查Diff。 【完成標(biāo)準(zhǔn)】 輸出 - 根因 - 修改文件 - 關(guān)鍵Diff - 執(zhí)行過(guò)的測(cè)試 - 測(cè)試結(jié)果 - 未驗(yàn)證部分。這里真正重要的并不是格式。而是六個(gè)詞Problem Scope Constraint Action Verification Evidence當(dāng)這六件事逐漸固定以后Agent的工作方式才會(huì)從嘗試幫你解決問(wèn)題。變成按照工程協(xié)議完成任務(wù)。從“代碼生成”到“工程執(zhí)行”開(kāi)發(fā)者真正要學(xué)的東西已經(jīng)變了AI編程剛開(kāi)始普及時(shí)大家主要比較哪個(gè)模型寫(xiě)代碼更強(qiáng)誰(shuí)生成函數(shù)更準(zhǔn)確誰(shuí)補(bǔ)全更快但Agent真正進(jìn)入項(xiàng)目以后問(wèn)題已經(jīng)發(fā)生變化。因?yàn)楝F(xiàn)在決定最終結(jié)果的不只有Model Intelligence。還有Execution Environment。Permission Boundary。Task Design。Verification System。所以未來(lái)真正拉開(kāi)Codex使用差距的很可能不是誰(shuí)會(huì)寫(xiě)更復(fù)雜的Prompt。而是誰(shuí)能建立一套更穩(wěn)定的Agent工程體系。讓Agent知道從哪里開(kāi)始。允許做到哪里。哪些事情不要做。什么狀態(tài)才算完成。完成以后拿什么證明。當(dāng)這些條件建立以后Codex才真正從“會(huì)幫你寫(xiě)代碼的AI”變成“能夠參與工程執(zhí)行的Agent”。而當(dāng)Codex再次告訴你Done。你真正應(yīng)該關(guān)注的也不再是這一句話。而是它后面有沒(méi)有一條完整的Task ↓ Change ↓ Test ↓ Evidence這才是真正可靠的完成。當(dāng)目錄、權(quán)限、依賴(lài)和驗(yàn)證流程都處理好以后Codex仍然可能出現(xiàn)另一類(lèi)問(wèn)題任務(wù)越長(zhǎng)越容易偏離最初目標(biāo)。這時(shí)候問(wèn)題已經(jīng)不再是環(huán)境或權(quán)限而是上下文污染、任務(wù)狀態(tài)丟失和階段性驗(yàn)證不足。下一步真正需要解決的是如何讓Agent在長(zhǎng)任務(wù)中持續(xù)保持目標(biāo)一致。