AI 模型 Canary 真正缺的,通常不是另一張模型排行榜,而是一個「誰有資格成為下一個 production 預設」的控制器。供應商推出候選版本時,團隊很容易把 alias 改掉、看幾筆成功案例就放量;等品質、schema、延遲或成本出問題,才發現當時跑的是哪個 resolved model、哪份 prompt、哪個 provider endpoint 都說不清楚。
這篇從一份可執行的 provider-neutral acceptance lab 出發,教你把模型升級做成可稽核的 production promotion controller:先封存不可變的 Release Bundle,再以同一請求的 champion/candidate 配對收證,只有符合資格的健康配對才進入預先登記的 60/120/240 統計閘門;遇到設定漂移、端點故障或供應商事故時,則走另一條互斥的凍結路徑。最後的回滾只改變未來流量,並留下可重播、可驗證的 receipt。
DeepSeek 在 2026 年 8 月 13 日的官方發布紀錄公布 V4 Pro,可作為「新候選版本出現」的日期化例子;本文沒有證據證明 DeepSeek V4 Pro 0813 曾發生隱藏 alias swap 或供應商端 rollback,也不把社群猜測當事故報告。這正是 promotion controller 的價值:不知道供應商內部發生什麼時,仍能用自己封存的身分、流量與決策證據安全處置。
先說結論:上線不是改 alias,而是核准一份 Release Bundle
🧭 記憶把手:安全升級=不可變 Release Bundle + 同請求配對 + 預先登記閘門 + future-only route receipt。
模型名稱只是一個入口;真正能被核准的是「模型身分、provider、endpoint、client、prompt、tool schema、context policy、grader、資料與統計計畫」共同形成的內容位址工件。
本文刻意不再教一輪通用 Golden Tasks、Shadow Traffic 或模型挑選,而是補上既有文章之後缺少的 production 控制面:
- AI Evals處理「如何把 AI 產品變成可重跑評測」;本文只把封存的 10-task manifest 當離線入口工件。
- 10 題模型路由 Eval處理「先選哪個候選」;本文處理候選選完後,怎麼取得 production promotion 資格。
- OpenRouter Provider Pinning處理 provider 選擇;本文要求 provider、endpoint、client 與 resolved model 全部進入不可變 bundle。
- DeepSeek API 成本控制處理預算與切換護欄;本文把 cost-per-result 與 usage 完整性變成 promotion hard gate。
- Qwen3.8-27B 本機 Agent 驗收處理單一模型與 runtime 協定;本文不評某個模型,而是管兩個已解析 release 的升降級生命週期。
- Agentic Transaction處理不重複副作用與可對帳補償;本文的 receipt 只證明路由決策,不會撤銷已完成的外部副作用。
- Context Compaction 驗收處理壓縮造成的重取成本;本文把 context policy hash 封進 release,防止它偷偷和模型一起變動。
第一步:把候選封成 content-addressed Release Bundle
你可以把 Release Bundle 想成「這次升級的密封證物袋」。本次 lab 的 ReleaseBundle是 frozen、canonical、content-addressed;任何會影響輸出的東西都要先正規化,再算 SHA-256。只要其中一個 byte 改變,就得到不同的 bundle ID,舊的通過紀錄不能移植過去。
- 模型身分:requested alias、resolved snapshot ID、resolver receipt hash。
- 服務路徑:provider、明確 HTTPS endpoint、client version。
- 系統設定:champion/candidate release document、prompt、tool schema、harness、context policy、grader 與 config hash。
- 驗收輸入:10-task offline manifest、兩個 shadow sentinel、19-scenario fixture。
- 決策規則:60/120/240 檢查點、統計方法、品質 effect 門檻,以及 usage、latency、cost 等 operational caps。
{
"bundle_id": "88aea68983d5ace5548c5c46646a1dea6f5636c3b95bb3d515951cad348b648f",
"champion_release_sha256": "95d2f90269d5acc95ffe80b60a23bd78c6abba28d6452dc5c164b3e0c213d438",
"candidate_release_sha256": "131a08d869c432e34d51825129586f4272085d06d382feb3c60fe8d337222016",
"checkpoints": [60, 120, 240],
"familywise_alpha": 0.05,
"per_look_alpha": 0.016666666666666666
}
alias 可以留在 bundle 裡,方便人讀;但它不能是唯一身分。lab 會拒絕未解析 alias,也會拒絕 resolved ID 仍含 latest這類可變 token。白話說:你可以提出「production-model」,但必須同時附上「它在核准當下實際解析到哪個 snapshot」與解析收據。之後即使同名 alias 指向別處,也不能冒用原 bundle 的資格。
第二步:用單向生命週期鎖住 promotion 順序
控制器不是一串可以任意跳過的 if。它把 route 寫成明確生命週期:
DRAFT -> OFFLINE_PASSED -> SHADOW -> CANARY -> PROMOTED
-> ROLLED_BACK
integrity / endpoint / provider failure ---------> FROZEN
- DRAFT:註冊時是 generation 1,candidate allocation 為 0;光是成功建立 bundle 不會分到真人流量。
- OFFLINE_PASSED:封存的 10 個 task 必須全部通過同一份 grader,schema 與 safety failure 都是 0。這 10 題只是 entry gate,不是線上統計樣本;題目設計沿用AI Evals 教學,本文不重複展開。
- SHADOW:必須依序收到 bundle 內那兩個 sentinel request hash;candidate 的 side-effect count 必須為 0,ledger 也必須等於封存的 canonical empty-ledger hash。
- CANARY:才開始用固定 subject bucket 接受有限 candidate 流量;本 fixture 上限是 1,000 bps,也就是 10%。
- PROMOTED/ROLLED_BACK/FROZEN:三者是可稽核的 applied route phase。品質證據可導向 promotion 或 rollback;身分、transport、control integrity 問題則 freeze,不能偽裝成模型勝負。
Shadow 的零寫入 ledger 是必要證據,但不是魔法防護。真實系統仍要在外層用 sandbox、唯讀 credential 或 idempotent tool 阻止副作用;沒被 instrumentation 看見的寫入,controller 無法憑一個數字消除。

