AI 工坊 The Workshop
10 min read

Prompt 消融實驗:結果變好後,我才知道自己還不知道哪個改動有效

一次內部評估裡,系統表現明顯改善;但同時改了多個地方的 before/after,不能直接說明因果。這是我如何用 2×2 消融,重新看待 Prompt 改版的方法。

Layers of blue and ivory paper sheets rise like a quiet landscape, one marked with a half-finished checklist, while a small figure holding a yellow folder stands before them and a coral sun glows in a distant opening.

TL;DR

  • 改版後結果變好,不等於知道是哪一項改動有效。
  • 同時修改多個 Prompt 因素時,單看 before/after 無法判斷因果。
  • 把「結構」與「情境規則」拆成 2×2 組合重跑,才看得出各自在什麼條件下有幫助。
  • Prompt 優化的重點不是規則愈多愈好,而是清楚知道每條規則的適用範圍與影響。
  • 保留可重跑的測試資料、Prompt 組裝方式與評分規則,才有辦法讓結論被驗證。

這是一個我做完覺得十分有趣的實驗,它把所有變因拆開,接著排列組合,再把這些結果拿出來一一比較,才看見較可靠的改善歸因,但這也不是一開始就想到,而是做完第一輪實驗後,發現不太合理,才回頭補做這個驗證。

日前在處理系統誤報率時,我曾經把一組內部評估的誤報問題明顯壓下來。第一眼看到結果時,我很想替它寫下一句結論:Prompt 調好了,但我後來發現,這個結論言之過早。

因為同一輪裡,我們同時動了輸出結構、共用判讀規則和情境規則。如果只比較改版前後,任何一項都可能被錯誤地冠上功勞。真正重要的問題不是「有沒有變好」,而是:到底是哪一個改動讓它變好?

這篇不公開任務類型、資料內容、模型、實作架構或原始數字;我想保留的是其中可複用的驗證方法:當 Prompt 一次改了很多地方時,怎麼避免把相關性誤寫成因果。

我們同時改了結構、共用規則和情境規則

有些 AI 任務看起來像是分類問題,實際上卻同時包含好幾層判斷。

模型不只要辨識輸入,還要理解哪些狀態是可接受的、哪些狀態需要保守處理、輸出要如何符合系統規格。只要其中一層沒有說清楚,模型就可能把「不確定」或「看起來不同」誤當成需要升級處理的事件。

當時我們同時調整了幾個地方:

  • 讓輸出結構更固定,減少模型為了填完格式而自行補出判斷。
  • 重整共用規則,讓模型知道哪些條件必須優先確認。
  • 加入特定情境下的正常基線與排除條件。

改完後,誤報確實降低了。

圖 1三項改動同時上線,功勞無從分辨

但「整包改完再看結果」只能告訴我們結果變了,不能告訴我們下一次應該保留什麼、重寫什麼,或哪一條規則其實不值得帶到別的情境。

用四種組合重跑同一批資料

所以我們把這次修改先抽象成兩個因素:

  • 結構因素:輸出契約與共用判讀邏輯的新版結構。
  • 情境因素:只針對特定使用條件加入的正常基線與排除規則。

接著,我們將已確認為正常的一組測試資料,放進四種組合重跑:

  • 舊結構 + 舊情境規則:誤報明顯,無法接受。
  • 新結構 + 舊情境規則:誤報下降,但仍未降至可接受範圍。
  • 舊結構 + 新情境規則:在這組測試資料上,誤報被壓到可接受範圍。
  • 新結構 + 新情境規則:同樣維持在可接受範圍。
結構 →情境 ↓
舊結構
新結構
舊情境規則
無法接受誤報明顯
仍未達標誤報下降
新情境規則
可接受誤報壓到可接受範圍
可接受同樣維持可接受
可接受改善但未達標無法接受
圖 22×2 消融:這組測試的達標關鍵是情境規則

我刻意省略資料量和實際數字,因為它們屬於內部專案內容;不過四種組合的關係,已經足以支持一個較精確的判斷。

在這組已確認為正常的測試資料、這四個版本與既定的誤報門檻下,新版結構有幫助;新情境規則才是能否達標的關鍵。這不代表所有任務都需要同一條規則,也不代表結構對其他指標沒有價值。

只要保留情境規則,即使沒有使用新版結構,結果仍能維持;反過來,只換結構而不補足情境基線,問題雖然變小,卻沒有真正消失。

假設 AI 把正常包裹判成異常

假設我要請 AI 檢查包裹,測試用的 100 件都已經由人確認正常。只要 AI 把其中一件判成異常,就算一次誤報。

包裹情境、數字和門檻都是為了說明方法而假設的,與內部專案無關,也沒有還原原始實驗。這裡先訂一個目標:100 件正常包裹中,誤報不超過 5 件。

