GPT 面向 SHA256 編程:從沒人要的雜湊到自建契約的循環自證

我一直覺得 GPT 在面向 SHA256 編程:寫起程式動不動就要算雜湊、對指紋,防禦心態重到像在防全世界。我自己平常大量使用 GPT 模型,乾脆就在自己的 harness 裡實際量量看,確認這到底是不是我的錯覺。

在系列前作裡,我聊過需求層面的過度設計。這次碰到的,則是同一種過度設計怎麼一路蔓延到防禦機制。

雜湊在 harness 裡到底在算什麼

雜湊說穿了就是檔案內容的指紋,只要改動一個字元,整串指紋就全變了。回頭翻我的 harness,這東西主要被塞在四種地方:

  1. 許可綁定:許可是允許 agent 執行動作的憑證檔,上面記著目標內容的指紋,內容一改許可就失效。
  2. 收據指紋:任務跑完留下紀錄,順便把產物指紋存進收據當憑證。
  3. 清單記錄:在清單裡逐個檔案記下指紋。
  4. 自產物回驗:剛寫完一個檔案,馬上回頭重新計算指紋來驗證有沒有寫錯。

舉個實際例子:有一支設定金鑰的腳本,把金鑰相關的每個檔案都算過一次指紋再寫進收據,一層疊一層,看起來滴水不漏。結果後續審計一查,這整段被判定完全沒人要求,從頭到尾根本沒人叫它算這些。

把既有的程式碼翻出來量一次

為了搞清楚狀況,我請 AI 把既有的程式碼徹底清點一遍。多個 Opus agent 從我過去所有的 session 紀錄裡,撈出模型實際寫進檔案的程式碼,整理出一千兩百多筆雜湊片段。接著由 Opus 在看不到作者是哪家模型的情況下,盲審判定每一筆「到底有沒有人要求寫」;判定的依據,就是寫下那段程式碼之前最近的一句 prompt。

這裡先說好,以下比例全是第一輪初判的結果。GPT 寫出來的雜湊片段裡,有 41% 被判定完全沒人要求;拿來當對照組的 Claude,這個比例只有 13%。當然這是我自己的 harness、自訂規則與 prompt 下跑出來的數據,不能直接當成兩家模型的通用結論。

而且防禦過重的分佈非常集中。兩家寫 try-catch 的量其實差不多,但在雜湊與完整性驗證這件事上,GPT 寫的量是 Claude 的好幾倍。在這些紀錄裡,GPT 常常替每道檔案寫入與流程關卡多蓋一座指紋哨站 🤣

坐在哪個位置,差很多

這裡需要先分清楚兩個位置。主控席指的是直接跟我對話、負責整體規劃與派工的頂層 session;執行席則是被派出去、拿著派工單單純幹活的 subagent。

數字一拆開就很有意思:當 GPT 坐在主控席、負責寫治理工具(例如 hook、許可、收據這類管控腳本)時,沒人要求的雜湊比例高達 72%。可是同樣是 GPT,一旦退到執行席照著派工單做事,這個比例就掉到 22–26%,跟 Claude 坐主控席時的 19–22% 差不多。進一步細分的話,在一般 repo 裡兩者分別是 22% 與 19%,治理工具裡則是 26% 與 22%。這組數據已經先剔除了誤判成雜湊的片段,統計口徑跟前面的 41%、13% 不一樣。

我的解讀是,在這些紀錄裡 GPT 家族確實有這種防禦過度的傾向,但問題幾乎都集中在它坐主控席自己做規劃的時候;一旦換到執行席,表現就跟 Claude 沒多少差別。

不過這裡要說明:執行席拿到的 prompt 是主控席產生的派工單。所以執行席被判定「有人要求」,指的是派工單裡有這道要求,不代表是我本人提的。

反駁階段撞見的循環自證

在初判之後,審計流程另外安排了兩位 Opus 擔任反駁者,專門針對初判為「沒人要求」的雜湊逐筆提出挑戰,GPT 與 Claude 寫的都在審查範圍內。結果這些「沒人要求」的判定幾乎全被反駁者推翻。