第三步:同一請求配對,避免把流量差異誤當模型差異
如果 champion 看的是週一客服單、candidate 看的是週五程式題,兩邊成功率不能直接比較。這份 controller 要求 same-request pair:同一個正規化 request hash 產生 baseline 與 candidate 兩臂結果,pair ID 重複提交時必須冪等;同一 ID 卻換了內容則拒絕。
每次 admission 還會釘住 phase、generation、arm、observed provider、endpoint、resolved model 與 client version。結果回來時再獨立帶一次這些欄位,controller 逐項比對 sealed release。這樣「送出時是 candidate A,回來時卻自稱 alias B」不會混進品質統計。
digest = sha256((experiment_salt + "\0" + subject_id).encode("utf-8")).digest()
bucket = int.from_bytes(digest[:8], "big") % 10_000
arm = "candidate" if bucket < candidate_allocation_bps else "baseline"
admission = {
"bundle_id": bundle_id,
"generation": current_generation,
"request_sha256": normalized_request_sha256,
"bucket": bucket,
"arm": arm,
"resolved_model_id": sealed_release[arm]["resolved_id"]
}
salted subject hash 讓同一個 subject 在同一 rollout 中黏在同一 bucket,避免使用者每次請求忽左忽右;candidate_allocation_bps又把爆炸半徑限制在預先核准範圍。這叫sticky bounded admission:sticky 解決體驗與歸因,bounded 解決風險,兩者缺一都不完整。
第四步:先做互斥歸因,再談模型品質
最容易做錯的地方,是把所有 failure 都塞進 candidate loss。lab 先依固定順序,把每一對 observation 放進唯一一個桶:
- CONFIG_DRIFT:provider、endpoint、client、resolved model 或 config 任一項和 bundle 不同,立即
FROZEN。這是實驗身分失效,不是模型退步。 - PROVIDER_OUTAGE:兩臂 transport 都失敗,包括共同 429/5xx,立即
FROZEN。共同事故絕不能算 candidate rollback 證據。 - ENDPOINT_FAILURE:只有 candidate 或只有 baseline transport 失敗,也在第一筆就
FROZEN。單臂端點完整性問題仍不是 paired quality。 - HEALTHY_PAIRED_QUALITY:只有兩臂 transport 都健康、身分完全相符,才有資格進入 60/120/240 品質母體。
健康 transport 之後還有 hard gates。usage 缺失時要明確記錄 usage_present=false,並讓 cost_microunits保持 JSON null後 freeze,不能偷補 0;baseline 發生 schema 或 safety failure 代表 control integrity 失效,也 freeze。candidate 的 schema/safety failure、延遲超過 800 ms,或成本超過 5,000 microunits,則是這份預先登記計畫中的 rollback 證據。800 與 5,000 是 fixture 的封存門檻,不是所有產品的通用數字。

