首页 / 资讯中心 / 文章详情

litellm:统一大模型接口的代理网关,让模型切换不再改代码

litellm:统一大模型接口的代理网关,让模型切换不再改代码 ★ FEATURED ARTICLE
先说结论litellm 不是一个模型也不是某个厂商的SDK它是一个把各种大模型接口收口成统一入口的代理网关。我把它接入生产环境之后最大的感受就一句话——终于不用再为“换模型”这件事改业务代码了。最近的几次模型选型变动我们几乎没有改动业务侧一行代码。原因很简单所有请求都先打到 litellm 网关由它去路由、转发、记账、兜底。如果你正在做 AI 应用工程化或者你所在的项目已经出现“每个业务线各接各的模型、各写各的调用代码”这种苗头那这篇文章应该能帮上忙。我会从为什么需要它、它内部怎么工作、怎么搭起来到生产环境真正会踩的坑按我自己的实操顺序讲一遍。1. 为什么我最终把litellm放在所有大模型调用前面1.1 一个典型的多模型接入痛点先描述一个我见过的、可能你也见过的场景。项目早期只有一两个模型接口业务代码里直接封装一个调用类把请求发出去就完事。团队大了以后A 业务想用某闭源厂商的对话模型B 业务觉得另一家模型的生成效果更好C 业务为了成本开始尝试本地推理框架部署的开源模型。麻烦来了。各家接口的消息结构不一样参数的命名不一样流式返回的格式不一样连鉴权方式都不一样。于是每个业务线被迫各自维护一套适配代码模型地址散落在配置中心、环境变量甚至某个人的本地环境变量里。更难受的是运维侧想统计这个月到底花了多少钱在模型调用上得去各个厂商后台分别拉账单想排查一次线上请求失败是因为哪一步日志里连统一追踪ID都没有。这个问题本质上不是“模型不够好”而是“接入层太混乱”。litellm 解决的就是这一层。1.2 litellm 的定位它不碰模型只做收口litellm 做的事情可以概括成一句话提供一个标准入口把“发出请求”和“由哪个厂商模型响应”解耦。业务代码只需要知道一个网关地址和一个逻辑模型名至于这个逻辑名后面挂的是哪个服务商、密钥放在哪里、超时多久、失败后切换到哪个备用模型全部由网关配置来控制。这个思路很像支付系统里的统一收单平台。商户不需要知道用户手里的卡是哪家银行发行的也不需要对每个渠道单独联调收单平台统一处理路由、对账、异常交易。litellm 在模型调用链条里干的就是收单平台的活。它本身不训练模型不做向量数据库也不生成内容。它是一个纯代理层把请求转发给真实的目标模型服务再把响应原样传回。这也是我最初敢放心接进来的原因——那一层收口做得越干净后续替换模型的自由度就越大。1.3 我们团队的使用场景我所在的组有多个业务线在同时调用大模型。具体点是三块一个客服知识库问答使用的是主打对话能力的闭源模型一个内容辅助写作工具经常需要流式输出一个代码片段解释服务主要走本地推理框架部署的开源模型。这三块的模型完全不同鉴权方式不同计费方式也不同。以前每个业务线自己对接、自己报预算财务想统一管理非常痛苦。引入 litellm 之后我们把所有真实模型地址和密钥收进网关配置三个业务线统一通过网关调用。对外他们只需要向网关申请一个虚拟密钥月底账单、调用量、失败率全部从网关一侧拉取。这一步完成之后我自己的体会是模型接入这件事终于从“每个业务线的临时任务”变成了“网关侧的一行配置变更”。2. litellm到底做了什么一次请求的完整旅程2.1 从业务代码到模型的完整路径为了讲清楚 litellm 的机制我拆一条完整请求链路给你看。业务代码发出请求时并不会直接访问厂商地址而是访问 litellm 代理暴露出来的 HTTP 接口。请求体里带的是一个逻辑模型名比如assistant-v1。代理收到请求后先校验这个虚拟密钥是否有权限、预算是否充足然后去配置的模型列表中查找assistant-v1对应的真实目标信息包括真实模型标识、目标服务的api_base、真实厂商密钥。代理用这些信息组装新的请求发给目标服务等待响应再把响应原样返回给调用方。这个过程中业务代码接触到的只有网关不接触任何真实厂商地址和密钥。厂商侧的限流、报错、密钥轮换在业务侧完全无感。2.2 模型名映射是核心机制litellm 的灵魂就在这一小段配置里。model_name是暴露给调用方的逻辑名litellm_params里是真实的目标描述。你可以理解成给每个模型起了一个“内部花名”业务侧只认花名。我看过不少初始使用者的困惑为什么我已经填了模型名却老是报模型不存在大概率是把“逻辑名”和“目标模型的真实标识”搞混了。在配置里model_name可以随意起它本身只是一个路由键真正决定发往哪里的是api_base和model字段。2.3 对外接口的统一和透传litellm 对外暴露的是一套符合主流聊天补全协议风格的 HTTP 接口。这意味着你原来用官方 SDK 写好的请求只要把 base URL 指到 litellm 代理地址把模型名改成逻辑名其它消息体基本不用动。流式请求同样支持。代理会以流式方式接收目标服务返回的增量内容再一点一点转发给业务端。工具调用、多轮对话、图片输入这种多模态请求也能透传前提是你挂载的模型本身支持这些能力。我在实践中发现一个值得注意的边界litellm 能帮你统一“传输协议”但不会帮你把某个模型不具备的能力硬生生变出来。如果一个模型本身不支持工具调用你通过 litellm 也一样调用不了。网关做的是“让支持的东西可以被统一地使用”而不是“让不支持的东西成为支持”。2.4 多目标策略故障转移与负载均衡litellm 真正拉开和其他简单代理差距的地方是它可以在一个逻辑名下面挂多个真实目标。最简单的是 fallback主模型返回错误或超时网关自动改发备用模型。还有负载均衡同一个逻辑名挂两个相同能力的模型实例按权重分发请求。高级一点的用法是设置模型冷却时间某个实例连续出错时自动暂时踢出路由池恢复后再重新加进来。这个设计对标的是传统微服务架构里的服务发现与熔断。区别在于litellm 的“节点”是不同厂商的模型 API而不是你自己的后端服务实例。对业务侧来说它始终只看到一个稳定的逻辑服务背后的模型调度全在网关层完成。3. 从零搭一个可用的litellm网关配置、启动与客户端切换3.1 准备工作与最小依赖先明确环境要求。litellm 本身是 Python 生态建议 Python 3.9 以上。装代理模式需要安装带 extra 的版本pip install litellm[proxy]如果你的调用量很小不需要持久化日志那么本地装完这一个包就能跑。但如果是生产用途我建议至少准备两样东西一个用于存储请求日志、虚拟密钥、预算记录的数据库Postgres 是比较稳妥的选择一个可选的 Redis 实例用于限流计数和缓存扛高并发时候会用到。我第一版部署时没有立刻上数据库结果想查历史日志无从下手后来还是老老实实把持久化补上。所以这里给你的建议是初始阶段可以不上 Redis但数据库尽量一开始就接上不然后面补数据链路比较痛苦。3.2 最小可用的配置文件创建一个config.yaml先把两个模型接进来。这里的重点是逻辑名和真实目标的映射。model_list: - model_name: assistant-v1 litellm_params: model: openai-compatible/assistant-v1 api_base: https://api.vendor-a.example.com/v1 api_key: os.environ.VENDOR_A_API_KEY - model_name: embed-text litellm_params: model: openai-compatible/embed-text-v3 api_base: https://api.vendor-b.example.com/v1 api_key: os.environ.VENDOR_B_API_KEY litellm_settings: drop_params: true set_verbose: false general_settings: master_key: sk-litellm-master-key-here database_url: os.environ.DATABASE_URL几点说明model里的前缀只是 litellm 用来区分协议类型的标识不一定代表某个特定厂商。我这里用openai-compatible表达“这是一个兼容标准聊天补全协议的接口”你实际填的时候按你后端服务的真实类型来定。drop_params: true可以自动丢弃目标模型不支持的参数减少因参数多余而报 400 的概率。但它也会掩盖一些问题所以我一般会在本地调试时关掉确认没问题再打开。master_key是网关管理员密钥它用于管理虚拟密钥、访问管理端点不是发给业务侧直接用的。3.3 启动代理服务开发环境直接命令行启动就行litellm --config config.yaml --port 4000启动成功后可以先用健康检查接口确认进程是活的再查看当前注册的模型列表。生产环境我更推荐用容器方式部署。把配置文件通过挂载目录放进去设置环境变量后启动。这里要注意资源分配litellm 本身是带内存的 Python 进程并发高时 CPU 和内存占用会明显上升不要把它当成一个“零开销”的纯网络代理来规划机器规格。3.4 客户端切换从直连到过网关接入 litellm 后客户端的改动非常小。以前你的代码里可能直接面向某厂商的接口地址现在只需要把地址换成 litellm 网关把模型名换成逻辑名同时改用网关签发的虚拟密钥。一个最小验证例子用 curlcurl http://localhost:4000/v1/chat/completions \ -H Authorization: Bearer sk-litellm-valid-key \ -H Content-Type: application/json \ -d { model: assistant-v1, messages: [{role: user, content: 你好}] }Python 侧也一样不改变消息结构import requests resp requests.post( http://localhost:4000/v1/chat/completions, headers{ Authorization: Bearer sk-litellm-valid-key, Content-Type: application/json, }, json{ model: assistant-v1, messages: [{role: user, content: 你好}], }, timeout60, ) print(resp.json())关键点在于业务代码只认assistant-v1这个逻辑名。哪天你想把背后模型从 A 厂商换成 B 厂商只需要在网关配置里修改model_list中assistant-v1对应的目标然后重启或热加载配置业务代码完全不用动。这一点在模型选型频繁变动的阶段价值非常大。4. 成本、权限与日志生产环境绕不开的三件事4.1 预算与限额防止失控模型接口是有真实金钱成本的不像普通代理调自己的内网服务可以随意。litellm 提供按虚拟密钥、按团队、按时间段设置预算的能力。比如model_list: - model_name: assistant-v1 litellm_params: model: openai-compatible/assistant-v1 api_base: https://api.vendor-a.example.com/v1 api_key: os.environ.VENDOR_A_API_KEY max_budget: 100 budget_duration: 30d意思是这个模型在 30 天内的总消耗预算上限是 100 美元超过之后新的请求会被拒绝。更精细的做法是按虚拟密钥分配预算比如某个业务线的早期验证阶段只给 20 美元的额度花完就停避免出现“月初没人管月底账单爆炸”的情况。我实际用下来预算配置最大的作用是给了业务方一个显式的成本边界。以前大家都不关心一次请求多少钱现在每次调用的花费会记录在案业务方自己在设计功能和并发时就会主动去算成本。4.2 虚拟密钥管理真实密钥不能直接外放litellm 的虚拟密钥体系值得单独拎出来讲。真实厂商密钥只存在网关配置或环境变量里业务侧拿到的是一把由网关签发的sk-litellm-开头的虚拟钥匙。虚拟密钥可以绑定某个团队、某个用户、某个预算范围也可以随时吊销。生产环境我强烈建议每个业务线、甚至每个上行服务单独申请一个虚拟密钥。好处是显而易见的某一个业务线的密钥泄漏了在网关侧吊销那一个就行其它业务不受影响月底财务对账时按虚拟密钥归类统计谁花了多少钱一目了然。如果不做这一步真实厂商密钥一旦被业务代码直接持有基本等于钥匙复制了无数份出了问题你根本不知道是哪一把泄漏的。4.3 可观测性日志比模型本身更值得花时间litellm 在持久化配置打开后会把每次请求的模型名、token 消耗、花费、延迟、响应状态、调用方虚拟密钥等信息写入数据库。再加上你在请求里透传的metadata可以把业务自定义的 ID 一起带上排障时非常有用。我的做法是调用方在请求体里附加一个metadata字段里面带上业务侧的请求 ID、用户 ID、功能模块。这样出问题时我不用去翻多个系统的日志对齐时间直接在 litellm 的日志里按请求 ID 过滤就能看到这一次调用经过网关时发生了什么是模型返回超时还是 token 数异常还是被预算拦截。如果你需要实时监控面板litellm 也预留了指标暴露接口可以接到常见监控系统里做看板和告警。阈值可以根据自家模型的响应时间分布来定比如观测到正常 p95 是 3 秒那告警就设在 8 秒以上避免日常偶发波动刷屏。4.4 内置价格表与自定义成本模型litellm 内置维护了一份主流模型的价格表常见的服务商和模型型号它会自动计算成本。但如果你接的是自己通过推理框架部署的开源模型那就需要自定义成本。比如你的本地模型每百万输入 token 定价很低甚至只是内部结算价可以在配置里加上成本映射让网关能按你的价格表记账。这样统计出来的花费才符合真实账单不然日志里这一项会一直是零。model_cost_map: openai-compatible/assistant-v1: input_cost_per_token: 0.0000001 output_cost_per_token: 0.0000002这个配置的价值在于即使目标模型不在内置价格表里你依然能得到准确到每笔请求的成本数据。财务要的不是“大概多少钱”而是可追溯、可对账的数字。5. 实战中踩过的坑和绕过的弯路5.1 token 计量不准未知模型导致日志花费为 0我第一次接本地模型时请求日志里花费一直是 0。排查了半天原因是 litellm 不认识我自定义的模型标识不知道它的价格自然就计不出金额。解决办法有两个方向要么把模型接成内置价格表中已有的形态要么像上面那样主动提供成本映射。比较隐蔽的是有些模型的计费口径还区分了“缓存命中的输入 token”和“未命中的输入 token”价格不同如果你只登记了一个平均值月底对账就会有偏差。这个坑提醒我两件事一是接入新模型后第一件事不是测响应速度而是确认日志里的成本和 token 数是否正常二是模型版本升级也可能会改变计费方式升级后需要重新核对一次网关统计。5.2 默认重试在高并发下可能造成雪崩litellm 默认的容错机制里包含重试这是好事。但有一次我们压测时发现某厂商接口开始限流后网关侧大量请求同时进入重试逻辑等于限流期间又给上游追加了成倍的压力反而让恢复变得更慢。后来我把重试策略显式调了一下不再每次都重试而是根据上游返回的错误码决定是否重试同时对超时时间做了更符合业务的配置。这里没有放之四海皆准的参数需要结合厂商的限流策略和你的业务容忍度来定。如果你拿不准我的建议是“宁可少试几次也不要默认无限重试”。模型侧在抖动时网关的重试只会让故障恢复时间延长而不是缩短。5.3 fallback 的隐性问题模型切换了业务不知道fallback 听起来很美好但有一个细节容易被忽略主模型和备用模型的能力边界不一定一样。比如主模型支持 200K 上下文备用模型只支持 32K那么一个长文档请求在主模型失败后切换到备用模型可能直接因为上下文超限再次报错。更隐蔽的是内容风格。主模型擅长按一定格式输出备用模型可能不擅长。用户明明看到响应成功但格式变了、质量下降了体验很奇怪。我的处理方式在请求日志里始终记录实际响应的目标模型并把它作为一个独立字段暴露出来。这样即使发生了 fallback运维侧和业务侧也能快速感知到“这次请求其实是备用模型应答的”而不是在用户开始抱怨格式不对时才来找我。5.4 高并发下网关本身也要扩容litellm 代理不是神单实例能扛的并发有限。在一次大流量活动前我做了一轮压测发现网关进程的 CPU 会率先成为瓶颈而不是下游模型。这是因为每个请求都要做密钥校验、预算查询、请求日志落库叠加起来消耗不小。生产部署上我的建议是网关至少两个实例前面用负载均衡分散流量数据库连接池要按实例数放大如果预算查询频率很高把 Redis 用上能明显降低数据库压力。litellm 本身支持横向扩展它并不维护唯一的内部状态真正要盯住的是共享数据库的负载。5.5 密钥与安全不要把管理端口暴露到公网最后说一个容易被忽视的安全问题。litellm 的运维管理接口功能强大可以生成密钥、修改配置、查看日志。如果这块直接暴露到公网即使有 master_key 保护被爆破的风险也会高很多。我当时做的最小化方案是管理接口只在内网网段开放公网请求一律只能访问聊天补全等业务路由同时在网关前面再挂一层白名单只放行自己服务的出口 IP。密钥管理这件事集中是方便了但也意味着这个节点本身成了高价值目标安全保护必须跟着升级。我实际踩过一次配置失误后现在的原则很简单暴露给外部的路由越少越好所有管理动作都走内网。宁可多加几步跳板操作也不给攻击者一个直连入口。最后再分享一个小技巧。接入 litellm 之后的很长一段时间里我觉得最值回票价的不是统一入口也不是自动账本而是把metadata里的业务请求 ID 一路带到日志里这个习惯。模型接口报错是常态但绝大多数问题其实不是模型本身的问题而是参数拼错、超时太短、预算用尽、密钥过期这类小事。有了统一的请求日志和业务 ID定位这些问题从“翻山越岭”变成了“按图索骥”。这个习惯越早养成后面排障省下的时间就越多。
阅读完成 · 觉得有帮助?
咨询建站