做AI应用最让人头疼的不是模型效果,而是各家接口不统一。今天要聊的LiteLLM(项目名是litellm),就是解决这个问题的开源方案:一套网关,把所有模型服务商的API收拢成同一个格式,后端怎么换,前端代码都不用动。
先给没听过这个项目的朋友一个定位:litellm是Python写的开源项目,核心价值是“统一LLM调用层”。它对外提供一个和主流大模型SDK兼容的标准接口,对内适配各家模型服务商的不同协议、不同鉴权方式、不同响应结构。也就是说,你的业务代码只认一套OpenAI兼容格式,实际的请求会被这个网关翻译成后端真实模型需要的格式,再把各家返回的数据转成统一结构回给你。
实战中我见过不少团队卡在“多模型接入”“成本统计”“密钥管理”这些问题上,litellm正好把这些都打包了:负载均衡、自动重试、模型降级、密钥轮换、预算控制、日志追踪,甚至多租户用量统计,开箱即用。适合谁用?如果你是正在做LLM应用的开发者、负责模型接入的后端工程师、或者想给团队搭一套统一AI入口的架构师,这篇内容能给你省不少调研时间。下面我会从选型思路、部署配置到踩坑排查,完整过一遍。
1. 为什么需要这么一层网关
先说说痛点。现在市面上的大模型接口,看起来都是“发消息-收回复”,实际细节差别很大:有的用路径参数鉴权,有的用请求头自定义字段;有的返回内容直接是字符串,有的是嵌套结构;有的定义了独立的流式协议,有的在JSON里塞了额外字段。如果业务代码里直接写死对某一家服务的调用,后续每换一个模型都要改业务逻辑,测试、联调、回归,一套流程全重跑。
更麻烦的是成本与权限管理。直连各家服务的话,密钥散落在各个服务、各个同事的本地配置里,出了泄露事故都查不到是谁、在什么时间用什么Key调的。出账单时想把费用按项目、按部门拆开,得自己写脚本对接各家账单API,字段差异大不说,统计口径还不一致。我见过一个团队为了做出跨模型的成本对比表,硬是手工维护了三个地方的Excel,月底三天都在对账。
litellm把这些琐碎能力沉淀到网关层。业务侧不再关心后端的模型是哪个服务商提供的,只关心“模型叫什么名字”。网关层根据模型名去路由表里找到真实配置,转发请求、返回结果。这样有几个直接好处:
- 模型替换是配置变更,不是代码变更。把路由表里的目标模型从A换成B,业务侧无感。
- 所有调用都经过统一入口,天然能做鉴权、限流、预算、日志。
- 模型能力可以组合,比如“主模型挂了自动降级到备用模型”,或者“让两个模型各回答一遍再选优”。
从架构上看,这种风格是典型的“门面模式”在AI基础设施上的应用。它不是某个公司发明的黑科技,而是把微服务里的API网关思路搬到了LLM场景。litellm能流行起来,核心原因是它在“统一标准”和“兼容广度”之间拿捏得比较准:对外标准固定,对内适配层足够开放,支持模型服务商也持续在增加。
2. 三种用法:SDK、网关、框架集成
litellm的灵活之处在于,它不强迫你必须用哪种方式接入。根据项目阶段和部署规模,有三种用法可以选,我按推荐程度排个序。
2.1 直接用Python SDK:适合快速验证
如果你的项目不需要多端共用入口,只是想在代码里统一换模型,那直接用litellm的Python包就够了。安装后,一句调用发出去,内部做格式转换。这种方式的好处是零部署成本,本地调试最快。
import litellm response = litellm.completion( model="gpt-4o", messages=[ {"role": "user", "content": "帮我写一段Python代码"} ] ) print(response.choices[0].message.content)注意这里的model参数,格式是“服务商前缀/模型名”。litellm根据前缀判断走哪套适配逻辑。比如某个模型的真实服务商是A,配置写成A/gpt-4o;如果是自建的Ollama本地模型,写成ollama/llama3.1:8b。这个前缀就是路由的第一层判断依据。
SDK方式对已有的代码改动最小,但如果你有多台机器、多个应用都要调用模型,我更建议用下面的网关模式。
2.2 网关模式:适合生产稳定运行
网关模式是litellm的完整形态。它启动一个HTTP服务,默认监听4000端口,对外暴露的接口风格和主流SDK客户端完全兼容。也就是说,你原来用某个官方SDK写的代码,只需要把base_url指到litellm服务,把api_key换成litellm自己签发的Key,就能无缝切换。
from openai import OpenAI client = OpenAI( api_key="sk-litellm-master-key", base_url="http://localhost:4000" ) response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "你好"}] )这段代码里,model不再直接对应某个真实服务商,而是网关里配置的逻辑模型名。网关收到请求后,根据这个名字去路由表查到真实参数,再转发出去。业务代码完全感知不到后端的模型切换。
网关模式解决的核心问题是“治理”:所有Key都掌握在网关管理员手里,开发同学只需要拿一个网关Key;调用日志统一沉淀;预算和限流在网关层强制执行。生产环境我基本只推荐这种用法。
2.3 与编排框架集成:适合复杂链路
还有一类场景,项目已经在用某些编排框架做Agent链路,希望模型调用也能走网关统一治理。litellm提供了和常见框架的集成层,比如可以直接用litellm的Router对象来管理和调用模型。这种方式适合既要享受网关治理能力,又要保留编排框架灵活性的团队。
from litellm import Router model_list = [ { "model_name": "primary-model", "litellm_params": { "model": "gpt-4o", "api_key": "sk-xxx" } }, { "model_name": "backup-model", "litellm_params": { "model": "ollama/llama3.1:8b", "api_base": "http://localhost:11434" } } ] router = Router(model_list=model_list) response = router.completion( model="primary-model", messages=[{"role": "user", "content": "你好"}] )Router对象内部会维护健康状态、调用计数、失败重试等逻辑,你可以直接调用它,也可以把它挂到HTTP服务上。这个对象也是网关模式的核心组件,单独拿出来说,是为了让你理解网关背后的关键机制。
3. 深入理解路由与模型配置
配置是litellm使用中最容易出问题的地方,也是最能体现设计思想的部分。一个配置项的理解偏差,可能导致线上调用全挂,所以我这里拆细一点讲。
3.1 配置文件的角色与加载顺序
litellm支持通过命令行参数、环境变量、YAML配置文件来设置启动选项。生产环境中,我推荐把模型列表、预算规则、数据库连接等都写进YAML配置,用--config参数加载。配置文件的加载顺序是:先读默认值,再读环境变量,最后读YAML中显式指定的值。所以如果你在同一个变量上设了环境变量又在配置文件里写了值,配置文件里的会覆盖环境变量。
3.2 模型列表:逻辑名与真实参数的映射
看一段最简配置:
model_list: - model_name: gpt-4o litellm_params: model: gpt-4o api_key: os.environ/OPENAI_API_KEY这里model_name是业务侧看到的逻辑名,litellm_params.model是真实调用时的模型标识。如果真实服务商是其他厂商,在这个字段里加前缀区分。api_key直接os.environ/加环境变量名,litellm启动时会去读对应环境变量,这样密钥不会进版本库。
多模型配置更实用,一个逻辑名背后可以挂多个真实模型,网关自动做负载均衡:
model_list: - model_name: chat-main litellm_params: model: gpt-4o api_key: os.environ/KEY_A rpm: 100 - model_name: chat-main litellm_params: model: claude-sonnet-4 api_key: os.environ/KEY_B - model_name: chat-embed litellm_params: model: text-embedding-3-small api_key: os.environ/KEY_C当路由表里两个条目有相同的model_name时,litellm视为同一个逻辑名的多个上游,采用加权轮询等方式分配请求。rpm表示每分钟最大请求数,用于控制单个上游的流量压力。
这里有个经验:逻辑名最好避开具体模型名,用业务语义命名,比如chat-main、code-review-model。这样后续替换模型时,业务侧连模型名都不用改,真正的“配置即一切”。
3.3 环境变量与密钥到底放哪里
litellm遵循常见的环境变量惯例,但不强制。如果你不在配置里写api_key字段,litellm会尝试从标准命名的环境变量读取默认Key。但生产环境多模型并存时,别指望默认Key,每个上游单独配置环境变量才是最清晰的。
密钥管理的经典坑是:配置文件提交到Git仓库,里面明文写了Key。litellm开箱推荐os.environ/VAR_NAME这种写法,但如果你用的是其他变量管理方式,只要保证进程启动时环境变量已就绪即可。部署时我建议配合密钥管理服务,把密钥注入容器环境,litellm只负责读取。
3.4 请求参数过滤与默认值
litellm的一个实用设计是“传入参数兼容”。比如某个服务商不支持max_tokens字段,litellm会在转发前把它转换或过滤掉;某个模型不支持top_p,它会忽略该参数而不是报错。这种宽容处理在现场非常有用,尤其是在多模型切换时,参数差异往往比接口差异更容易引发事故。
但要注意,宽容过滤也可能掩盖参数无效的隐患。比如temperature不在模型支持范围内,某些服务商是直接忽略,某些是报错。litellm的默认行为是尽量兼容,如果你希望严格模式,可以通过配置开启参数校验。不过我实际用下来,宽容模式在早期迭代阶段更省心,等稳定了再决定要不要收紧。
4. 部署实操:从零搭一套网关服务
理论说再多,不如直接把服务跑起来。下面按完整流程过一遍,环境假设是Linux服务器,有Docker和Python环境。
4.1 安装与启动
最简单的安装方式是pip:
pip install 'litellm[proxy]'[proxy]这个extra会装上运行HTTP网关所需的所有依赖。装完后直接用命令启动:
litellm --config config.yaml --detailed_debug--config指定配置文件;--detailed_debug是调试期的输出模式,会打印每个请求的完整流转过程,包括用了哪个上游模型、耗时多少、返回的原始内容是什么。上线前记得去掉这个参数,否则日志量会非常大。
如果服务器上有Docker,我更推荐容器化部署。litellm官方镜像打好了运行环境,省去Python依赖折腾,还能直接用编排工具管理。启动方式类似,把配置通过挂载目录或环境变量传进去,服务监听在容器内的4000端口,映射到宿主机端口即可。
4.2 用Docker Compose跑一套完整服务
写一个最简的编排文件:
services: litellm: image: ghcr.io/berriai/litellm:main-stable ports: - "4000:4000" environment: - LITELLM_MASTER_KEY=sk-your-master-key - DATABASE_URL=postgresql://user:pass@db:5432/litellm volumes: - ./config.yaml:/app/config.yaml command: ["--config", "/app/config.yaml"] depends_on: - db db: image: postgres:16 environment: - POSTGRES_USER=user - POSTGRES_PASSWORD=pass - POSTGRES_DB=litellmLITELLM_MASTER_KEY是网关的管理员Key,调用端和运维端都靠它鉴权。DATABASE_URL是可选配置,不填的话litellm用本地SQLite存数据;填了PostgreSQL,多个副本实例可以共享同一份数据,日志、预算、Key信息都不会各自为政。生产环境我强烈建议上独立PostgreSQL,不然网关启动多个副本时数据会乱。
启动后,访问http://localhost:4000应该能看到健康检查页面。这时用管理接口创建一个调用Key:
curl http://localhost:4000/key/generate \ -H "Authorization: Bearer sk-your-master-key" \ -H "Content-Type: application/json" \ -d '{ "models": ["chat-main"], "max_budget": 50 }'返回的JSON里包含key字段,这就是业务侧要用的Key。把Key分发给应用服务后,它们的调用就都走网关治理了。这个Key还可以设置模型白名单、预算上限、过期时间,粒度很细。
4.3 关键运维参数怎么看
网关跑起来后,有几个参数的效果值得说透。
cooldown_time是模型健康检查的“冷却时间”。某个上游连续报错达到阈值后,litellm会把它临时标记为不健康,进入冷却期。冷却期内请求自动路由到其他健康上游。冷却期结束再试探性地放一点流量过去,成功率高就恢复。默认值一般是300秒,但如果你有多条可以互相兜底的线路,可以把冷却时间调短一点,比如60秒。
max_retries是单次请求的重试次数。注意这里的重试和HTTP客户端层面的重试是两回事。litellm的重试逻辑更聪明,它知道哪个上游刚才失败了,下一次重试会换一个上游,避免同一个坏节点反复被打。这个参数建议设置成2到3次,太多的话,下游服务恢复慢的时候请求会堆积很久,影响整体延迟。
timeout和stream_timeout也要单独设。非流式请求的默认超时时间如果太短,长任务容易误判失败;流式请求的超时设置逻辑不一样,它衡量的是“两个数据包之间的间隔”,不是整个响应的总时长。我曾经把流式超时设成和普通超时一样,结果模型思考时间长一点,连接就被断开了,前端表现为“生成一半突然停了”。
4.4 高可用:多副本与负载均衡
litellm网关本身是无状态服务,只要共享数据库,可以水平扩展多个实例。前面用Nginx这类负载均衡工具把流量分发到多个litellm实例就行。需要注意,每个实例都要能读取同一个数据库,否则某个实例上创建的Key在另一个实例上校验会失败。
还有一点,模型的调用量有突发峰值时,网关的限流逻辑是按实例内存计算的还是按数据库统一计算的,取决于你用的限流模式。如果追求严格的全局限流,建议把限流数据也落到Redis这种共享存储上,配置里加上对应连接即可。不做这个配置的话,每个实例各自计数,总流量可能达到“实例数×单实例限流值”,需要你心里有数。
5. 上游模型的管理策略与动态切换
网关的优势在于对上游模型的集中治理。这块是配置技巧最密集的区域,展开讲几个高频需求。
5.1 模型组与模型降级
线上最怕“主力模型挂了,业务全瘫”。litellm的fallbacks机制就是干这个的:当一个模型连续失败,自动把请求发送到备用模型。配置有两种方式,一种在发起请求时动态指定,一种在配置文件里静态声明。
response = router.completion( model="chat-main", messages=[{"role": "user", "content": "你好"}], fallbacks=[{"chat-main": ["chat-backup"]}] )这个语义是:chat-main失败时,自动尝试chat-backup。备用链路本身也可以再配置降级,形成多级保障。注意,降级不是无限重试,它有次数限制,也受冷却时间约束,防止备用链路也被打爆。
5.2 模型加白与禁用
运维中会碰到“这个模型还没测试完不能开放”“那个模型预算超了要停掉”这类需求。litellm支持在运行时动态管理模型组。
通过管理接口可以临时创建一个只包含某几个模型的Key,或者修改已有Key的模型列表。实测下来,这种运行时调整的能力比改配置文件热加载更灵活,尤其适合线上快速止血的场景。比如发现某个新模型有乱码问题,直接调用管理接口把该模型从对应Key的允许列表里移除,比让业务方改配置快得多。
5.3 成本追踪与预算控制
成本是LLM应用绕不开的话题。litellm把每次调用的模型、Token数、费用、请求方、时间戳都记录下来,落到数据库里。它还有一个实用的维度:tags。你可以给请求打标签,比如project=payment、env=production,然后在统计时按标签聚合出成本。
预算控制分两层:全局预算和Key级预算。Key级预算特别好用,给每个业务方或者每个测试账号各自设一个额度,额度用完,这个Key的请求直接被拒绝。实际使用中我会把预算设成月度重置,并配合报警。litellm支持预算超限后触发Webhook,我集成了内部告警,费用一异常就能收到通知。
5.4 多租户场景:用户维度的用量隔离
如果你的应用服务多个客户,需要按用户维度统计用量和限流,litellm的用户管理接口可以派上用场。通过/user/new创建用户,把用户和Key绑定,调用时在请求参数里带上user字段,网关就能把该用户的调用行为单列出来。这对于“免费用户每天只能发100条,付费用户不限额”这类需求,可以实现得很干净。
6. 踩坑实录:我从现场总结的排查手册
再顺的工具也有坑。下面几个问题是我和团队在实际运行中遇到过的,概率不低,值得提前防备。
6.1 环境变量没生效,Key报错
最常见的启动问题:明明在.env里设了KEY_A,配置文件也写了os.environ/KEY_A,结果启动时一直报鉴权失败。排查思路很简单:看服务进程是不是加载了.env。用pip方式启动时,litellm不会自动读项目里的.env文件,你得用export先把变量导出,或者用python-dotenv在启动入口里显式加载。
另外,环境变量名要注意命名冲突。如果你给两个不同的上游模型都定义了同一个变量名,后加载的会覆盖先加载的,导致两个模型用同一个Key。我在配置里习惯把所有Key加上明显前缀:GM_API_KEY、GATEWAY_DB_URL这种,从源头杜绝覆盖。
6.2 流式响应时客户端挂断
用网关转发流式响应时,我自己遇到过“业务方收到的内容不完整”的问题。表象是用户端边看边生成,但到某个位置突然结束,没有报错。查日志发现网关已经把完整响应给了业务方的网络层,是业务方的HTTP客户端提前超时断开了。
问题根源在于,很多HTTP客户端的read_timeout默认值是固定的,而大模型流式输出时,模型思考阶段可能长时间没有数据包返回。解决方法是,调整业务方客户端的read_timeout,保证它大于模型两次输出之间的最大间隔。我的经验值是至少60秒,如果模型要联网搜索或深度推理,可能得两分钟。这个参数不调到位,流式体验就会随机性中断。
6.3 模型返回结构不完全兼容
litellm虽然做了大量兼容工作,但不同模型返回的JSON里,某些字段还是会有差异。比如有的模型会额外返回usage.prompt_tokens_details,有的没有;有的模型工具调用字段结构和主流格式略有差别。如果你的业务代码强依赖这些非核心字段,建议在网关后面再做一层数据整形。
我遇到过一个案例:某模型的返回内容里包含了额外的审核字段,litellm原样把它透传了,业务方没做好兼容处理,导致解析异常。这类问题不算litellm的缺陷,它是“尽量兼容但不保证完全一致”的定位。我的建议是,对返回结构做一层“防御性解析”,只取核心字段,其余字段当你不需要时就忽略掉。
6.4 日志太多,磁盘告警
--detailed_debug这个参数真的很方便,但也真的很占空间。有一次我们开着这个参数跑了一周,结果网关服务器磁盘告警。排查发现日志文件已经几个GB,全是每次请求的完整响应体。生产环境务必用info级别的常规日志,详细调试日志只在定位问题时临时开启,定位完立刻关掉。
另外,litellm的请求日志和系统日志是两套。请求日志写了数据库,用于成本统计;系统日志打印在控制台或写文件,用于排查服务问题。别把它们混在一起看。查问题的时候,先明确你要看哪类日志,能省不少时间。
6.5 数据库连接被占满
PostgreSQL连接数默认上限大约100。litellm实例多了,每个实例都会占连接,再加上其他业务也在用同一个库,连接数很容易打满。报错表现为间歇性的“无法写入日志”“请求延迟变高”,而且越到业务高峰越明显。
解决思路有两步:一是给litellm独占一个实例或数据库,不和其他业务混用;二是在数据库中适当调高连接数,同时给每个litellm实例的连接池设定合理上限。这里没什么银弹,关键是监控好连接数和请求量的比例。线上环境我习惯把数据库连接数指标做到告警里,低于阈值就及时扩容或回收空闲连接。
6.6 重试引发的重复扣费
如果你在业务方加了一层重试逻辑,网关侧又设了max_retries,双重重试可能导致同一用户的同一请求被发送到上游多次,结果就是重复扣费。虽然不是每次重试都会成功,但只要有一次成功,前几次失败的请求如果实际上游已经处理了,费用照算。
我的建议是重试责任要划分清楚:要么业务方重试,网关做死转发;要么业务方只发一次,网关负责重试兜底。两边都重试是最坏的情况。另外配合幂等键设计,请求ID全局唯一,即使重试,也能在统计时识别出重复调用。
7. 进阶用法:不止于调用转发
litellm的能力边界比很多人印象中的更宽,除了对话补全,还有几个高频方向值得说。
7.1 Embedding与图像生成
除了completion,litellm对向量化模型接口也做了统一适配。调用方式基本一致:
response = litellm.embedding( model="chat-embed", input=["这是第一段文本", "这是第二段文本"] )这样你的向量化逻辑也可以做到“换模型不换代码”。图像生成接口同样有封装,不过图像领域的接口差异比文本更大,实际使用前建议先在官方文档确认目标模型的支持情况。
7.2 工具调用(Function Calling)的统一适配
各家模型对工具调用的协议定义有差异,尤其是函数声明的JSON Schema规范。litellm会把不同模型的工具调用请求和响应都转换到主流格式。这一点在Agent开发里很关键:你的Agent框架里定义了一套工具调用逻辑,如果底层模型换了但工具调用格式没兼容好,整个Agent的规划能力就废了。
我在实际项目里测试过几种不同来源的模型,工具调用兼容性不算完美,偶发字段映射偏差,但核心链路是通的。用之前强烈建议写一个“工具调用冒烟测试”,把定义好的工具集跑一遍,遍历所有模型,确认返回的工具参数能被你的代码正确解析。
7.3 缓存:省钱又降延迟
litellm支持语义缓存功能。当请求内容与历史请求足够相似时,直接返回缓存结果,不再真实调用上游模型。这个特性在特定场景收益巨大:比如客服系统的欢迎语、重复的FAQ、固定模板的请求,命中缓存后延迟从秒级降到毫秒级,成本直接归零。
缓存模式要区分“精确缓存”和“语义缓存”。精确缓存适合完全相同的请求;语义缓存会做向量相似度匹配,灵活性更高,但也可能误命中,把不该复用的结果返回给用户。我的建议是,面向用户的对话场景默认关闭语义缓存,只在知识问答、固定模板场景开启,并设置好相似度阈值。
8. 从网关到平台:团队使用的落地建议
最后聊点组织层面的经验。很多团队引入litellm,一开始只是想“换个模型不用改代码”,用着用着就把它变成了一个内部AI平台。这个过程有几个节点需要注意。
账号体系。如果你只是让几个人用,管理员Key直接共享也能跑。但团队一超过十人,就必须做Key隔离。每个人一个Key,每个项目一组Key,出了问题能定位到人,预算能分摊到项目。litellm的Key管理接口能支持这个策略,但需要有人维护。
权限分离。平台管理员、模型接入负责人、普通开发者,三种角色的权限应该不同。管理员只管Key和预算,模型接入负责人只管路由配置,普通开发者只知道自己要用的Key。这个权限划分litellm本身提供了一些基础能力,落地时还需要结合内部流程做配套。
规范先行。如果没有约定,团队里会出现各种逻辑模型名:有人叫gpt-4o,有人叫chat-4o,还有叫primary的。时间一长,没人分得清哪个是哪个。我建议在接入第一天就定一份“模型命名规范”,比如场景-能力-序号,并写入团队的README,让所有人照这个规范申请模型名。
监控可视化。litellm提供了基础统计接口,但要说承载团队的可观测性需求,还是差点意思。我的做法是把litellm的日志数据同步到内部的可观测平台,做一张“模型调用大盘”,看请求量、错误率、耗时、费用趋势。数据在数据库里躺着,通过SQL查询出指标,配一个仪表盘,基本够用。
9. 写在最后的经验
我没有把litellm当成一个“调用库”,而是当成了一层基础设施来运营。它最值钱的地方不是帮你省掉几次改代码的时间,而是把“模型接入”这件事从“每个项目各自搞”变成了“平台统一管”。实际运营半年之后,团队换新模型的成本几乎降到了零:配置里加一行,测试跑一轮,发布就完了。
如果你正准备引入litellm,我的建议是从小处开始:先搭一个实例,接入两三个模型,跑通调用和日志,再逐步加预算、降级、多租户这些能力。不用一开始就追求全功能,把它用顺手了,自然会长成你想要的样子。工具是死的,怎么用是活的,配置和策略的设计,才是真正体现功力的地方。