模型字串(model string)是您程式碼庫中最危險的常數。團隊常將其視為標籤,但它卻決定了哪個檢查點(checkpoint)進行回答、呼叫成本為何,以及請求是否能成功。因此,AI 模型棄用與版本控制應納入您的發布流程,而非一次性的啟動決策。本指南使用 TokenLab 的公開模型 API(觀察於 2026-10-03),展示您可以自動檢查的項目、必須在其他地方驗證的內容,以及當 ID 停止運作時該如何處理。
重點摘要
- 生命週期資料存在於模型物件中。請從 Get a Model(觀察於 2026-10-03)讀取
tokenlab.lifecycle,而不是從部落格文章中複製日期。 - 我們在 2026-10-03 檢查的所有五個模型皆為
active,且沒有棄用或停用日期。我們的證據中沒有實際棄用的模型,因此下方的失敗路徑是記錄在案的行為,而非我們觀察到的實際案例。 - 缺失的模型會回傳
model_not_found,但狀態碼會因端點而異。請根據code進行分支判斷,切勿僅依賴狀態碼。 - 鎖定確切的 ID 並將其保留在一個設定層中。TokenLab 表示它不會自動替換您發送的模型。
- 備援(fallback)必須是經過測試且預先核准的選擇。不同模型之間的效能、請求格式、限制和價格皆有所不同。
AI 模型棄用與版本控制在 API 中的呈現方式
Get a Model 參考文件(觀察於 2026-10-03)指出 lifecycle 欄位涵蓋了可用性、發布日期、棄用日期和替換模型。List Models 參考文件(觀察於 2026-10-03)則在每個清單項目中增加了 tokenlab.lifecycle、tokenlab.aliases、tokenlab.commercial 和 tokenlab.badges。同一頁面列出了 tokenlab.accepted_request_formats 和 token 限制,這些對於任何模型更換都至關重要。
以下是我們在 2026-10-03 從 claude-sonnet-5 的即時模型 API 中讀取的內容。這是觀察值的濃縮呈現。我們的證據記錄了數值,而非原始的巢狀鍵名,因此請對照即時回應確認確切的鍵名。
GET https://api.tokenlab.sh/v1/models/claude-sonnet-5
category: chat
lifecycle stage: active
released: 2026-06-30
replacement model: None
deprecated_at: None
retired_at: None
max input tokens: 1000000
max output tokens: 128000
accepted formats: anthropic_messages, openai_chat_completions
將這五個生命週期概念對應到這些欄位,如下所示:
| 概念 | 觀察到的回應欄位 | claude-sonnet-5 的值 |
|---|---|---|
| 階段 (Stage) | lifecycle stage | active |
| 發布 (Release) | released | 2026-06-30 |
| 棄用 (Deprecation) | deprecated_at |
None |
| 停用 (Retirement) | retired_at |
None |
| 替換 (Replacement) | replacement model | None |
空值並不總是代表有問題。對於 glm-5.2 和 gpt-5.5,即時 API(觀察於 2026-10-03)顯示 released 為 None,而階段為 active。檢查程式應在設定了 deprecated_at、retired_at 或階段非 active 時失敗,而不應在缺少發布日期時失敗。
我們也讀取了 deepseek-v4-pro 和 deepseek-v4-flash(皆於 2026-04-24 發布,皆為 active,無替換模型)。這使得五個模型皆無棄用訊號。已棄用模型的替換欄位是我們唯一無法觀察到已填入數值的欄位。
當依賴項目出錯時會失敗的指令碼
下方的檢查程式僅使用已記錄的 GET /v1/models/{model} 端點,由於目錄是公開的,因此不需要 API 金鑰。它會在 ID 缺失、設定了 deprecated_at 或 retired_at、階段非 active,或是模型不再接受請求格式時失敗。
#!/usr/bin/env python3
"""當我們依賴的模型 ID 缺失、已棄用或已停用時失敗。"""
import json
import sys
import urllib.error
import urllib.request
BASE = "https://api.tokenlab.sh/v1/models/"
# 模型 ID -> 我們程式碼實際發送的請求格式
DEPENDENCIES = {
"gpt-5.5": "openai_chat_completions",
"claude-sonnet-5": "anthropic_messages",
"deepseek-v4-pro": "openai_chat_completions",
}
def fetch(model_id):
req = urllib.request.Request(BASE + model_id, headers={"Accept": "application/json"})
try:
with urllib.request.urlopen(req, timeout=15) as resp:
return resp.status, json.load(resp)
except urllib.error.HTTPError as exc:
try:
body = json.load(exc)
except ValueError:
body = {}
return exc.code, body
def check(model_id, fmt):
try:
status, body = fetch(model_id)
except urllib.error.URLError as exc:
return [f"could not verify: {exc.reason}"], {}, []
if status in (400, 404):
return [f"missing: HTTP {status}"], {}, []
if status != 200:
return [f"could not verify: HTTP {status}"], {}, []
info = body.get("tokenlab", {})
life = info.get("lifecycle") or {}
problems = []
if not life:
problems.append("no lifecycle block returned")
stage = life.get("stage") # 請對照即時回應確認確切的鍵名
if stage not in (None, "active"):
problems.append(f"stage={stage}")
for key in ("deprecated_at", "retired_at"):
if life.get(key):
problems.append(f"{key}={life[key]}")
if fmt not in info.get("accepted_request_formats", []):
problems.append(f"format {fmt} not accepted")
return problems, life, info.get("aliases", [])
def main():
failed = False
for model_id, fmt in DEPENDENCIES.items():
problems, life, aliases = check(model_id, fmt)
if problems:
failed = True
print(f"FAIL {model_id}: {'; '.join(problems)}")
if life:
print(f" lifecycle: {json.dumps(life)}")
else:
print(f"ok {model_id} aliases={aliases}")
sys.exit(1 if failed else 0)
if __name__ == "__main__":
main()
請在 CI 和夜間排程中執行此指令碼,因為模型可能會在您未進行 commit 的情況下變更狀態。想像一個夜間作業在週二退出代碼為 1,而您上一次部署是在一週前。這就是重點:失敗會出現在管線中,而不是客戶的聊天視窗中。該指令碼會在失敗時列印整個生命週期區塊,以便人工查看替換值,而無需猜測鍵名。
已停用或未知模型的回傳內容
證據記錄了未知和不可用的模型,而不是單獨標記為「已停用」的回應。因此,在您驗證之前,我們將停用視為同一類錯誤。以下是文件內容,皆觀察於 2026-10-03:
| 位置 | 回應 | 來源 |
|---|---|---|
GET /v1/models/{model} |
404 model_not_found |
Get a Model |
| 使用拼寫錯誤或不可用模型的聊天請求 | 400 model_not_found,可選包含 did_you_mean, suggestions, hint |
Errors agents can act on |
| 所選交付層級無供應量 | 503 all_channels_failed 或 503 delivery_tier_unavailable, retryable: false |
Errors agents can act on |
| 金鑰缺乏模型存取權限 | 403 model_not_allowed |
Handle API errors |
狀態碼在不同端點之間並不一致,因此一個修正重點很重要:我們原始文章的備援程式碼將 404 和 410 視為「識別碼已消失」。這對於聊天請求是錯誤的。記錄的聊天回應是 400,且錯誤指南建議透過 HTTP 狀態和 code 來處理錯誤。請根據 code == "model_not_found" 來編寫您的邏輯。
例如,文件顯示拼寫錯誤的 ID 會回傳 did_you_mean: "gpt-5.6-terra"。您的用戶端應按此順序反應:
- 不要重複發送未經修改的請求。錯誤指南將
400、403和404標記為不可重複。 - 將
did_you_mean視為建議。僅在您的產品已具備變更模型權限時才進行切換。 - 如果 503 回應主體顯示
retryable: false,請停止重試。文件指出名稱相似並不代表可用性。 - 將回應標頭中的 Request ID 與模型及您自己的作業 ID 一起儲存。
- 通知相關人員。已鎖定的 ID 消失是一個發布事件,而非暫時性錯誤。
鎖定確切 ID 或追蹤別名
最佳實踐指南(觀察於 2026-10-03)建議在每個請求中明確指定模型 ID,且不要自動切換模型。它警告說,替換模型可能會在未經程式碼變更的情況下改變品質、價格、上下文長度、工具或媒體支援。錯誤指南補充說,TokenLab 不會自動替換您發送的模型。
| 策略 | 行為 | 主要風險 |
|---|---|---|
鎖定確切 ID(如 gpt-5.5) |
您會獲得該模型,否則會收到錯誤 | 如果 ID 被移除,會導致嚴重失敗 |
| 追蹤別名 | 名稱隨時間推移可能指向新位置 | 行為和成本可能在未更動程式碼的情況下改變 |
| 設定檔中的內部路由名稱 | 您的程式碼請求 primary_chat;設定檔將其對應到確切 ID |
您需自行負責並測試對應關係 |
關於別名,我們發現了一個缺口。List Models 參考文件稱每個項目都有 tokenlab.aliases。我們擁有的五個即時摘錄(claude-sonnet-5, deepseek-v4-pro, deepseek-v4-flash, glm-5.2, gpt-5.5)中,沒有一個顯示別名值。我們無法告訴您這些模型今天有哪些別名。上述指令碼會為每個依賴項目列印 aliases,請執行它並閱讀輸出。
另外兩個目錄事實有助於理解。2026-10-02 的目錄將版本列為單獨的確切 ID,例如 claude-sonnet-4-6, claude-sonnet-5 和 claude-sonnet-5-5。並列並不代表其中一個是另一個的繼承者。replacement 值是唯一應該體現這種關係的地方。一個 ID grok-voice-latest 的名稱中包含 "latest"。我們沒有證據顯示它繼承自什麼,因此在信任它之前請檢查其模型頁面。市場風格的前綴 ID 並非 TokenLab 別名。遷移指南稱 TokenLab ID 不帶有 OpenRouter 提供者前綴,並建議將您自己的 LiteLLM 別名與 TokenLab ID 分開。
針對已消失之鎖定模型的備援模式
將備援視為您預先測試過的第二個鎖定模型。請針對每個呼叫點進行選擇,記錄哪個模型處理了每個回答,並使用相同的夜間指令碼進行驗證。在配對之前比較兩個模型:
| 模型 (即時 API,觀察於 2026-10-03) | 每 1M token 輸入/輸出 | 最大輸入/輸出 token | 請求格式 | 列出的能力 |
|---|---|---|---|---|
gpt-5.5 |
$1.50 / $9.00 | 1,000,000 / 128,000 | openai_chat_completions, openai_responses |
coding, json-mode, prompt-cache, reasoning, tool-use, vision |
claude-sonnet-5 |
$0.90 / $4.50 | 1,000,000 / 128,000 | anthropic_messages, openai_chat_completions |
json-mode, prompt-cache, reasoning, tool-use, vision |
deepseek-v4-pro |
離峰 $0.66 / $1.98;尖峰 $1.32 / $3.96 | 1,000,000 / 384,000 | anthropic_messages, openai_chat_completions, openai_responses |
json-mode, prompt-cache, tool-use |
這三者的定價最後更新於 2026-10-02T16:53:30.068Z。差異很重要。claude-sonnet-5 不接受 openai_responses,因此基於 Responses 的呼叫者無法備援至此。deepseek-v4-pro 未列出 vision 或 reasoning,因此帶有圖片的請求會在此失敗。DeepSeek 的尖峰時段為北京時間 09:00-12:00 和 14:00-18:00。
以下是成本估算範例。假設每日負載為 10M 輸入和 2M 輸出 token,這是我們的說明用數字,而非實際測量值:
gpt-5.5: 10 × $1.50 + 2 × $9.00 = $15 + $18 = 每日 $33。claude-sonnet-5: 10 × $0.90 + 2 × $4.50 = $9 + $9 = 每日 $18。deepseek-v4-pro離峰: 10 × $0.66 + 2 × $1.98 = $6.60 + $3.96 = 每日 $10.56。deepseek-v4-pro尖峰: 10 × $1.32 + 2 × $3.96 = $13.20 + $7.92 = 每日 $21.12。
這些僅為估算值,且快取輸入的定價方式不同。下方的程式碼僅在重試無法修復的錯誤時進行備援。429 或可重試的 5xx 錯誤應遵循正常的重試規則。
import logging
import os
import openai
log = logging.getLogger("model-routing")
client = openai.OpenAI(
api_key=os.environ["TOKENLAB_API_KEY"],
base_url="https://api.tokenlab.sh/v1",
timeout=30.0,
max_retries=0, # 我們自行決定什麼情況適合重複發送
)
ROUTES = {
"primary_chat": {"model": "gpt-5.5", "fallback": "claude-sonnet-5"},
"coding_agent": {"model": "claude-sonnet-5", "fallback": "deepseek-v4-pro"},
}
def _error(exc):
body = exc.body if isinstance(exc.body, dict) else {}
err = body.get("error", body)
return err if isinstance(err, dict) else {}
def _chat(model_id, text):
resp = client.chat.completions.create(
model=model_id,
messages=[{"role": "user", "content": text}],
)
return resp.choices[0].message.content
def call_model(route, text):
"""回傳 (回答的模型, 文字)。請記錄回答的模型。"""
cfg = ROUTES[route]
try:
return cfg["model"], _chat(cfg["model"], text)
except openai.APIStatusError as exc:
err = _error(exc)
code = err.get("code")
gone = code == "model_not_found"
no_supply = (
code in ("all_channels_failed", "delivery_tier_unavailable")
and err.get("retryable") is False
)
if not (gone or no_supply):
raise # 401/402/403/429 等錯誤需要不同的修復方式
log.warning("鎖定的模型 %s 不可用 (%s);使用 %s",
cfg["model"], code, cfg["fallback"])
return cfg["fallback"], _chat(cfg["fallback"], text)
備援只會跳轉一次,絕不連鎖。如果備援也失敗,請拋出錯誤並通知相關人員。靜默的連鎖替換是漂移問題的另一種形式。
AI 模型棄用與版本控制的發布檢查清單
在變更生產環境模型 ID 之前執行此清單。它呼應了最佳實踐指南(觀察於 2026-10-03)並增加了生命週期檢查。
- 列出所有生產環境中的模型 ID,並標註每個 ID 的呼叫點。一個設定層應該能在幾秒鐘內回答這個問題。
- 針對當前和候選 ID 執行生命週期指令碼。讀取
deprecated_at,retired_at, 階段和替換模型。 - 使用 Get a Model 中的
accepted_request_formats,確認候選模型接受您的程式碼所發送的請求格式。 - 對照您最長的實際請求和輸出上限,比較最大輸入和最大輸出 token。
- 比較能力。在產品依賴的情況下,確認 vision, tool-use, json-mode 和 reasoning。
- 根據當前價格重新計算成本,並附上計算過程。對於 DeepSeek,請分開計算尖峰和離峰時段。
- 使用您將要發布的確切候選 ID 和格式重播代表性請求。包含一個失敗案例,例如未知的 ID。
- 驗證錯誤路徑。您的用戶端應根據
code進行分支,保留 Request ID,且不要重試400,403或404。 - 將變更作為設定檔編輯發布,並將舊 ID 保留為記錄在案的回滾值。
- 檢查每個回應記錄是否儲存了實際處理該請求的模型。
- 發布後重新執行夜間指令碼。確認備援 ID 也通過檢查。
在文件未說明的地方,檢查清單就是保障。它們沒有提供棄用提前通知期,也沒有通知管道。請分開驗證提供者的通知,並檢查模型頁面是否有任何生命週期說明。
常見問題
如何判斷 TokenLab 模型是已棄用還是已停用?
呼叫 GET /v1/models/{model} 並讀取 tokenlab.lifecycle。檢查階段、deprecated_at、retired_at 和替換模型。截至 2026-10-03,我們讀取的五個模型皆為 active,且未設定日期。請再次輪詢,因為該值僅反映您詢問當下的狀態。
未知模型 ID 會回傳什麼 HTTP 狀態碼?
這取決於端點。Get a Model 回傳 404 model_not_found,而使用錯誤模型的聊天請求則回傳 400 model_not_found。在所選交付層級無供應量的模型會回傳 503。請根據 code 欄位進行分支判斷。
我應該重試 model_not_found 錯誤嗎?
不應該。重複相同的請求不會改變結果。如果存在 did_you_mean 和 suggestions,請檢查它們,但僅在您的產品已具備權限時才切換模型。否則,請使用預先核准的備援並通知您的團隊。
如果我的模型消失,TokenLab 會自動將我的請求切換到另一個模型嗎?
文件指出它不會自動替換您發送的模型。請發送明確的 ID 並自行處理錯誤。您的備援應該是一個經過測試的設定值,而不是對平台的假設。
在下一次模型變更之前,請在模型目錄中比較當前的 ID、限制和價格。
來源
價格觀測於 2026-10-03
- TokenLab Docs: List Models觀測於 2026-10-03
- TokenLab Docs: Get a Model觀測於 2026-10-03
- TokenLab Docs: Build a reliable TokenLab integration觀測於 2026-10-03
- TokenLab Docs: Handle API errors觀測於 2026-10-03
- TokenLab Docs: Errors agents can act on觀測於 2026-10-03
- TokenLab Docs: Migration Guides觀測於 2026-10-03
- TokenLab live model API: claude-sonnet-5觀測於 2026-10-03
- TokenLab live model API: deepseek-v4-pro觀測於 2026-10-03



