「我們的代理指令檔老是對不起來」,順著想下來的解法就是別再維護副本。寫好 AGENTS.md,從它產生 CLAUDE.md、.cursor/rules/*.mdc 和 .github/copilot-instructions.md,收工。
這個我做了。它會動。它同時也沒有解決問題,而這兩句話之間的落差值得攤開講清楚,因為我也是在真的機器上跑過之後才看見的。
python3 agent_fanout.py
create CLAUDE.md
create .cursor/rules/from-agents-md.mdc
create .github/copilot-instructions.md
一個來源,數個衍生檔,每一份最上面都帶一行標頭,免得有人不小心就改了衍生副本:
<!-- Generated from AGENTS.md by agent-fanout. Do not edit this file. -->
把它加進 CI,直接去改 CLAUDE.md 的那個 PR 就會讓建置亮紅燈:
- run: python3 agent_fanout.py . --check
這涵蓋的是這個儲存庫,而且是在有跑 CI 的那些機器上。聽起來像是全都涵蓋了,直到你把它沒涵蓋的部分列出來。
其他儲存庫。 你的團隊不只一個儲存庫。每個都有自己的 AGENTS.md,而它們在某個時間點是彼此複製貼上來的。產生讓每個儲存庫內部保持一致,儲存庫與儲存庫之間照樣愈走愈遠。
全域設定。 Claude Code 除了專案檔之外還會讀 ~/.claude/CLAUDE.md。Cursor 有使用者層級的規則。這些都在所有儲存庫之外,永遠不會進 CI,而它們剛好就是大家安放「不想在 code review 上爭論的那條規則」的位置。
兩次產生之間的空窗。 產生只發生在有人去跑它的時候。從那一刻到下一次 CI 之間,衍生檔可以被改、被用。代理程式立刻就讀到了;CI 要等推送才會發現,前提是有人推送。
沒有 CI 的儲存庫。 雛形、隨手複製的副本、某人上禮拜二開的那個儲存庫。指令在這些地方被改得最隨意,而它們一道關卡也沒有。
是機器,不是儲存庫。 跑代理程式的單位是一台筆電。一台筆電裡有很多簽出目錄、同一個儲存庫的好幾份副本,還有一個家目錄。任何以儲存庫為單位運作的東西都看不到這一層。
預防是制度。偵測是量測。制度會以你看不見的方式被繞過去,除非你去量。
python3 agent_drift.py ~/work ~/side-projects
Scanned 47 instruction files.
DRIFT: 2 documents, 5 distinct versions between them.
claude-code:CLAUDE.md
6 copies, 3 versions
9b01aeaa204d 3 files, 406 lines
2dc3c616c279 2 files, 411 lines
這一支掃的是路徑而不是儲存庫,分組依據是內容相似度而不是檔名 —— 兩個互不相干的專案有不一樣的 CLAUDE.md,那不是偏移,把它報成偏移會讓整份輸出變成廢紙。(把這套分組做對花了三次改寫,同一個 CLAUDE.md,在一台筆電裡有 18 個版本。)
拿整個工作目錄去跑,不要只跑一個專案。有價值的結果都是跨儲存庫的那些。
| 範圍 | 回答 | |
|---|---|---|
agent-fanout | 單一儲存庫 | 「衍生檔是不是最新的?」 |
agent-drift | 整台機器、多條路徑 | 「副本到底是在哪裡分家的?」 |
產生消掉的是副本存在的*理由*。偵測抓的是那些因為你沒預料到的理由而存在的副本。兩者都不多餘,只做前面那一件會給你一種已經蓋住的錯覺 —— 這才是我真正想提醒的失效模式,因為我自己就踩進去過。
兩支都是單檔 Python,沒有相依套件,該唯讀的地方唯讀,MIT:
AGENTS.md 只寫一次,其餘產生出來誠實的終點是這兩支腳本都不必要,因為資產根本不再是躺在一台台筆電上的檔案 —— 它們放在同一個審過的地方,不需要任何人複製就抵達每一台機器。這就是我在 untactit 做的事,目前尚未正式發表。
這兩支腳本本身就站得住,不靠它。用得上的就用,其餘的不必理會。