昨天做完 Abstention Test 之後,我開始碰到一個很現實的問題。
Day 5 的邏輯很直覺:
不知道的時候,不要亂答。
但如果把這句話推到極端,也會出現另一個問題。
假設一個 AI 為了避免犯錯,十題只回答一題,其他九題全部說 UNKNOWN,它的錯誤率可能真的很低。
可是這樣的系統真的有用嗎?
反過來,如果 AI 每一題都回答,Coverage 看起來很漂亮,但它可能正在把大量低把握的答案一起送出去。
所以今天我想處理的問題,不再只是:
「AI 會不會拒答?」
而是:
「當我要求 AI 回答更多問題時,我到底承擔了多少額外風險?」
這就是今天的主題:
Risk-Coverage。
Day 5 的 Abstention 比較像是一個二元決策。
一筆案例進來,最後不是:
ANSWER
就是:
UNKNOWN
但在實際系統裡,我不一定只能用一條固定規則。
如果每一筆答案都有一個 confidence score,那我就可以設定 threshold。
例如:
confidence >= 0.9
才允許回答。
threshold 很高時,系統只回答自己最有把握的案例。
threshold 降低之後,就會開始接受更多案例。
這時候就出現兩個很重要的量:
Coverage
以及
Risk
Coverage 可以先理解成:
系統願意處理、願意回答的案例比例。
假設總共有 10 題。
如果 threshold 設得很高,只留下 3 題:
Coverage = 3 / 10 = 30%
如果 threshold 降低,最後 10 題全部都回答:
Coverage = 10 / 10 = 100%
所以 Coverage 越高,代表系統願意處理的範圍越大。
在使用體驗上,這通常是好事。
因為沒有人希望 AI 每次都只回:
UNKNOWN
但是 Coverage 高,本身不代表可靠。
所以還需要第二個指標。
今天先把 Risk 定義成:
在「已經選擇回答」的案例裡,錯誤答案佔多少。
公式可以寫成:
Risk = Errors among selected cases / Selected cases
假設 AI 選擇回答 8 題,其中錯 2 題:
Risk = 2 / 8 = 25%
注意這個 denominator 很重要。
Risk 並不是:
總錯誤數 / 全部資料
而是:
總錯誤數 / 系統真的選擇回答的資料
因為今天真正想知道的是:
當系統決定「這題我敢回答」之後,它有多常答錯?
這裡要先把資料來源講清楚。
Day 6 的 confidence 並不是任何真實 LLM 提供的 confidence。
我今天使用的是 Synthetic Demo Data。
也就是我刻意建立一組:
case_id
confidence
correct
資料,單純用來驗證 Risk-Coverage evaluator 的行為。
confidence 只是我設計的 synthetic score。
它目前不能解讀成:
「模型真的有 98% 的機率答對。」
今天只是先假設:
score 越高,系統越優先保留這筆答案。
至於這個 confidence 本身到底可不可信,會留到下一天再處理。
我今天的 Demo 一共有 10 筆案例。
Confidence 從高到低分別是:
0.98
0.93
0.88
0.82
0.76
0.69
0.61
0.54
0.43
0.31
程式會直接把資料中實際存在的 confidence values 當作 thresholds。
也就是說,不是自己硬切:
0.9
0.8
0.7
...
而是直接觀察:
如果 threshold 剛好落在每一個 observed confidence 上,系統會得到什麼 Coverage 與 Risk?
實際結果如下:
| Threshold | Coverage | Risk |
|---|---|---|
| 0.98 | 10% | 0% |
| 0.93 | 20% | 0% |
| 0.88 | 30% | 0% |
| 0.82 | 40% | 25% |
| 0.76 | 50% | 20% |
| 0.69 | 60% | 約 17% |
| 0.61 | 70% | 約 29% |
| 0.54 | 80% | 25% |
| 0.43 | 90% | 約 33% |
| 0.31 | 100% | 40% |
【截圖位置 1】Day 6 Risk-Coverage 實際輸出
建議截 Threshold / Coverage / Risk 的完整結果。
如果只看表格最上面:
Threshold = 0.98
Coverage = 10%
Risk = 0%
這看起來非常漂亮。
Risk 是 0%。
但是它只回答了 10% 的案例。
換句話說:
10 題裡只處理 1 題。
如果這是一個客服系統,這種「零風險」幾乎沒有實際價值。
它不是因為真的解決了風險問題,而是因為它幾乎沒有在工作。
這讓我第一次很清楚地看到:
低 Risk 本身不是目標。
真正需要考慮的是:
在可接受的 Risk 下,可以拿到多少 Coverage?
也就是 Reliability 不再是一個單一分數,而開始變成 trade-off。
這次結果裡還有一個我原本很容易忽略的地方。
例如:
Coverage 40% → Risk 25%
Coverage 50% → Risk 20%
Coverage 60% → Risk 約 17%
Coverage 增加了,Risk 居然反而下降。
如果直覺上認為:
回答越多 → 一定越危險
就會覺得這結果怪怪的。
但仔細看 Risk 的定義,其實很合理。
Risk 是:
累積錯誤數 / 已選案例數
假設在 Coverage 40% 時:
4 題裡錯 1 題
Risk = 1 / 4 = 25%
接著新增的第 5 題是正確答案:
5 題裡仍然只錯 1 題
Risk = 1 / 5 = 20%
再增加一題正確答案:
6 題裡仍然只錯 1 題
Risk = 1 / 6 ≈ 16.7%
所以 Risk-Coverage Curve 並不保證是一條漂亮的單調曲線。
它會受到:
每一個新加入案例到底是對還是錯
影響。
這個現象其實很重要。
因為它提醒我:
不能只看一兩個 threshold,就直接說某個 score 很可靠。
真正有價值的是觀察整條 selection behavior。
做到這裡,我開始覺得 Risk-Coverage 不只是「多算一個 metric」。
它其實比較像一個部署決策工具。
假設今天有兩種場景。
第一種是一般 FAQ。
可能我願意接受:
Risk <= 10%
換取比較高的 Coverage。
但如果今天是在處理一個高風險任務,我可能希望:
Risk <= 1%
就算 Coverage 比較低也沒關係。
所以系統真正的問題,不是:
哪一個 threshold 是最好的?
而是:
在我的任務裡,我願意用多少 Risk 換多少 Coverage?
這個答案不應該由 evaluator 自己決定。
Evaluator 的責任是把 trade-off 算出來。
真正的 threshold policy,應該取決於:
這也是我目前不想隨便在 Quality Gate 裡寫一個固定 threshold 的原因。
Day 6 的邏輯其實沒有很複雜。
先取出每一筆:
confidence
correct
接著把 observed confidence values 由高到低排序。
對每一個 threshold:
selected = confidence >= threshold
然後計算:
Coverage
= selected_count / total_count
以及:
Risk
= selected_errors / selected_count
最後每一個 threshold 都形成一個 operating point。
概念上大概像:
for threshold in thresholds:
selected = [
record
for record in records
if record.confidence >= threshold
]
coverage = len(selected) / len(records)
errors = sum(
not record.is_correct
for record in selected
)
risk = errors / len(selected)
現在的 Foundation v1.1 會把結果整理成 structured metric。
每一個 point 都會留下:
threshold
coverage
risk
selected
errors
這樣未來真的要畫 Risk-Coverage Curve 時,就不用重新算一次。
既然名字都叫:
Risk-Coverage Curve
理論上今天好像應該直接畫一條圖。
但我這次刻意先沒有加 matplotlib。
因為目前這個專案還在建立 evaluator 的底層。
如果今天為了文章好看,先把資料計算、plotting、輸出邏輯全部混在一起,之後反而比較難整理。
所以現在我先讓 metric 產生 structured result。
例如:
{
"threshold": 0.82,
"coverage": 0.4,
"risk": 0.25,
"selected": 4,
"errors": 1
}
之後不管是:
都可以直接吃 artifact 裡的結果。
我現在希望慢慢維持一個原則:
Metric 負責計算,Visualization 負責顯示。
做到今天,我覺得 Day 5 和 Day 6 看起來很像,但其實在問不同的問題。
Day 5 的問題是:
系統知不知道什麼時候應該拒答?
所以我看的是:
Day 6 則更像:
如果我改變接受答案的 threshold,整個系統的 Risk 與 Coverage 會怎麼變?
Day 5 比較像在評估一個固定 policy。
Day 6 則開始評估:
不同 policy operating points。
這也讓 Reliability Lab 從「評分 AI」慢慢走向:
幫系統做部署決策。
這裡還是要再強調一次。
今天表格裡的:
0.98
0.93
0.88
...
全部都是 synthetic confidence。
它們是為了測試 evaluator 而設計的數字。
所以目前我能說的是:
Risk-Coverage evaluator 能根據 confidence ranking,正確計算不同 threshold 下的 Coverage 與 Risk。
但我還不能說:
某個真實模型在 threshold 0.82 時,Risk 是 25%。
更不能說:
AI 的 0.82 confidence 真的代表 82% 機率正確。
這兩件事情目前都還沒有做 real experiment。
做到這裡,我原本應該很開心。
因為現在已經可以做:
confidence threshold
↓
selected answers
↓
Coverage
↓
Risk
看起來好像已經有一套不錯的 selection policy。
但是我突然發現整套方法其實都建立在一個還沒有回答的假設上:
那個 confidence 到底可信不可信?
假設一個系統每次答錯,都還是給自己:
confidence = 0.99
那 Risk-Coverage evaluator 就算算得再準,也只是在使用一個很差的 score 排序。
甚至更進一步:
如果 AI 說:
我有 90% 把握。
這句話到底代表什麼?
真的表示:
類似情況下大約 90% 會答對?
還是只是模型隨口產生了一個看起來很有自信的數字?
這就是 Day 6 留下來最大的問題。
Day 5 做完 Abstention 時,我原本把重點放在:
AI 要學會說不知道。
但 Day 6 讓問題變得更完整。
一個真正實用的系統,不可能只追求最低錯誤率。
因為如果幾乎所有案例都拒答,Risk 再低也沒有意義。
真正需要處理的是:
在可以接受的 Risk 下,我能保留多少 Coverage?
今天的 Synthetic Demo 裡:
Coverage 10% → Risk 0%
Coverage 40% → Risk 25%
Coverage 60% → Risk 約 17%
Coverage 80% → Risk 25%
Coverage 100% → Risk 40%
也讓我看到 Risk-Coverage 並不是一條必然單調的線,而是會隨著被納入的案例正確與否產生變化。
目前 Reliability Lab 已經開始把:
Accuracy
Consistency
Format Compliance
Abstention
Coverage
Risk
Quality Gate
接成同一條工程思路。
但今天最後反而冒出一個更根本的問題:
如果我要靠 confidence 決定哪些答案值得相信,那我是不是應該先驗證 confidence 本身?
所以 Day 7,我要開始做:
Calibration。
下一步不只是問:
AI 有多有自信?
而是問:
AI 說自己有 90% 把握時,它真的大約有 90% 會答對嗎?