白色卡片上的赭紅色 Claude 星形標誌與黑色字樣

用 mattpocock-skills 以三項測試進行修剪 — 讓 Fowler 異味與 codebase-design 保持分開

Matt Pocock 的 mattpocock-skills 外掛教的是如何為智能體寫作:修剪、密集詞彙,以及為什麼 Fowler 異味與 Ousterhout 模組要分開。

本頁目錄

引言

看了一支關於 Matt Pocock 的 mattpocock-skills 外掛的影片。這個外掛以一個叫 grill-me 的 skill 為核心,影片形容它是 5 行指令,會在 agent 寫任何程式碼之前逼你把計畫講清楚。這個 repo 早在之前的 session 就裝了這個外掛,比起重複影片的摘要,更想搞清楚那種精簡背後的真正準則,所以要求做一次老實的稽核:到底有沒有真的在用 mattpocock-skills,還是只設定好就晾在那沒動。讀了外掛自己的原始碼之後,影片裡兩個具體的說法在被複述到任何地方之前就先被糾正了。grill-me 是包住一個 28 行基礎單元的 7 行包裝,而 Fowler《Refactoring》的程式碼異味其實住在跟大多數人猜的不一樣的 skill 裡。而且,把這套準則真正拿來用而不只是讀過,在加入的同一個 session 裡就在自己的審查流程中抓到一個真的 bug。這篇文章要講的是外掛自己的 meta-skill、writing-for-agents,實際上怎麼談修剪與密集詞彙,接著談它內建的兩套獨立程式碼異味系統,以及為什麼要把它們分開,而且是逐檔案對照原始碼確認的,不是對照一份改寫過的摘要。

影片的說法,以及為什麼需要驗證

影片的框架具體到可以直接驗證grill-me 用 5 行程式向使用者逼問計畫,而 Fowler 的程式碼異味詞彙(Shotgun Surgery、Feature Envy、Data Clumps)住在外掛的 codebase-design skill 裡,也就是講深層模組設計的那個。兩個說法乍看都很合理。5 行符合一個強硬的系統提示詞可以有多短,而 codebase-design 聽起來就正是那種會定義「爛程式碼長什麼樣」的 skill。

兩個說法讀了外掛自己的 repo 之後都站不住腳。 這些 skill 分散在 5 個目錄裡,不是 1 個:engineeringproductivityin-progressmiscdeprecatedgrill-me 確實只有 7 行,但它住在 productivity/ 底下,而它實際的內容只有一個指令:呼叫 Skill 工具去執行 grilling,一個真正負責逼問的 28 行 skill。codebase-design 也真實存在,也確實定義了一套詞彙,但跟 Fowler 的完全是另一套。

撐起這種精簡文字的準則

這個外掛內建了一個叫 writing-for-agents 的 meta-skill,整篇主題就是怎麼寫剩下的 24 個 skill。它點出三個各自獨立的槓桿,沒有一個是「越短越好」。

no-op 測試

一句話能留下來,唯一的理由是它會改變模型的預設行為。 用 skill 自己的說法:這句話會改變模型的動作,還是只是把模型反正都會做的事重講一遍?沒通過這個問題的句子會直接整句刪掉,而不是修短,因為把一個預設值的重述縮短,它仍然只是重述。

leading word 這個概念

詞彙也不是裝飾。這個 skill 把一個緊湊、已經預訓練過的概念叫做「leading word」:一個模型從自己的訓練裡已經帶著分散式定義的 token,拿來重複使用,取代一整段說明。grilling 本文之所以幾乎沒花什麼字講機制,正是因為它完全是用三個這樣的 leading word 組出來的。「design tree」「frontier」「rounds」各自頂替一句說明,讀者一旦記住這個詞,之後每次使用就只花一個字,而不是十個字。

資訊層級

長度本身也不是敵人,而這正是「grill-me 只有 5 行」這種說法漏掉的部分。這個 skill 定義了一套層級:in-file step 是 agent 依序執行的動作,in-file reference 是同一份檔案裡按需查閱的內容,disclosed reference 則被移到旁支檔案、藏在一個指標背後,只有那個指標觸發時才會載入。

