跳到主要內容

【2026 最新】AI 模型 Canary 上線教學:配對閘門、故障歸因與回滾收據

最後更新: ·
AI 模型 Canary 上線控制器教學:Release Bundle、配對閘門與回滾收據

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
  1. DRAFT:註冊時是 generation 1,candidate allocation 為 0;光是成功建立 bundle 不會分到真人流量。
  2. OFFLINE_PASSED:封存的 10 個 task 必須全部通過同一份 grader,schema 與 safety failure 都是 0。這 10 題只是 entry gate,不是線上統計樣本;題目設計沿用AI Evals 教學,本文不重複展開。
  3. SHADOW:必須依序收到 bundle 內那兩個 sentinel request hash;candidate 的 side-effect count 必須為 0,ledger 也必須等於封存的 canonical empty-ledger hash。
  4. CANARY:才開始用固定 subject bucket 接受有限 candidate 流量;本 fixture 上限是 1,000 bps,也就是 10%。
  5. PROMOTED/ROLLED_BACK/FROZEN:三者是可稽核的 applied route phase。品質證據可導向 promotion 或 rollback;身分、transport、control integrity 問題則 freeze,不能偽裝成模型勝負。

Shadow 的零寫入 ledger 是必要證據,但不是魔法防護。真實系統仍要在外層用 sandbox、唯讀 credential 或 idempotent tool 阻止副作用;沒被 instrumentation 看見的寫入,controller 無法憑一個數字消除。

AI 模型 Canary promotion controller 的 Release Bundle 與 DRAFT、OFFLINE PASSED、SHADOW、CANARY、PROMOTED、ROLLED BACK、FROZEN 生命週期
每一個 phase 都綁定同一份 Release Bundle;只有 CANARY 收集健康配對,身分或服務路徑異常則進入 FROZEN。

第三步:同一請求配對,避免把流量差異誤當模型差異

如果 champion 看的是週一客服單、candidate 看的是週五程式題,兩邊成功率不能直接比較。這份 controller 要求 same-request pair:同一個正規化 request hash 產生 baseline 與 candidate 兩臂結果,pair ID 重複提交時必須冪等;同一 ID 卻換了內容則拒絕。

每次 admission 還會釘住 phasegenerationarm、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 放進唯一一個桶:

  1. CONFIG_DRIFT:provider、endpoint、client、resolved model 或 config 任一項和 bundle 不同,立即 FROZEN。這是實驗身分失效,不是模型退步。
  2. PROVIDER_OUTAGE:兩臂 transport 都失敗,包括共同 429/5xx,立即 FROZEN。共同事故絕不能算 candidate rollback 證據。
  3. ENDPOINT_FAILURE:只有 candidate 或只有 baseline transport 失敗,也在第一筆就 FROZEN。單臂端點完整性問題仍不是 paired quality。
  4. 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 的封存門檻,不是所有產品的通用數字。

AI 模型 Canary 互斥歸因流程,依序區分 CONFIG DRIFT、PROVIDER OUTAGE、ENDPOINT FAILURE 與 HEALTHY PAIRED QUALITY
先排除身分與 transport 問題,剩下的健康同請求配對才進品質閘門;因此 outage 不會被誤報成 candidate 回歸。

第五步: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

  1. 在 gateway 外先做 resolver adapter:把 requested alias 解析成 immutable ID,保存 resolver receipt;解析不完整就不註冊 bundle。
  2. 封存 release documents:champion 與 candidate 的 provider、endpoint、client、prompt、tool、context、grader、config 全部 canonicalize 後計算 bundle ID。
  3. 接既有離線 eval:AI Evals輸出的 task manifest 當 entry artifact;controller 只驗證 exact 10/10、grader 綁定與零 schema/safety failure,不在這裡重寫題庫。
  4. 建立 pair adapter:對同一個 request 產生兩臂 observation;工具副作用必須在外層 sandbox 或 idempotent harness 中處理。
  5. 先跑 attribution,再跑 statistics:identity/transport 分支互斥;只有健康 pair 呼叫品質 gate。
  6. 把 route store 變成 generation CAS:每次 admission 讀到哪個 generation,就把它釘在 work record;promotion、rollback、freeze 都用新 generation 改未來 allocation。
  7. 保存 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_FAILUREFROZEN;兩臂同時失敗則是 PROVIDER_OUTAGEFROZEN。兩種都不進 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 個決策規則

  1. 不要核准一個 alias;核准一個 content-addressed Release Bundle。
  2. 不要把 transport failure 記成 candidate loss;先走互斥 attribution。
  3. 不要讓非 eligible event 推進 60/120/240 horizon。
  4. 不要在 interim superiority 時提早 promote;證據不足就 HOLD,final 無邊界就 INCONCLUSIVE。
  5. 不要把 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 的分界。

ALPHALAB 社群

有問題?來 Telegram 聊

和 Terry、編輯、其他網友一起討論這篇文章。提問、分享觀點,回覆更即時。

加入 Telegram 討論

📩 訂閱 AlphaLab 電子報

每週最多兩封,收到週報精選與關鍵 Alpha Signal。

我們不會 spam,隨時可退訂。