Day 3 我把一條寫作規則接進了控制流:AI 每次寫入草稿,內容品質站就自動檢查,不通過就把原因送回去,通過才往人審走。畫成圖是這樣:

這一站的紅燈會改變整條流程的下一步。所以今天要問一個很少被問的問題:這一站自己壞了,誰會知道?
先說給不寫程式的讀者:這篇有技術細節,但問題本身不是工程師專屬。只要你開始讓 AI 幫你產東西,貼文、報告、信件都算,你就需要某種自動把關,而把關的東西自己也會壞。看完這篇,你至少能帶走三個問題,拿去問自己的工具或團隊:該擋的,有測過真的會擋嗎?不該擋的,有測過不會誤傷嗎?把關壞掉的時候,你會知道嗎?
家裡裝了煙霧警報器,你不會裝完就相信它一輩子有效。你會按一下測試鈕,確認它會響。
品質站就是內容產線的警報器。這篇要做的兩件事,說穿了就是:每天按一遍測試鈕,然後挑一天,拿一點煙靠近感應器,確認真的有狀況時,它真的會響。

起點是 Day 3 那次受控測試。同一份違規測試稿,照正確的接法送進品質站,得到「不通過」;換一種不對的接法送,卻回報「通過」,因為它根本沒有真的檢查。規則沒變,接法變了,結果就反過來。
從那天起,「檢查器可能失效」對我來說就不是抽象風險,而是一個可以核對的具體問題。這篇就來把它核對完。
先補一個背景:我當初會寫這些測試,不是被哪次事故推的,而是想把自己的判斷寫下來,讓 AI 照著做。判斷一旦寫成規則交出去,就多了一個新問題,我得確認規則沒有走樣,這才有了測試。
但「發文的判斷」聽起來就很難寫成規則。發文本來就沒有一套客觀標準,什麼是好內容,我自己也說不出完整定義。所以我實際的做法是反過來的:我不把「什麼是好」寫成規則,只把「我確定不要什麼」寫成規則。
每一條規則背後,都是一次真實的判斷。我在某篇稿子上說過「這種句型不行」,那次的「不行」裡,有一部分是可以逐字指認的:某個句型、某個詞、某種樣式。我只把指認得出來的那部分寫進機檢;指認不出來的部分,語氣對不對味、觀點夠不夠好,照樣留給人審。規則不是從一套標準推導出來的,是從一次一次具體的「不要」裡萃取出來的。
那為什麼這樣算是對的?因為「對」的標準從頭到尾不是「機檢覺得內容好」,而是「機檢跟我過去的判斷一致」。這也是固定測試稿真正的身分:應攔截組,是我說過「不行」的例子;應放行組,是我說過「可以」的例子。它們是判斷的存檔。測試全綠的意思是:今天的機檢,跟當初做這些判斷的我,還是同一個標準。內容好不好,機檢從來沒有回答過,它只回答有沒有踩到我確定的底線。

測試的核心只有一句:先寫好答案,再對答案。
準備一份故意寫進違規句型的測試稿,預期它亮紅燈;再準備一份乾淨稿,預期它亮綠燈。跑完把實際結果跟預期比對:一樣,這項測試通過;不一樣,測試轉紅,代表品質站的行為變了。改了規則之後把舊樣本全部重跑一遍,就叫回歸測試。
到這裡才需要補名詞。這些先寫好的測試稿,工程上叫 fixture;預期結果叫 assertion。整理成一張對照表:
| 先說白話 | 再補技術詞 |
|---|---|
| 工作流中間的紅綠燈 | Gate |
| 固定測試稿 | Fixture |
| 預期結果 | Assertion |
| 改規則後重跑舊樣本 | Regression test |
| 在暫存副本故意改壞規則 | Mutation |
Day 3 說流程的「通過/不通過」;到了測試的世界,同一件事習慣說綠燈與紅燈。

我的品質站測試套件有 10 份固定測試稿,對應 13 項結果檢查:10 項對檔案,5 份應攔截、5 份應放行;另外 3 項不給檔案,直接對腳本送入異常輸入。寫這篇的當天重跑,結果 13 PASS、0 FAIL。
| 測試群組 | 數量 | 預期 | 防守目標 | 不能證明 |
|---|---|---|---|---|
| 應攔截 | 5 份測試稿 | 紅燈 | 已知規則漏擋 | 未知風險都被涵蓋 |
| 應放行 | 5 份測試稿 | 綠燈 | 乾淨稿、引用、範圍與方法文件被誤擋 | 所有正常寫法都不會誤擋 |
| 防禦性輸入 | 3 項腳本輸入 | 依契約綠燈 | 異常輸入把流程弄掛 | 壞掉時放行適合所有把關 |