資訊層級:什麼留在檔案內,什麼移到指標背後三層由上而下疊放:一律載入、依序執行的 in-file step;同一檔案內、按需查閱的 in-file reference;被移到旁支檔案、只有指標觸發才載入的 disclosed reference。左側量表顯示由上而下常駐載入的比重逐漸縮小。常駐載入比重1in-file step代理程式的操作步驟。每一輪都一定載入。2in-file reference定義與規則。需要時才查閱。3disclosed reference移到旁支檔案。只有指標觸發才載入。
writing-for-agents 的資訊層級:什麼留在檔案內,什麼移到指標背後。

codebase-design 加上完整的詞彙表和 ASCII 圖總共 114 行,但用這個測試來看沒有一行是灌水,因為每一行都是讀者真正需要按需查閱的參考資料,而不是重述預設值。一個 15 行的 skill 和一個 114 行的 skill 都可能被正確地修剪;這套準則實際衡量的是層級結構,不是行數。

兩套刻意分開的程式碼異味詞彙

code-review 的異味清單是 Fowler《Refactoring》裡那份的 12 項版本:Mysterious Name、Duplicated Code、Feature Envy、Data Clumps、Primitive Obsession、Repeated Switches、Shotgun Surgery、Divergent Change、Speculative Generality、Message Chains、Middle Man、Refused Bequest。每一項都用同樣的方式寫:它是什麼、怎麼修。而且兩者都受清單上方只寫過一次的兩條規則約束:文件化的 repo 標準優先於這份基準,而且每一項異味都只是一個貼了標籤的判斷依據,從來不是硬性違規。

codebase-design 完全不用那套詞彙。它的詞彙表定義了 Module、Interface、Implementation、Depth、Seam(歸功於 Michael Feathers)、Adapter、Leverage、Locality,每一項都附有明確警告,提醒不要用大多數人會直覺選用的替代詞:Module 不要用「unit」或「service」,Interface 不要用「API」或「signature」,Seam 不要用「boundary」。一個深層模組把小小的 Interface 配上大量的 Implementation;淺層模組則相反,skill 說要避免這種情況。它還指名自己的出處,然後又部分否定了它。 Ousterhout 原本的指標是用 Implementation 行數對 Interface 行數的比例來衡量 Depth,而這個 skill 指名批評這種框架,因為它會獎勵灌水 Implementation,改用「depth as leverage」來衡量。

兩套刻意分開的程式碼異味詞彙左右兩張卡片,以「≠」分隔號並排。左邊是 code-review 的 Fowler 異味清單:Mysterious Name、Duplicated Code、Feature Envy、Data Clumps、Shotgun Surgery,另外還有 7 項。受兩條規則約束:文件化的專案標準優先於此基準,且每一項異味都只是判斷依據而非強制違規。右邊是 codebase-design 的 Ousterhout 系詞彙:Module、Interface、Implementation、Depth、Seam、Adapter、Leverage、Locality,以「深度即槓桿」衡量,而非 Ousterhout 本人「實作行數對介面行數」的比例指標。右卡下方的註記顯示,這套詞彙也被 improve-codebase-architecture 重複使用;左邊那套則沒有被其他任何地方重複使用。code-reviewFowler 異味基準• Mysterious Name• Duplicated Code• Feature Envy• Data Clumps• Shotgun Surgery• 另外 7 項專案標準優先於基準每項異味都是判斷依據codebase-designOusterhout 系詞彙• Module• Interface• Implementation• Depth• Seam• Adapter• Leverage• Locality深度 = 槓桿而非 Ousterhout 本人的比例詞彙不共用improve-codebase-architecture 也會使用
Fowler 異味與 Ousterhout 系模組:兩套從未合併的詞彙。

