1. 从一次线上限流说起:路由策略到底解决什么问题
同一个业务代码,dev 环境跑本地 Ollama 的小模型,prod 环境调云上的 Qwen 大模型,中间还夹着一个 Azure 的 gpt-4o 做高质量场景——这种多模型服务架构现在很常见。问题也随之而来:某天下午云厂商突然限流,接口开始返回 429,业务侧直接雪崩;或者你想把自托管的 vLLM 换成另一家云,结果发现所有调用点都写死了 SDK 和地址,改一遍要动十几个文件。
这就是路由策略和引擎可替换性要解决的核心矛盾。业务代码不应该知道后端到底是谁,这个决策应该由网关一层统一完成。网关按模型名把流量分到不同后端,业务只认一套 OpenAI 兼容接口。后端可以分三层:本地推理(Ollama)、自托管 GPU(vLLM)、托管云 LLM(百炼、Azure、Bedrock 都是可替换实例,没有谁有特权)。
业务对这三层完全无感的前提是流量必须经过网关。如果某个服务直连供应商,后端切换就够不到它,零改动契约直接破灭。路由键就是模型名——local/*走本地推理,qwen*走百炼,gpt-*走 Azure,claude*走 Bedrock。模型名天然携带了"该去哪"的语义,比在业务里写 if-else 干净得多。而且模型名还是可扩展维度,后续加租户路由、成本路由都不用改动业务代码。
兼容层是关键中的关键。网关对外只暴露 OpenAI 兼容接口(/v1/chat/completions),业务用同一套 SDK 调用。背后是 Ollama 还是 vLLM,对业务是黑盒。兼容层让推理引擎变成可插拔组件,这是引擎可替换的技术前提。没有兼容层,换一家厂商就要换一套 SDK,零改动无从谈起。
引擎可替换性等于架构韧性。没有 GPU 时本地推理指向 Ollama,有 GPU 后指向 vLLM,换云厂商只改后端地址,某厂商限流就切另一家——这些场景下业务改动都是零。引擎选型是部署适配,不进架构主线,架构只保证"网关抽象了引擎"这一点。
但 fallback 不是简单切一下就行,它分三种情况,处理方式完全不同。同模型跨厂商(比如 Azure 和百炼都提供 gpt-4o 类能力),语义差异小,可以透明切换,metadata 记录一下就行。跨模型降级(大模型切到本地小模型),质量可能明显下降,业务侧需要感知,触发质量监控或人在回路。全挂(所有后端都不可用),服务中断,必须返回结构化错误,fail-closed。
关键决策是:切换不主动通知调用方。后端对业务无感正是引擎可替换的核心收益。但不通知不等于不可见,响应 metadata 可以带回证据,比如X-Upstream-Model和X-Fallback: true,调用方按需读取。跨模型降级的质量风险不靠路由自己解决,而是靠可观测体系接力——监控 golden signal 的质量退化,把 fallback 比例突增当异常信号,关键业务检测到走 fallback 模型时强制升级人工。
fallback 分级契约的落地缺口常在这里:全挂时绝不能返回降级模型的空壳或缓存旧答案冒充成功。调用方以为成功、实际是错的,比直接报错更危险。这一点在后面的配置和验证环节会反复强调。
2. TaoToken 前置:Base URL、Key 与模型 ID 三件套
在动手写 LiteLLM 路由配置之前,先把接入层的东西准备好。TaoToken 在这里扮演的是统一入口的角色,它提供 OpenAI 兼容接口,你拿到的是一套标准的 Base URL、API Key 和模型 ID,业务侧不需要为每个后端单独适配 SDK。
先明确三件套分别是什么。Base URL 是https://taotoken.net/api,注意这个地址不带任何查询参数,直接作为 OpenAI SDK 的base_url使用。API Key 在控制台的 API Keys 页面生成,格式是标准的sk-开头字符串。模型 ID 则是你在请求体model字段里填的值,比如gpt-4o、claude-3-5-sonnet、qwen-max这类。
如果你用的是 Claude Code 这类工具,配置方式略有不同。Claude Code 走的是 Anthropic 兼容协议,需要在 settings 里指定ANTHROPIC_BASE_URL和ANTHROPIC_API_KEY,模型 ID 填 Claude 系列。而 Cline、Cursor 这类走 OpenAI 协议的插件,填的是base_url和api_key,模型 ID 按插件要求填。Codex 的话,配置在auth.json里,同样是 Base URL、Key、Model ID 三件套。
这里要提醒一个常见误区:很多人以为接入就是"连上后就能用",实际上模型 ID 的写法直接决定路由走向。比如你填local/llama3,网关会按前缀路由到本地 Ollama;填qwen-max,路由到百炼;填gpt-4o,路由到 Azure 或对应后端。模型名就是路由键,写错了就走到错误的后端,报错信息往往还看不出根因。
获取 Key 的入口在控制台,生成后建议立刻复制保存,页面刷新后不再完整显示。如果你要做多环境隔离,可以生成多个 Key,dev 和 prod 分开,方便后续按 Key 维度做限流和审计。
接入文档里有各语言 SDK 的完整示例,Python、Node.js、Go 都有。核心就是把base_url指向https://taotoken.net/api,api_key填你的 Key,然后正常调用chat.completions.create。业务代码里不需要出现任何后端厂商的名字,这是引擎可替换性的起点。
对于长期编码和 Agent 场景,Coding Plan 提供了更稳定的配额和优先级,适合把路由层跑在生产环境。如果只是验证模型效果,模型对话页面可以直接试,不用写代码。排障和接入细节查接入文档,里面有各协议的对照表。
3. 可复制的 LiteLLM 路由配置片段
路由是通用能力,直接基于 LiteLLM 的配置实现,不需要自研。LiteLLM 的config.yaml支持声明式路由,把模型名、后端地址、fallback 策略都写进配置,业务代码零改动。
先看一个完整的config.yaml片段,覆盖本地 Ollama、百炼 Qwen、Azure gpt-4o 三个后端,以及 fallback 策略:
model_list: - model_name: local/llama3 litellm_params: model: ollama/llama3 api_base: http://localhost:11434 model_info: tier: local - model_name: qwen-max litellm_params: model: openai/qwen-max api_base: https://taotoken.net/api api_key: os.environ/TAOTOKEN_API_KEY model_info: tier: cloud - model_name: gpt-4o litellm_params: model: openai/gpt-4o api_base: https://taotoken.net/api api_key: os.environ/TAOTOKEN_API_KEY model_info: tier: cloud router_settings: routing_strategy: simple-shuffle fallbacks: - gpt-4o: ["qwen-max", "local/llama3"] - qwen-max: ["gpt-4o", "local/llama3"] context_window_fallbacks: - gpt-4o: ["qwen-max"] num_retries: 2 timeout: 30 allowed_fails: 3 cooldown_time: 60这段配置里几个关键点。model_list定义了逻辑模型名到实际后端的映射,model_name是业务侧看到的模型名,litellm_params里的model是 LiteLLM 内部识别的 provider 格式。注意api_base统一指向https://taotoken.net/api,api_key从环境变量读,不要硬编码在配置文件里。
router_settings里的fallbacks定义了降级链。gpt-4o失败时先试qwen-max,再试local/llama3。context_window_fallbacks处理的是上下文超限的情况,比如 gpt-4o 上下文不够时切到 qwen-max。num_retries是单后端重试次数,allowed_fails是触发熔断的失败阈值,cooldown_time是熔断后冷却秒数。
如果你用 TOML 格式(比如某些 Go 项目),等价配置长这样:
[[model_list]] model_name = "gpt-4o" [model_list.litellm_params] model = "openai/gpt-4o" api_base = "https://taotoken.net/api" api_key = "env:TAOTOKEN_API_KEY" [router_settings] routing_strategy = "simple-shuffle" num_retries = 2 timeout = 30 [router_settings.fallbacks] "gpt-4o" = ["qwen-max", "local/llama3"]启动 LiteLLM 代理:
export TAOTOKEN_API_KEY="sk-你的key" litellm --config config.yaml --port 4000业务侧调用时,base_url指向http://localhost:4000,模型名填gpt-4o,剩下的路由和 fallback 都由 LiteLLM 处理。业务代码里看不到 Azure、百炼、Ollama 任何一个名字。
这里有个容易踩的坑:model_name和litellm_params.model不要写混。model_name是业务调用的键,litellm_params.model是 LiteLLM 识别 provider 的格式。如果你把model_name写成openai/gpt-4o,业务调用时也得传这个全名,路由键就乱了。
另外,api_base末尾不要带/v1,LiteLLM 会自己拼接路径。带了/v1会变成/v1/v1/chat/completions,直接 404。这个错误在日志里表现为Not Found,但根因是地址拼接问题,排查时容易绕弯。
4. 验证请求与主备切换是否生效
配置写完不代表路由就对了,必须用请求日志验证主备切换是否真的生效。这一步很多人跳过,结果线上真出问题时才发现 fallback 根本没触发。
先验证基础请求能通。用 curl 直接打 LiteLLM 代理:
curl -X POST http://localhost:4000/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer sk-你的key" \ -d '{ "model": "gpt-4o", "messages": [{"role": "user", "content": "用一句话说明什么是路由策略"}] }'正常返回里会有choices数组,message.content是模型回复。同时看 LiteLLM 的日志输出,会打印实际命中的后端。如果日志里显示openai/gpt-4o且api_base是https://taotoken.net/api,说明主后端路由正确。
接下来验证 fallback。最直接的办法是临时把主后端的api_key改错,或者把api_base指向一个不存在的地址,然后重新发请求。观察日志里是否出现Fallback to qwen-max这类记录,以及最终返回是否成功。
更可控的方式是用 LiteLLM 的/health端点配合手动熔断。先连续发几次失败请求触发熔断:
for i in {1..5}; do curl -X POST http://localhost:4000/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer sk-你的key" \ -d '{"model": "gpt-4o", "messages": [{"role": "user", "content": "test"}]}' done当失败次数超过allowed_fails,该后端进入冷却。此时再发正常请求,日志里会显示走了 fallback 链。冷却时间过后,主后端恢复,请求重新回到主后端。
响应 metadata 里可以带回证据。LiteLLM 支持在响应头里加X-Upstream-Model和X-Fallback,需要在配置里开启:
litellm_settings: set_verbose: true json_logs: true request_timeout: 30开启后,每次请求的日志里会记录model、api_base、fallback_used等字段。把这些日志接到你的可观测体系,就能监控 fallback 比例。如果某段时间 fallback 比例突增,说明主后端有问题,需要告警。
验证跨模型降级时要注意质量差异。gpt-4o切到local/llama3后,回复质量可能明显下降。这时候业务侧如果对质量敏感,应该读取X-Fallback: true并触发人工审核或降级提示。不要假装没发生,调用方以为成功、实际是错的,比直接报错更危险。
全挂场景也要测。把所有后端的api_base都改错,发请求,确认返回的是结构化错误而不是空壳。LiteLLM 默认会返回 503 加错误详情,业务侧应该捕获这个错误并 fail-closed,而不是返回缓存旧答案冒充成功。
5. 本篇常见错排查:401、local proxy failed、reading choices、OAuth
配置和验证过程中,有几类报错反复出现,这里逐个对照排查。
401 Unauthorized。最常见的原因是api_key没传对。检查三点:环境变量TAOTOKEN_API_KEY是否 export 成功,config.yaml里是否写的是os.environ/TAOTOKEN_API_KEY而不是硬编码,业务侧请求头里的Authorization是否是Bearer sk-xxx格式。如果 Key 是从控制台复制的,注意不要带多余空格。还有一种情况是 Key 被禁用或过期,去控制台确认状态。
local proxy failed。这个报错通常出现在 LiteLLM 代理启动阶段,根因是端口被占用或配置文件语法错误。先检查 4000 端口是否被其他进程占用,lsof -i:4000看一下。如果是 YAML 缩进问题,LiteLLM 启动时会报解析错误,仔细检查model_list和router_settings的层级。TOML 格式的话,注意[[model_list]]是数组表,不要写成[model_list]。
reading choices 报错。这个错误信息通常是Error reading choices或choices is None,根因是后端返回的响应格式不符合 OpenAI 规范。常见于自托管 vLLM 或 Ollama 的版本不兼容。检查后端的 API 版本,Ollama 需要 0.1.30 以上才完整支持 OpenAI 兼容接口。如果是 vLLM,确认启动时加了--enable-auto-tool-choice等参数。另一个可能是api_base末尾多了/v1,导致路径拼接错误,返回了非 JSON 内容。
OAuth 相关报错。如果你用的是 Claude Code 或 Codex 这类工具,报错里可能出现OAuth token expired或invalid_grant。这类工具走的是 Anthropic 或 OpenAI 的 OAuth 流程,需要检查ANTHROPIC_BASE_URL是否指向https://taotoken.net/api,以及ANTHROPIC_API_KEY是否有效。Codex 的auth.json里,base_url和api_key要对应填好,模型 ID 填 Claude 或 GPT 系列。如果 OAuth 流程走不通,改用 API Key 方式接入,避免 token 刷新问题。
fallback 不触发。配置了fallbacks但主后端失败时没切。检查router_settings里的fallbacks键名是否和model_name完全一致,大小写敏感。另外num_retries和allowed_fails的配合要注意,如果num_retries设得太大,单后端会重试很多次才触发 fallback,看起来像没切。cooldown_time设得太短,后端刚熔断就恢复,也会导致 fallback 不稳定。
模型 ID 写错导致路由到错误后端。比如想走本地 Ollama 但填了llama3而不是local/llama3,网关找不到匹配的model_name,可能报model not found或走到默认后端。检查model_list里的model_name和业务请求里的model字段是否完全一致。
排查时建议开set_verbose: true,日志会打印每次请求的完整路由决策过程,包括命中的后端、重试次数、fallback 链。把日志级别调到 DEBUG,能看到 LiteLLM 内部的router模块输出,定位问题快很多。
6. 把路由层跑稳之后:接入入口与长期方案
路由配置跑通、fallback 验证生效之后,接下来就是把它接到真实业务里。接入入口有三个方向,按你的场景选。
如果你在排障或做接入,先去 API Keys 页面生成生产环境的 Key,然后对照接入文档把 Base URL、Key、Model ID 三件套填到你的 SDK 或工具里。文档里有 Python、Node.js、Go、Java 的完整示例,以及 Claude Code、Cline、Codex 的配置说明。注意生产环境的 Key 和 dev 分开,方便按 Key 维度做限流和审计。
如果你只是想验证某个模型的效果,或者对比不同后端的回复质量,直接用模型对话页面,不用写代码。选好模型 ID,输入 prompt,看返回结果和 metadata 里的X-Upstream-Model,确认路由走向符合预期。
如果你是长期编码或跑 Agent 场景,建议上 Coding Plan。路由层跑在生产环境对稳定性和配额要求更高,Coding Plan 提供了更稳定的优先级和配额保障,适合把 LiteLLM 网关作为常驻服务。配置方式不变,还是 Base URL、Key、Model ID 三件套,只是 Key 换成 Coding Plan 对应的。
最后提醒一个实操细节:LiteLLM 的配置文件建议纳入版本管理,但api_key不要提交。用环境变量或密钥管理服务注入。config.yaml里的fallbacks链要根据实际后端可用性定期 review,比如某个云厂商下线了某个模型,降级链要同步更新。路由策略不是配一次就完事,它跟着后端生态一起演进。
把路由层跑稳之后,业务代码里就再也看不到后端厂商的名字了。换引擎、切厂商、加降级,都只动网关配置,业务零改动。这才是引擎可替换性真正落地的地方。