2026/10/21 4:56:49

LiteLLM:用一套统一接口轻松对接所有大模型API

LiteLLM:用一套统一接口轻松对接所有大模型API 一个不少做AI应用的朋友应该都有过这种体验项目刚起步时调研模型、接API、调参数忙一整天。等到第二个项目又换了一家的模型。这时候你发现每换一家模型厂商就要重写一遍封装SDK的调用方式千奇百怪请求格式不一样鉴权方式不一样连返回结构都不一样。这种碎片化带来的心智负担比模型效果本身还折磨人。所以当我第一次看到LiteLLM这个项目时第一反应是这东西早该有了。LiteLLM本质上是一个大语言模型调用层的统一网关最核心的价值是让你用一套代码、一套接口去对接市面上几乎所有的模型服务。它既能作为Python库直接嵌入你的代码也能单独跑成一个代理服务帮团队屏蔽底层各种模型SDK的差异。这篇文章我会从实际项目视角出发聊聊LiteLLM解决的是什么问题、核心功能怎么用、部署时有哪些坑以及基于我自己的实操经验它到底适合用到什么程度。1. 项目定位为什么会有LiteLLM这种东西1.1 被几十个SDK支配的痛先回忆一下传统接模型API的方式。假设你的项目要用两三种不同厂商的模型最简单粗暴的做法是每个厂商的SDK分别安装一遍分别写一个服务层封装再分别处理token统计、超时重试、错误码映射。代码很快会变成一堆if-else分支每个分支里夹着一种厂商特有的数据结构。过了一段时间因为效果或者成本原因你们决定把其中一个模型换成另一个新出的开源模型。重新封装一套SDK不是技术难点真正的难点在于涉及到所有上层逻辑的适配——原来某个字段在返回结构里的位置不一样了原来的鉴权参数需要换一个环境变量名了。这类改动在功能上毫无增量的地方就是纯消耗人力。我见过更极端的例子某项目组为了对比多个闭源模型和开源模型的效果写了四套调用模块每一套的返回结构还都被下游代码直接引用了。后来他们要加一个统一的对齐评测流程结果评测代码里到处是if provider xxx。这种代码看着都想跑。1.2 LiteLLM是怎么解决这个痛点的LiteLLM的解题思路非常直白把不同厂商的模型请求、响应统一成一套格式。你可以把它理解为一个“万能转接头”内部已经适配了各家SDK的差异你只需要面向一个稳定接口编程。它在代码层面做的事情特别朴素把你传入的模型名映射成对应的SDK、对应的endpoint、对应的鉴权参数然后调用对应厂商的原生接口再把返回结果清洗成统一结构。开发者不需要关心某个模型在底层到底走的是REST还是gRPC也不需要关心某个厂商的流式返回格式是不是和别人不一样。这套方案能流行起来靠的是另一个很聪明的工程决策LiteLLM采用的统一消息格式本身就是业界主流的“角色消息”结构——system、user、assistant这种对话式格式。也就是说你在LiteLLM里写的请求体跟你在大部分模型平台上写的请求体几乎没有差别迁移成本很低。2. 核心能力拆解不只是“统一接口”这么简单2.1 统一调用层一套代码接所有模型LiteLLM最核心的Python接口是completion用法非常接近普通SDK。下面这段代码是我在实际项目里常用的最小示例import litellm response litellm.completion( model厂商前缀/模型名, messages[ {role: system, content: 你是一个简洁的助手。}, {role: user, content: 介绍一下LiteLLM} ], temperature0.7, )这个函数返回的结果不管底层是哪个厂商结构基本一致。response.choices[0].message.content能拿到回复文本response.usage.prompt_tokens、response.usage.completion_tokens能拿到token统计response.model能拿到实际被调用的模型名。最让我省心的是LiteLLM不需要你去手动处理厂商SDK的安装冲突。你只需要装一个litellm它内部会把要用到的厂商SDK作为依赖按需拉起。这意味着你项目里的依赖列表再也不会出现一堆以厂商命名的包了。注意model参数里的厂商前缀/模型名是有讲究的前缀用来告诉LiteLLM该走哪个SDK。不同的模型服务商前缀不一样。这个前缀可以在LiteLLM文档里查到如果你连接的是私有化部署的模型服务也可以通过自定义配置来映射。2.2 模型路由与负载均衡统一接口只是第一层能力LiteLLM还有一层更进阶的功能——路由。它的Router类可以让你在同一个接口里根据策略选择把请求发给哪个模型或哪个部署实例。举个例子你有一个稳定版本和一个实验版本的模型希望默认情况走稳定版本某些请求走实验版本。或者你有多个不同地区的同厂商模型服务想按轮询方式分散压力。这些都可以用Router来实现from litellm import Router router Router( model_list[ { model_name: stable-chat, litellm_params: { model: 厂商前缀/稳定模型, api_key: 密钥, } }, { model_name: experimental-chat, litellm_params: { model: 厂商前缀/实验模型, api_key: 密钥, } } ] ) response router.completion( modelstable-chat, messages[{role: user, content: 你好}] )这里有个关键点model_name是你在代码里使用的虚拟名字真正调用哪家模型由litellm_params决定。这样当你想把某个虚拟模型从供应商A切到供应商B时只需要改配置不用改业务代码。Router还支持配置重试和健康检查。比如某个模型实例返回429限流错误时Router会自动换到下一个可用实例重试这个对线上稳定性提升非常明显。我实测过的效果是配置了Router之后因为限流导致的请求失败率能下降一个量级——前提是你得有多个可用的模型来源。2.3 成本与用量追踪做AI应用的人都知道没有成本意识的项目活不过三个月。不同模型的token定价差异巨大如果每次都是人工估算费用很容易失控。LiteLLM内置了成本计算逻辑。普通的completion调用返回里会带一个cost字段代表这次请求消耗了多少钱。如果你的模型不在内置价格表里也可以自己在配置里维护价格信息指定每百万输入token和每百万输出token的价格。成本追踪这块实际用起来很舒服但是要注意一点cost字段依赖的模型名匹配是精确匹配的。如果你用了自定义模型名并且没有配置价格那么这个字段就是0。想统计出准确成本最好在初始化模型时把价格配置一起维护好。2.4 密钥与权限管理当一个团队共用一个模型服务时最让人头疼的就是密钥管理。给每个人都发同一个厂商API密钥权限是省事了但密钥泄露后谁也说不清楚是从哪儿漏的。LiteLLM的代理模式支持自定义密钥管理机制你在代理层配置一个虚拟密钥真正的厂商密钥放在服务端环境变量里。团队成员只需要面向代理调用模型不需要直接接触底层厂商密钥。这个做法我在一个实际项目里用过。那时候团队里有四五个人后端服务还有定时任务都通过代理访问模型服务。配置好虚拟密钥之后密钥暴露面小了很多换某个人权限时也只需要在代理层操作不影响其他人。3. 实操从安装到跑通第一行代码3.1 环境准备和安装LiteLLM对Python版本有要求建议使用Python 3.9以上版本。安装非常简单直接用pippip install litellm如果你要用代理模式还需要额外安装它的启动依赖。一般这样装就够了pip install litellm[proxy]安装的时候有个小提醒LiteLLM的依赖项比较多因为要兼容各种厂商SDK。如果安装速度实在太慢可以考虑用国内镜像源。装完以后先用litellm --help验证一下能不能正常输出帮助信息确认安装无误。3.2 最小调用示例先跑通再谈封装安装完成之后第一步是环境变量配置。LiteLLM在调用具体模型时会自动读取对应的环境变量作为默认密钥。不同厂商的密钥环境变量名不一样所以你要去查一下文档确认对应关系。假设你要用的模型服务对应的环境变量名是MODEL_API_KEY那么可以这么设置export MODEL_API_KEY你的密钥设置好环境变量之后在Python里执行import litellm response litellm.completion( model厂商前缀/模型名, messages[ {role: user, content: 你好请简单介绍下你自己} ], ) print(response.choices[0].message.content)如果你得到一个正常的文本输出说明模型调用跑通了。这里我建议你换三种不同厂商的模型分别试一次你就能直观感受到统一接口带来的体验——后面再换模型基本不需要改代码结构。我第一次跑通的时候最大的感受是这正是AI应用框架该有的样子。底层模型可以随意换上层业务代码却可以保持稳定。3.3 通过代理网关访问如果你不是一个人在写代码或者你希望把模型调用能力开放给其他服务那么代理模式才是主力场景。启动代理服务最简单的命令是这样litellm --model 厂商前缀/模型名 --port 4000这样会启动一个本地监听4000端口的服务提供与主流聊天补全接口兼容的HTTP端点。下游服务只要把base_url指向http://localhost:4000就能像调用常见模型API一样去访问代理。更正式的用法是先写一个配置文件把模型列表、密钥、路由策略、安全设置都写进去。下面是一个简化的配置示例model_list: - model_name: chat-main litellm_params: model: 厂商前缀/模型A api_key: os.environ/MODEL_A_API_KEY - model_name: chat-fallback litellm_params: model: 厂商前缀/模型B api_key: os.environ/MODEL_B_API_KEY litellm_settings: drop_params: true set_verbose: false general_settings: master_key: 代理管理密钥关键点解读一下model_name是暴露给使用方的虚拟名字调用方不需要知道底层是哪个厂商。api_key可以指定从环境变量里读取这样密钥不会写死在配置文件里更安全。master_key是代理的管理密钥相当于代理自己的管理员口令用来做后续的密钥生成和管理。用配置启动litellm --config ./config.yaml --port 4000这样的架构特别适合微服务场景。多个后台服务不再各自安装厂商SDK也无需各自管理密钥只需要知道代理地址。我的体验是有一个这样的网关之后新服务接入模型能力的时间从几小时缩短到十几分钟。4. 进阶功能实战4.1 配置模型回退别让单点故障拖垮线上服务模型调用最怕的就是线上服务正跑着模型平台突然限流或报错。这时候如果没有容错机制用户体验就是一落千丈。LiteLLM提供了模型回退机制允许你在主模型失败时自动切换到备用模型。在Router里配置回退很简单from litellm import Router router Router( model_list[ { model_name: chat-service, litellm_params: {model: 厂商前缀/主模型, api_key: 密钥A} }, { model_name: chat-service, litellm_params: {model: 厂商前缀/备用模型, api_key: 密钥B} } ], fallbacks[{chat-service: [chat-service]}], ) response router.completion( modelchat-service, messages[{role: user, content: 你好}], )注意这里两个模型的model_name都叫chat-service这没问题因为litellm_params里可以指定不同的实际模型。设置fallbacks之后当主模型调用失败Router会自动尝试列表里的下一个配置。回退机制我还是建议设置超时时间不然请求可能会卡在某个模型的等待上。LiteLLM支持在参数里配置超时时间比如router.completion( modelchat-service, messages[{role: user, content: 你好}], timeout30, )给每个请求设置一个合理超时是保证整体服务不被拖垮的重要习惯。4.2 流式输出让回答像打字机一样逐字出现现在的对话应用如果不做流式输出用户等待感受会非常差。LiteLLM对流式输出的支持和普通SDK几乎一致关键是设置streamTrue。response litellm.completion( model厂商前缀/模型名, messages[{role: user, content: 写一篇短文}], streamTrue, ) for chunk in response: delta chunk.choices[0].delta.content if delta: print(delta, end, flushTrue)流式返回的chunk结构是统一的每个chunk里可能带着增量内容。有一点要注意不同模型对流式响应的细节处理略有不同有的会在最后一个chunk里带finish_reason有的不会。你在做数据清洗时要容忍这些差异直接在消费端判断是否还有内容即可不要过度依赖某个厂商特定的结束标记。4.3 自定义提示词模板少写样板代码在真实项目里我们经常遇到这类需求同样的系统提示词在多个地方复用。LiteLLM允许你在请求参数里传入额外的自定义参数底层SDK不认识的参数也不会报错因为它默认会把未知参数过滤掉。对于频繁使用的提示词我习惯在项目里维护一个模板函数统一拼接system和user消息。这样代码里的业务逻辑就不会被长提示词淹没。这个不是LiteLLM独有的功能但它和LiteLLM的组合非常舒服——因为不管底下换的是哪家模型消息结构都是一样的。5. 常见问题与排查实录5.1 模型名格式总是报错这是我见过最多的问题没有之一。很多人直接把一个不带前缀的模型名传给completion结果LiteLLM不知道应该调用哪个SDK直接报错。解决方法是确认模型名前缀是否匹配你使用的模型服务。如果是一般平台模型前缀就是平台标识如果是自定义部署的模型服务需要在配置里写明model和对应的api_base。你可以在LiteLLM文档或源码里的常量表中查所有支持的前缀。排查命令也可以这样先设环境变量LITELLM_LOGDEBUG再调用litellm.completion日志会把匹配到的SDK打印出来一眼就能看出前缀是否被正确识别。5.2 环境变量不生效很多人设置好环境变量以后发现LiteLLM还是报鉴权错误。大概率是因为环境变量名和你调用的模型服务不匹配或者变量值中包含空格、换行符。我排查这类问题时会先写一段代码直接读取系统环境变量确认值存在且正确import os print(os.environ.get(MODEL_API_KEY))如果值本身没问题再检查代码里是否显式传了api_key参数把环境变量覆盖了。在LiteLLM里显式传参优先于环境变量这是个容易忽略的行为。另外要注意的是环境变量变更后运行中的Python进程不会自动读取新值你需要重启服务才能生效。不要在这个问题上浪费时间。5.3 代理模式鉴权问题代理模式跑通之后下一步就是给下游服务分配密钥。用master_key启动代理以后新生成的虚拟密钥需要调用管理接口来创建。一个常见的坑是下游服务请求头里如果带了错误的密钥格式代理会返回401但这个401响应体的结构可能和模型平台的鉴权错误结构不一样。在下游SDK里要确保它把代理返回的错误信息原样输出到日志里不要只打印状态码。我遇到过好几次只打状态码导致排查了半小时才发现是密钥格式问题。5.4 超时与并发控制模型平台不稳定时超时是最常见的表现形式。LiteLLM默认的超时策略不一定适合每家公司你需要根据自己的场景显式指定。对于对话类应用我一般设置15到30秒对于长文本生成任务会设置更长一些。并发控制也是重要环节。如果你在代理模式下没有加任何并发限制某次高峰流量可能会把你的模型调用预算烧穿。LiteLLM允许在代理配置里设置限流策略。我这边的经验是先统计正常业务峰值QPS然后在此基础上留20%到30%的余量设置一个硬上限宁可让极少数的请求排队或失败也不能让整个系统的预算失控。6. 一些真实的使用心得LiteLLM算是我在最近一年多里用得比较顺手的一个基础设施类项目。它简化了多模型接入流程减少了团队里的重复封装代码也让模型切换从一个谨慎的架构决策变成了一个普通的配置变更。不过它也不是银弹。如果你的项目极其简单只用一家模型平台那LiteLLM能带来的收益很有限额外引入一个抽象层反而会增加排查链路。但如果你的项目需要频繁评估新模型、需要在多个模型之间做容灾切换、需要把模型能力开放给多个内部服务那么LiteLLM的性价比就很高。我在实际项目中琢磨出一套判断标准当你发现自己的代码里开始出现针对不同厂商的if-else分支或者每个新服务都要重复封装一遍模型调用时就说明该上统一接入层了。LiteLLM是这类需求里很成熟的一个选项而且它是开源的遇到问题可以看源码心里踏实。最后说个细节无论你用不用LiteLLM模型调用层的统一设计都值得做。因为大模型领域迭代太快了今天的最优模型半年后可能就被另一个取代。谁的系统能低成本切换到新模型谁的迭代速度就更快。LiteLLM帮我把这部分成本压到了很低这也是我愿意持续用它的原因。