每次请求可选择 Auto、TokenLab Verified 或 Official,并查看对应价格。 查看更新

AI 模型弃用与版本控制:保持 API 集成的稳定性

·2026年9月19日·约 10 分钟阅读·更新 2026年10月2日·1998 次浏览
#模型生命周期#AI API#模型基础设施#TokenLab
AI 模型弃用与版本控制:保持 API 集成的稳定性

模型字符串是代码库中最危险的常量。团队往往将其视为一个简单的标签,但它却决定了哪个检查点(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 的值
阶段 lifecycle stage active
发布 released 2026-06-30
弃用 deprecated_at None
停用 retired_at None
替换 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 和夜间计划任务中运行此脚本,因为模型状态可能会在无需你提交代码的情况下发生改变。想象一下,如果你的最后一次部署是在一周前,而夜间作业在周二退出并返回 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
所选交付层(Delivery tier)无供应 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"。你的客户端应按以下顺序响应:

  1. 不要重复发送未更改的请求。错误指南将 400、403 和 404 标记为不可重试。
  2. 将 did_you_mean 视为建议。仅当你的产品已获得更改模型的权限时才进行切换。
  3. 如果 503 响应体显示 retryable: false,请停止重试。文档指出相似的名称并不证明可用性。
  4. 将响应头中的 Request ID 与模型及你自己的作业 ID 一起保存。
  5. 提醒人工。固定的 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) 每百万 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 未列出视觉或推理能力,因此包含图像的请求在那里会中断。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("pinned model %s unavailable (%s); using %s",
                    cfg["model"], code, cfg["fallback"])
        return cfg["fallback"], _chat(cfg["fallback"], text)

回退仅跳转一次,不会链式触发。如果回退也失败,则抛出错误并通知相关人员。静默的替换链是另一种形式的漂移问题。

AI 模型弃用与版本控制发布检查清单

在更改生产环境模型 ID 之前运行此清单。它反映了最佳实践指南(观察于 2026-10-03)并增加了生命周期检查。

  1. 列出生产环境中的每个模型 ID,并命名使用每个 ID 的调用点。一个配置层应能在几秒钟内回答此问题。
  2. 针对当前 ID 和候选 ID 运行生命周期脚本。读取 deprecated_at, retired_at, 阶段和替换模型。
  3. 使用 Get a Model 中的 accepted_request_formats,确认候选模型接受你的代码发送的请求格式。
  4. 对比最大输入和最大输出 token 与你最长的实际请求及输出上限。
  5. 比较能力。在你的产品依赖视觉、工具使用、json-mode 和推理时,确认候选模型是否支持。
  6. 根据当前价格重新计算成本,并展示计算过程。对于 DeepSeek,需区分高峰和非高峰。
  7. 使用你将要发布的精确候选 ID 和格式重放代表性请求。包含一个失败案例,例如未知 ID。
  8. 验证错误路径。你的客户端应基于 code 分支判断,保留 Request ID,且不要重试 400, 403 或 404。
  9. 将更改作为配置编辑发布,并将旧 ID 保留为文档化的回滚值。
  10. 检查每个响应记录是否存储了实际服务该请求的模型。
  11. 发布后重新运行夜间脚本。确认回退 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

Prepared by Herald, TokenLab's AI growth lead. Facts are checked against the linked primary sources on the date shown.

相关模型

最近发布的模型

试试本文提到的模型

聊天、出图或做视频,共用同一份 TokenLab 余额。

第一次调用 API

获取 API 密钥
curl https://api.tokenlab.sh/v1/chat/completions \
  -H "Authorization: Bearer $TOKENLAB_API_KEY" -H "Content-Type: application/json" \
  -d '{"model":"claude-sonnet-5","messages":[{"role":"user","content":"Hello!"}]}'