把模型切换交给平台:我用一层网关统一管 5 个模型的 API
项目做到第三个月,我发现自己攒了一堆「临时方案」。
代码里散着五处模型调用,写法各不相同:有两处是if model == "a"硬分支,有一处把 key 直接写在配置文件里,还有一处是同事塞进.env忘了同步。上周只是想换一个模型,我翻了五个文件才改完,改完还得逐个手测。
后来我把这件事交给了一层网关。原理不复杂:在本地跑一个 LiteLLM 代理,业务代码只认一个地址、一个 key、一套 OpenAI 兼容的接口,模型往哪走、走哪家、用什么参数,全丢给代理那一侧管。
这篇文章把搭建过程和我踩的坑记下来。
为什么值得多这一层
三个理由,都是实际撞上的。
第一个是换模型不用改代码。业务里写fast还是strong,具体对应哪家的哪个模型,只在配置里改一行,代码不动,也不用重新发版。
第二个是key 只留一份。以前五处调用五个 key,现在网关持有真 key,业务侧只拿一个虚拟 key。真 key 的暴露面小了很多,虚拟 key 还能随时吊销、单独限额。
第三个是调用日志能对账。以前月底看账单只知道总数,不知道是谁在调用。现在每个请求都带虚拟 key,按 key 分组就能看出哪个功能最费钱。
代价是多一层转发,首字延迟会涨一点;另外多了一个要维护的进程。对我们这种规模的项目,这笔交换划算。
装:一条 compose 起服务
我用 Docker 起的,图省事。
# docker-compose.ymlservices:litellm:image:ghcr.io/berriai/litellm:main-latestcontainer_name:litellmrestart:unless-stoppedports:-"4000:4000"volumes:-./config.yaml:/app/config.yamlenvironment:-LITELLM_MASTER_KEY=sk-换成你自己的command:["--config","/app/config.yaml","--port","4000"]起来之后curl http://127.0.0.1:4000/health能通,服务就算活着。
第一次起的时候我忘了挂config.yaml,容器是起来了,但所有模型调用全 404。它默认没有任何可用模型,却不报「配置缺失」,只报模型找不到 —— 很容易误判成 key 的问题,白查半天。
配置:五个模型怎么摆
config.yaml里最关键的是model_list。我的思路是先给别名,再绑上游:别名是稳定的,上游随便换。
model_list:-model_name:fast# 业务侧用这个名字litellm_params:model:deepseek/deepseek-chatapi_key:os.environ/DEEPSEEK_API_KEY-model_name:stronglitellm_params:model:openai/gpt-4o-miniapi_key:os.environ/OPENAI_API_KEY-model_name:local# 本地那台机器上跑的litellm_params:model:ollama/qwen2.5:7bapi_base:http://host.docker.internal:11434os.environ/XXX是让网关从环境变量里取 key,不把明文写在配置文件里,这点别省。
业务代码只剩一个地址
改完之后,业务侧就是这一小段:
fromopenaiimportOpenAI client=OpenAI(base_url="http://127.0.0.1:4000/v1",api_key="sk-虚拟key",# 不是上游真 key)resp=client.chat.completions.create(model="fast",# 别名,不是具体模型名messages=[{"role":"user","content":"..."}],)以后换模型,就改config.yaml里fast那一行的model,重启容器,代码一个字不用动。
我踩过的四个坑
流式输出会比直连慢一点。代理要等上游吐出第一段再转发,首字延迟多了几百毫秒。聊天类交互能感觉到,后台任务无所谓。
默认超时偏长。我印象里默认是 600 秒。做批量任务时,只要有一个上游抽风,整个请求能挂十分钟,后来我在客户端那侧单独设了短超时。
模型名里的 provider 前缀丢了会报错错方向。deepseek/deepseek-chat里的斜杠前缀是网关用来路由到具体厂商的。写成deepseek-chat,它会当成 OpenAI 的模型去找,最后抛出来的却是AuthenticationError: No api key passed in.—— 报错指向「key 不对」,真实原因其实是路由前缀没了。这个错误最容易把排查带偏。
虚拟 key 要单独生成。LITELLM_MASTER_KEY是管理用的,别直接塞进业务代码。业务侧统一用/key/generate发的虚拟 key,按用途发、按用途吊销。
最后算一笔账
这层东西本身不花钱,本地跑,内存占用很小。换来的是三件事:改模型不改代码、key 只维护一份、每个功能的调用量能查。
对我来说最值的是第三条。以前想优化性能只能凭感觉,现在至少知道调用量压在哪几个接口上。
自查清单
config.yaml已挂进容器,/health能通- 每个
model_name都是别名,不是具体模型名 - 每个
litellm_params.model都带 provider 前缀 - 上游 key 走环境变量,没写进配置文件
- 容器访问宿主机服务用了
host.docker.internal(Linux 需补extra_hosts) - 业务侧用的是虚拟 key,不是 master key
- 客户端侧单独设了短超时,不依赖默认值