在這個例子裡,我想試兩種改法:

  • A 是新結構:把回答整理成「觀察到什麼 → 有沒有符合異常條件 → 最後判定」,希望模型先檢查條件,再下結論。
  • B 是新情境規則:告訴模型「外箱有配送用貼紙是正常的,不能只因為有貼紙就判定異常。」補上這條,模型才知道這種情況可以接受。

A 像是把檢查表排清楚,B 則是告訴檢查員哪些情況本來就沒問題。檢查表排得再整齊,檢查員如果不知道配送貼紙是正常的,還是可能把它當成瑕疵。

虛構教學例子 · 非內部實驗數據A = 新結構 B = 新情境規則100 件正常包裹 · 誤報越少越好 · ≤ 5 件達標
結構 →
情境 ↓
舊結構新結構 A舊情境
規則
都不改30件未達標
只改 A12件未達標
新情境
規則 B
只改 B4件達標
A + B3件達標

橫向比較:情境不變,只換結構。
縱向比較:結構不變,只換情境規則。

圖 3同一批 100 件正常包裹,四種 Prompt 各跑一次

四個結果分別是:都不改,誤報 30 件;只改 A,12 件;只改 B,4 件;兩者都改,3 件。接下來的每一張圖,都用這同一組數字。

先改 A 或先改 B,算出來的貢獻不同

我們原先高估新版結構的貢獻,就跟修改順序有關。套用到包裹的例子,會是下面這種情況。

如果我先換 A,再加入 B,我看到的路徑是 30 → 12 → 3。第一步少了 18 件誤報,第二步又少了 9 件。我很容易寫成:「結構貢獻 18 件,情境規則只貢獻 9 件,所以結構比較重要。」

18 件和 9 件都算對了,但不能就這樣認定 A 比 B 有用。測 A 時還沒有 B;測 B 時,A 已經先減少了一部分誤報。B 能改善的是剩下的問題,兩者的比較條件不同。

如果先改 B,再改 A,路徑就變成 30 → 4 → 3:B 先減少 26 件,A 後來只再減少 1 件。四個版本都沒變,只是換個順序比較,這次就變成 B 的貢獻比較大。

虛構教學例子 · 非內部實驗數據A = 新結構 B = 新情境規則100 件正常包裹 · 誤報越少越好 · ≤ 5 件達標

路徑一:先 A,後 B

都不改30件未達標
只有 A12件未達標
A + B3件達標
① → 加入 A · 少 18 件② → 再加 B · 少 9 件

路徑二:先 B,後 A

都不改30件未達標
只有 B4件達標
A + B3件達標
① → 加入 B · 少 26 件② → 再加 A · 少 1 件

兩條路都從 30 到 3,總共少 27 件。
加入同一項改動,先加或後加,改善件數也會不同。

圖 4都少了 27 件誤報,算給 A、B 的件數卻不同

想像兩個人整理同一間房間。第一個人進去時東西還很亂,收掉大半後,第二個人能收的就少了。不能只看各自收了多少,就說第二個人比較不會整理。這個比喻對應的是比較條件不同;實際測試時,每個 Prompt 版本都是各自跑同一批資料。

A 單獨少 18 件誤報,B 單獨少 26 件,兩個一起用卻只少 27 件,不能直接把 18 + 26 加起來。它們可能改善了部分相同的問題,但光看總件數,還不知道是哪幾件重疊,也不能確定原因。

不過,先改的項目也不一定佔便宜。有些改動要搭配另一項才有用,後加入的反而可能改善更多。只照一種順序測,就看不到換個順序後會怎樣。

固定其他條件,再比較每項改動的效果

有了四種組合,我就可以固定一個因素,看看另一個因素改了以後差多少。每個因素都有兩次比較:

  • 沒有新情境規則時,換結構:30 → 12,少 18 件。
  • 已有新情境規則時,換結構:4 → 3,只少 1 件。
  • 沒有新結構時,換情境規則:30 → 4,少 26 件。
  • 已有新結構時,換情境規則:12 → 3,少 9 件。

這四個差值都算得出來,先看哪一格都一樣。結構在沒有情境規則時,能少 18 件誤報;有了情境規則後,只再少 1 件。同一項改動的效果會隨另一項改動而變,這就是這個例子裡的交互作用。

也可以從兩者都改的完整版開始,每次拿掉一項,檢查剩下的版本還能不能達標。

虛構教學例子 · 非內部實驗數據A = 新結構 B = 新情境規則100 件正常包裹 · 誤報越少越好 · ≤ 5 件達標
共同起點:完整版 A + B3件達標

只拿掉 A

保留 B4件達標

4 − 3 = 多 1 件
仍然達標

只拿掉 B

保留 A12件未達標

12 − 3 = 多 9 件
不再達標

