Day 9 跑第一個正式 REAL benchmark 時,我遇到了一個很現實的問題。
不是模型答錯。
不是 calibration 算壞。
而是:
網路斷了。
當時完整 benchmark 原本需要 48 次 API request,但第一輪跑到第 37 次時網路中斷。
結果是:
同一批 12 筆 REAL records 使用 2~6 個 bins 計算 ECE,本次資料得到完全相同的結果;這主要與 confidence 高度集中、取值離散有關,不能解讀成 ECE 一般而言不受 binning 影響。
最後我只能等網路恢復後,把完整的 48 次 request 從頭再跑一次。
也就是說,那一天實際總共送出了 85 次 request。
這件事當時看起來只是「網路不好」。
但回頭看,其實暴露的是 AI Reliability Lab 自己的一個 reliability 問題:
如果未來一個實驗有 100 題、500 題甚至更多,難道只要第 499 次 request 斷線,就要全部重新開始?
所以 Day 11 我沒有再增加新的 calibration metric。
今天反而回頭修實驗基礎設施本身。
目標只有一個:
讓 REAL Experiment 可以 checkpoint,也可以在中斷後安全 resume。
一開始我以為 resumable experiment 可能很簡單。
例如跑到第 30 題,就記:
current_question = 30
下一次從第 31 題開始。
但真正開始設計後,發現這樣不夠。
因為目前一個 benchmark case 不只會呼叫模型一次。
Day 9 的每一題包含:
所以即使同樣是 D9-009,也可能出現:
如果只記錄「跑到 D9-009」,根本不知道這題裡哪些 request 已經真的成功過。
所以 Day 11 把一個 case 再拆成更小的 logical request slot。
例如 D9-009 可以變成:
D9-009:primary
D9-009:sample:1
D9-009:sample:2
每一個 slot 都有 deterministic identity。
這樣 checkpoint 不再只是記錄「做到哪一題」,而是可以精確知道:
哪些 logical request 已經成功完成?
Resume 時,只要看到某個 slot 已經存在,就不應該再次送出。
接下來的問題是:
如果要讓實驗恢復,需要把什麼寫進 checkpoint?
這次保存的資訊包含:
store 設定但有一些東西刻意不存:
這裡其實延續了前幾天一直在做的原則:
為了 reproducibility 保存需要的資訊,但不要為了方便把敏感資料一起留下來。
假設昨天用:
gpt-5.6-luna
今天卻把模型改成另一個模型。
或者昨天:
sample_count = 3
今天變成:
sample_count = 5
如果程式看到 checkpoint 就直接繼續跑,最後的 dataset 其實是兩種不同設定混在一起。
表面上看起來完整,實際上實驗已經失去一致性。
所以 Day 11 加入了 experiment identity validation。
Resume 之前會檢查:
只要 identity-defining setting 不一致:
拒絕 resume。
不是偷偷繼續。
也不是偷偷建立另一種設定的資料。
這件事比「可以續跑」本身還重要。
因為 reliability 不只是程式不要 crash,也包括:
crash 之後不要悄悄產生一份方法ologically 不一致的資料。
還有另一種滿尷尬的情況。
假設 API request 成功了,程式正在更新 checkpoint,結果這時程式被強制關掉。
如果直接對 checkpoint 原檔寫入,有可能最後留下半個 JSON。
下一次 resume 時,程式看到檔案存在,卻不知道這是一個有效 checkpoint,還是寫到一半的檔案。
所以 Day 11 也補上 atomic write。
概念是:
先寫 temporary file,確認寫入完成後,再 replace 正式檔案。
這不能解決世界上所有 filesystem failure,但至少比直接覆寫正式 checkpoint 安全很多。
而最後公開 REAL output 也採取類似概念:
實驗還沒完整結束時,不會直接把半成品放到正式 public run directory。
只有所有 logical requests 都存在之後,才進行 final publication。
在真的花 API 額度之前,我先用 fake provider 測試中斷與恢復。
Day 11 最後完整 test suite:
93 passed in 2.13s
其中 resumable runner 至少驗證了這些情況:
這些都通過後,才進 REAL smoke test。
這次我沒有等待網路真的再斷一次。
而是加入一個 controlled interruption mechanism。
它的用途很單純:
故意在成功完成 N 個新的 logical requests 後停止。
這不是 fake provider failure。
API request 還是真的。
只是 runner 被要求:
做到這裡先停。
REAL smoke experiment 使用四個 Day 9 已經存在的 case:
每題這次使用:
因此:
4 cases × 3 logical requests = 12 logical requests
第一次執行:
| 項目 | 數量 |
|---|---|
| Planned logical requests | 12 |
| Newly completed | 5 |
| API attempts | 5 |
| Remaining | 7 |
跑完五個之後,runner 按照設定停止。
這時候最重要的事情有兩個。
第一:
Checkpoint 還在。
第二:
正式 public REAL dataset 還沒有被發布。
也就是說,系統知道這是一個 incomplete experiment。
它不會因為已經拿到五個成功回答,就讓外面看到一份看起來像完整 run 的資料。
接著重新啟動同一個 run。
Runner 先讀 checkpoint。
確認 experiment identity 完全一致後,找到五個已經完成的 logical request。
所以這五個:
直接 skip。
第二個 session 最後是:
| 項目 | 數量 |
|---|---|
| Previously completed and skipped | 5 |
| Newly completed | 7 |
| API attempts | 7 |
加起來:
5 + 7 = 12
整個 REAL experiment 的 API attempts 最後就是:
12。
不是:
5 + 12 = 17
而且最後檢查 12 個 logical request ID:
沒有任何 duplicate。
這就是 Day 11 最重要的驗證結果。
這次四個 case 最後也正常組裝成完整 record:
| Case | Primary | Correct | Self Confidence | Samples | Agreement |
|---|---|---|---|---|---|
| D9-001 | Paris | Yes | 1.0 | Paris, Paris | 1.0 |
| D9-006 | 40 | Yes | 1.0 | 40, 40 | 1.0 |
| D9-009 | 20 | No | 1.0 | 20, 20 | 1.0 |
| D9-011 | INSUFFICIENT_INFORMATION | Yes | 1.0 | INSUFFICIENT_INFORMATION ×2 | 1.0 |
但這裡要特別說:
這四筆不是拿來做新的 calibration evidence。
今天的目的不是比較模型表現。
就算 D9-009 又答錯,而且這次三個回答全部都是 20,也不應該把四題拿去算新的 ECE 然後下結論。
今天真正被測的是:
實驗 runner 自己。
目前採用的 policy 是:
如果 run 還沒完成:
checkpoint 保留。
因為下一次需要靠它 resume。
但當實驗已經完整完成,而且 final public output 已經成功發布後:
completed checkpoint 刪除。
這次 REAL smoke test 最後 checkpoint 也確實成功清掉。
正式留下的是:
metadata.json
records.jsonl
resume_summary.json
也就是最終可公開、非敏感,而且能描述這次 experiment 的資料。
不是。
這是今天一定要講清楚的限制。
目前 runner 可以保證:
已知已經成功完成並寫入 checkpoint 的 logical request,在 validated resume 時不會被故意重新送出。
但還存在一個更麻煩的 edge case。
假設發生:
從 client 的角度,它只能看到:
這個 slot 沒完成。
下一次 resume 時,這個 request 仍然可能再次送出。
所以 Day 11 做的不是 distributed system 意義下的「真正 exactly-once delivery」。
比較精確的說法是:
Validated resume prevents known completed logical requests from being intentionally reissued.
這個限制我反而覺得值得保留。
因為如果我把它寫成「現在 API experiment 保證 exactly once」,那其實是在隱藏一個很重要的 distributed systems 問題。
回頭看整件事滿有趣。
Day 9 第一次 benchmark 斷網的時候,我只覺得:
好吧,只能再跑一次。
結果最後:
37 次 request 作廢,完整 benchmark 又重新跑了 48 次。
到了 Day 11,我才真正把這件事當成工程問題。
現在 runner 的流程變成:
REAL request → success → sanitized result → atomic checkpoint
如果中斷:
Checkpoint → identity validation → skip completed slots → continue remaining requests
全部完成後:
Private staging → validate completeness → publish final REAL output → remove completed checkpoint
這讓之後 benchmark 規模開始變大時,不需要每次都賭:
希望這幾百次 request 中間都不要出任何事情。
今天沒有增加新的 calibration metric。
也沒有增加新的 benchmark。
反而花了一整天改善「實驗怎麼可靠地跑完」。
最後完成:
完整測試:
93 passed
REAL smoke:
Day 9 的斷網原本只是一次失敗。
Day 11 之後,它變成 AI Reliability Lab 的一個正式能力。
而且我開始慢慢覺得:
AI Reliability 不應該只是在研究模型會不會出錯。
拿來研究模型的那套系統,本身也必須值得信任。