昨天把固定的 Eval Dataset 建起來之後,我終於可以比較不同版本的 AI Pipeline,每次換 Prompt、調 Retrieval 或改模型,都能重新跑同一批問題,再看看結果到底變好還是變差,不過真的開始累積結果之後,我很快又遇到一個問題,如果最後只得到一個 78% 或 85% 的 Accuracy,其實我還是不太知道下一步該改哪裡。
因為兩個系統就算最後都是 80 分,失敗原因可能完全不同,一個可能經常找錯文件,另一個可能文件找對了,但模型最後整理答案時產生幻覺,如果全部都只被記成「錯誤」,最後看到的就只是一個數字,所以今天我開始把 Eval 的錯誤再往下拆。
以前看到 AI 回答錯誤,我第一個反應很常是打開 Prompt,想看看是不是指令寫得不夠清楚,但現在有了完整 Pipeline 之後,這個做法其實很容易改錯地方。
目前我的簡化流程大概是:
User Question
↓
Retrieval
↓
Context
↓
Prompt
↓
LLM
↓
Answer
假設今天問:
會員取消訂單後,退款需要幾個工作天?
標準答案是:
退款會在 5~7 個工作天內完成。
結果 AI 回答:
通常會在 3 個工作天內完成退款。
以前看到這個結果,我可能直接開始修改 Prompt,加上「請嚴格依照文件回答」「禁止自行推測」之類的規則,但現在我會先去看 Retrieval 到底拿回什麼資料。
如果 Retriever 根本沒有抓到退款政策,而是抓到另一份「取消訂單處理時間」的文件,那 LLM 後面再怎麼調都很難回答正確,真正要修的是 Retrieval;相反地,如果正確文件明明已經在 Context 裡面,模型卻還是自己產生 3 天,那問題才比較可能落在 Prompt 或 Generation。
所以今天我先做了一個很簡單的 Error Type:
Retrieval Error
Context Error
Generation Error
Format Error
Unsupported Answer
Retrieval Error 代表需要的資料根本沒有被找到,Context Error 則是資料有找到,但 Context 組裝時被截掉或排序太後面,Generation Error 是正確資料已經存在,模型最後還是回答錯,Format Error 則是內容本身可能正確,但 JSON、欄位或輸出格式不符合系統要求。
最後一個 Unsupported Answer 我覺得特別重要,因為有時候模型的回答讀起來很合理,甚至剛好跟正確答案差不多,但從提供給模型的文件裡根本找不到這個資訊,這種答案如果只看文字可能會被當成成功,但真正放到 Production 裡其實風險很高。
開始分類之後,原本一整排紅色的 Failed Case 突然變得比較有意義。
假設我跑 100 題:
Correct 78
Retrieval Error 9
Context Error 3
Generation Error 5
Format Error 2
Unsupported Answer 3
看到這個結果之後,我就不會馬上去換模型,因為目前最大的問題其實是 Retrieval,如果先處理搜尋品質,很可能一次就能改善最多題目。
如果下一個版本變成:
Correct 84
Retrieval Error 3
Context Error 2
Generation Error 7
Format Error 1
Unsupported Answer 3
那就代表前一輪調整確實有讓 Retrieval 變好,只是現在主要瓶頸慢慢跑到 Generation,接下來才值得花時間處理 Prompt、Context 表達方式或模型本身。
這種做法讓我第一次很明顯感覺到 AI Engineering 跟單純「調 Prompt」的差別,因為我不需要一直靠感覺猜是哪裡壞掉,每一次調整都有比較明確的方向。
原本我做 Eval Dataset,只是想知道新版到底有沒有比舊版好,今天做到這裡之後,我才發現它其實還可以直接拿來 Debug。
每一題現在除了 Question、Expected Answer,我還想慢慢補上:
{
"question": "會員取消訂單後,退款需要幾個工作天?",
"expected_answer": "5~7 個工作天",
"retrieved_docs": [],
"model_answer": "",
"result": "",
"error_type": "",
"notes": ""
}
這樣之後每次跑 Evaluation,我留下的就不只是最後一個 Score,而是一批可以重新追蹤的錯誤紀錄,哪一題找錯文件、哪一題模型亂補資訊、哪一題格式壞掉,都可以慢慢累積。
等資料量開始增加之後,我甚至可以直接看某個版本到底解掉了哪些舊問題,又新增了哪些新的錯誤。
做到 Day 11,我現在比較不會看到 Accuracy 從 82% 升到 85% 就立刻覺得新版一定比較好,因為如果新增的 3% 是簡單題答對,但同時出現更多 Unsupported Answer,那在實際產品裡可能反而更危險。
所以接下來 Eval 對我來說會開始有兩層,第一層還是整體指標,用來快速看版本變化,第二層則是錯誤類型與失敗案例,讓我知道分數變化到底是從哪裡來。
這樣每一次改 Prompt、調 Chunk、改 Top-K 或換模型,都可以留下比較完整的紀錄,而不是試完覺得「好像比較好」就直接上線。
明天我想再把這套 Evaluation 往 Production 靠近一點,開始加入 Latency、Token Cost 這些指標,因為真正上線之後,答案答得準只是其中一件事,如果每個問題都要等十幾秒,而且成本一直往上堆,系統一樣很難真的拿來用。