Day 30:稽核要問什麼、你要留什麼
這是什麼、解決什麼問題
稽核不會問技術問題。
不會問你用 KServe 還是 Seldon,不會問模型架構。 會問的是:
「這個在跑的模型,是誰決定要上線的?根據什麼?」
而這類問題的共同特徵是:答案要嘛當時留了,要嘛就是沒有。 沒有一題是可以事後補的。
這篇是七個問題,和每一題的證據長什麼樣。
什麼時候你會用到
- 內稽、外稽、主管機關檢查之前
- 模型上線之前(這才是有用的時機)
- 要設計證據保存機制
七個問題
Q1:線上跑的是哪一個模型、哪一版?
證據:
oc get isvc -A -o custom-columns=\
'NS:.metadata.namespace,NAME:.metadata.name,\
VER:.metadata.labels.model-registry/version,\
SHA:.metadata.annotations.model/sha256'
⚠️ label 是約定不是機制(Day 16 講過)。 要它可信,得有機制去驗——或至少留下驗過的紀錄。
而「約定」的實際下場長這樣——同一條指令在我自己的 lab 上跑:
NS NAME VER SHA
llm-serve-demo iris-sklearn <none> <none>
llm-serve-demo llm-platform <none> <none>
llm-serve-demo llm-scratch <none> <none>
三行全是 <none>,因為沒有人貼那個 label——包括我。
我的 isvc 上只有 opendatahub.io/dashboard=true 和兩個 KServe 自己加的 annotation。
稽核問「線上是哪一版」,而答案的來源是一個沒有人負責貼的 label, 那就等於沒有答案。
⭐ 比較可靠的做法是讓服務自己回答(Day 18 的做法):
curl -s https://<你的服務>/model
# {"serving_digest":"sha256:4d694be9…","in_registry":true,"status":"production",
# "metrics":{"test_loss":3.462,"perplexity":31.88}}
這個端點是跑在容器裡的程式算出來的,不是誰記得要貼的標籤。 label 當索引可以,當證據不行。
Q2:這一版是怎麼訓練出來的?
證據:pipeline run 的紀錄、訓練資料的 digest、參數、程式碼 commit。
這一題檢驗的是 Day 16 的血緣做得夠不夠。 只有路徑沒有 digest,等於答「大概是那份資料」。
Q3:誰核准它上線的?
證據:變更單、PR 的 approver、或部署紀錄裡的執行者。
⚠️ 這一題最常沒有答案,因為部署常常是「工程師手動 oc apply」。
技術上完全合理,稽核上完全不合格。
最低成本的補法:部署走 GitOps(Day 25), 那麼「誰按了 merge」就是核准紀錄。
Q4:上線前做了哪些檢查?
證據:Day 28 五道門各自的執行紀錄, 含「被擋下來」的案例。
全部都通過的紀錄說服力有限—— 稽核會想看門真的擋過東西。
Q5:模型上線後表現如何?
證據:監控的歷史資料、漂移報告、 以及「什麼都沒發生」的那段紀錄。
留存期限要和資料保存政策一致。
Q6:出問題時怎麼處理?
證據:回滾演練的紀錄(Day 19)、事故處理紀錄、 負責人是誰。
Q7:舊模型下線了嗎?
證據:Day 19 的下線清單, registry 的狀態不再是 LIVE。
⚠️ 這一題常常被漏,然後 registry 上有五個 LIVE 但實際只跑一個。 一份會說謊的清單,比沒有清單更糟——因為有人會相信它。
⭐ 哪些只能事前留
把七題分成兩類,這個分法比七題本身有用:
| 問題 | 事後補得回來嗎 | |
|---|---|---|
| Q1 | 線上是哪版 | ✅ 現在就查得到 |
| Q2 | 怎麼訓練的 | ❌ 當時沒記就沒了 |
| Q3 | 誰核准的 | ❌ 當時沒留就沒了 |
| Q4 | 做了哪些檢查 | ❌ 同上 |
| Q5 | 上線後表現 | ⚠️ 只能從現在開始 |
| Q6 | 怎麼處理問題 | ✅ 可以現在寫 |
| Q7 | 舊的下線了嗎 | ⚠️ 查得到,但對不對得起來要看紀錄 |
Q2、Q3、Q4 是硬的。 它們就是「當時做了沒有」,沒有第二次機會。
所以這篇真正的用途是在模型上線之前讀,不是在稽核前讀。
證據該怎麼留
| 原則 | 說明 |
|---|---|
| 自動產生 | 人工整理的證據,稽核前才生的那種,說服力最低 |
| 不可篡改 | 至少要有時間戳;理想上是 append-only |
| 可重現 | 給定同樣的輸入,能重新產生一次 |
| 有保存期限 | 太久也是風險(尤其含個資) |
| 找得到 | ⭐ 存在但找不到,等於沒有 |
最後一項最容易被低估。證據散在五個系統裡、每個都要不同權限, 實際上就是答不出來。
一個實用的做法:每次上線產生一份 manifest, 把七題的答案(或指標)收在同一份文件裡, 以模型的 digest 當主鍵。
怎麼確認你準備好了
| 檢查 | 判準 | |
|---|---|---|
| 1 | 七題都有答案 | 寫下來,不是「應該可以查」 |
| 2 | 證據是自動產生的 | 不是稽核前才整理 |
| 3 | Q2/Q3/Q4 的機制已就位 | ⭐ 這三題補不回來 |
| 4 | registry 沒有說謊 | LIVE 的數量對得上實際 |
| 5 | ⭐ 自己先演練一次 | 見下 |
第 5 項:
隨機挑一個線上模型,計時,把七題答完。
超過三十分鐘答不完,稽核當天就會有問題。
這個演練的價值不在於答完,在於它會精準指出你缺哪一塊。 而且它很便宜——一個下午。
常見問題
Q:這些是哪一部法規要求的? A:我沒有列法條,這是刻意的。 不同行業、不同國家適用的規範不同,而且會變。 這七題是共通的骨架——各種 AI 治理框架問的東西大同小異。 你的合規要求要去問法遵,不要問工程師(包括我)。
Q:留這麼多證據不會很麻煩嗎? A:自動產生就不麻煩。 麻煩的是人工整理。 如果你覺得麻煩,通常表示流程裡有太多手動步驟—— 那本身就是稽核會問的事。
Q:小團隊也要做到這樣嗎? A:看受不受監理。不受監理的話, Q1、Q2、Q7 是划算的最小集合—— 它們對你自己除錯的價值就超過成本了。
Q:稽核發現問題會怎樣? A:不知道,這超出我的經驗範圍。 但可以確定的是:「我們沒有紀錄」比「紀錄顯示我們做錯了」難處理—— 後者至少證明你有在管。
這七題,你們現在幾題答得出來?
🧪 這篇的實驗環境與 lab 檔案(最後更新 2026-08-30)
本篇在 ODH 3.6.0-ea.1 上對帳(共用區塊寫的 3.5.0 是 Day 1–9 的版本)。
叢集
- CRC 2.63.0 · OpenShift 4.22.7 · Kubernetes v1.35.6
- 單節點:13 vCPU / 40 GiB RAM / 120 GB disk
- 宿主:Framework Laptop 16(Ryzen AI 7 350 · 8C/16T · 64 GB RAM · 1 TB NVMe · RTX 5070 顯卡模組)
- ⚠️ 叢集內看不到 GPU(CRC 是 VM,RTX 5070 未 passthrough)
Operator
opendatahub-operator.v3.5.0← 即 RHOAI 3.x 的上游開源版cert-manager-operator.v1.20.0(3.x 的必要相依;2.x 不需要)openshift-pipelines-operator-rh.v1.23.2、openshift-gitops-operator.v1.21.3
DataScienceCluster 開啟的元件
kserve、aipipelines、dashboard、workbenches、modelregistry、kueue(Unmanaged)- 其餘(
ray/trustyai/feast/aigateway…)為Removed
叢集外的依賴(跑在宿主的 podman 上,crc start 不會帶起來)
- Harbor v2.15.2(私有 registry)· MinIO(S3)
模型端
- Python 3.12.13 · PyTorch 2.11.0+cu128 · FastAPI + uvicorn · prometheus-client
lab 檔案
- YAML/Containerfile/腳本:github.com/ryanGTR/openshift-ai-30days
(含 Day 對照表;主機名是佔位符,跑
set-lab-host.sh換成你自己的) - 服務的那個模型:llm-from-scratch(從零手刻的小 GPT)
⚠️ ODH ≠ RHOAI:元件同源,但 namespace 與部分名稱不同
(我這裡是 opendatahub,商用版是 redhat-ods-* 那一套)。
指令的邏輯可以照用,字串要自己對一次。
留言與指正
我特別想知道:這七題,你們現在幾題答得出來?