第五步:60/120/240 的 exact paired gate 怎麼讀?
每個健康 pair 都有一個預先封存的 binary invariant 結果。兩臂都通過或都失敗時,這筆不提供「誰較好」的方向;真正進入 paired exact test 的是 discordant pairs:candidate 通過、champion 失敗記為 win,反過來記為 loss。
eligible checkpoints = [60, 120, 240]
effect = (candidate_wins - candidate_losses) / eligible_checkpoint
test = one-sided exact paired-binomial tail
= exact McNemar / sign-test form on discordant pairs
familywise alpha = 0.05
alpha per look = 0.05 / 3
absolute effect threshold = 0.10
Bonferroni 在這裡不是裝飾:因為事先核准三次 regression look,所以每次用 0.05/3,控制三次 regression early-stop 檢查這一個方向的 familywise 第一類錯誤;240 的 superiority 是另外預先登記、只在終點執行的判定,不能把前述 0.05/3說成同時涵蓋兩個方向的所有決策。只有符合 HEALTHY_PAIRED_QUALITY的 pair 才推進 60、120、240;config drift、outage、endpoint failure 與 integrity event 都不會被硬塞進分母。本 fail-closed lab 遇到它們會直接終止並 freeze。
- 60 或 120:若達到預先登記的 regression 證據,可以提早
ROLLBACK;即使 superiority p-value 很漂亮,也只能HOLD。 - 240:只有最終 horizon 同時通過品質與所有 operational gates,才可
PROMOTE。 - 240 仍沒有越過邊界:結果是
INCONCLUSIVE,不是偷偷挑另一個時間窗,也不是把「未證明退步」改寫成「已證明安全」。 - 第 241 個 eligible pair:controller 會拒絕,避免事後延長樣本直到得到想要的答案。
acceptance demo 故意製造 60 個 candidate loss、0 win,在第一個 checkpoint 得到 effect −1.0 與 exact one-sided p-value 約 8.67e-19,因此產生 quality rollback。這證明的是 controller 會依封存規則執行,不是某個真實模型真的退步。
第六步:Rollback 只切未來 admission,並用 CAS 留下收據
「回滾」常被誤解成時光倒流。這份 controller 的承諾更窄也更可驗證:rollback 接收 bounded reason、evidence hash、expected generation 與 idempotency key;取得 SQLite writer lock 後,重新驗證 live evidence,再用 generation compare-and-swap(CAS)把未來 candidate allocation 改為 0。
{
"idempotency_key": "rollback-demo-quality-001",
"previous": {"phase": "CANARY", "generation": 4, "candidate_allocation_bps": 1000},
"next": {"phase": "ROLLED_BACK", "generation": 5, "candidate_allocation_bps": 0},
"cutoff": {
"future_admissions_start_generation": 5,
"in_flight_policy": "pinned_to_admission",
"completed_effects_policy": "untouched"
}
}
route、terminal decision、lifecycle event 與 canonical receipt 在同一個 BEGIN IMMEDIATE transaction 內提交。generation 4 已入場的 candidate work 繼續綁在原 admission;已完成的 response 與副作用完全不動;generation 5 起的新 admission 才全部回 baseline。這和Agentic Transaction互補:前者切路由,後者處理外部操作的冪等、unknown 與補償,不能用一張 rollback receipt 取代。
同一程序正常重啟後,用相同 key 與相同 request 再呼叫,會讀回 byte-identical receipt,不新增 generation;同一 key 換 payload 會失敗;兩個 concurrent caller 則由 SQLite serialization 收斂成一次 commit。測試也注入「route update 後拋錯」,確認整個 transaction abort,不留下半張收據。這是 local SQLite 的正常 restart 與 transaction-abort 證據,不是分散式共識證明。
實作順序:把 controller 接進現有 model gateway
- 在 gateway 外先做 resolver adapter:把 requested alias 解析成 immutable ID,保存 resolver receipt;解析不完整就不註冊 bundle。
- 封存 release documents:champion 與 candidate 的 provider、endpoint、client、prompt、tool、context、grader、config 全部 canonicalize 後計算 bundle ID。
- 接既有離線 eval:把AI Evals輸出的 task manifest 當 entry artifact;controller 只驗證 exact 10/10、grader 綁定與零 schema/safety failure,不在這裡重寫題庫。
- 建立 pair adapter:對同一個 request 產生兩臂 observation;工具副作用必須在外層 sandbox 或 idempotent harness 中處理。
- 先跑 attribution,再跑 statistics:identity/transport 分支互斥;只有健康 pair 呼叫品質 gate。
- 把 route store 變成 generation CAS:每次 admission 讀到哪個 generation,就把它釘在 work record;promotion、rollback、freeze 都用新 generation 改未來 allocation。
- 保存 receipt 並演練 replay:部署前測相同 key 重播、conflicting key、兩個 concurrent caller 與 transaction abort,確認不會重複切換或留下半套狀態。
一開始不要直接接 production provider。先用固定 fixture 驗證狀態機、歸因與收據,再替每個 trusted integration input 加上真實的 resolver、usage、safety、grader 與 side-effect instrumentation。controller 能檢查它收到的資料是否和 bundle 一致,但不能憑空證明 upstream 說了真話。
AlphaLab 封存驗收證據:50 個測試、19 條 trace
本文的數字綁定 AlphaLab 內部同一份 checksum-locked acceptance artifact,不是根據文字大綱推測。封存內容包含完整 controller、demo、兩份 release document、19-scenario fixture、50 個正向與對抗式 unittest、兩組隔離 SQLite state、canonical report/receipt,以及統一索引它們的 acceptance manifest。封存 manifest 以 SHA-256 綁定各工件,避免混用版本。
封存驗收環境是 CPython 3.9.6、SQLite 3.51.0;50 個 unittest 全數通過,manifest 索引 19 個 acceptance traces/scenarios。驗收另在兩個全新目錄各跑一次 demo,逐 byte 比較 report 與 receipt,兩組輸出完全相同;transaction-abort、restart replay、concurrent caller 與多種 record corruption 也各有測試。
這不是公開下載套件。目前沒有可供讀者取得的 stable、checksum-bound bundle,所以本文只教 protocol、資料邊界與關鍵 excerpt;只靠文章片段不能重建或重跑那份私有 acceptance lock。controller 使用 Python standard library,測試 endpoint 刻意設為 .invalid;這組結果只證明「本地合成觀測下,封存的控制面按規則重現」,不代表真實 provider、模型或 production route 已被測過。
這份驗收證明什麼,又沒有證明什麼?
- 證明:不可變 bundle、生命週期、互斥歸因、eligible-only sequential gate、hard gates、sticky admission、future-only rollback 與 receipt replay 在這份封存 fixture 中可重現。
- 沒有測真實模型:lab 沒有呼叫、排名、promote、freeze 或 rollback 任何 provider 與 production route。
- 沒有證明跨 provider 因果:fixture 固定同一 provider、endpoint 與 client,只隔離兩個 pinned snapshot;跨 provider 比較需要另一份預先登記的 provider-effect control。
- 沒有把小樣本包裝成廣泛品質:10 個 offline tasks、2 個 sentinels、19 個控制情境與合成 pair 都不能代表整個真實世界。
- 沒有替 upstream 背書:resolver、usage、grader、safety verdict 與 side-effect ledger 是 trusted integration inputs;controller 驗一致性,不獨立測量外部真相。
- 沒有讓 Shadow 自動無副作用:零寫入 ledger 需要完整 instrumentation;未觀測到的 effect 仍要靠 sandbox 或 idempotency 防止。
- 沒有讓 rollback 撤銷過去:它只切 future admission;in-flight work 仍照 admission 執行,completed response/effect 不變。
- 沒有證明極端耐久性:SQLite 測試涵蓋正常 committed restart、concurrent caller、record corruption 與 injected abort;不涵蓋 SIGKILL、停電、儲存故障、network filesystem、replication 或 distributed failover。
- 沒有證明 DeepSeek 0813 被暗換或回滾:官方 availability 是候選事件的例子,隱藏 alias swap/server-side rollback 仍未經證實。
AI 模型 Canary 常見問題
1. 10 個 Golden Tasks 就是 Canary 的 10 個線上樣本嗎?
不是。10 個 task 是 OFFLINE_PASSED入口工件;線上品質 gate 使用另一批同請求 healthy pairs,第一個 checkpoint 是 60。兩個 pool 在 manifest 中明確分開。
2. Candidate 單邊 5xx 會自動判定模型退步並 rollback 嗎?
不會。單臂 transport failure 歸為 ENDPOINT_FAILURE並 FROZEN;兩臂同時失敗則是 PROVIDER_OUTAGE並 FROZEN。兩種都不進 paired quality,也不冒充模型 rollback。
3. 為什麼只計 eligible healthy pairs?
因為統計問題是「兩個已確認身分的健康模型回覆,誰較常通過同一 invariant」。若 request、provider、endpoint 或 transport 不同,資料回答的是另一個問題;混在分母只會讓 attribution 失真。
4. 60 筆時 candidate 明顯更好,可以直接 promote 嗎?
這份計畫不允許。60 與 120 只允許 regression early-stop;superiority 一律 HOLD。Promotion 只能在 240 個 eligible healthy pairs 的 final horizon 發生。
5. 240 筆沒有顯著退步,就代表可以上線嗎?
不代表。若最終沒有跨過預先登記的 promotion 或 regression 邊界,狀態是 INCONCLUSIVE。此外 schema、safety、usage、cost、latency 與 control integrity 仍各自有 hard gate。
6. 為什麼要同一請求配對,不能比較兩群使用者平均值?
同請求配對能消掉大量任務難度差異。兩群平均值仍會受 cohort、時間與流量組成影響;若產品只能做分群實驗,就要另訂適合該設計的統計計畫,不能沿用這份 exact paired gate。
7. Rollback 之後,已送進 candidate 的工作會被取消嗎?
不會。generation 4 已入場工作仍 pin 在原 admission;rollback 把 generation 5 起的 future allocation 改為 baseline。取消、補償與外部 side effect 對帳要由執行層另行處理。
8. 同一個 idempotency key 重送,會再切一次 route 嗎?
相同 request 不會。正常 restart 後會回傳原本的 canonical receipt bytes;同 key 不同 payload 會失敗;concurrent identical calls 會序列化成一次 commit。
9. 這份 lab 可以直接證明多供應商 fallback 安全嗎?
不能。本 fixture 故意固定一個 provider/endpoint/client 來隔離 snapshot effect。多供應商設計必須把 provider effect、資料路徑、錯誤型態與 fallback policy 另行封存與預先登記。
給實作者的 5 個決策規則
- 不要核准一個 alias;核准一個 content-addressed Release Bundle。
- 不要把 transport failure 記成 candidate loss;先走互斥 attribution。
- 不要讓非 eligible event 推進 60/120/240 horizon。
- 不要在 interim superiority 時提早 promote;證據不足就 HOLD,final 無邊界就 INCONCLUSIVE。
- 不要把 rollback 說成撤銷過去;用 generation CAS 切 future admission,並保存可重播 receipt。
接著閱讀
左右滑動查看更多推薦
結語:先讓升級留下證據,再讓流量往前走
下一次候選模型出現時,先不要問「要開幾%」。先封存 Release Bundle,確認 DRAFT 到 CANARY 的每個 entry artifact,再讓同一請求的 champion/candidate observation 通過互斥歸因與 hard gates。最後把 promotion、rollback 或 freeze 寫成新的 generation 與 canonical receipt。當模型名稱、provider route 或團隊成員改變時,你仍能回答三件事:當時核准的是什麼、哪份證據觸發決策、哪些流量從哪一代開始受影響。這才是 AI 模型 Canary 從實驗技巧升級成 production control plane 的分界。