反駁者拿出來的理由千篇一律:程式碼裡既有的契約要求使用 sha256。所謂契約,就是寫死在程式裡的運作規則,像是「許可必須帶有目標內容的 sha256 才算有效」「收據必須記錄產物的 sha256」。既然既有程式碼明文規定要驗,寫這行雜湊當然就算依約辦事。

然而回頭一對帳才發現,那些被反駁者拿來當依據的契約,多半是 GPT 在更早的 session 裡自己寫進去的。

GPT 先在系統裡立了一套要求雜湊的規矩,後續每次寫雜湊就能順理成章說成是在配合規矩,完美的循環自證就此成形。這裡跳出來替雜湊辯護的是 Opus 反駁者,但反駁者依據的卻是 GPT 以前自己蓋出來的規矩,等於規矩在替雜湊「自己背書」。

正因如此,前面提到的 41% 與 72% 採用的都是反駁前的初判。我沒有採納反駁者的推翻結果,因為那些辯護理由完全建立在自建契約之上;要是把這類自我生成的規矩也算成正當需求,沒人要求的比例就會直接掉到接近零。

防了一個設計明說不防的威脅

話說回來,比對雜湊到底是在防什麼?在我的這套機制裡,單純想抓不小心的意外改動,核對檔案的修改時間與大小早就綽綽有餘;雜湊多防的,是有人刻意改了檔案內容、還處心積慮把修改時間與大小改回原樣的惡意竄改。

諷刺的是,部分帶有雜湊綁定的 hook,檔頭註解自己就寫得一清二楚:這道檢查只防誠實但健忘的 agent 不小心走偏,根本不防刻意規避。因為 agent 跟檢查關卡跑在同一個使用者身分下,agent 要是真的想繞過,它自己隨時都能改寫許可檔。

所以問題的核心不是程式寫太多,而是主控席在規劃防禦時,自作主張替系統明文宣告不防的威脅加上了重裝防護,而雜湊就是這層過度設計留在程式碼裡的痕跡。大部分問題都出在主控席的這類腦補;當然執行席照著派工單做事時,確實也有一部分沒人要求的雜湊是順手多塞進去的。

整批拆除與重新設計

既然自家機制根本不需要這層雜湊防禦,我就沒打算在舊結構上縫縫補補,而是直接把整套許可機制重新設計,徹底拔除雜湊綁定。

我開了工作項逐檔清理。新版的許可改成純粹綁定身分欄位:哪個 session、哪一張許可、目標路徑、到期時間,而且用過一次就作廢。要是某個流程真的需要固定檔案內容,就在發出許可的當下另存一份快照,許可裡只記錄這份快照的路徑。

收據與清單裡的指紋欄位全部移除,後續真的需要比對檔案新舊的地方,一律改看修改時間與檔案大小。

前面提到的審計涵蓋了我所有 session 寫過的所有程式碼,但這次拆除只針對我自家機制所在的三個 repo。扣掉合理的保留項之後,三個 repo 裡負責驗證用途的雜湊正式歸零。保留下來的只有兩類:第一類是純粹拿來當資料編號、完全不拿來校驗內容的雜湊(例如去重用的雜湊碼);第二類則是由外部工具格式硬性規定、且每一項都經我親自拍板確認的例外。

防回流規則與每日巡檢

清理乾淨之後,為了防止以後 GPT 坐上主控席又手癢把雜湊寫回來,我設了兩道防線。

第一道是常駐規則:自家的 harness 機制一律不算、不存、也不比對內容雜湊;之前拔除的欄位如果本來就沒有程式在讀取,就絕對不准再補其他檢查欄位來充數。

第二道是每日回流偵測:排程每天巡檢一次,只要發現新檔案出現雜湊、或是既有檔案裡的雜湊行數增加,就提醒我。

不過寫這篇文章的時候,這個巡檢偵測才剛上線幾天,目前為止都安安靜靜。這只代表這幾天還沒抓到回流,並不代表這套規則已經經受住考驗、保證有效。

有沒有人要,看人說過的話

要判斷一個機制到底有沒有人需要,標準很單純:只看人類實際說過什麼話。程式碼裡那些看似嚴密的規矩,可能是 AI 自己野蠻生長出來的。