我以非開發者的身分為醫院建置內部工具,這表示大部分程式碼都是由 AI 撰寫的。所以當然我做了該做的事:我也要 AI 來審查程式碼。

不是隨便審查。我設定了不同的角色——一個代理負責實作,另一個執行測試,另一個則專門閱讀程式碼尋找安全性問題。不同的指令、不同的焦點、對同一份工作的不同檢視。這感覺就像一個審查流程。好幾個月以來,安全性審查員幾乎沒有標記任何問題。

我當時還以為這是好消息。

接著,在這個網站上三天內,陌生人就在同樣的系統中發現了八個真正的缺陷。其中沒有一個是我的審查員找出的。

讓我停下來的數字

這段期間誠實的統計如下。一個檢查原本應該斷言某件事被偵測到,而不是斷言特定那件事被偵測到。一個排除守衛驗證了某種模式可以辨識資料夾,但從未驗證實際的發行執行是否真的排除了它——這已經默默地對六個乾淨的檔案錯誤評分了好幾週。相反方向的盲點是,擴大同樣的模式可能會吞掉真正的程式碼,並產生一個完美的綠色「零問題」。一份列印在報告中且完全排除在通過/失敗判斷之外的例外清單。一個我幾個月前親手執行過一次、結果已經悄悄過期的演練。一個本該每天執行所有這些檢查的排程工作,卻根本沒有註冊。

大約八個,視你如何計算。每一個都是由我設定之外的人發現的,通常在評論中,通常針對我剛剛自豪地寫過的東西。

我的審查代理已經閱讀同樣的程式碼好幾個月了。它從來沒有提到其中任何一個問題。

我搞反的部分

我第一個直覺是審查員需要更好的指令。這是舒適的結論,因為可以用提示來修正。

然後某個討論串中的人說出了真正解釋這件事的話:兩個模型達成共識往往是一種失敗模式,卻有兩個不同特徵。

我一直把角色分離當成獨立性。但事實並非如此。我的實作者和審查員在底層是同一個模型,只是被指派了不同的工作。不同的帽子,同一個先驗。實作者在結構上無法注意到的任何事情,審查員也同樣無法注意到——不是因為它粗心,而是因為它共享了相同的盲點形狀。要求它檢查工作,等於要求同一個心智重新考慮自己的假設,而這正是心智最不擅長的事。

還有第二個更糟的層面。我的代理並不在關於我系統的對話之外。它們就是對話本身。它們持有我給它們的脈絡、我選擇的框架、我用來描述自己工具的詞彙。一個閱讀我文章的陌生人沒有任何這些東西。他們並不更聰明——他們只是站在我無法站立的位置,而任何我簡報過的東西也無法站在那裡。

人類的對應情況是兩位共事多年的同事:仍然是兩個人,仍然有兩個特徵,但他們的分歧已經化解成一種共享的看待方式。同樣的陷阱。AI 只是在第一天就透過建構的方式立即達到這個狀態。

我誤讀的沉默

這就是刺痛的地方,因為我已經用不同的包裝寫過兩篇關於這個確切錯誤的文章。

一個找不到任何問題的審查員,和一個沒有真正尋找問題的審查員,會產生相同的輸出:什麼都沒有。我知道這點。我已經根據這個原則建置了失效保險——檢查會在缺乏新鮮訊號時發出警報,正是因為從外部看來,沉默和健康看起來是一樣的。而我仍然把審查員好幾個月的安靜解讀為程式碼乾淨的證據。

我從未對它進行過種子測試。我從未給它一個包含已知植入缺陷的檔案來確認它仍然能看見。我確實對我的自動化掃描器做了這件事——植入了十個已知的壞模式,發現它抓到了七個——但我從未想過對審查員做同樣的事,因為審查員感覺像是同事而不是儀器。這就是整個錯誤的一句話。它是一個儀器。而我從未拿火柴去測試它。

我建置的替代塔樓

回顧過去,我這個月建置的所有東西,都是對不信任前一層沉默的紀念碑。

我不再信任自己對程式碼的判斷,所以讓 AI 來審查。我不再信任審查,所以建置了一個有明確規則的機械化掃描器。我不再信任掃描器的綠燈,所以植入了已知的壞裝置來證明它能看見。我不再信任那些裝置仍然有意義,所以建置了一個故意破壞每個守衛並確認它因自己的原因變紅的演練。然後我不再信任演練,因為結果發現沒有人在檢查演練本身是否仍在執行——所以另一台機器,以不同的排程,現在會監視它的生命證明。

五層。每一個層級的存在,都是因為它下面的層級可能會安靜地失效而不告訴我。

而這是我無法靠建置來逃避的事:所有這些層級都是由我設計,並由同一個 AI 撰寫的。它們以不同的方式失效,這確實有幫助——規則回歸、失效閘道,以及停止的排程器,不會在同一天因同樣的原因發生。但失敗模式的多樣性並不是來源的獨立性。整個塔樓共享同一個作者。

真正站在外面的事物

在這個整個設定中,唯一曾經可靠地站在外面的就是其他人。

不是因為他們比我的代理更厲害的工程師——其中幾個最尖銳的修正來自三句話,來自從未看過我一行程式碼的人。他們之所以有效,是因為他們不在假設被提出時的那個房間裡。他們閱讀我感到滿意的東西,而感到滿意結果證明是測試不足的可靠標記。

所以我最後採用的做法,簡單地陳述如下:

房間內的共識不是證據。如果第二個意見來自同一個模型、同一個會話,或是我提供的同一個框架,那就是帶有兩個特徵的一個意見。它對抓出失誤很有用。但對抓出假設毫無價值。

任何只能保持沉默的東西都不是檢查。包括審查員。對它進行種子測試,或者承認你把它的安靜解讀為它從未聲稱的某件事的證明。

真正外部的檢查必須付出代價才能獲得。陌生人的注意力、公開的錯誤、一篇邀請人們針對你最佳作品的文章。它無法規模化,它不會每晚執行,而這是我唯一不屬於我自己假設下游的層級。

這對某個整個工具組都是由 AI 建置的人來說,是個不舒服的結論。我不會假裝這能整潔地解決。但五層自動化檢查,而找到活躍錯誤的東西,卻是一個只有五分鐘空閒時間且對我是否正確沒有利害關係的陌生人。

請考慮這是我交還的籬笆。如果你正在使用 AI 建置並讓 AI 檢查工作:你的審查員上次告訴你什麼你不想聽的事是什麼時候?如果答案是「它其實沒發現什麼」——那就是我花了幾個月時間誤讀為好消息的同樣沉默。


這是 Stolen from the Feed 的一部分——一個非開發者從 dev.to 帶回家並實際建置的東西。From Zero to Ship 的姊妹系列。