上个月我接手了一个内部AI平台的项目代码里到处散落着各家大模型API的调用逻辑有的用官方SDK有的直接拼HTTP请求还有几个服务是自己封了一层又一层。出了问题找起来特别痛苦一会儿是这个模型超时了一会儿是那个接口密钥过期日志格式五花八门每个服务都有自己的错误处理方式。接手第一个星期我每天都陷在这种接口泥潭里。后来我把这套东西全部切到了litellm上也算是把这堆烂账彻底理清了。这篇文章我就想从一个实际维护者的角度聊聊litellm到底能解决什么、怎么把它部署起来、以及在生产环境里踩过的那些坑。不管你是刚接触大模型应用开发还是已经在多模型场景里挣扎了一段时间这篇内容应该都能给你一些参考。1. 先搞清楚一件事为什么需要统一的模型网关1.1 多模型接入时最让人抓狂的那些事先说一个很普遍的场景。你所在的小组里有人调GPT系模型写文案有人用开源模型做向量化还有人接了某个闭源模型做代码补全。每个人负责一个模块看起来风平浪静。但只要业务上出现这个模型效果不行我们换个模型试试的需求麻烦立刻来了。代码层面不同模型商的API格式差异很大。有的用messages结构传对话有的用prompt字段传文本有的支持流式返回有的只给完整JSON认证方式也不同有的是Authorization: Bearer有的是自定义请求头。这些差异直接导致了换模型就要动业务代码的窘境。而且密钥管理也成问题——每个人的密钥放在自己的环境变量里没有统一的轮转机制谁离职了谁的密钥还留在系统里想想都后背发凉。成本核算更是一笔糊涂账。每个模型商的账单格式不同、计量口径不同有的按Token算有的按次算有的还分输入输出不同价格。你根本没办法在月底快速回答老板一个问题这个月我们在模型上到底花了多少钱各业务线占比多少1.2 litellm 的核心解决思路litellm的思路很直接它把所有模型商的API差异封装在一个统一的接口后面你只需要用一种格式去请求它由它负责把请求转换成各家模型商能识别的格式再把返回结果统一转换回来。它的形态上有两种用法。第一种是当成SDK直接嵌入你的Python项目里调用风格特别简单。第二种是作为独立代理服务跑起来你的业务代码通过HTTP去请求它然后由它转发给真正的模型商。第二种形态在团队协作和生产环境里更实用因为代理服务本身是独立部署的下游业务完全不感知模型切换只需要改代理的配置就行。这个设计天然解决了几件事一是接口标准化业务侧代码只需要维护一种调用方式二是密钥集中管理所有密钥收拢到配置文件或环境变量里不用散落在各个服务中三是模型可切换改配置就能换模型甚至支持多个模型自动升降级。本质上它给团队提供了一个API适配层把上游模型商的差异挡在了外面。2. 核心功能逐项拆解与实操要点2.1 统一调用接口的设计逻辑litellm把各种模型商的调用统一成了一个标准结构。以对话补全接口为例不管你接的是哪家模型最终请求格式都长这样{ model: gpt-3.5-turbo, messages: [ {role: system, content: 你是一个助手}, {role: user, content: 你好} ], temperature: 0.7 }底层对应的映射关系是litellm在内部维护的一张模型路由表。当你把请求发过来它会根据model字段去找到对应的模型商、对应的API格式、对应的认证方式然后把你的标准请求转换成目标模型商的格式发起实际调用。这里面有一个经常被忽略的细节Token的统计口径。litellm在统一接口时不仅做了格式转换还把各家返回结果中的用量信息也统一了输出结构里固定包含prompt_tokens、completion_tokens、total_tokens这三个字段。这就为后续做成本追踪打下了基础。如果你自己在SDK层面封装过多个模型商的调用你真的会明白这有多省心。2.2 模型路由与故障转移litellm支持在配置里定义一组模型并设置路由策略。举个例子你可以配置三个模型优先级依次排当主用模型返回错误或超时时自动去调用下一个备用的模型model_list: - model_name: my-primary litellm_params: model: gpt-4o api_key: os.environ/KEY_A - model_name: my-fallback litellm_params: model: claude-3-5-sonnet api_key: os.environ/KEY_B请求进来时你只需要传model: my-primarylitellm会在那个主用模型出问题时自动切换。这个能力在业务高峰期特别有存在感。以前我遇到过某模型商限流整个服务直接没法响应后来加了备用模型同一个请求在几十毫秒级别就被切到了备用链路用户几乎无感知。不过路由策略也别太乐观生产环境里我明确遇到过几个问题一是切换可用但切换后质量没人把关——备用模型效果可能明显弱于主用模型业务侧必须有心理预期二是超时时间设置不当导致雪崩——如果主用模型本身只是响应慢但还没到超时阈值备用模型不会触发这时候需要合理设置timeout参数。核心逻辑是超时时间、重试次数、备用模型触发条件这三者必须联动配置而不是拍脑袋定。2.3 成本追踪与用量统计成本追踪是litellm非常有价值的模块。因为它在代理层拦截了所有请求所以天然能记录每次调用的Token数、模型来源、请求时间、响应耗时。这些数据可以写入数据库默认支持SQLite生产环境建议用PostgreSQL然后在管理面板里按模型、按用户、按时间段聚合展示。实操层面成本计算的核心在于价格表的维护。litellm内置了很多常见模型商的默认价格但如果你用的是国内某些模型商的自定义模型或是有特殊折扣的价格就必须自己在配置里重写价格逻辑。以某个模型为例如果你知道它的输入价格是每百万Token 10元、输出价格是每百万Token 30元配置是这样写的model_list: - model_name: my-cn-model litellm_params: model: my-cn-model api_key: os.environ/KEY_C model_info: input_cost_per_token: 0.00001 output_cost_per_token: 0.00003单位注意一下这里填的是单个Token的价格所以每百万Token 10元换算过来就是10 / 1000000 0.00001。我踩过的一个坑是价格表写错了一个零导致某个月的成本报表直接翻了几十倍差点让老板以为团队模型用量爆炸了。所以关于成本追踪我的建议是前期一定要用真实账单核对至少一周确认litellm统计的金额和模型商账单对上再放开全量接入。2.4 轻量级代理服务的形态litellm可以当作一个独立服务启动默认监听4000端口所有下游服务都去请求这个地址。这个代理服务有几个典型的使用场景。第一个场景是团队内部统一出口。前端、后端、算法各团队都走你这一个口子密钥不再分发给个人只在代理这一层统一保管。换密钥只需要改代理配置下游业务零感知。第二个场景是本地开发联调。你可以让代理跑在开发环境下游开发人员不需要各自申请各模型商的密钥只需要知道代理地址和一个通用凭据就行。第三个场景是模型切换的灰度验证。代理层可以针对不同请求来源做转发比如10%流量转发到新模型90%留在旧模型。虽然litellm本身没有内置复杂的百分比灰度能力但配合上游网关做简单的分流是可行的。实际落地时我更推荐先在代码里用一个可控的开关位来切换某个业务线走新模型跑几天看效果再逐步放大流量。3. 从零搭建一套可用的 litellm 代理服务3.1 环境准备与安装方式litellm本质上是一个Python包安装很简单pip install litellm[proxy]注意要带[proxy]这个extra否则你只装了SDK核心代理服务模式相关的依赖不会装全。这里有一个小坑如果你用的是一个比较精简的Python基础镜像装完后可能会有一些系统库缺失比如libffi、openssl相关的依赖直接表现就是启动报错。这种情况建议直接用官方Docker镜像省去一堆麻烦docker pull ghcr.io/berriai/litellm:main-latest镜像里的依赖是预装好的环境变量、配置文件挂载进去就能跑。我自己的习惯是本地开发用pip安装方便调试代码生产部署用Docker干净省事。3.2 配置文件的核心写法litellm的配置文件是YAML格式核心要素是model_list和litellm_settings两部分。下面是一份我实际用过的精简配置里面去掉了具体厂商信息保留了完整的结构model_list: - model_name: assistant-main litellm_params: model: gpt-4o api_key: os.environ/KEY_A timeout: 30 - model_name: assistant-fallback litellm_params: model: claude-3-5-sonnet api_key: os.environ/KEY_B timeout: 30 - model_name: embedding-main litellm_params: model: text-embedding-3-large api_key: os.environ/KEY_A litellm_settings: drop_params: true set_verbose: true num_retries: 3 request_timeout: 30 retry_after: 5 general_settings: master_key: sk-123456 database_url: postgresql://user:passlocalhost:5432/litellmmodel_name是你业务侧的逻辑模型名你可以在业务代码里稳定地使用这个名字而底层实际接哪个模型完全由配置文件决定。这个抽象层是litellm的灵魂。os.environ/KEY_A这种写法表示从环境变量里读密钥而不是直接把密钥写死在配置文件里避免配置文件泄露导致密钥泄露。drop_params这个参数值得单独说。它的作用是当请求里传了某些参数而目标模型不支持时litellm会自动忽略这些参数而不是直接报错。比如response_format这个参数某个模型不支持另一个模型支持有drop_params: true切换到不支持的模型时请求不会失败。代价是部分参数静默失效所以还是要自己把握好参数兼容性。3.3 启动服务并完成联通验证配置写好后通过命令行启动export KEY_Ask-xxxx export KEY_Bsk-xxxx litellm --config config.yaml --port 4000看到日志输出Uvicorn running on http://0.0.0.0:4000说明代理服务已经起来了。这时候先用curl做一个最简单的心跳测试确保代理在运行curl http://localhost:4000/health/liveliness然后试着一个真实调用验证配置文件里的模型映射是否能通curl http://localhost:4000/v1/chat/completions \ -H Content-Type: application/json \ -H Authorization: Bearer sk-123456 \ -d { model: assistant-main, messages: [{role: user, content: 你好做个自我介绍}] }这里请求路径是/v1/chat/completions它模拟了主流模型商兼容的接口格式所以你甚至可以把一些原本面向特定模型商开发的应用直接把base_url改成litellm的地址就能把底层模型替换掉。这个兼容性设计在迁移存量项目时帮了大忙。3.4 业务代码接入时的两种姿势接入litellm有两种常见姿势取决于你已有的技术栈。如果项目是Python技术栈直接pip安装litellm SDK然后用统一方法调用最直观import litellm response litellm.completion( modelassistant-main, messages[ {role: user, content: 你好} ], api_basehttp://localhost:4000, api_keysk-123456 ) print(response.choices[0].message.content)这里有一个容易搞混的概念modelassistant-main是逻辑模型名真正生效的模型在litellm代理的配置里。但你必须同时传api_base指向代理服务否则litellm会把你写的逻辑模型名当成真实模型商去请求然后报一个不认识的模型错误。如果项目是Node.js、Go或者其他语言要么用对应语言的OpenAI兼容SDK把base_url指向litellm要么直接发HTTP请求。原理是一样的litellm对外提供了OpenAI兼容的接口格式所以任何支持自定义base_url的客户端SDK都可以无缝对接litellm代理。4. 生产环境落地时的常见问题与排查4.1 请求超时与重试引发的连锁问题生产环境最容易踩的坑就是超时重试这套机制。litellm默认会对失败的请求做重试num_retries控制重试次数retry_after控制重试间隔。听起来很美好但你仔细想想如果模型商那边已经卡住了你这边拼命重试只会把短暂的限流问题放大成持续的资源耗尽。我的建议是把num_retries设置在2到3之间不要超过3request_timeout根据业务场景来普通对话生成给30秒长文档生成给60秒以上重试要加上指数退避不要让重试请求在同一个时间点全部打出去。另外跨模型的重试路由也要注意。如果主用模型已经返回了一个正常的业务错误比如请求参数不合法、内容审核不过但litellm的配置里错误归类不细它可能也会触发重试或故障转移。这会导致一个很尴尬的局面用户本来内容违规被拒了结果代理帮他又转发到了另一个模型试试看另一个模型给了完全不同的回答。这个行为在审核场景下是有潜在风险的。你要么在配置里把错误类别区分清楚要么在业务侧严格校验后再进代理。4.2 Token统计偏差和成本报表失真Token统计这块我上面提到过价格表写错的问题。还有一个常见问题有些模型商的返回结果里usage字段并没有给全比如prompt_tokens和completion_tokens缺了一个。litellm这时会用它的估算逻辑来补比如用字符数除以某个系数来估算。估算出来的数字和真实计费账单必然有偏差。所以我的经验是litellm的成本报表适合用来做趋势监控和环比分析比如看看这周比上周增长了还是下降了哪个业务线用量增长最快。它不适合用来做精确的对账工具。如果你们的财务要求非常精确的账单还是要以模型商官方账单为准。如果你在用流式输出模式Token统计的偏差会更明显。因为在流式返回时litellm需要把每个增量chunk都统计起来最后汇总成完整的usage。极端情况下如果客户端中途断开了连接litellm统计的Token数会和模型商实际计费不一致。这个偏差在生产环境里几乎无法完全消除只能通过日志记录来定位异常。4.3 多模型切换时的上下文管理问题很多人在多模型切换时都会忽略一个问题不同模型的上下文长度限制不一样。你在A模型上能跑通的长对话切到B模型可能直接报context length exceeded。比如A模型支持128K上下文B模型只支持32K你把一个60K上下文的请求传过去B模型直接拒绝。这个问题的处理方法有几个一是在路由策略里让litellm根据请求的Token数自动过滤掉不支持的模型二是给模型按上下文大小分组比如长上下文组和短上下文组业务侧根据场景选择用哪个组而不是盲选一个逻辑模型名。另外切换模型时的prompt格式也可能需要微调。同样的系统提示词在A模型上表现很好切到B模型上风格可能完全变了。这不是bug是不同模型本身的性格差异。如果业务对回答风格有强要求适配工作是必要的不要指望一个提示词通吃所有模型。4.4 高并发下的稳定性调优litellm代理毕竟是单服务形态高并发环境下它本身可能成为瓶颈。默认情况下它依赖Python异步框架整体吞吐量还可以但也不是无限扩展。我的建议是在最前面再加一层负载均衡后面挂多个litellm实例共享同一个数据库。这里有个关键点要提醒你如果多个litellm实例共享同一个数据库那么每个实例的配置尽量保持一致否则会出现同一个逻辑模型名在不同实例上路由到了不同模型这种诡异问题。另外数据库的连接池大小要根据实例数量调不然数据库那边先撑不住了。高并发下还有一个细节日志量非常大。litellm默认会打印每一次请求的详细日志包括请求体、响应体。如果你们接入的业务量一天几十万次调用这些日志能把磁盘刷爆。我建议在生产环境把set_verbose设为false只保留关键日志或者接入专门的日志系统做采样采集。写在最后的经验之谈litellm这套东西说它复杂吧装起来跑起来也就半小时的事说它简单吧真正用起来之后会发现有很多需要根据自己业务去调的地方。我个人觉得它最大的价值不是省了那几行适配代码而是逼着团队建立了模型接入规范化的意识——密钥集中管理、调用链路由统一出口、成本可观测、模型切换有预案。这些习惯一旦养成后面不管新增多少模型、切换多少次供应商整个系统都能保持稳定。最后分享一个小技巧每次改动litellm配置之前先备份当前配置文件和对应的数据库改动后用一小段测试脚本跑一遍核心链路。这样出了问题可以快速回滚到上一版尤其是模型路由、超时参数这些直接影响线上请求的配置项。生产环境里稳比快重要得多。