左右分開測,每次都從完整版開始。
測完拿掉 A 的版本,先還原 A,再測拿掉 B。

圖 5每次回到完整版,再各自拿掉一項

拿掉 A、保留 B,誤報從 3 件變成 4 件,仍達標。另一次回到完整版,拿掉 B、保留 A,誤報從 3 件變成 12 件,就不達標。測完拿掉 A 的版本後,要回到完整版,再測拿掉 B 的版本。如果一路拆下去,第二次的比較條件又變了。

在這個例子裡,少了 B 就過不了門檻,所以要達標,保留 B 更重要。不過,拿掉 A 多 1 件、拿掉 B 多 9 件,加起來的 10 件和整體改善的 27 件對不上。這很正常:消融回答的是「其他改動還在時,少了這一項會差多少」,無法直接把全部 27 件分配給 A 和 B。

原本的內部實驗也是補齊四格後,我們才發現先前把結構的貢獻看得太高。在那組正常資料和誤報門檻下,補足情境基線更重要。至於結構裡的輸出契約、共用判讀邏輯,這輪仍是放在一起測;想知道各自的影響,還得再拆開比較。

下次記錄改善件數時,我會把比較的兩個版本一起寫下來,免得過一陣子只記得少了幾件,忘了當時是怎麼比的。

把 Prompt 規則分成全域、共用和情境三層

這次之後,我不再把 Prompt 想成一段可以愈寫愈長的文字。

比較合理的做法是把它拆成幾層:

  • 全域規則:所有情境都必須遵守的定義與邊界。
  • 共用規則:輸出格式、判讀順序,以及模型應保留不確定性的時機。
  • 情境規則:只在特定條件下才成立的正常基線、排除條件與覆寫規則。
圖 6Prompt 的三層適用範圍

這樣做不只是為了讓設定檔看起來更整齊,也能讓它更容易維護與擴充。更重要的是,它迫使我們回答一個很實際的問題:某條規則解決了眼前問題後,它會不會悄悄影響到其他情境?之後會有另一篇文章談談我遇過的另一個問題,發現 Prompt 已經改到極限了怎麼辦?

如果不知道一條規則的適用範圍,就很難全然放心地複用它,要據此下決策也容易被挑戰,明明花了時間做實驗,卻說不清楚為何要這樣做。

下次改 Prompt 時,我會留下哪些紀錄

這次沒有讓我得到一個「萬用 Prompt」,反而讓我多了一個比較慢、但可靠的工作流程:

  1. 先把要解決的錯誤類型寫清楚,不讓單一總分掩蓋真正的風險。
  2. 修改 Prompt 時,記下每個改動想處理什麼;不要一次加入太多無法拆開的修補。
  3. 先用 before/after 看整包改版有沒有改善;要分辨兩個改動各自的影響,再補齊 2×2。四組用相同的測試資料、模型版本、生成設定和評分方式,分散或隨機安排執行順序,再重跑幾輪,看差異是否穩定。
  4. 將測試資料、Prompt 組裝方式、模型版本與評分規則一起保存;存下 Prompt 文字,不等於存下實驗。詳細實驗經驗請看 LLM 模型選型怎麼做?快 8–10 倍,為什麼我們還是放棄了這個設定。
  1. 01定義錯誤類型不讓單一總分掩蓋真正的風險
  2. 02記錄改動目的不一次加入無法拆開的修補
  3. 03保留對照組至少 before/after,或 2×2 組合
  4. 04保存整套實驗測試資料、Prompt 組裝、模型版本、評分規則
圖 7下一次修改 Prompt 的四個步驟

誤報降低後,還要檢查漏報

  • 這次只測正常樣本,看得到誤報,卻不知道模型會不會漏掉真正的異常。還要放入已確認的異常樣本,檢查漏報。畢竟模型全部回答「正常」,誤報也會是零,但異常就全漏掉了。
  • 一組內部測試結果,不足以推論到所有任務或情境;單次相差一兩件,也可能只是模型輸出的波動。
  • 局部改善,不等於整個系統的品質、成本與可靠性都已改善。
  • 消融實驗能協助定位改動的影響,但不能替代正式驗收、長期監測或使用者回饋。

但我覺得,這些限制反而是方法真正有價值的地方。

它讓我們知道這輪實驗回答了什麼,也知道下一輪還缺什麼。

結果變好當然值得高興;但如果不知道它為什麼變好,下次遇到相似問題時,我們還是只能靠運氣重來一次。

如果你也曾遇過「改版後變好了,但不知道是哪一段救了你」的情況,歡迎到 Threads 和我交流。

延伸閱讀:完整因子設計

這種四格對照在實驗設計裡,屬於兩個因素、各有兩種設定的完整因子設計。四種組合都測過,才能比較一個因素的效果會不會隨另一個因素改變。想多了解這個方法,可以看 NIST:Full factorial designs。