這個外掛裡,兩套詞彙彼此完全沒有互相借用。負責跑程式碼庫健康掃描的 improve-codebase-architecture,被明確要求使用 codebase-design 的用詞,不要滑向「component」「service」「API」或「boundary」。這兩套系統解決的是不同問題:一套是指出一份 diff 已經出了什麼錯,另一套是在任何東西出錯之前,指出該怎麼形塑一個 Interface。 像一份簡短摘要那樣把兩者塌縮成一份清單,會丟失各自存在的理由。

真正拿這套準則來用,而不只是講它

讀懂這套準則是一回事,實際拿來用是另一回事,而這個 repo 早就有地方可以用它。它自己的發布流程在每次合併前都會跑一次程式碼審查,對照的是一份 repo 專屬的檢查清單,但那份清單從來沒有一個通用的程式碼異味面向。agent 提出的方案,是把 code-review 的 12 項基準連同出處一起,折進既有的檢查清單裡當作第 8 個面向,而不是把外掛的 code-review skill 當成第二個獨立的審查再跑一遍。 同一份 diff 跑出兩份審查結果,代表每次發布都要協調兩者之間的分歧;一份檢查清單多一個面向則不必。

同一次造訪也順手補上了這個 repo 一直沒有的領域詞彙表。要求立刻播下種子而不是延後,詞彙從實際的程式碼裡挑(ArticleLocaleFigureThumbnail,另外還有 8 個),而不是憑空發明,每一個都在寫下來之前,對照實際定義它的檔案確認過。

修剪測試也用在了這個 repo 自己的文字上,範圍比全面重寫窄得多。選擇只對這個 repo 裡最長的兩份 skill 檔案跑 no-op 測試和 leading word 測試,把每一段事件敘述和防護表格原封不動地留著:writing-for-agents 自己就說,那種素材是合理的快取:「沒寫下來的慣例、一個選擇背後的理由、沒有任何設定會招認的坑」,不是該修剪的灌水。

改變了什麼,又是怎麼驗證的

這次對 repo 自己 skill 跑的修剪流程,與其說找到了灌水,不如說更確認了這套準則本身。 把 no-op 測試和 leading word 測試套用到一份 988 行的發布 skill 和一份 609 行的風格指南上,各自只找到 3 處合理的刪減,不是幾十處。這是設計上就該低的數字,不是漏抓:兩份檔案的長度大半是從真實發布中學到的事件記憶、精確指令、精確的失敗模式,而這正是 writing-for-agents 自己的規則會保護、而不是標記出來的東西。

新加的審查面向,第一次真正的考驗來自一份真的動到程式碼、而不只是文件的 diff,而它真的抓到了東西。在把一段重複的 fence 解析程式合併成一個共用函式的過程中,收尾規則悄悄變鬆了:它改成只比對 fence 標記開頭的文字,而不是要求整行都只能是 fence 字元(也就是相鄰函式原本就在用的那條更嚴格的規則),而且這個新函式自己的註解還宣稱它共用了那條規則,實際上並沒有。剛加進去的第 8 個面向,在這次 pull request 合併之前就抓到了它。 這個 bug 是真的,當時在語料庫裡是潛伏而非已經發作,並在引入它的同一個 session 裡修好。這份異味基準不是檢查清單上的裝飾,它改變了實際被審查的內容。

總結

Matt Pocock 的 mattpocock-skills 外掛之所以精簡,是因為裡面的 meta-skill 精確定義了該刪什麼、該留什麼: 重述過的預設句被刪掉,密集的預訓練詞彙取代說明,參考資料被移到指標背後,而不是內嵌。它內建的程式碼異味詞彙不是一套,而是兩套: Fowler 那套,用在一份已經出錯的 diff 上;修改過的 Ousterhout 那套,用在還沒出錯之前形塑一個 Interface。那支影片把詞彙和行數這兩種區別都混在一起,而讀原始碼、不讀摘要,正是抓出這一點的方法。真正把這套準則用起來,把一套詞彙折進既有審查裡,而不是另外跑一個工具,讓這個差異變得看得見:它改動的那份檢查清單,在那次發布上線之前,抓到了自己內部的一個 bug。

分享這篇文章