昨天我們談到 Cache 與回應加速,解決了「同一問題反覆查詢」造成的延遲與成本浪費。
但 LLM 應用還有另一個大挑戰:
👉 模型與知識庫版本要怎麼管理?
在 Day 13 我們談過「知識會過期」的問題:當文件內容更新時,需要偵測 Data Drift 並重建知識庫。而今天我們會談談 LLM 與知識庫(Knowledge Base / Index)要如何安全地做版本切換:
因此,我們需要一個 Registry,讓系統能清楚知道「現在線上用的是哪個版本」,並支援 升級、灰度、回滾、稽核追蹤。
⚠️ 註:在本文中,「模型」泛指外部 LLM API 版本與知識庫版本。
在多數企業場景中,知識庫(KB)更新的頻率通常高於 LLM API 版本切換;
但對於「靜態知識庫、偏追最新模型」的場景,情況未必成立。
案例 | 描述 | 來源 |
---|---|---|
GPT-4o 更新回滾(2025-04-29) | 2025 年 4月,OpenAI 因近期更新導致 ChatGPT 呈現「過度奉承/過度贊同」(sycophantic)傾向而回退版本,並說明後續會調整個性與訓練流程。 | The Verge(存取日期:2025-10-06) |
GPT-5 語氣微調2025-08-19) | GPT-5 上線後,OpenAI針對使用者回饋調整語氣更友善、避免過度討好;屬官方產品調整方向的新聞摘要。 | PCWorld (存取日期:2025-10-06) |
Anthropic Claude 品質下降[註] | 社群使用者反映 Opus 4 / 4.1 輸出品質波動;Anthropic 在 Reddit 發文表示已修復數個問題並持續監控。此為社群回報+官方回應帖,非第三方評測。 | Reddit (存取日期:2025-10-06) |
[註] 以社群貼文為主、並非官方聲明
👉 這些案例清楚顯示:就算模型來自外部 API,沒有完善的版本管理,升級也可能帶來風險。
面向 | 目的 / 解決的痛點 | 典型做法 / 欄位 | 例子以及指標 |
---|---|---|---|
版本管理 (Versioning) | 追蹤線上版本、有問題時可以快速回滾 | model_version , kb_version , tags , created_at |
gpt-4o-mini-2024-08-06 、kb-2025-09-14 |
治理 (Governance) | 避免不同團隊各自亂接模型、黑箱 | 模型清單、描述、Owner、審批流程(RBAC) | owner=ml-team , approved_by=rm1, rm2 , approvals_required=2 , policy=unique_prod |
可追溯性 (Traceability) | 追 Bug/成本暴衝來源 | audit_log (誰在何時做了什麼)、meta (指標)、version (哪個版本) |
detail.rollback_to=true 、eval_f1=0.89 |
部署一致性 (Consistency) | Dev/Stg/Prod 一致 | 階段標籤:None / Staging / Production / Archived |
嚴禁跳階部署、唯一 Production |
合規性 (Compliance) | 回答「當時跑哪個版本」 | 不可變審計(append-only)、絕對時間與版號 | 金融/醫療審核、RCA 報告引用 |
一個簡單的自建登錄表,可以包含:
欄位 | 說明 | 範例 |
---|---|---|
model_name |
模型用途名稱 | customer-support-bot |
provider |
模型供應商 | openai / anthropic |
model_version |
API 版本或日期 | gpt-4o-mini-2024-08-06 |
kb_version |
知識庫 / 索引版本 | kb-2025-09-14 |
endpoint |
呼叫 API 的 URL | https://api.openai.com/v1/chat/completions |
pricing |
成本資訊 | prompt: $0.15/M, completion: $0.60/M |
status |
階段狀態 | Staging / Production / Archived |
工具/平台 | 定位 | 優勢 | 限制/注意 | 典型場景 |
---|---|---|---|---|
MLflow Model Registry | 開源模型登錄 | 版本化、階段標籤(Staging/Prod)、API 友善 | 需自行維運、RBAC/審批要自加 | 自建 MLOps、內部平台 |
Weights & Biases (W&B) | 實驗追蹤+模型管理 [註1] | 可視化實驗、指標對照、簡單版本治理 | 偏實驗管理;審批/唯一 Prod 要自行設計 | 研發迭代快、重視實驗史 |
AWS SageMaker Model Registry | 雲上託管 [註2] | 與 SageMaker/CI/CD 深整合、權限好管 | 綁雲、跨雲/混合場景較受限 | 全託管 AWS 場景 |
Azure ML / Vertex AI Registry | 雲上託管 | 與各自雲服務無縫整合、治理能力佳 | 鎖定雲供應商 | 已使用對應雲 AI 服務 |
自建簡化版(DB/Git + API) | 輕量治理 | 高彈性、能客製唯一 Prod、審批、審計 | 要自行設計 Schema/交易/備援 | 需要貼合業務流程與多模型/多 KB 管理 |
[註1] 定位:實驗追蹤+模型管理。2024 年後官方逐步強化 Model Registry 與版本治理能力;若要唯一 Production/審批流,仍常見自訂擴充。
[註2] 雖與 SageMaker 訓練整合佳,亦可從外部來源(如 S3)註冊模型,不必限制於 SageMaker 訓練作業。
Registry 工具選型表
⚠️ 本文 Demo 的重點在於「Registry 管理流程」,
不包含實際模型推理或知識庫檢索,只模擬登錄與狀態管理。
因為篇幅關係,完整可執行專案一樣會放在 GitHub,完整的細節請看
README.md
。
以下用一個 faq-bot
模型走完:註冊 ➜ 上線 v1 ➜ 建立 v1.1 金絲雀 ➜ Promote/Rollback ➜ 封存 ➜ 稽核查驗,所有操作都透過 REST API 完成,並且能用 API 管理模型清單,例如:
Production
模型的資訊 Demo 會存在
SQLite
,正式上線時請參考後面的資料庫比較表選型
curl -sX POST localhost:8000/models -H 'Content-Type: application/json' \ -d '{"name":"faq-bot","owner":"hazel","description":"Q&A bot"}' | jq
重點:相同模型只需註冊一次,之後所有版本都掛在這個模型底下。
執行結果:
❯ curl -sX POST localhost:8000/models -H 'Content-Type: application/json' \ -d '{"name":"faq-bot","owner":"hazel","description":"Q&A bot"}' | jq
{
"id": 1,
"name": "faq-bot",
"owner": "hazel",
"description": "Q&A bot",
"created_at": "2025-09-14T07:04:35.965212"
}
None
)並逐步上線階段流轉規則:
None → Staging → Production → Archived
直接跳階(例如None → Production
)會被拒絕。
# 建立 v1.0.0(stage 預設 "None")
curl -sX POST localhost:8000/models/faq-bot/versions -H 'Content-Type: application/json' \
-d '{"version":"1.0.0","artifact_url":"s3://bucket/faq-bot/1.0.0","tags":["baseline"],"meta":{"commit":"a1b2c3"}}' | jq
# None → Staging
curl -sX POST localhost:8000/models/faq-bot/versions/1.0.0/transition \
-H 'Content-Type: application/json' -d '{"to_stage":"Staging","actor":"hazel"}' | jq
# Staging → Production(正式上線)
curl -sX POST localhost:8000/models/faq-bot/versions/1.0.0/transition \
-H 'Content-Type: application/json' -d '{"to_stage":"Production","actor":"hazel"}' | jq
執行結果:
# 建立 v1.0.0(stage 預設 "None")
❯ curl -sX POST localhost:8000/models/faq-bot/versions -H 'Content-Type: application/json' \
-d '{"version":"1.0.0","artifact_url":"s3://bucket/faq-bot/1.0.0","tags":["baseline"],"meta":{"commit":"a1b2c3"}}' | jq
{
"id": 1,
"version": "1.0.0",
"stage": "None", # 預設 "None"
"artifact_url": "s3://bucket/faq-bot/1.0.0",
"tags": [
"baseline"
],
"meta": {
"commit": "a1b2c3"
},
"created_at": "2025-09-14T07:05:08.323930",
"updated_at": "2025-09-14T07:05:08.323934"
}
# None → Staging
❯ curl -sX POST localhost:8000/models/faq-bot/versions/1.0.0/transition \
-H 'Content-Type: application/json' -d '{"to_stage":"Staging","actor":"hazel"}' | jq
{
"id": 1,
"version": "1.0.0",
"stage": "Staging",
"artifact_url": "s3://bucket/faq-bot/1.0.0",
"tags": [
"baseline"
],
"meta": {
"commit": "a1b2c3"
},
"created_at": "2025-09-14T07:05:08.323930",
"updated_at": "2025-09-14T07:06:11.279993"
}
# Staging → Production(正式上線)
❯ curl -sX POST localhost:8000/models/faq-bot/versions/1.0.0/transition \
-H 'Content-Type: application/json' -d '{"to_stage":"Production","actor":"hazel"}' | jq
{
"id": 1,
"version": "1.0.0",
"stage": "Production",
"artifact_url": "s3://bucket/faq-bot/1.0.0",
"tags": [
"baseline"
],
"meta": {
"commit": "a1b2c3"
},
"created_at": "2025-09-14T07:05:08.323930",
"updated_at": "2025-09-14T07:06:37.803683"
}
A/B 只在 metadata 與 tags 內登記,不做流量實作;這樣最精簡、也便於延伸。
# 建立 v1.1.0(預設 None)
curl -sX POST localhost:8000/models/faq-bot/versions -H 'Content-Type: application/json' \
-d '{"version":"1.1.0","artifact_url":"s3://bucket/faq-bot/1.1.0","tags":["canary"],"meta":{"commit":"d4e5f6","traffic_share":20}}' | jq
# None → Staging(開始 A/B)
curl -sX POST localhost:8000/models/faq-bot/versions/1.1.0/transition \
-H 'Content-Type: application/json' -d '{"to_stage":"Staging","actor":"hazel"}' | jq
唯一 Production 規則:同一模型同時間只能有一個
Production
。
當 v1.1.0 Promote 至Production
,v1.0.0 會被自動降級為Staging
。
curl -sX POST localhost:8000/models/faq-bot/versions/1.1.0/transition \
-H 'Content-Type: application/json' -d '{"to_stage":"Production","actor":"hazel"}' | jq
執行結果:
# 把 v1.1.0 升級到 production
❯ curl -sX POST localhost:8000/models/faq-bot/versions/1.1.0/transition \
-H 'Content-Type: application/json' -d '{"to_stage":"Production","actor":"hazel"}' | jq
{
"id": 2,
"version": "1.1.0",
"stage": "Production",
"artifact_url": "s3://bucket/faq-bot/1.1.0",
"tags": [
"canary"
],
"meta": {
"commit": "d4e5f6",
"traffic_share": 20
},
"created_at": "2025-09-14T07:07:27.098726",
"updated_at": "2025-09-14T07:08:30.322485"
}
# 可以看到 v1.0.0 被降級回 staging
❯ curl -s localhost:8000/models/faq-bot/versions | jq 'map({version,stage})'
[
{
"version": "1.0.0",
"stage": "Staging"
},
{
"version": "1.1.0",
"stage": "Production"
}
]
rollback_to
審計旗標)
rollback_to
是審計用旗標,不改變邏輯;真正回滾就是把目標版本 Promote 成Production
。
curl -sX POST localhost:8000/models/faq-bot/versions/1.0.0/transition \
-H 'Content-Type: application/json' \
-d '{"to_stage":"Production","actor":"hazel","rollback_to":true}' | jq
效果:
Production
Staging
/audit
會多一筆 detail.rollback_to=true
的 transition
記錄執行結果:
# 把 v1.0.0 重新推到 production
❯ curl -sX POST localhost:8000/models/faq-bot/versions/1.0.0/transition \
-H 'Content-Type: application/json' \
-d '{"to_stage":"Production","actor":"hazel","rollback_to":true}' | jq
{
"id": 1,
"version": "1.0.0",
"stage": "Production",
"artifact_url": "s3://bucket/faq-bot/1.0.0",
"tags": [
"baseline"
],
"meta": {
"commit": "a1b2c3"
},
"created_at": "2025-09-14T07:05:08.323930",
"updated_at": "2025-09-14T07:10:12.279054"
}
# 驗證 v1.1.0 回到 staging
❯ curl -s localhost:8000/models/faq-bot/versions | jq 'map({version,stage})'
[
{
"version": "1.0.0",
"stage": "Production"
},
{
"version": "1.1.0",
"stage": "Staging"
}
]
# 查看 audit logs
❯ curl -s localhost:8000/audit | jq
[
...,
...,
{
"id": 8,
"model_name": "faq-bot",
"version": "1.0.0",
"action": "transition",
"actor": "hazel",
"from_stage": "Staging",
"to_stage": "Production",
"detail": {
"rollback_to": true # <--- 可以看到 rollback_to = true
},
"created_at": "2025-09-14T07:10:12.278070"
}
]
curl -sX POST localhost:8000/models/faq-bot/versions/1.1.0/transition \ -H 'Content-Type: application/json' -d '{"to_stage":"Archived","actor":"hazel"}' | jq
注意:
Archived
後的模型不能再 Promote。
執行結果:
❯ curl -sX POST localhost:8000/models/faq-bot/versions/1.1.0/transition \ -H 'Content-Type: application/json' -d '{"to_stage":"Archived","actor":"hazel"}' | jq
{
"id": 2,
"version": "1.1.0",
"stage": "Archived",
"artifact_url": "s3://bucket/faq-bot/1.1.0",
"tags": [
"canary"
],
"meta": {
"commit": "d4e5f6",
"traffic_share": 20
},
"created_at": "2025-09-14T07:07:27.098726",
"updated_at": "2025-09-14T07:12:57.089496"
}
# 想要將被 archived 的模型直接升級到 Production 環境會被拒絕。
❯ curl -sX POST localhost:8000/models/faq-bot/versions/1.1.0/transition \
-H 'Content-Type: application/json' -d '{"to_stage":"Production","actor":"hazel"}' | jq
{
"detail": "Illegal transition Archived → Production"
}
# 查目前 Production(應為 v1.0.0)
curl -s localhost:8000/models/faq-bot/production | jq
# 列所有版本與狀態
curl -s localhost:8000/models/faq-bot/versions | jq 'map({version,stage,tags,meta})'
# 篩出「回滾」transition(detail.rollback_to = true)
curl -s localhost:8000/audit | \
jq 'map(select(.action=="transition" and (.detail.rollback_to==true)) |
{version, from_stage, to_stage, detail})'
執行結果:
# 查目前 Production(應為 v1.0.0)
❯ curl -s localhost:8000/models/faq-bot/production | jq
{
"id": 1,
"version": "1.0.0",
"stage": "Production",
"artifact_url": "s3://bucket/faq-bot/1.0.0",
"tags": [
"baseline"
],
"meta": {
"commit": "a1b2c3"
},
"created_at": "2025-09-14T07:05:08.323930",
"updated_at": "2025-09-14T07:10:12.279054"
}
# 列所有版本與狀態
❯ curl -s localhost:8000/models/faq-bot/versions | jq 'map({version,stage,tags,meta})'
[
{
"version": "1.0.0",
"stage": "Production",
"tags": [
"baseline"
],
"meta": {
"commit": "a1b2c3"
}
},
{
"version": "1.1.0",
"stage": "Archived",
"tags": [
"canary"
],
"meta": {
"commit": "d4e5f6",
"traffic_share": 20
}
}
]
❯ # 篩出「回滾」transition(detail.rollback_to = true)
curl -s localhost:8000/audit | \
jq 'map(select(.action=="transition" and (.detail.rollback_to==true)) |
{version, from_stage, to_stage, detail})'
[
{
"version": "1.0.0",
"from_stage": "Staging",
"to_stage": "Production",
"detail": {
"rollback_to": true
}
}
]
# 跳階:None → Production(應被拒)
curl -sX POST localhost:8000/models/faq-bot/versions/2.0.0 -H 'Content-Type: application/json' \
-d '{"version":"2.0.0"}' | jq
curl -sX POST localhost:8000/models/faq-bot/versions/2.0.0/transition \
-H 'Content-Type: application/json' -d '{"to_stage":"Production"}' | jq
執行結果:
# 先建立 v2.0.0 模型
❯ curl -sX POST localhost:8000/models/faq-bot/versions -H 'Content-Type: application/json' \
-d '{"version":"2.0.0","artifact_url":"s3://bucket/faq-bot/2.0.0","tags":["baseline"],"meta":{"commit":"a1b2c4"}}' | jq
{
"id": 3,
"version": "2.0.0",
"stage": "None",
"artifact_url": "s3://bucket/faq-bot/2.0.0",
"tags": [
"baseline"
],
"meta": {
"commit": "a1b2c4"
},
"created_at": "2025-09-14T07:18:12.723062",
"updated_at": "2025-09-14T07:18:12.723065"
}
# 想要從 None 直接推到 Production 會被拒絕
❯ curl -sX POST localhost:8000/models/faq-bot/versions/2.0.0/transition \
-H 'Content-Type: application/json' \
-d '{"to_stage":"Production","actor":"hazel"}' | jq
{
"detail": "Illegal transition None → Production"
}
以上就是最小可跑的 Model Registry 操作流程演示。
💡 想更符合真實模型上限的場景的話,可以將 線上 KPI(如 eval_f1
, latency_p95_ms
, traffic_share
)寫入 meta
,在 Promote 模型之前做 Stage Gate 自動檢查;或把唯一性從「自動降級」改成「嚴格禁止,需要人工降級」以符合更嚴謹的治理流程。
在多模型、多團隊、需嚴格治理與審計的情境成立。
小團隊或低流量可考慮 Git+YAML 作為輕量 Registry;單一模型、簡單路由亦可用 DynamoDB 等 KV/NoSQL(搭配條件寫入與 TransactWriteItems)滿足需求。隨規模與治理需求再演進至 RDB。
交易與一致性(ACID):確保 Promote / Rollback 不出錯
資料約束(Constraints) :把規則寫死在 DB 層
複雜查詢(Query Flexibility):找到正確的候選版本
稽核與追蹤(Auditability):回答「誰在什麼時候上線了什麼」
遷移與治理(Schema Evolution):讓 Registry 可持續演進
關聯式資料庫能提供 強一致性、嚴格約束、靈活查詢、審計能力與持續演進,這些正是 Registry 管理的核心需求。
選項 | 一致性/交易 | 查詢複雜度 | 維運 | Production 版本鎖定 | JSON / Tags 查詢[註1] | 成本 | 推薦指數(1~5⭐) |
---|---|---|---|---|---|---|---|
Postgres (RDS/Aurora) | 強 | 高 | 中(託管後低) | Partial Unique 直接做到 | JSONB+GIN 高效 | $$ ~ $$$ (常駐實例/叢集、IOPS/儲存) |
⭐⭐⭐⭐⭐ |
MySQL (RDS/Aurora) | 強 | 中 | 中(託管後低) | 無 Partial;要生成欄位/觸發器[註2] | JSON 索引較弱 | $$ (同級通常比 PG 稍省) |
⭐⭐⭐ |
DynamoDB | 中(有事務)[註3] | 低~中 | 低(Serverless) | 條件寫入+TransactWrite/鎖定旗標 | 以鍵設計/GSI 間接達成 | $ → $$$ (低流量很省;高 QPS/多 GSI 昂貴) |
⭐⭐⭐ |
MLflow / SageMaker 等 | 由平台決定 | 中 | 低(託管) | 平台內建 | 平台內建 | $ ~ $$ (平台代管;Artifact/請求用量計費) |
⭐⭐⭐⭐ |
[註1] 這欄「JSON / Tags 查詢」之所以重要,是因為模型登記常要靠可變的中繼資料 (Metadata)與語義標籤來做日常營運決策與治理,像是:
零 schema 變更地記錄指標與脈絡:eval_f1
、dataset_sha
、traffic_share
、framework
、commit
、region
…實驗不斷增加欄位,用 JSON 最彈性。
快速篩選/路由:找出task=faq 且 tag=canary 且 f1>0.88
的版本,用於上線、回滾或灰度放量。
治理與稽核:標記approved
,owner
,risk_level
,用標籤與 JSON 查詢切出合規清單與審計報表。
可發現性:跨團隊用關鍵字/標籤搜尋,減少口耳相傳與維運盲點。[註2] MySQL 8.0.13+ 可用 Functional/Generated Index 模擬 Partial Unique。做法:建立產生欄位或函數索引,僅在
stage='Production'
時產生非空值,配 UNIQUE 約束達成「每模型僅一個 Production」。
[註3]一致性/交易:有限事務支援(TransactWriteItems,最多 25 項);非完整 ACID(無 RDB 等級的隔離級別如 READ COMMITTED)
Q:我們不自己訓練模型,還需要 Registry 嗎?
A:需要,但重點會放在知識庫 / 索引版本。Registry 幫你把「LLM 選型 + 知識庫版本」當作可觀測、可回滾的單位;多數回滾都只需切換知識庫的版本。
Q:model_version
和 kb_version
要怎麼搭配?
A:常見做法是固定 model_version
(外部 API),用 kb_version
做金絲雀與回滾;遇到供應商大更新才動 model_version
。
今天我們把焦點放在 模型版本與週期管理 (含知識庫版本管理)。
它的角色,就像「LLM 與知識庫的版本控制中心」:確保任何時候,線上跑的都是唯一可控的組合(模型 API + 知識庫版本),並且所有升級、回滾都有紀錄可查:
我們談到:
有了 Registry,我們就能同時管理 外部模型版本 與 知識庫版本,並且確保系統在演進的過程中 更新有紀錄、問題能回滾、團隊能協作。這是企業級 LLM 應用走向成熟的必備基礎建設。
👉 明天(Day 23),我們會進一步探討 重新訓練與持續學習 (Retraining & Continual Learning) :如何讓模型隨著資料更新而持續進步。