應攔截的五份,各防一類已知問題:編造事件句型、矯情驚訝開場、出版社資訊洩漏、金鑰樣式、AI 套話。每一份的預期都是紅燈。它們防的是同一種退化:哪天規則被改壞,該擋的東西悄悄放行。
應放行的五份,預期都是綠燈:乾淨稿、帶引用的稿、不在內容目錄的檔、不是文字稿的檔、方法文件。這一組跟攔截組一樣重要。誤擋跟漏擋一樣,都會讓人不再信任檢查,教學文示範一句錯的就被擋下,這個站就沒人想用了。

防禦性的三項比較特別:給品質站壞掉的輸入,空的、格式錯的、指到不存在的檔案。依目前契約,這三種都亮綠燈、不中斷流程。這是一個想清楚的取捨:內容檢查站選擇「壞掉時放行」,因為後面還有人審;換成發布出口那道閘,就該反過來「壞掉時擋下」。工程上這兩種選擇叫 fail-open 和 fail-closed。壞法不是只有一種,連怎麼壞都要先選好。

拆開來,品質站有三種壞法,各需要不同的測試:
| 失敗類型 | 長什麼樣 | 較適合的驗法 |
|---|---|---|
| 漏擋 | 該擋的測試稿亮了綠燈 | 應攔截組 |
| 誤擋 | 引用或乾淨稿亮了紅燈 | 應放行組 |
| 接線錯 | 直接跑會擋,從流程呼叫卻放行 | 整條路徑的受控演習 |
最陰險的是第三種:檢查器本身沒壞,但流程看起來有執行、其實沒有掃。Day 3 的接線落差就是活例子,也是為什麼除了測試稿,還要定期做整條路的演習。

還剩一個更根本的懷疑:這套測試自己會不會只是擺著好看?永遠全綠的測試,跟沒有測試一樣,它可能只是沒在看。
要確認測試真的在看,就故意弄壞一次。驗收條件先寫死:把品質站整組複製到暫存目錄,移除暫存副本裡的一條規則(引文豁免),重跑同一套測試;對應「引用豁免」的那一項必須轉紅,其他項不受影響,正式的品質站一個字都不動。
寫這篇的當天實跑,結果照著驗收條件走:「引用豁免」那一項轉紅,預期綠燈、實際紅燈,整套 12 PASS、1 FAIL,其餘 12 項不受影響。正式品質站未改動,原套件同日重跑仍是 13 PASS、0 FAIL。
這就是拿煙靠近感應器。警報器響了,我才敢繼續相信它。工程上這種做法叫 mutation:不是測內容,是測「測試會不會發現規則壞了」。它只證明這一種改壞方式會被抓到,不能證明任何改壞都會被抓到。

把邊界說清楚。13 項全綠證明的是:已經寫進套件的行為,今天都成立。
它不證明沒有未知的漏洞,不證明規則涵蓋了所有該擋的問題,也不證明 AI 收到紅燈之後修得回來。最後這件事值得單獨一提:紅燈只是把原因送回去,AI 會不會修、修得對不對,需要另一組紀錄來評估,這個系列後面再處理。綠燈是「已知範圍內沒退化」,不是「沒有問題」。

發文線的品質站走完了測試台,其他工作流也適用同一組問題:應該擋什麼?拿什麼固定樣本證明它真的會擋?
| 工作流 | 可機械檢查的候選 | 仍由人判斷 |
|---|---|---|
| 發文 | 明確紅線、敏感資訊樣式、檔案與格式 | 經驗是否真實、內容值不值得公開 |
| 深度研究 | 來源欄位、連結、未確認狀態是否保留 | 來源強度、來源是否支持主張 |
| 會議總結 | 時間戳、說話者未確認標記、必要欄位 | 說話者身分、重要承諾是否聽對 |
| 投影片 | 頁數、超版面、重複畫面與輸出檔 | 敘事順序、證據與視覺有沒有說過頭 |
這張表講的是設計位置,不是每一項都已經接成現役的自動把關。
先立一個這系列內要兌現的承諾:下一組固定測試稿,我要補給報告的交付出口,就是發布前負責掃金鑰和敏感資訊的那道檢查。理由有二。第一,它是全產線最該「壞掉時擋下」的一站,發出去的東西收不回來;第二,它有現成的應攔截素材,這道掃描在自己的實測裡被打穿過不只一次,靠的都是我沒想到的輸入形狀,那些繞法本身就是一份份該亮紅燈的測試稿。Day 5 講交付出口的時候,正好回來看這個承諾。
Day 2 說人工判斷沉澱成驗證,流程就能多自動化一格。這篇補上它的下半句:沉澱出來的驗證也是程式,也會壞,所以要有固定測試稿看著它。
規則看內容,測試看規則,人看整條線。

今天把品質站本身放上了測試台:10 份固定測試稿、13 項檢查、一次受控破壞,證明這站的紅綠燈是可驗證的。但草稿通過品質站、通過人審之後,還有一個問題沒回答:同一份成品,該留在本機、放進有門禁的入口、做成可撤回的分享,還是長期公開?下一篇來拆。
參考資料:
我平常在這些地方輸出,有興趣歡迎交流: