1. LiteLLM 是什么一个把上百种模型统一起来的接入层这两年做 AI 应用的人应该都有同一种感受模型服务越来越多各家 API 的调用方式却千奇百怪。有的用/v1/completions有的用/v1/messages鉴权方式、超时策略、流式返回格式、token 计费方式全都不一样。今天用 A 厂的模型写好的代码明天想换成 B 厂基本等于重写一遍对接层。LiteLLM 干的事情就是把这层脏活累活包下来对外提供一个统一的调用入口内部再去适配不同模型厂商的协议。我第一次接触 LiteLLM 是在做一个内部工具的时候。当时项目里要同时接入好几种模型有的负责写代码有的负责总结文档还有本地部署的开源模型要给客户做演示。一开始我老老实实按厂商文档写适配器接了两家就发现不对头每家都有自己的一套错误码、重试逻辑、超时配置光是把这些差异抹平代码量就已经超过了业务本身。后来在社区看到 LiteLLM思路一下就通了——它相当于在业务代码和模型厂商之间插了一层网关你只需要按一套 OpenAI 兼容的格式发请求它会帮你把请求翻译成目标模型认识的样子再把结果翻译回统一格式返回。这个项目本质上是标准的网关模式。不管底层接的是云厂商还是本地跑的开源模型对上层业务来说都只是一个base_url加一个 API Key 的事。这样一来业务代码可以做到完全不知道底层模型的差异模型切换、灰度、降级都变成配置问题而不是代码问题。1.1 为什么需要这样一层“胶水”你可以把不同模型厂商理解成不同国家的插座标准形状不一样、电压不一样、接口规范也不一样。LiteLLM 就是那个万能转换头让你无论飞到哪个国家插上自己的设备就能用。对于个人开发者来说这层转换省的是时间对于团队来说省的是整个基础设施的复杂度。没有这层统一抽象时常见的痛苦是这些同一段业务逻辑要写好几份适配代码每接入一个新模型就要复制改一遍。模型厂商升级 SDK 或调整接口业务代码被动跟着升级。错误处理不统一有的厂商限流返回 429有的返回 503有的把限流信息藏在响应头里。日志格式五花八门排查一个问题要把不同服务的日志拼起来看。想做一个模型选路和自动降级只能靠硬编码改一次路由要发布一次代码。LiteLLM 把这些横切关注点收敛到一个模块里业务侧只需要关心“发什么消息、拿什么回复”。后面接哪个模型、要不要切换、哪家便宜、哪家稳定都不需要业务侧感知。1.2 SDK 模式与代理模式怎么选LiteLLM 提供了两种用法很多人第一次接触会混淆这里说清楚。SDK 模式是直接在 Python 代码里调用litellm.completion()之类的方法把 LiteLLM 当成一个库来用。请求从你的进程发出LiteLLM 在进程内部完成协议转换、重试、路由然后返回结果。这种方式适合换模型、统一接口、简化代码的场景不需要额外部署服务。代理模式则是把 LiteLLM 跑成一个独立服务暴露一个兼容 OpenAI 的 HTTP 接口。业务代码不直接感知 LiteLLM 的存在它只需要把自己的请求发到一个地址上剩下的由代理服务完成。适合团队共享模型入口、需要统一鉴权与计费、多业务复用同一批模型配置的场景。对比维度SDK 模式代理模式部署复杂度零额外部署跟着业务进程走需要起一个独立服务接入成本改代码用 LiteLLM 的方法发起请求改配置业务侧只换 base_url适合场景单应用、个人项目、快速验证多应用、多团队、统一管控扩展能力依赖进程内配置可以集中管理模型、密钥、预算、日志故障隔离模型超时会拖住业务进程资源代理独立承载业务侧更安全我的建议是如果只是自己写脚本、做小工具用 SDK 模式就够了少维护一个服务。如果团队里有多个项目要共用模型资源或者需要给非技术同学提供可视化配置入口直接上代理模式省心得多。1.3 核心能力速览LiteLLM 能做的事远不止协议转换这里列几个我实际用下来觉得最值钱的能力统一接口所有模型都走 OpenAI 风格的 Chat Completions 格式参数命名一致返回结构一致。协议适配内部封装了各家厂商的鉴权、请求签名、响应解析逻辑相当于把各家的 SDK 都替你包了一层。失败重试统一的重试策略、超时控制、指数退避不用自己在业务代码里写循环。模型路由支持按策略分发到不同模型也可以配置故障转移主模型挂了自动切备用模型。成本统计按模型单价和 token 消耗估算每次调用的成本代理模式还能把花费记录到数据库。缓存可以对相同请求做缓存降低重复调用成本缩短响应时间。2. 五分钟跑通第一个补全调用先给没接触过的朋友一条完整的上手路径。假设你的机器上已经有 Python 3.9 以上的环境我们直接用 pip 装一个最小可用的 LiteLLM然后发起第一次请求。2.1 安装与环境准备安装很简单一条命令pip install litellm装完之后在终端里确认一下版本python -c import litellm; print(litellm.__version__)然后准备 API Key。LiteLLM 约定通过环境变量加载密钥这样密钥不会写死在代码里。你可以在终端 export也可以写进.env文件用dotenv加载。我习惯在项目根目录放一个.env格式大概是这样的OPENAI_API_KEYsk-xxxx ANTHROPIC_API_KEYsk-ant-xxxx GOOGLE_API_KEYAIzaxxxxPython 这边加一行from dotenv import load_dotenv load_dotenv()这里有个容易被坑的地方LiteLLM 默认会按照环境变量的名字去判断该用哪个厂商的密钥。比如请求openai/gpt-4o它默认找OPENAI_API_KEY请求anthropic/claude-3.5-sonnet它默认找ANTHROPIC_API_KEY。如果变量名不对请求会报环境变量未找到的错误。2.2 最简单的 completion 请求安装好之后写一个最基础的脚本import litellm response litellm.completion( modelopenai/gpt-4o, messages[ {role: user, content: 用一句话介绍你自己} ] ) print(response.choices[0].message.content)这个调用和 OpenAI SDK 的写法几乎一样区别在于model参数变成了openai/gpt-4o这种带厂商前缀的格式。LiteLLM 看到openai/前缀就知道要把请求转成 OpenAI 的协议于是去读OPENAI_API_KEY向对应的服务端点发起请求。如果你想调用别家的模型比如 Anthropic 的 Claude只需要把 model 参数改成response litellm.completion( modelanthropic/claude-3-5-sonnet-20241022, messages[ {role: user, content: 用一句话介绍你自己} ] )其他代码完全不用动。这就体现出统一接口的价值了业务逻辑不用关心底层模型是谁只需要维护好 messages 的结构。2.3 流式输出与对话补全的区别很多模型厂商都支持流式输出也就是响应内容分块推回来而不是等全部生成完再一次返回。LiteLLM 把流式行为也统一了开启方式非常简单response litellm.completion( modelopenai/gpt-4o, messages[{role: user, content: 写一段 500 字的短文}], streamTrue ) for chunk in response: delta chunk.choices[0].delta.content if delta: print(delta, end, flushTrue)注意流式返回的对象和一次性返回的对象结构不一样。一次性返回里内容是放在response.choices[0].message.content流式返回里每一块的内容是放在chunk.choices[0].delta.content。新手第一次用流式最容易在这个字段上写错。还有一点流式输出时token 用量统计通常会放在最后一个 chunk 的usage字段里如果你在大量使用流式接口记得处理这个细节否则成本统计会漏掉最后一段。2.4 model 参数背后的 provider 解析逻辑很多人第一次看modelopenai/gpt-4o会好奇这个斜杠前面的名字是不是必须的。是必须的但它的作用不是区分模型而是区分厂商协议。LiteLLM 内部有一个厂商列表它通过斜杠前面的部分找到对应的 provider 处理器。如果你不写前缀直接传gpt-4oLiteLLM 会尝试自动猜测厂商但这种猜测不是百分百可靠。比如某些模型名在多家厂商都有同名型号猜错就麻烦了。所以我建议任何时候都明确写上前缀宁可长一点也不要让路由逻辑靠猜。市面上也有一些纯开源模型跑在本地的 Ollama 或者兼容 OpenAI 协议的本地服务上LiteLLM 同样支持。例如本地跑一个聊天模型可以这样指定response litellm.completion( modelollama/llama3.1:8b, messages[{role: user, content: 你好}], api_basehttp://localhost:11434 )这种场景下前缀变成了ollama/同时通过api_base告诉它本地服务的地址。LiteLLM 强大的一个体现就是它给本地模型和云端模型保留了同样的使用姿势切换体验非常顺滑。3. 代理模式把多模型接入做成一个网关SDK 模式解决的是“代码里别写死某一家模型”代理模式解决的是“基础设施层面把模型入口统一收口”。如果你的团队有多套系统都要用大模型让每套系统单独接 LiteLLM SDK 并不划算因为模型配置、密钥管理、预算控制都是重复的。这时候把 LiteLLM 跑成一个网关让所有业务都通过这个网关访问模型会顺手很多。3.1 为什么要起一个代理服务代理模式最大的好处是业务侧演化成了零代码接入。业务系统只需要把 OpenAI SDK 的base_url指向 LiteLLM 代理地址其他什么都不用改。对于已经用了 OpenAI SDK 的老项目改动量常常只是一个环境变量。举个例子原先业务代码里可能是这样调 OpenAI 的from openai import OpenAI client OpenAI(api_keysk-xxx) resp client.chat.completions.create( modelgpt-4o, messages[{role: user, content: 你好}] )接入 LiteLLM 代理后只需要改成from openai import OpenAI client OpenAI( api_keysk-any-key, base_urlhttp://your-gateway-host:8000 ) resp client.chat.completions.create( modelgpt-4o, # 这个值由网关解析 messages[{role: user, content: 你好}] )你甚至可以把api_key写成一个固定的虚拟密钥真正的厂商密钥只存在网关服务端业务侧永远接触不到。这对安全管理来说是个巨大的加分项。代理模式还能顺带解决一个现实问题业务团队每个人都去厂商官网申请密钥充值的额度分散在好多个人头上账单管理很痛苦。统一收口之后密钥统一由运维或平台负责成本也统一核算避免资源浪费。3.2 一份最小可用的代理配置代理模式需要一个 YAML 配置文件用于声明你能使用哪些模型、模型的真实名字是什么、密钥从哪来。先看一个非常精简的示例model_list: - model_name: gpt-4o litellm_params: model: openai/gpt-4o api_key: os.environ/OPENAI_API_KEY - model_name: claude-3-5 litellm_params: model: anthropic/claude-3-5-sonnet-20241022 api_key: os.environ/ANTHROPIC_API_KEY - model_name: local-llama litellm_params: model: ollama/llama3.1:8b api_base: http://localhost:11434这里面的逻辑是model_name是业务侧看到的别名litellm_params.model是 LiteLLM 内部真正要访问的模型标识。业务调用的时候传gpt-4o网关收到后把它映射成openai/gpt-4o再发起请求。这种别名的设计很有用比如你想要在某个时间窗口把业务从 GPT-4 平滑切到另一个模型只要在网关配置里把gpt-4o这个别名的 target 改掉业务代码一行不用动。启动代理服务litellm --config config.yaml --port 8000看到监听日志之后就可以用 OpenAI SDK 或 curl 来测试了curl http://localhost:8000/v1/chat/completions \ -H Content-Type: application/json \ -H Authorization: Bearer sk-any-key \ -d { model: gpt-4o, messages: [{role: user, content: 你好}] }3.3 业务代码接入代理的方式代理暴露的接口是 OpenAI 兼容的所以几乎所有语言的 OpenAI SDK 都能直接连上来。Python 侧的 OpenAI SDK 设置方式上面已经演示过其他语言也大同小异无非是设置base_url和api_key。如果业务代码原来是直接调 HTTP 接口的也没问题。LiteLLM 代理对外暴露的路径基本能对齐 OpenAI 的路径结构比如/v1/chat/completions、/v1/embeddings、/v1/models这些都会提供。你可以先用/v1/models看一下网关注册了哪些模型确认配置生效了。3.4 鉴权与多用户隔离代理默认为了内网调试方便可能会让请求直接绕过鉴权。但一旦多人使用就不能裸奔了。LiteLLM 代理支持配置 master key也就是管理用的密钥同时可以为不同团队或应用生成虚拟密钥。虚拟密钥的好处是可以控制某个密钥能用哪些模型、每分钟最多请求多少次、总共能花多少钱。这相当于在网关层面做了一层细粒度的访问控制。比如 A 团队只能用通用模型B 团队可以用更强的模型但不能超过月预算都能通过密钥策略表达出来。我个人建议即使是团队内部使用也至少配上 master key不要开放无鉴权模式。否则一旦网关地址被外部扫描到你就等于把花钱的入口免费送给了别人。4. 进阶玩法路由、重试、预算与可观测性跑通基础调用之后LiteLLM 真正拉开差距的地方在于那些生产环境才用得上的能力。下面按路由、重试、成本、可观测性四个维度分别展开。4.1 路由策略让流量自动找到最合适的模型代理模式下业务侧只需要传入一个抽象的模型名。至于这个请求到底发给哪个厂商的哪个模型可以由网关动态决定。LiteLLM 支持多种路由策略最常见的几种优先级路由根据model_list中配置的顺序选模型前面的优先挂了才用后面的。加权随机路由给每个模型配一个权重按权重比例分配流量。最少请求路由选当前并发请求数最少的模型适合负载均衡。延迟优先路由根据历史平均响应延迟选最快的一个。路由配置放在litellm_settings里。举个例子litellm_settings: routing_strategy: latency-based-routing model_list: - model_name: chat-model litellm_params: model: openai/gpt-4o model_info: priority: 1 - model_name: chat-model litellm_params: model: anthropic/claude-3-5-sonnet-20241022 model_info: priority: 2这个配置的意思是业务侧调用chat-model网关会优先发往优先级为 1 的 GPT-4o如果 GPT-4o 不可用或者超时再切换到优先级为 2 的 Claude。对于不想让业务感知具体模型变化的场景这个机制非常实用。4.2 重试、超时与故障转移的配置权衡生产环境里模型厂商的接口大概率不会一直稳定。LiteLLM 把重试和超时也集中管理了。最常用的几个参数是timeout、num_retries和max_retries。import litellm litellm.timeout 30 litellm.num_retries 3这里的逻辑是单个请求等待上限 30 秒失败后最多自动重试 3 次。LiteLLM 内部对重试做了随机抖动和指数退避不是傻傻地立刻重发因此可以在一定程度上降低触发厂商限流的概率。配置重试时有一个权衡重试次数设太多用户侧感知的响应时间会被拖长请求队列也会越积越多设太少偶发的抖动直接变成失败返回用户体验不好。我一般先将超时控制在 20 到 30 秒重试控制在 2 到 3 次再根据线上监控调整。故障转移和重试不太一样它指的是主模型连续失败后把请求转给备用模型。上面说的 fallback 配置就是干这个的。举个例子model_list: - model_name: chat-model litellm_params: model: openai/gpt-4o api_key: os.environ/OPENAI_API_KEY fallbacks: [anthropic/claude-3-5-sonnet-20241022]当openai/gpt-4o超时或返回错误时代理自动转给 Claude。备用模型也需要在配置里准备好。这种机制比业务侧自己写 try-except 去切换要干净得多因为重试和切换状态都被代理收拢了业务侧拿到的是一个正常成功响应。4.3 成本追踪与实际落地的预算控制大模型应用上线之后成本往往是最让人焦虑的问题之一。LiteLLM 内置了价格表可以根据模型、输入 token 和输出 token 估算每次调用的成本。SDK 模式下响应对象里一般可以拿到 usage 信息和 Cost 相关信息代理模式下花费数据可以写入数据库方便后续对账。一个比较落地的做法是在代理配置中启用预算控制general_settings: master_key: sk-master-xxx database_url: postgresql://user:passlocalhost:5432/litellm启动后代理会把每次调用的模型、token 数、估算成本写入数据库。配合虚拟密钥层面的限额可以做到某个团队或某个应用的钱花到一定数额之后自动拒绝请求。这种预算护栏在多人共用网关时极其重要否则月底账单会让人措手不及。4.4 留痕日志、缓存与追踪注意点日志是排查问题的基本手段。代理模式下LiteLLM 会把请求和响应的核心信息记录下来包括模型、耗时、token 数、错误信息。开启数据库后这些记录会自动持久化。团队内部可以基于这些日志做告警和统计比如某个模型连续报错就通知运维。缓存方面如果同一段提示词反复请求相同参数和相同内容的结果可以缓存一段时间减少重复调用和费用。配置 Redis 之类的缓存服务后可以在代理配置中声明缓存策略。需要注意的是缓存命中后你拿到的可能是历史结果如果业务对实时性敏感比如想通过提示词动态调节输出风格就必须谨慎开启缓存。延迟监控可以通过_response_ms这类字段获取单次请求的毫秒耗时。我习惯把请求耗时、token 数、成本三个指标一起塞进日志这样排查慢请求和费用异常时可以快速定位。5. 我踩过的坑常见报错与排查记录LiteLLM 用起来不算难但生产环境里细节很多。下面把我在实际项目里遇到的问题整理成表格和清单希望你能躲开我走过的弯路。5.1 高频报错对照表报错或现象典型原因处理办法AuthenticationError/ 401密钥不存在、变量名不对、key 被吊销检查环境变量名与 model 前缀是否匹配RateLimitError/ 429超出厂商调用配额提高重试间隔降低并发或启用备用模型TimeoutError模型响应太慢超时阈值太短调大timeout检查模型服务是否过载NotFoundError/ 404model 参数中的模型名不存在核对厂商官方模型名确认前缀正确环境变量相关的报错.env没加载或命名拼写错误先用print(os.environ)确认加载成功流式输出取不到内容用了非流式的字段读流式结果检查delta.content字段与streamTrue是否匹配代理模式下业务报“模型不存在”model_list里没配置这个别名检查 YAML 配置确认model_name正确本地模型请求失败api_base没设置或本地服务没启动确认本地服务端口可达地址是否正确5.2 部署性能调优的几条经验网关本身是一个 Python 服务并发能力取决于你启动了多少 worker。默认单进程跑可能承载不了太高的 QPS。生产环境建议使用多 worker 启动比如用 gunicorn 或者 uvicorn 的多 worker 模式。数据库存储会带来额外写入开销如果请求量很大建议把日志写入做成异步的不要让它阻塞核心请求路径。我在一个项目里曾经因为日志写入太慢导致整体响应时间翻倍后来改成异步批量写入才算解决。缓存命中率对成本和延迟影响很大但缓存设置也不能一概而论。对于相同提示词的高频调用比如内容审核规则固定、关键词抽取模板固定缓存效果很好。对于发散性强的对话类请求缓存命中率很低配置了反而增加复杂度。5.3 工程化接入时的避坑清单这部分是综合多个项目经验整理出来的优先级从上到下模型名称一律写完整前缀不要靠自动推断。密钥不要留在代码仓库里统一走环境变量或密钥管理服务。代理模式必须配置鉴权哪怕是内网环境。重试策略、超时时间在 LiteLLM 层统一设置不要在每段业务代码里重复写。大并发场景下给代理前面的负载均衡预留健康检查接口。定时任务类流量要设置合理的并发限制避免瞬时打爆厂商配额。成本记录不要依赖最终账单在代理层先做一版预估成本。模型版本升级后在代理配置里确认模型名没有过期厂商经常悄悄废弃旧版本。6. 用了一段时间之后的真实感受把 LiteLLM 引入实际项目之前我一直是手动维护模型适配层每天都跟各家厂商的文档打交道。换成 LiteLLM 之后最直观的感受是写业务代码的时候不需要再想“这个模型是哪个厂商的”模型切换、故障转移、成本统计都变成了配置和运维问题。尤其是代理模式团队里多个应用共用同一个网关密钥管理、预算控制、日志审计都集中了省下的不只是代码量还有跨团队协调的成本。如果说有什么需要适应的地方那就是它的配置项很多一开始容易陷入文档里出不来。我的建议是从最小配置起步先把一个模型的调用跑通再慢慢加路由、重试、预算、缓存。避免一上来就追求大而全配置复杂了排错反而困难。如果你正在为多个模型服务的差异化接口发愁或者想给团队搭一个统一的大模型入口LiteLLM 值得花一个下午试试。
阅读完成 · 觉得有帮助?