昨天整理 AI Demo 為什麼不能直接上線時,我一直碰到一個很麻煩的問題:傳統程式出錯通常很好認,API 回 500、資料庫連不上、程式直接 crash,至少你知道「它壞了」,但 AI 系統最麻煩的狀況,反而是它什麼錯誤都沒有。
API 正常回 200,模型也順利產生答案,畫面看起來完全沒事,只是答案其實沒有很好。
那這到底算成功還是失敗?
我以前做 AI 功能時,很常用一個非常直覺的方法測試:自己丟幾個問題進去,看答案感覺對不對,如果大部分看起來合理,就覺得 Prompt 應該調得差不多了。
但真的開始用 Engineering 的角度整理之後,才發現「我覺得回答還不錯」其實是一個非常危險的測試標準。
假設今天做一個文件問答系統,我問它某個問題,AI 回了一段很完整的答案,第一眼可能覺得寫得很好,可是仔細拆開來看,問題馬上出現。
它回答的內容有沒有真的來自文件?
有沒有漏掉重要條件?
引用的資料是不是對的?
問題明明不知道答案時,它會不會自己補一個看起來很合理的說法?
這些其實全部都是不同的事情,如果只用「整體感覺不錯」去判斷,很容易把它們混在一起。
更麻煩的是,人本身的判斷也會飄,同一個答案今天看可能給 8 分,過兩天再看可能只剩 6 分,如果連評分方式都不固定,就很難知道下一版 Prompt 到底有沒有真的變好。
這件事有點反直覺。
遇到 AI 回答不好的時候,我第一個反應通常都是去改 Prompt,加一句「請務必根據資料回答」、再補一條規則、調一下 temperature,然後重新測一次。
但今天我先停在前面,沒有急著改,而是先把「好的回答」拆成幾個可以檢查的條件。
例如一個 RAG 問答,我可能至少會在意:
這樣一拆,我才發現以前說的「AI 答錯」,其實可能是完全不同的問題。
有時候不是模型不會回答,而是 Retrieval 一開始就拿錯文件;有時候文件明明找對了,模型卻忽略其中一個限制;還有一些情況答案本身沒錯,但引用來源根本支撐不了它。
如果沒有先把這些東西分開,最後很容易所有問題都怪到 Prompt 身上。
接著我做了一件很普通,但以前常常懶得做的事:把測試問題留下來。
以前測 AI 功能時,我常常想到什麼就問什麼,改完 Prompt 再隨便問幾題,所以新版看起來比較好,有時候只是因為我剛好換了一組比較簡單的問題。
現在我開始固定一小組測試題,裡面故意放不同類型,例如文件裡可以直接找到答案的、需要整合兩段資訊的、資料不足不能亂回答的,以及很容易被模型誤導的問題。
這樣每改一次 Prompt、模型或 Retrieval 設定,就重新跑同一批題目。
做到這裡才第一次有一種「我真的在測系統」的感覺,而不是一直跟 AI 聊天,然後憑印象判斷它有沒有變聰明。
傳統 API 很多時候可以直接寫 assert,例如預期 status code 是 200、某個欄位應該等於特定值,但生成式 AI 很難每次要求它輸出完全一樣的句子。
所以測試方式也要跟著改。
有些東西還是可以直接判斷,例如 JSON 格式對不對、必要欄位有沒有出現、引用的 document ID 存不存在;有些則比較適合用規則或評分,例如答案有沒有包含關鍵資訊,甚至可以再讓另一個模型協助判斷。
但這裡又會出現新的問題:如果用 AI 評 AI,那評分的 AI 自己會不會也判錯?
所以我現在比較傾向把能寫死的條件先寫死,真的無法直接判斷的內容,再交給模型評分,而不是什麼都丟給另一個 LLM 說「請幫我評 1 到 10 分」。
至少這樣出了問題,我比較知道要去哪裡找。
有了固定測試題之後,我才發現 Prompt version 這件事為什麼重要。
以前 Prompt 改掉就改掉了,頂多 Git 裡面看得到差異,但現在如果 v1 在 20 題裡通過 14 題、v2 通過 17 題,我至少開始有東西可以比較。
而且如果 v3 為了改善某一類問題,結果讓原本答對的題目壞掉,也會馬上看出來。
這種感覺跟一般軟體的 regression test 很像,只是以前寫 AI Demo 時,我幾乎沒有把它當成同一件事。
昨天我還在想,Prompt 到 Production 中間到底多了什麼,今天我覺得其中一個很重要的答案就是:你必須先知道怎麼證明新版真的比舊版好。
不然換模型、改 Prompt、調 RAG、增加 Agent,看起來一直在做事,最後卻只能靠一句「感覺好像比較準」決定要不要上線。
接下來我想真的做一組最小版 Eval,把測試題、預期條件跟每次結果記下來,看看一個原本只能靠人工試玩的 AI Demo,能不能慢慢變成一個至少知道自己什麼時候退步的系統。