一个多月前我们团队的业务接入场景突然变得很复杂。本来的思路是固定用某一家的模型API结果上半年内部好几个项目分别提出要试试不同的模型有的更擅长代码生成有的在处理长文档时表现更好还有一批老接口只认自家SDK。结果就是代码里东一个import、西一个base_urlprompt模板散落得到处都是环境变量也是五花八门。那阵子我每天被不同模型供应商的限流、鉴权、报错格式来回折腾才认真去研究liteLLM。这篇文章不是官方文档的翻译是我从调研选型、本地部署、配置踩坑到小规模生产使用的完整记录适合那些想用一套API统一管理多个模型项目的开发者参考。1. 项目背景当项目里同时接入三家模型API我决定自建一个网关1.1 多供应商调用带来的四个真实痛点先说清楚我为什么要往项目里塞一个中间层而不是继续在业务代码里一个个适配。多供应商调用并不是什么新问题但真的落到工程里痛点比想象中具体。第一SDK和调用方式很难统一。A家SDK的命名习惯和B家完全不一样同一个生成对话补全的需求有的接口叫completions有的叫messages还有的直接是RESTful风格的/v1/generate。业务层为了兼容这些差异会写出一堆if-else分发逻辑。刚开始可能只有两三个分支看着还能忍后面每接一个新模型都要动一遍公共代码回归测试成本越来越高。第二参数语义不一致。最大输出长度这个参数不同供应商有时候叫max_tokens有时候叫max_output_tokens还有的要写成max_new_tokens。如果只是名称不同还好说问题是有的供应商要求用字符串传temperature有的只接受浮点数有的会把top_p和top_k都当成必填项。业务方根本不应该去关心这些底层细节但它们又实实在在地影响调用成功率和生成质量。第三限流和故障不在同一节奏上。每个供应商的限流阈值不一样有的按每分钟请求数RPM限制有的按每分钟token数TPM限制还有的要区分并发连接数。一旦某个供应商的高峰时段出现大量429而业务代码里没有统一的降级策略影响就直接扩散到用户侧了。第四成本不可见。多个项目共用一个API key时很难统计某个业务线到底用了多少token费用算在哪个成本中心头上。月底对账基本都是模模糊糊的这在团队规模变大之后是非常头疼的管理问题。这四点本质上都指向同一个诉求在模型供应商和业务代码之间加一层标准化的接入层让上层只认一种API风格把路由、鉴权、限流、计量这些脏活留给接入层去处理。1.2 为什么选择LiteLLM而不是自己封装SDK其实我们自己内部也讨论过要不要写一个统一封装层。毕竟多模型调用这件事看起来就是把各家SDK包一层然后暴露统一的接口工作量好像不大。但我后来算了笔账发现自研方案很容易在三个地方失控。第一个失控点是新供应商的适配节奏。我们团队不可能持续关注所有模型的接口变更。今天这个接口更新了鉴权方式明天那个模型下线了某个参数跟得慢就要出批量故障。而LiteLLM这类专门做模型接入的开源项目适配工作比一个业务团队快得多它支持的模型提供商一直在更新等于帮我把踩坑清单提前趟了一遍。第二个失控点是隐形的工程成本。统一封装层看起来简单但要做模型回退、负载均衡、预算控制、日志追踪、多租户密钥管理这些功能合在一起就是一个小型网关系统。公司内部如果已经有平台组可以认真考虑自研但对我们这种想快速解决业务问题的团队来说直接站在开源实现上更划算。第三个失控点是生态兼容。现在很多开发框架比如LangChain、LlamaIndex默认都支持OpenAI风格的API配置。LiteLLM对外暴露的就是OpenAI兼容接口这意味着业务代码只需要改一个base_url原本的调用逻辑基本不用大动。如果自研这套生态兼容也要自己去维护。LiteLLM当然也有学习成本配置文件的字段不少生产化之后还要考虑部署形态。但至少它把核心的模型路由和统一接入做成了可复用的能力而不是让我们从零开始造轮子。2. LiteLLM核心设计一个统一接入层到底做了什么2.1 请求从进入网关到返回的全过程在用LiteLLM之前我一直把它理解成一个多模型的SDK聚合库后来自己部署完才搞清楚它真正的产品形态是一个具备完整生命周期管理的网关服务。一个典型的请求流程是这样的业务服务按照OpenAI SDK的写法把请求发送到LiteLLM暴露的HTTP接口比如http://localhost:4000。请求头里带着对外暴露的模型名一般是自己在配置里定义的别名比如gpt-4o-mini。网关先做身份校验校验通过的请求会进入路由模块路由模块根据model_list配置找到这个别名对应的真实模型和真实供应商。接下来是参数归一化环节。业务代码里传的max_tokens、temperature这类OpenAI风格参数会被翻译成目标供应商API实际需要的字段格式。比如某个供应商只认max_output_tokens这时候网关会完成映射。最后网关发起真实请求拿到响应后再把响应结构转回OpenAI风格返回给调用方。如果开启了fallback或负载均衡这个链路里还会多一步当主模型返回错误、超时或者触发限流时网关会按照预定策略把请求转发到备用模型上对上游业务来说整个过程是无感的。流式请求会透明地按块转发token用量和成本数据会被异步记录下来。这一套流程听起来不复杂但真正做起来需要大量细节处理。尤其是参数归一化并不是简单翻译字段名就行还要处理不同供应商对参数取值范围、必填校验的差异。LiteLLM把这些差异都封装在内部这是它最大的价值。2.2 参数归一化与模型映射的底层逻辑我刚开始配置的时候对model_list里model_name和litellm_params.model这两个字段很困惑为什么都是模型名却要写两遍。后来才理解model_name是对外暴露的名字也就是业务侧请求时用的模型名litellm_params.model是真实供应商模型标识LiteLLM会拿这个标识去查对应的供应商SDK。这种设计让别名变得非常灵活。举个例子我可以在外面暴露一个gpt-4o-mini的名字但内部真实指向openai/gpt-4o-mini也可以暴露一个text-model的名字通过修改底层配置把请求从A供应商切换到B供应商业务代码完全不用动。这一点在模型版本升级、供应商切换时特别有用。参数归一化是另一个核心机制。LiteLLM内部维护了一套参数字段映射表把供应商之间差异较大的字段统一成一套标准参数。业务层用OpenAI风格传参网关负责转换成目标API能够识别的格式。比如某些接口不支持top_p或者只支持整数形式的temperature这些细节都会被网关处理掉。不过要注意归一化不等于所有参数都能自动兼容。有些供应商有自己独有参数比如Anthropic的system字段单独支持一些扩展Gemini的safetySettings也无法用OpenAI风格直接表达。这种情况需要在litellm_params里显式写供应商特有参数。我在配置里踩过这种坑后面第5章会专门讲。2.3 限流、预算与可用性控制一个只做转发的网关意义有限真正让LiteLLM有价值的是它把限流、预算、可用性控制这些运维能力也纳入进来了。限流层面网关支持配置每个模型的RPM和TPM上限。当请求量达到阈值不需要等供应商返回429网关自己就会先拦截一部分流量避免因为突发流量击穿供应商配额。预算管理方面可以按团队、按API key、甚至按整个网关设置Token预算或费用上限超过后自动拒绝新请求。这个功能在多个业务线共用一个接入层时尤其有用能避免某条业务线把整个预算打光。可用性控制的核心是fallback。简单理解就是配置一个主模型列表和备用模型列表主模型不可用时自动切换到备用。这个能力直接降低了单点模型故障的影响范围。我把fallback配置放在第4章详细演示这里先让你理解它在整个网关中的定位。3. 本地跑通LiteLLM的完整链路3.1 安装与依赖环境LiteLLM本身是一个Python包部署形态有两种一种是直接在业务代码里调用litellm.completion()更像SDK模式另一种是启动一个独立的网关服务对外提供OpenAI兼容HTTP接口。我推荐优先跑通第二种因为它的隔离性和可替换性更好后续接多个业务线也更方便。安装之前先准备一个干净的Python环境我用的是Python 3.10和venv。装包时需要注意基础包和网关服务依赖可能是分开的。如果只想在代码里调用安装litellm就够了但要启动网关服务建议安装带[proxy]扩展的版本避免后面缺依赖。python3 -m venv liteenv source liteenv/bin/activate pip install litellm[proxy]装完之后可以检查版本litellm --version如果这个命令能正常输出版本号说明环境基本就绪。我在一些老机器上遇到过pydantic版本冲突通常是因为系统里其他项目已经装了不同版本。解决办法是重建虚拟环境或者把pydantic升级到LiteLLM要求的版本。3.2 第一个配置文件model_list与model_name的写法LiteLLM的配置以YAML为主最核心的段落是model_list。第一次配置我建议只放两个模型跑通链路之后再逐步加。下面这份配置我加了注释方便你理解每个字段的意图model_list: - model_name: gpt-4o-mini litellm_params: model: openai/gpt-4o-mini api_key: os.environ/OPENAI_API_KEY - model_name: claude-3-5-sonnet litellm_params: model: anthropic/claude-3-5-sonnet api_key: os.environ/ANTHROPIC_API_KEY litellm_settings: drop_params: true set_verbose: false这里要提醒几个细节。model字段里的前缀openai/、anthropic/是LiteLLM判断供应商的凭据不能省略也不能随便写。api_key既可以直接写明文也可以像我这样用os.environ/XXX引用环境变量。直接用环境变量引用更安全也方便在不同环境里切换密钥。drop_params: true的意思是当请求参数在目标供应商API里不存在时网关直接把它丢弃而不是报错。这个选项对我们这种多供应商统一接入非常实用但也要注意副作用后面我会专门讲它在某些场景下掩盖问题的情况。3.3 启动网关并用OpenAI SDK直接调用配置写好后启动网关只需要一条命令litellm --config config.yaml --port 4000启动日志里会显示一个master key默认格式类似sk-...这个master key是访问网关所有管理接口和调用接口的根凭证。第一次我直接用了默认key没有做任何修改结果发现后面做多租户管理时会很被动。建议启动前先设置一个自定义的master key可以直接写在配置里general_settings: master_key: sk-my-master-key然后就可以用OpenAI SDK调用网关。以Python为例from openai import OpenAI client OpenAI( base_urlhttp://localhost:4000, api_keysk-my-master-key ) response client.chat.completions.create( modelgpt-4o-mini, messages[ {role: user, content: 你好介绍一下你自己} ] ) print(response.choices[0].message.content)注意这里的model传的是model_list里定义的model_name也就是对外别名。只要配置里有这个别名网关会自动找到对应的真实模型。我用同一个SDK直接切换到claude-3-5-sonnet只改了model字段返回结构和格式几乎一致。这一步跑通之后我就确信这套方案可以落地到业务代码里不需要再维护两套SDK调用逻辑。4. 一张配置表管好全部模型进阶用法4.1 模型回退与负载均衡配置本地跑通基础功能后我开始把更真实的需求填进配置里。第一个就是模型回退。业务场景很典型某个主模型偶尔会触发限流或者临时不可用希望系统自动切换到一个价格更便宜或响应更快的备用模型上。LiteLLM的fallback配置可以按模型维度做。下面的示意配置表示当gpt-4o-mini不可用时请求自动尝试gpt-4o当claude-3-5-sonnet不可用时请求自动尝试claude-3-haikurouter_settings: routing_strategy: simple-shuffle fallbacks: [ { gpt-4o-mini: [gpt-4o] }, { claude-3-5-sonnet: [claude-3-haiku] } ]我还测试了负载均衡。同一个模型名后面可以配多个真实供应商实例通过设置权重来分配流量。这样可以降低单一供应商的调用压力某些场景下还能减少限流概率。配置逻辑和fallback类似只是model_list里同一个model_name可以出现多条model_list: - model_name: gpt-4o litellm_params: model: openai/gpt-4o api_key: os.environ/OPENAI_API_KEY weights: 0.7 - model_name: gpt-4o litellm_params: model: openai/gpt-4o api_key: os.environ/OPENAI_API_KEY_2 weights: 0.3有人可能会问同一个模型配两个不同API key有什么用。实际用起来还是有意义的比如项目里有多个配额来源或者不同业务位共享不同额度这样做可以把负载分散开。不过也要注意同一供应商的多个key如果都挂在同一个账户下限流总量并不会改变只是并发上限分摊了。4.2 成本与Token用量统计成本可视化是我比较看重的一块。LiteLLM默认会记录每次请求的模型名、总token数、成本金额等信息。这些数据可以通过回调机制导出。最简单的做法是开启成功回调把数据写入日志或数据库。我在测试环境用prometheus回调做了一版生产环境把回调接到已有的监控系统。配置片段如下litellm_settings: success_callback: [prometheus] failure_callback: [prometheus]打开网关的/metrics端点可以看到litellm_total_tokens、litellm_cost等指标。对于不想接入Prometheus的小团队也可以先写CSV日志。做法是自定义一个回调函数或者用网关自带的日志记录把关键字段打出来。我后来用量统计做了一件很实际的事按团队维度拆分预算。LiteLLM支持在配置里定义team然后给每个team分配不同的模型访问权限和预算限额。这样当某个团队的业务量膨胀时能第一时间看到成本趋势而不至于等下个月账单出来才发现异常。4.3 多租户密钥管理与团队隔离多租户是网关类组件比较实用的能力。LiteLLM允许管理员用master key生成多个虚拟key每个key可以绑定团队、限制模型范围、设置预算和过期时间。我在项目里实际这样操作的先创建一个team比如叫team-a然后给这个team生成一个专用keycurl http://localhost:4000/key/generate \ -H Authorization: Bearer sk-my-master-key \ -H Content-Type: application/json \ -d {team_id: team-a, max_budget: 100}生成的虚拟key会返回给team-a的使用方业务代码里只需要把它当成一个普通OpenAI key看待即可。如果某个key泄漏了可以单独吊销不会影响其他团队。这个设计解决了我之前提到的成本归属问题。因为每个虚拟key的usage都可以独立查看不再需要所有业务线共享一个key月底对账时直接按key维度拉明细就行。4.4 与LangChain等框架的集成我们项目里有一部分逻辑用LangChain做Agent编排之前绑定的是OpenAI的模型实例。把LiteLLM作为网关接入后LangChain侧基本不需要改代码只需要把底层LLM的base_url配置改为http://localhost:4000模型名改为网关里定义的别名。以LangChain的ChatOpenAI为例from langchain_openai import ChatOpenAI llm ChatOpenAI( modelgpt-4o-mini, openai_api_basehttp://localhost:4000, api_keysk-my-master-key )这样LangChain的调用链就统一走网关底层模型可以随时换成其他供应商。对于团队里已经用LangChain搭了挺多能力的组来说这个兼容性很关键几乎是无痛迁移。5. 我踩过的坑配置、参数与流式响应5.1 模型名冲突与供应商参数不一致第一次配多模型的时候我犯过一个很隐蔽的错两个供应商都提供了gpt-4o-mini这个名字对应的能力但它们的真实模型是不同的我在model_list里给两个模型用了同一个model_name结果导致路由无法确定该走哪条链路请求时好时坏。原因很简单LiteLLM的model_name是路由主键不能重复。如果确实需要一个对外名字指向多个实例必须确保它们的真实模型能力等价并且你确实想要负载均衡效果。否则就应该拆成不同的别名比如gpt-4o-mini-openai和gpt-4o-mini-anthropic。参数不一致问题也让我头疼过一阵。某个供应商不支持top_p如果请求里带了它网络层会直接报错。我开了drop_params: true之后这类问题确实被屏蔽了但反过来也会让我忽略一些本应该暴露的配置错误。比如我明明拼错了某个自定义参数名网关不会立刻报警而是默默丢参生成结果质量变差又很难定位原因。后来我的做法是网关路由到公共模型的请求尽量只用通用参数比如temperature、max_tokens、messages供应商特有参数要么单独写litellm_params要么通过请求里的额外字段显式传不依赖drop_params去兜底。5.2 流式请求的兼容性处理流式响应是LiteLLM网关绕不开的环节。业务系统里有些场景是用户希望看到打字机效果所以我在第一次集成时就测试了streamTrue参数。大多数情况下OpenAI SDK向网关发起流式请求网关会向真实供应商发起流式请求然后一块块转发回来。这个链路看起来直接但实际有两处容易出问题。第一个是不同供应商的流式chunk格式不完全一样。OpenAI风格是choices[0].delta.contentGemini的流式响应里可能会有额外字段。LiteLLM会尽量把这些差异转成统一的chunk格式但如果你在业务代码里直接读取原始流对象并且高度依赖某个供应商特有的时间戳、token计数等字段就很可能出现兼容性问题。第二个是stream_options配置。某些场景下调用方希望在流式返回的最后一个chunk里看到完整用量统计OpenAI SDK通过stream_options{include_usage: True}实现。LiteLLM对不同供应商的支持程度不完全一致我在其中一个供应商的模型上遇到过usage信息缺失的情况。排查下来发现是网关对该供应商的流式响应解析逻辑和OpenAI不完全对等最后我把用量统计统一放在网关的成功回调里不依赖流式响应末尾的usage字段才稳定下来。如果你在业务代码里需要对每个chunk做精细处理建议先在一个普通模型上确认返回结构再切到其他模型。别想当然认为流式结构一定一致。5.3 超时与重试策略的简化思路网关引入之后超时和重试就变得比之前更复杂了。因为现在多了一个中间落地节点如果网关自身超时时间设置不当底层模型还没响应完上游业务已经断开了。我一开始在每个模型各自的litellm_params里配置了timeout后来发现全局统一设一种超时并不合理。不同任务差异很大有的简单摘要只需要几秒长文本生成可能要一两分钟。建议至少区分流式和非流式场景。流式请求的超时要放宽一些因为连接保持时间比较长。重试方面我的经验是优先用fallback而不是retry。因为同一个供应商如果因为限流或故障告警短时间重试大概率还是失败直接切换到备用模型反而更有效。如果一定要配置重试记得重试次数不要太多并设置指数退避。否则压力高峰期会放大一倍流量到网关网关再放大到供应商容易引发雪崩。我还发现一个容易被忽略的点网关对上游供应商的请求如果已经发出去了但长时间没有响应LiteLLM默认会等待较久。这时如果上游业务已经抛弃了这个请求网关这边的资源还在占着。所以一定要在网关层和上游调用方同时设置超时双重保护。6. 从网关到平台化LiteLLM在生产中的扩展方向6.1 垂直扩展部署到Kubernetes后的配置要点本地和测试环境跑通之后我们很快就把网关作为独立服务部署到了Kubernetes里。这个阶段没有想象中复杂但有一些生产环境特有的问题需要注意。首先是配置分离。建议把密钥类的环境变量放到Kubernetes的Secret中而不是写进配置文件。网关本身支持从环境变量读取密钥所以配置里只保留os.environ/XXX这种占位符运行时注入真实值。这样即使配置仓库泄漏也不会直接暴露密钥。其次是多副本部署时的Redis依赖。LiteLLM的单实例模式很简单但一旦跑多个副本就需要共享限流计数、虚拟key、预算额度之类的状态信息否则每个副本各自维护一套状态限流和预算都会不准确。官方支持用Redis做缓存和状态存储生产环境跑多个副本时一定要把Redis配置好。还有一个容易忽视的问题是健康检查。网关的HTTP接口可以直接作为Kubernetes的存活探针和就绪探针但要注意探针请求也会消耗网关资源。我建议把探针指向一个单独的管理接口不要让探针把模型调用计入了业务用量统计。6.2 横向整合统一审计日志与告警网关天然是所有模型调用的收口点所以它也是做审计日志和告警的最佳位置。业务代码里零零散散的调用日志没有统一格式排查问题非常费劲有了LiteLLM之后我可以把每次请求的来源key、团队、模型名、供应商、耗时、token数和成本信息统一打印或者回传到日志平台里。我在生产环境做了两件比较有效的事情。第一是接入统一的请求ID在业务代码和网关之间透传同一个trace id这样从用户请求到模型响应整条链路都能串起来。第二是根据网关暴露的指标配置告警规则比如限流错误率超过阈值、某个团队的预算接近上限、某个模型供应商的错误率突增都会触发告警。这一层做扎实之后开发同学就不会再因为某个模型到底什么时候开始变慢去翻多个平台的日志了。所有信息在统一的地方都能看到。6.3 留给读者的一些后续方向如果你准备在自己的项目里采用LiteLLM我从个人实践出发给你一个路线建议。第一步先不要在业务里大规模改动而是单独把网关跑起来接入两个最常用的模型让有一两个业务试点切到新接入层。第二步确认用量统计、日志、限流和fallback满足了团队基本需求再逐步把其他业务线迁移过来。第三步如果团队内有多语言栈比如既有Python也有Java可以考虑让网关成为标准入口所有语言都通过HTTP调用这样公共SDK只需维护一份。我目前还在完善的工作是更细粒度的模型路由策略希望能让某些任务的调用默认走更便宜的模型同时允许用户显式指定高规格模型。这个方向依赖LiteLLM的路由配置和业务侧的模型元信息做起来有一点复杂度但收益是成本控制更精细。回到最初的问题为什么选择LiteLLM。对我来说最重要的是它把多模型的工程复杂度收拢到了一个可维护的边界之内。它不是一个万能工具配置细节不少有些模型的兼容性也不完美但从实际效果看确实让我从各家SDK都要会写的状态中解放出来把精力放回业务逻辑本身。最后提醒一句LiteLLM迭代比较快你在网上看到某条配置时最好结合当前版本文档确认一下字段是否仍然有效。我在这篇文章里给的是个人实践时能跑通的写法但版本更新后个别参数可能会调整。上手前建议先在自己的测试环境里跑一遍别直接搬上生产。