最近帮团队把整个模型接入链路从直连各家API统一改成了走LiteLLM,前后花了一个下午,第二天就用起来了。消息传开后,几个组的同事轮流来找我聊同一件事:这东西到底是干啥的、值不值得在服务链路里多加一层、接完之后日常怎么维护。问的人多了,我就把这几天从验证到落地的完整过程整理成了一篇,里面既有配置细节,也有实测后才发现的问题。如果你正被"多个模型API格式不统一、切换成本高、密钥和预算管不住"这些事困扰,这篇文章应该能帮你少走不少弯路。
1. 先弄明白一件事:接入多家大模型,真正的成本在哪
1.1 所谓"换模型",换的其实是协议而不是模型
很多人以为接新模型最麻烦的是模型效果调优,我实际做下来发现,真正耗时间的反而是API层的适配。各个厂商给出的接口看着都差不多,实际细节差异很大。
举个最简单的例子,对话补全接口的参数命名和返回结构在不同厂商之间就存在差异。有的返回里直接带usage统计,有的要单独再查一次;有的流式返回格式和普通返回完全两套;有的消息里角色字段叫"role",有的模型走兼容接口时还要求额外传一些特定参数。更不用说各家SDK的版本、错误码、超时机制都不一样。
我最初给项目做了个简单封装,把对接的模型一个个适配成内部统一格式。业务方调用时传模型名加消息,由封装层翻译成各家格式。一开始只支撑两个模型,封装还算薄,后来需求多了,要接不同形态的开源模型,或者同一模型走不同部署渠道时,封装层开始疯狂膨胀。每个新模型都要写适配、做测试,还要处理各家接口偶尔的breaking change。到那一步我才意识到,接入和维护的成本根本不是模型本身,而是那些不断变化的API差异。
1.2 LiteLLM解决的核心问题:把API方言收敛成一种
LiteLLM做的事情说白了就是在一堆"API方言"之上提供一个统一入口,把各家协议翻译成同一套规范。它的输出格式基本上照着目前大家最熟悉的OpenAI SDK兼容接口来设计,也就是说,只要你之前调用过OpenAI格式的接口,接LiteLLM几乎不需要重新学。
它有两种工作方式。一种是作为Python SDK直接在项目里用,进程内完成翻译和转发;另一种是起一个独立的代理网关服务,所有请求先到网关,网关再按配置把请求分发到真实的模型厂商。两种方式我后面都会展开讲,但先记住一个核心结论:LiteLLM的价值在于把"模型接入"从业务代码里剥离出来,变成一层独立的基础设施。
这层基础设置一旦立起来,你切换模型、调整降级策略、配置预算上限的时候,就不需要去改动每一条业务代码了。业务方只知道自己在调用一台"模型网关",网关后面到底接的是哪个厂商、有没有做负载均衡、是不是走了备用链路,业务方完全不关心。
1.3 什么时候你会明显感受到这个收敛的价值
说个我实际遇到的案例。某次要上线一个新功能,我们提前把一个问题较多的模型流量切到了另一个更强的新模型。当时因为配置了LiteLLM网关,这次切换在网关层只改了配置,模型名对应的真实后端变了,上层业务代码一行没动。
如果还是旧架构,这种切换至少涉及SDK更换、请求格式调整、超时和重试参数重新对齐,前后怎么也要一到两天。而通过网关,切换动作缩短到几分钟。再考虑到线上还要灰度、观察监控、出问题回滚,这种"把切换变成改配置"的能力,才是它真正值钱的地方。
2. 网关模式和SDK模式,自己适合哪一种
2.1 代理网关模式:24小时在线的"翻译官"
代理网关模式下,LiteLLM作为一个独立服务运行,你把它部署在能访问到各个模型厂商API的服务器上。业务方所有请求统一发给这个网关,网关读取配置文件里登记的模型列表,把请求按模型路由到对应的厂商。
这个模式的最大优点是多语言友好。不管业务是Python、Java、Go还是Node.js,统一通过HTTP调用,不需要在每种语言里都装一遍LiteLLM的SDK。对团队协作也非常有利:不同小组可以共用同一套模型入口,密钥、预算、各种限制都集中在网关层管理,不用把厂商的key分发到每个服务的环境变量里。
网关模式还天然适合做"模型运维"的操作面。今天某个模型上游不稳定,你可以在网关配置里临时把流量切到备用模型;某个月度预算快用完了,直接在配置里调低限额;想知道每个应用各自调用了多少量,网关日志里都有。这些事情如果散落在多个服务的代码里,协调起来会非常痛苦。
2.2 进程内SDK模式:适合脚本和轻量工具
SDK模式则是把LiteLLM作为Python库引入项目,调用它的chat.completion这类方法,方法内部同时完成格式翻译、模型路由和请求转发。它的优势是部署极其简单,没有额外服务,也省去了一次HTTP跳转的延迟。
但代价也很明显。第一,只有Python项目能用,而且要处理依赖版本,LiteLLM的依赖和某些框架的依赖偶尔会有冲突;第二,统一管控能力弱,每个部署了SDK的进程都是独立的,密钥管理、预算统计各算各的,形不成全局视角。我自己的经验是,SDK模式适合快速验证想法、写离线脚本、做notebook实验,或者项目本身很小、不打算引入基础设施。
2.3 我的选型建议
这里给一个比较直接的判断标准:如果你只是自己一个人调试模型,SDK模式够了;如果你有一个服务要长期跑在线上,或者有多个服务都要调模型,又或者几个人共用一组模型,启动网关模式。网关模式前期多出来的部署工作量非常小,一个配置文件加一个进程而已,但它把后面所有的模型管理都收拢到了一个点上。我这次帮团队做的改造,走的就是网关模式,后面的章节也都以网关模式为主来展开。
3. 从零部署一个LiteLLM网关,跑通第一个请求
3.1 安装和一份最小可用的config.yaml
安装很简单,直接pip装带proxy扩展的版本,它会一并把网关服务所需的依赖带进来。注意如果你后面打算用Postgres存储密钥等数据,还需要额外装数据库驱动。
pip install 'litellm[proxy]'装完之后,核心工作是准备一份配置文件,默认叫config.yaml。我先给你看一份最小配置长什么样:
model_list: - model_name: gpt-3.5-turbo litellm_params: model: openai/gpt-3.5-turbo api_key: os.environ/OPENAI_API_KEY - model_name: llama3.1 litellm_params: model: ollama/llama3.1 api_base: http://localhost:11434配置里有两个字段要特别说明。model_name是暴露给业务方的别名,你希望业务方怎么称呼这个模型,就怎么写。litellm_params.model才是LiteLLM真正用来找上游的真实模型ID,一般由"厂商前缀/模型ID"组成。api_key可以写成os.environ/某个环境变量名,LiteLLM会去读当前进程的环境变量,不会把密钥明文留在配置里。
os.environ/OPENAI_API_KEY这种写法是我比较推荐的,它意味着同一个配置文件在不同环境间可以复用,只要各环境维护自己的环境变量即可。另外那个ollama/llama3.1,是我本地挂了一个Ollama服务用来做测试的,它不需要API key,但需要指定api_base指向本地服务地址。
3.2 启动网关,理解配置加载过程
启动网关前,先设置主密钥。主密钥是管理整个网关的最高权限凭证,生成虚拟key、查看模型列表、修改配置等管理操作都要用它。设置方式是通过环境变量传入:
export LITELLM_MASTER_KEY=sk-1234 litellm --config config.yaml --port 4000这里有一点值得解释:LITELLM_MASTER_KEY不是给模型厂商用的,而是给网关自身的管理接口用的。LiteLLM启动时会读取这个环境变量,要求所有访问方在HTTP头部带上它。业务方以后拿到的虚拟key也是基于这个主密钥衍生出来的。
启动后如果一切正常,终端会打印出监听地址,然后进入等待请求的状态。此时可以先用一个最简单的方式验证网关是否存活:看一下模型列表接口。很多新手会忘记,配置是否真的被加载是可以主动检查的,不用直接发起对话请求。
curl --location 'http://localhost:4000/model/info' \ --header 'Authorization: Bearer sk-1234'如果返回的列表里包含你配置的两个model_name,说明配置加载成功,网关路由也正常注册了。
3.3 用curl和标准SDK各打一次,确认链路是通的
网关跑起来后,就可以模拟业务方的方式发起对话请求了。下面的curl会请求网关,网关再根据配置把请求转发到对应的真实模型:
curl --location 'http://localhost:4000/chat/completions' \ --header 'Authorization: Bearer sk-1234' \ --header 'Content-Type: application/json' \ --data '{ "model": "gpt-3.5-turbo", "messages": [ {"role": "user", "content": "用一句话介绍你自己"} ] }'如果你前面的环境变量配置正确、网络能访问到对应厂商的API,响应的JSON结构和OpenAI官方返回的结构几乎一致。这也是LiteLLM一个比较省心的地方,业务方拿到的响应格式是统一的,不需要根据模型来源写不同的解析逻辑。
如果业务方是用Python写的,还可以直接使用OpenAI SDK,把base_url指向网关地址:
from openai import OpenAI client = OpenAI( api_key="sk-1234", base_url="http://localhost:4000" ) resp = client.chat.completions.create( model="gpt-3.5-turbo", messages=[{"role": "user", "content": "你好"}] ) print(resp.choices[0].message.content)到了这一步,一个最小可用的网关链路就算跑通了。业务代码几乎不改,只需要把原来SDK里的base_url换成网关地址,把API key换成网关下发的key,就能开始走新链路。
4. model_list是LiteLLM最核心的路由逻辑,值得单独拿出来讲
4.1 model_name与真实模型ID是两套体系
初次用LiteLLM的人,最容易被model_list搞晕的地方就是,为什么一个模型要在两个地方写名字。其实理解成"对外别名"和"真实地址"的关系就会很清楚。
model_name是逻辑名,是业务方眼中的模型;litellm_params.model是物理ID,是LiteLLM用来定位上游的钥匙。为什么要分两层?举个例子,某天厂商把某个模型升级了,接口从旧版迁到新版,但效果表现最接近。如果业务代码里写死了旧ID,你就得一个个服务去改版本号。有了网关这层,你只需要把配置里litellm_params.model换成新版ID,model_name保持不变,业务方完全无感知。这就是模型切换时不改代码的底层原理。
4.2 provider前缀的规则和常见写法
litellm_params.model里那个斜杠前面的一段,是LiteLLM用来识别走哪家协议的前缀。不同的上游渠道,前缀写法不同。比如经典OpenAI协议是openai/...,Ollama是ollama/...,还有针对自建推理服务的hosted_vllm/...等等。如果模型走的是OpenAI兼容接口,有时也可以不写前缀,LiteLLM会按OpenAI协议来处理。
这里有一个我实际踩过的细节:model_name千万不要带前缀,保持一个干净的业务别名即可。一个常见错误是有人把model_name也写成openai/gpt-3.5-turbo,结果业务方传的model里带着斜杠,前端请求参数解析偶尔会把斜杠导致的路由索引搞错,排查起来很隐蔽。我自己的习惯是model_name里只用字母、数字、短横线这些简单字符,比如gpt-35、llama3.1,返回值也容易读。
4.3 同一别名挂两个模型,就拿到了自动降级
model_list还有一类更进阶的用法:同一个model_name可以挂多个litellm_params,分别指向不同厂商的真实模型。LiteLLM内部会在第一个模型不可用或返回异常时,自动把请求转发给第二个。
这个能力在业务上非常实用。比如某个模型厂商偶尔抖动,你可以在同一别名下挂一个备用模型。对业务方来说,它调的始终是一个model_name,网关层决定这次请求到底发给谁。我做过一个模拟,把主模型的API地址故意配错,网关马上切到备用模型,业务方收到的是完全正常的响应,只是延迟稍微高了一点。如果不用LiteLLM,这种降级需要业务代码里自己try-except再换模型重试,每个调用方都要独立实现一遍,很容易遗漏。
具体配置方法如下:
model_list: - model_name: chat-main litellm_params: model: openai/gpt-3.5-turbo api_key: os.environ/OPENAI_API_KEY - model_name: chat-main litellm_params: model: anthropic/claude-3-5-sonnet api_key: os.environ/ANTHROPIC_API_KEY这里有两个model_name都叫chat-main,配置加载顺序即优先级顺序,第一个失败后LiteLLM尝试第二个。需要注意的是,两个后端的响应格式虽然都收敛成了OpenAI风格,但不同厂商的模型能力边界和返回细节仍然有差异。备用的模型不一定在所有任务上效果接近,降级策略最好用在聊天这类通用场景,用在一些强依赖特定输出格式的任务上要先做好测试。
5. 团队共用时的密钥、预算和限速配置
5.1 用master key生成虚拟key
网关起来后,如果直接把主密钥发给所有业务方,一旦有人误用或者泄漏,等于把整个网关的控制权交了出去。所以我在部署完成后做的第一件事,就是收回主密钥,改用虚拟key分发给各业务方。
虚拟key通过管理接口生成。下面的curl会用主密钥调用/key/generate,生成一个归属于指定模型、带预算上限的key:
curl --location 'http://localhost:4000/key/generate' \ --header 'Authorization: Bearer sk-1234' \ --header 'Content-Type: application/json' \ --data '{ "models": ["chat-main"], "max_budget": 5.0 }'响应里会带一个新的sk-开头的key,这就是给业务方使用的虚拟key。生成后我只把虚拟key发出去,主密钥留在服务器环境变量里。这样即便某个虚拟key泄漏,我可以在网关里单独吊销它,其余业务方的访问完全不受影响。
5.2 预算不是绝对的,得配合模型级别限制
max_budget看着简单,实际使用里要明白它到底在哪一层生效。虚拟key上的预算,限制的是"这把key在所选模型上的累计消耗",这是按key维度统计的。如果你想限制"某个模型在一段时间内总共最多花多少钱",需要在模型配置里也设置模型级别的预算。
两种方法我都在用:给大模型统一设模型级日预算,防止某天某个异常任务把账单打爆;给每个业务方单独key设key级预算,方便月底根据不同项目分摊成本。两者配合起来,一个管"总量",一个管"个体",比较稳。
预算的有效期是可以配置的,不是只能永久累计。比如按天、按月重置的周期预算,可以在生成key时传budget_duration参数,LiteLLM会按周期滚动统计。具体字段和周期格式可以在它的管理接口文档里找到。这里有个小提醒:预算刷新不是实时的,它有一个统计窗口,极端情况下可能等一会儿才生效。做超预算控制时不要把响应延迟的实时性要求想得太高。
5.3 限速和重试:网关层统一的稳定性策略
在网关层面统一做限速,比在业务层各自实现要省事得多。限速维度常用两个:每分钟请求数rpm和每分钟token数tpm。这两个参数既可以在模型配置里写,也可以在生成虚拟key时限制。比如担心某个调用方写了个死循环疯狂刷请求,给它key加上rpm限制,马上就打不到后面的模型了。
LiteLLM内置的重试机制也要学会用。默认在请求遇到瞬时故障时会有一定次数的重试,你可以调整重试次数,以及按错误类型决定是否重试。比如429限流、5xx这类错误适合重试,而400参数错误重试一百遍也没用。我在配置里倾向把重试次数设得保守一点,因为重试会增加上游负载,太激进的配置反而容易把本来就不稳的上游打得更惨。
6. 上线后我踩过的几个坑
6.1 网关超时和上游超时,要分开设置
上线第一天我就遇到一个诡异情况:某个模型偶尔会请求耗时很长,网关本身没有超时,导致HTTP连接长时间挂着,下游服务的连接池也随之占满。后来才意识到,网关进程和上游厂商之间的超时,以及LiteLLM对外服务的超时,是两个不一样的东西。
建议在配置里显式设置上游请求的超时时间,比如60秒,超过就直接返回错误,避免无限等待。从业务方角度看,合理的超时时间应当根据模型场景设定,对话生成类偏长,检索类偏短。网关的超时设置主要起到兜底作用,不要让异常慢请求拖垮整个服务。
6.2 流式响应别被网关悄悄缓冲
支持流式输出是现在很多应用的基本要求,字一个字地蹦出来,体验好。但代理型网关天然有个风险,就是它可能为了"方便",把上游的流式响应整个攒完再一次性返回给你。这样业务方拿到的虽然是完整内容,但"流式"名存实亡,首字延迟反而更糟。
LiteLLM网关是支持流式透传的,我实测过把请求里的stream: true传过去,响应是逐个chunk返回的。不过还是建议你在集成阶段就用代码验证一下,用个简单脚本循环读取响应块,观察是否真的是持续输出,而不是一次性收完。测试很简单,在接收端打印每个chunk到达的时间,时间间隔均匀基本就稳了。
6.3 SQLite够用,但团队场景建议上Postgres
LiteLLM默认用SQLite记录密钥、预算、配置等数据,本地试用没问题。但团队场景下,尤其并发请求多时,SQLite的写并发会成为瓶颈。我后来把它切到了Postgres,步骤不复杂,只需要给进程提供数据库连接地址:
export DATABASE_URL=postgresql://user:password@host:5432/litellm切换之后,多个网关实例如果要做高可用,也才能共用同一份状态。如果你打算跑多实例部署,前置条件就是把存储换成Postgres,否则两个实例各用自己的SQLite,密钥和预算数据完全对不上。
6.4 什么时候不该上LiteLLM
写到这里也得泼点冷水。如果项目规模很小,固定用一两个模型,短期内也不打算换,那直接调官方API反而更简单。上LiteLLM等于多引入一个组件,多一个进程要维护,多一层故障点,这些成本是实打实的。
另外如果你们需要调用的模型有高度定制的前处理,比如特别复杂的prompt拼接逻辑、特制的认证方式,网关的统一模型抽象反而可能变成限制。大多数情况下LiteLLM能覆盖需求,但遇到非常特殊的场景,先花半小时确认它能支持,不要默认什么都行。
就我自己这段时间的体会来说,LiteLLM最漂亮的一点是它把"接入模型"从开发问题变成了配置问题。团队再也不用每接入一个新模型就兴师动众改一遍业务代码,预算和密钥也有了统一的管控点。如果你正处在"模型数量开始变多、接入方式杂乱无章"的阶段,这个工具值得花一天时间试跑一遍;但如果你只有一个模型且很稳,那不妨等一等,等到真有多模型需求的那天再上也不迟。