前幾天把 AI 功能拆開來看之後,我開始整理輸入、輸出、模型呼叫和錯誤處理,也慢慢發現 AI 系統有一個很麻煩的地方,同一段程式碼沒有改,結果還是可能因為 Prompt 的調整而產生很明顯的差異。
以前做一般 Web 功能時,我對「修改」的理解通常很直接。
程式碼改了就測,沒改到的地方大多可以先假設行為維持原樣,但 AI 功能沒有這麼單純,有時候只是在 Prompt 裡補一句規則,原本正常的案例變好了,另外幾個案例卻開始出現奇怪的結果。
所以 Day 6 我想處理的是一個前面一直碰到,但還沒有真正整理好的問題:Prompt 到底要怎麼測。
最早做 AI Demo 時,我的測試方式其實很隨意。
準備幾個問題丟進去,看回答感覺對不對,如果結果看起來合理,就繼續往下一個功能做;Prompt 有修改的話,再把剛才那幾個問題問一次,差不多就算測完了。
Demo 階段這樣真的很快,尤其只是要確認想法能不能做出來時,幾個案例就已經可以看出大概方向。
問題是功能越做越完整之後,我開始記不得自己之前到底測過什麼。
今天改了一句 Prompt,測了三個問題都正常,過兩天又改另一段規則,前面的案例其實已經壞掉了,只是我沒有重新問,所以一直到後面才發現。
這時候我才開始理解,AI Engineering 裡面為什麼會一直提到 evaluation。
今天先不碰很複雜的評估框架,我想從最簡單的方式開始,把平常原本就會手動測的問題固定下來。
例如假設現在有一個文件問答功能,我可能先整理:
案例 1:文件裡可以直接找到答案
案例 2:答案分散在兩個段落
案例 3:文件裡沒有答案
案例 4:使用者問題很模糊
案例 5:使用者要求模型猜測文件沒有提供的資訊
案例 6:輸入包含錯字或口語說法
以前這些情況可能都是想到才測,現在先把它們變成固定案例,每次修改 Prompt、模型設定或 RAG 流程之後,都重新跑一次。
光做到這一步,我就發現測試方式已經差很多。
因為以前測 AI 很容易挑自己知道「它會答對」的問題,久了之後 Demo 看起來一直很穩,但那些真正容易失敗的邊界情況反而沒有留下來。
接下來又碰到另一個問題。
一般程式測試很好理解,例如輸入 1 和 2,預期輸出就是 3,但如果今天測的是摘要、分類、文件問答,很多時候根本沒有唯一的標準答案。
假設原文寫的是「系統將於晚上八點停止服務」,模型回答「服務會在 20:00 暫停」其實也可以接受,如果測試只是直接比較兩段文字是否完全相同,這個答案反而會被判定失敗。
所以我目前先把測試條件拆得簡單一點,不要求文字完全一致,而是確認幾件比較重要的事情。
有沒有回答到問題
有沒有引用正確資訊
有沒有自己補出不存在的內容
格式是否符合系統要求
遇到不知道的問題時,有沒有正常說不知道
這樣還不算完整的 evaluation system,但至少開始把「感覺回答得不錯」變成幾個可以檢查的條件。
做到這裡之後,我又順手補了一個以前完全沒做的東西,就是幫 Prompt 留版本。
以前我的 Prompt 常常直接寫在程式裡,想到什麼就改什麼,最後只知道現在這版效果還可以,卻很難回答到底是哪一次修改讓結果變好。
所以現在我開始留下很簡單的紀錄:
v1:基本角色與回答格式
v2:加入不知道時禁止猜測
v3:要求回答附來源
v4:調整文件不足時的回覆方式
每次版本變動之後,再用剛才固定下來的測試案例重新跑。
這樣做之後,Prompt 才開始有一點像其他程式碼資產,可以知道改了什麼,也比較有機會比較修改前後到底差在哪裡。
今天做的事情看起來沒有新增任何功能,畫面也完全不會變漂亮,甚至 Demo 給別人看的時候,沒有人會知道背後多了一組測試案例。
但這幾天越整理越能感覺到,真正讓 AI Demo 可以長期維護的東西,很多都藏在這些看不到的地方。
Prompt 有版本、測試案例可以重跑、失敗情況有留下來,下一次模型或流程修改之後,至少可以知道原本會做的事情有沒有被弄壞。
明天我想接著處理 evaluation 裡更麻煩的一塊,因為現在雖然已經有測試案例,但如果每一筆結果最後還是要我自己從頭看到尾,那案例一多,測試時間一樣會越來越長。