Anthropic 說可以信任模型判斷,我的 `CLAUDE.md` 分三層來實踐
Anthropic 說可以信任模型判斷,我的 CLAUDE.md 分三層來實踐
Anthropic 最近那篇 The new rules of context engineering for Claude 5 generation models,講了六組「以前怎麼做/現在怎麼做」的對照:以前寫死規則,現在讓模型自己判斷;以前給範例,現在設計更有表達力的介面;以前把所有東西塞在最前面,現在漸進式揭露;以前靠重複強調,現在把指示寫進 tool description 就好;以前靠 CLAUDE.md 存記憶,現在讓模型自動記憶;以前給簡單的 spec,現在可以給測試套件、rubric 這種更豐富的參考資料。合起來就是他們把 Claude Code 的系統提示詞砍掉超過 80%,沒有看到可量測的品質下降。
很多人將這篇文章解讀成「模型變強了,規則不重要了」,然後教你怎麼去改你的 Claude.md 。
但我覺得不是這樣,文章裡有一句話我更在意:
[!note] Claude 現在有 memory、artifacts、skills 可以用,代表它自己就能決定怎麼在 session 之間載入和分享 context。
換句話說,不是規則變少,是「誰來決定什麼時候載入什麼」這件事,從寫 prompt 的人手上,轉移到模型自己手上。這個轉移本身沒問題,但轉移的前提是 Opus, Sonnet, Haiku 等模型有能力能接得住,不然還是只能靠 Fable 打天下。
我過去這半年一直在整理 Claude Code 的規則系統,踩了一堆坑,拷貝了很多人的 Skill 回來學習和改善,但還是常常出現(特別是最近 Opus 好像變笨了):模型跳過檢查、patch 重套、editable install 掩蓋發布問題、批次作業差點因為輸入範圍判斷錯誤整批重跑。
每次出事,最自然的反應都是「加一條規則,以後別再犯」。久了,CLAUDE.md 就越來越像事故現場的紀念碑,測試規定、Git 規定、安全規定、除錯流程、commit 前檢查、指令目錄、工具使用習慣,什麼都有,理論上很完整,但我愈來愈不確定模型到底有沒有真的抓住優先順序,還是每次 session 都背著一大包文字,偶爾從裡面撿到一兩條。
我不再按主題整理規則
以前比較直覺的分類法,是把測試規則放一起、Git 規則放一起、安全規則放一起。人看文件時很合理,但模型不是照文件目錄在工作,它面對的是當下的 context:有些東西永遠在、有些需要時才載入、有些得等它自己想到才會去找,這三種規則失效的方式不一樣。
我現在大致規劃如下:
| 層級 | 位置 | 怎麼出現 | 放什麼 |
|---|---|---|---|
| 憲法層 | 全域 CLAUDE.md | 每個 session 都在 | 行為底線、不可自行跨越的界線 |
| 深度層 | 專案記憶/文件目錄 | 先讀索引,需要時展開 | 事故背景、案例、架構理由、過去決策 |
| 機械層 | Commands / skills | 任務觸發才載入 | 發布、commit、批次處理等工作流程 |
分這三層不是為了看起來有架構,是因為我發現,如果模型已經偏離基本紀律,它通常不會主動去載入一份叫做「紀律」的文件。不要假裝執行過測試、不要把推測寫成已驗證事實、碰到正式資料或不可逆操作先確認影響範圍、一個關卡沒過不要自己找理由跳過,這些不應該是有空再查的,而是每次都該做的事。但「發布 Python wheel 前要檢查產物內容」這種有明確觸發時機的東西,放在對應 command 裡更合理,不需要每次對話都佔位子。
Anthropic Blog 說的是 progressive disclosure,我後來比較習慣用另一種方式想:不要先問這條規則屬於測試、Git 還是安全,先理解若這條規則沒有被執行,或是他會在什麼時候失效。
信任模型判斷這件事,不是所有模型都能照抄
這大概是原文最容易被誤用的地方。Claude Fable5 能少一點鉅細靡遺的規定,是因為它有足夠能力理解原則、辨認例外、自己補齊中間推理。但日常工作流裡不會只用一個最強模型,其他小型模型拿來做整理、轉換、簡單修補、跑檢查,中階模型可能負責某些子代理審查,比較難的設計、除錯、跨檔案推理才交給最強的模型,這些模型不能用同樣的規則來限制,會很慘(對,因為我的 Opus 就幾乎每天翻車,白眼)。
小模型常見的問題不是完全做不出來,而是卡住後還會硬往前走,把不確定性包裝成一句看似合理的話:「這應該沒問題」、「這個檢查先略過」、「雖然測試沒跑完,但改動不大」。這種時候給它更多抽象原則通常沒什麼用,它需要的是欄杆。
原文另外兩組對照剛好給了我所謂的邊界該長什麼樣子的線索。一組是用設計更有表達力的介面取代範例教學,文章舉的例子是 Todo 工具把狀態設計成 pending / in_progress / completed 這種 enum,模型光看參數就知道該怎麼用,不需要額外範例。另一組是用測試套件、rubric 這種可驗證的參考資料取代簡單的文字 spec。這兩件事合起來,其實就是我對其他模型的做法:不是寫更多說明文字讓它「理解」,是把驗收條件做成它可以直接對照的東西:狀態式的參數設計、跑得出結果的測試、grep 有沒有零命中等,這些不需要判斷力,只需要照著核對就好。
所以我的做法是憲法層儘量精簡,但機械層維持原狀。對需要由小模型執行的 command,我會把失敗處理寫得很硬:同一關卡修兩次還過不了,就停下來,附上已執行的指令、錯誤輸出、嘗試過的修法再升級,也不是因為第三次一定不會成功之類的,而是這時候多半缺的不是再亂槍打鳥的不斷測試,而是需要新的資訊。
然後最忌諱的是,不能只靠一句「請仔細分析」就希望模型很聰明的執行完,要列假設、找證據、逐項排除,不要留一個很大的自行思考空間然後期待它剛好走到正確答案,這和擲筊差不多。而 grep 有沒有零命中、測試有沒有全綠、輸入數量和輸出數量對不對得起來,這些能用機器判定的事,就不該完全單純的相信模型說「我檢查過了」就了事。
強模型需要留有邊界,讓它處理例外和整合;弱模型需要收斂,讓它在清楚的輸入、步驟與驗收條件下完成事情。原文說的「設計介面」跟「豐富參考資料」,某種程度上就是把「邊界」轉換成「收斂」的具體工具。
CLAUDE.md 不是維基
這是另一個這兩年我常犯的錯誤,我常常讓全域規則越來越大,每次翻車就加一條,久了以後原本應該是幾條高優先原則的 CLAUDE.md,變成把所有歷史事故都寫進去的操作手冊和維基百科,這邊很尷尬的是,就算定期整裡,但還是很容易不小心就無性增值的沒完沒了。
我現在給它一條有點殘酷的規則:想加東西進去,就得同時刪掉、合併,或壓縮別的東西,不是硬做字數守恆,而是逼自己檢視一遍:這條新規則到底要取代什麼?它重要到應該擠進每個 session,還是其實只適合出現在某個固定任務裡?
照這個原則整理了一次,把生命週期流程圖搬出全域檔案、刪掉指令目錄(工具本來就會注入可用指令清單)、把 commit 前自審清單移到 commit command、把除錯升級表移到實際發生故障時才會用到的流程。最後 CLAUDE.md 少了約 47%,然後再開新的 Session 用 /doctor 跑了一次檢查確認。
不是說少了約 47% 一定比較好,這部分我還在累積經驗,不然「砍 prompt 以後感覺比較聰明」也很容易只是另一種確認偏誤 XD
規則最好帶著傷疤
我現在也比較少從最佳實踐清單或結果直接抄成功典範規則,不是說最佳實踐沒價值,模型其實越來越聰明,單看成功點費的組合規則經驗,很多規則會變得越來越像儀式,但問題是,怎麼錯的其實不太知道,要事後檢查,所以現在重要的 command 或規則,我會盡量留下它的來源,哪一次失敗讓它出現、當時的防線為什麼沒擋住、它到底在防哪一種錯。
然後我個人非常熱愛搭配 Spectra 結合 Sequential Thinking (雖然有人覺得 Sequential Thinking 沒有使用價值了,但我覺得善用它,會讓思維和程式品質都有客觀的提升),我在規劃開始後,都會至少留下三輪檢查:先驗證實際配置不只看預設值、用 dry-run 確認它眼中的輸入宇宙、在正式開工前就想好中斷續跑與去重策略。
還有一條是閾值。只要系統要用某個分數或門檻自動決定結果,我現在都要求先拿正例和最靠近界線的反例來重新測量,如果兩邊沒有分離帶,先不要急著調,因為那通常不是閾值選得不好,是拿來判斷的特徵根本不夠有辨識力。也因此補了一條很土但很實際的規定:量測工具要盡量用真實實作本體,一個簡化版模擬器看起來很快,但它若和真正引擎在同一份資料上給出相反結論,後面的精密調參只是往錯的方向跑得更快。
這些慘痛經驗不需要全部放在全域 prompt 裡,但該被留下來(我有用 Github 搭配,每次 commit 都會有軌跡留下),幫助我以後要修改規則時,知道這不是手賤隨手加上的 :P
Commit 前檢查,是一個很典型的搬遷案例
現在的 commit 檢查大致有:跑 linter 檢查 patch 重套後留下的重複定義、看 git status 確保 .rej、.orig、備份檔沒有混進去、重構後搜尋舊 helper 確認新舊邏輯沒有並存、看相似呼叫端的錯誤處理是否一致、跑一份小型安全檢查(secret 比對、環境變數白名單、數字上下界與 URL 驗證)、把 staged diff 從頭讀完不只看 --stat。其中有些還是需要人來判斷,例如兩個呼叫端的錯誤語意算不算一致;但只要可以客觀驗證的地方,我就盡量不讓模型用一句「已檢查」帶過,因為模型最危險的時候,常常不是不知道,而是很自然地假設自己知道。
Context engineering,最後其實是治理問題
我現在也不太把 context engineering 理解成「提示詞怎麼寫比較強」,提示詞當然重要,但真正難的事情是:你要決定哪些規則值得常駐、哪些只該在任務發生時出現、哪些要留下事故背景、哪些應該變成測試或工具、哪些則該被刪掉。
這和維護一個逐漸變大的程式碼庫很像。程式碼不是越多越完整,規則也不是越多越可靠,你需要邊界、需要模組、需要知道依賴從哪裡來,也需要定期刪掉已經不再服務任何風險的東西。
Anthropic 把系統提示詞砍掉 80%,是因為不同規則不該全部擠在同一個 context 裡;但這件事是否適合每個 agent 都照抄,還是要取決於你真正的工作流,我這次重新檢視後,暫時得到的結論是:每條規則都該知道自己為什麼存在、該在哪裡生效、什麼時候可以退場,而且這個答案,對強模型跟弱模型可能完全不同。
兩個常被問到的問題
既然模型能自己判斷,為什麼還要留 CLAUDE.md?
因為「模型有能力判斷」和「每一次都會在正確時機判斷」不是同一件事。專案記憶、搜尋、RAG 都很適合存放架構背景、事故紀錄、歷史決策,但前提是模型得先知道自己需要查,而且查得到正確的東西。底線不該依賴這件事。「不能假稱已執行測試」「動正式資料前先確認範圍」這類規則仍然該常駐,不是因為模型完全不懂,是因為不該把這種風險押在一次檢索有沒有剛好命中。Memory 比較像檔案室,全域原則比較像門口的規定,兩個都重要,但不能互相取代。
Default-deny 不會讓模型動不動就卡住,最後都要人接手嗎?
會,所以 default-deny 不能只寫成「失敗就停止」。如果模型停下來時只留一句「無法完成」,等於把問題丟回給人。真正有用的停止至少要留下:卡在哪個關卡、跑過哪些檢查、哪些輸出不符合預期、已經試過哪些修復、為什麼接下來需要新資訊或人工判斷。這樣人接手時不是從零開始。而且如果某個 command 很常把模型卡住,第一個該懷疑的通常不是模型夠不夠聰明,而是流程設計得不夠好,驗收條件不清楚、可觀測性不足、任務拆得太大。Default-deny 不是要製造停滯,是阻止模型在證據不足時硬闖。