先说个背景我这半年一直在做智能客服相关的内部系统涉及多家模型服务商的接口接入。刚开始图省事直接在业务代码里挨个写适配结果改到第三家的时候光是消息长度、返回结构、超时行为这些边角差异就已经让代码里全是 if else。后来朋友推荐我试了一下 litellm我才发现这玩意儿能在一个统一的接口后面把模型调用、密钥管理、成本统计、降级策略全都收进去。这篇不是官方文档的翻译是我自己从接入到上生产这段时间的实践总结适合那些想把多个模型统一纳管、又不想在业务代码里折腾底层差异的团队参考。1. 为什么我放弃了逐个适配模型接口的写法1.1 业务代码里最尴尬的四段重复逻辑先说最直接的原因业务代码里一旦直连两个以上的模型服务就必然出现重复劳动。每家服务商的 HTTP 接口长得不一样鉴权方式也不一样有的用专属 Header有的用替代路径有的还要在 body 里塞特定字段。我在项目里最先做的就是给每家模型各写一个 Request 构造器、一个 Response 解析器再在业务层统一成内部结构。第一版跑通只用了一周但后面每加一家模型就得重写一遍这四件事。更重要的是返回结构不统一。同样是问一句答一段有的返回字段会被不同方式封装有的则跟错误码混在一起。这导致我每接一家模型就要重新处理正常结果和异常结果两条分支。做智能客服时这个问题尤其头疼因为用户问句只要稍微长一点各家对超长文本的截断策略就不一样解析出来的内容经常莫名其妙就缺了一截。后来我把这三段重复逻辑列了一下一是鉴权方式不一致二是消息帧格式不一致三是对错误的表达不一致。这三件事单看都不难但叠加起来每换一个模型就要返工一轮维护成本直线上升。相反如果有一个统一的模型网关把这些差异全部吃进去业务层就只需要面对一套格式和一套错误码逻辑会清爽很多。提示如果你只是在一个离线脚本里调用单个模型逐家适配不算什么问题。可一旦模型数量超过三个或者代码要被多个团队复用统一接入层的价值就会凸显。1.2 真正让我下决心换方案的那次线上故障我本来还想着再忍忍等模型稳定下来就不折腾了。结果有一次线上事故让我彻底改观。当时某家模型的接口调整了返回结构我们按旧版解析报错了。因为那部分代码是直接写在业务层里的整个请求处理链就断掉了。后台日志刷出来一堆解析异常用户那边收到的就是空回复和错误提示。如果当时有一层代理或 SDK 封装正常情况下应该把模型厂商那边的变化隔离在接入层返回结构变化了只需要改接入层解析函数业务层接口保持稳定。那次事故最大的启示不是某厂不靠谱而是我们自己在架构上把业务逻辑和模型适配绑得太紧了导致任何一家的接口波动都可能全链路遭殃。这也是我决定花时间研究 litellm 的直接原因。1.3 litellm 切入的方式格式统一 接入层代理litellm 的做法我理解下来只有一句话在模型服务商外面包一层统一的 OpenAI 兼容接口然后用一个代理或 SDK 做转发。不管背后接的是哪家模型业务层看到的都是同一套请求格式和同一套返回格式。你可以把它当成一个翻译官专门把各家模型的不同协议翻译成业务团队最熟悉的那套协议。这样做的好处是我加新模型时不需要改业务逻辑。我只需要在 litellm 配置里加一行记录指向新模型的名称和密钥业务层代码一行都不用动。对团队协作来说这意味着模型接入能力可以从业务代码中抽离出来交给专门的维护者。也正是因为这种接口格式统一的思路我后来把内部好几个项目都接到了这套网关上来省下了大量重复造轮子的时间。2. 先分清 litellm 的两种用法是当库用还是当网关用2.1 嵌入式 SDK适合单一脚本和个人工具我第一次上手 litellm 时对它的印象是一个 Python 库。因为它的基本用法确实简单装好之后直接调用一个函数传入模型名和消息就能拿到回复。这种用法对应的是 SDK 模式把 litellm 嵌进你自己的 Python 进程里由它直接帮你做格式统一和请求转发。这种模式的优点是不需要额外部署服务。对于一次性脚本、本地调试小工具、或者必须嵌在现有进程里的插件这种方式最轻量。我早期做数据标注辅助工具时就是把 litellm 装进本地脚本里用小样本数据批量跑模型对比效果非常简单。不过 SDK 模式有一个隐藏限制它只在当前进程里生效。如果我写了十个 Python 脚本每个脚本都要用模型那每个脚本都得装一遍 litellm配置信息也要各自维护。一旦密钥更新就要挨个改。这种模式适合个人或小脚本但不够适合团队统一的模型治理。2.2 独立代理适合团队和稳定服务litellm 真正吸引我的场景是它的代理模式。在这个模式下litellm 会独立运行成一个服务监听到一个本地端口。业务应用只需要把向模型请求的地址指向这个代理剩下的转发、鉴权、重试都由代理来做。你可以把代理理解成一个中间层网关它跟 SDK 是一套代码但运行形态完全不同。我最早在团队里落地时第一步就是起一个代理服务放在内网让所有项目把 base_url 指到它那边。项目代码基本不用大动因为它们本来就用 OpenAI 兼容协议现在只需要把 base_url 改成网关地址模型名改成我们内部约定的别名。这一步让我非常舒服因为它意味着对业务代码的侵入几乎是零。另外代理模式天然是集中式的。密钥不会分散在各业务服务器上而是统一存在代理配置里。这样换密钥、启停模型、统计调用量都只需要操作代理这一层。对安全控制而言这种集中暴露接口、隔离真实密钥的方式也更容易管理。2.3 我在这两者之间做选择的标准我在实际选择时主要看三个维度调用量、维护频率、共享人数。如果系统只是我一个人的数据处理脚本调用量不大那就用 SDK 模式运行起来最省事。如果系统要给多个后端服务共用或者模型切换很频繁那就用代理模式。因为代理模式的配置改动受控多个团队可以共享同一套模型资源还能统一做成本核算。有一个很容易忽略的点是日志集中度。SDK 模式下每个进程各自打日志出问题时要到不同机器上翻日志。代理模式下日志集中在一个服务里我可以直接按时间、按模型名去统计调用量和错误数。对于团队协作和线上排查来说这个差别往往是决定性的。经验之谈如果你已经部署了代理模式但个别离线任务还想用 SDK也没必要强行统一。litellm 两套用法是兼容的关键是要搞清楚每套场景各自的边界别混用。3. 拿来即用的 SDK 接入步骤与最小示例3.1 安装前的环境留意点先聊环境。litellm 需要 Python 3.8 以上我用的是 3.10跑得很顺利。安装命令很简单用 pip 装主包即可。不过有一点要提醒如果你的机器上同时存在多个 Python 环境最好先用虚拟环境隔离不然很容易装着装着把依赖装到别的项目里去了。安装完成后我建议先做一件事查一下版本号。因为 litellm 迭代非常快不同版本的默认行为和参数可能会有差异。我遇到过同样一段配置在小版本号之间行为不同的情况所以锁定版本在生产环境中也很重要。你可以在 requirements 文件里固定版本避免别人拉代码时拿到一个行为不一致的新版本。3.2 一个调用示例看输出格式如何统一SDK 模式下一个最简单的调用就是给模型传消息列表然后拿返回。我这里写一个示例from litellm import completion response completion( modelopenai/gpt-4o-mini, messages[ {role: system, content: 你是一名代码审查助手。}, {role: user, content: 帮我看看这段代码可能有什么问题。} ] ) print(response.choices[0].message.content if hasattr(response, choices) else response)注意上面这个例子里模型名称带了openai/前缀。这个前缀的作用是告诉 litellm 应该走哪种协议。如果你接的是另一家服务商同样是前面的协议名加后面的模型名。这个命名规则看起来简单但非常关键因为 litellm 就是靠前缀来选择对应的适配器的。返回结构经过统一后所有模型都会变成类似 OpenAI 的格式也就是choices下面有message和content。这样我在业务层只需要解析这一种结构就够了。可能有读者会问那不同模型对同样输入的回复质量不一样怎么办那是效果层面的事但接口层面的格式差异已经被抹平了。3.3 中文环境下关于 model 参数与 API Key 的注意点接下来是几个我踩过的坑。第一个是中文环境下的模型名称拼写。有的服务商官方文档写的是中文名或简称但 API 里要求的是完整英文标识符。如果照搬文档里的显示名请求就会报模型不存在。所以配置模型名时一定以服务商 API 文档中的模型标识为准不要去猜。第二个坑是 API Key 的注入。SDK 模式下你可以直接在代码里写密钥但这非常不安全特别是代码要提交到仓库里时。更稳妥的方式是通过环境变量传。litellm 会自动读取常见的环境变量名比如各家服务商默认约定的那个环境变量名。我不建议你为了省事把密钥硬编码配一个.env文件并用配置加载组件读取这个成本很低但安全收益很高。第三个坑是模型列表。如果你在一个项目里同时用了好几种模型最好把可用模型和别名统一维护在一个配置里。我是用一个内部常量文件来管理的业务代码只引用别名不直接写长模型名。这样做的好处是将来换模型只需要改配置不需要全局搜代码去替换。# 示例环境变量写法 export OPENAI_API_KEYsk-xxxx export ANOTHER_PROVIDER_API_KEYanother-xxxx提示测试不同模型的耗时和成本时建议先用小批量数据跑一遍再逐步放大。别一上来就全量切换万一某个模型对某类输入有系统性偏差影响面会很大。4. 更接近生产的玩法以代理模式做模型网关4.1 代理模式的架构位置与收益代理模式最终形成的样子是业务应用 → litellm 代理服务 → 各家模型服务。这个代理服务可以部署在一台普通机器上也可以放到容器里。它对外暴露一个 OpenAI 兼容接口对内则统一管理模型路由、密钥、重试和成本记录。从架构角度看这等于把模型接入变成了一个独立的基础服务。业务应用不再需要知道背后具体是哪家模型也不需要持有各家模型的密钥。代理层变成了一个统一的入口它甚至可以决定某个请求到底走哪个模型而不需要业务层关心。这种解耦带来的是更清晰的职责边界也让模型策略调整变成了配置问题而不是改代码问题。4.2 一份可以起步的代理配置代理模式的配置通常放在一个 YAML 文件里。一份最简配置大概长这样model_list: - model_name: gpt-4o-mini litellm_params: model: openai/gpt-4o-mini api_key: os.environ/OPENAI_API_KEY - model_name: claude-sonnet litellm_params: model: anthropic/claude-3-5-sonnet api_key: os.environ/ANTHROPIC_API_KEY litellm_settings: drop_params: true num_retries: 2 request_timeout: 120这里的关键是model_name和litellm_params。model_name是给业务方看的别名litellm_params则指定真实的模型标识和密钥。业务应用请求时只需要传model_name网关会把它映射到真实的模型服务上。drop_params这个参数值得说一下。不同模型支持的参数不一样比如有的模型不支持某个专有参数。如果开了drop_paramslitellm 会在转发前自动剔除目标模型不支持的参数这对多模型切换特别有用。否则一个请求在模型 A 上正常转发到模型 B 上可能就报未知参数错误。代理服务启动命令也不复杂指定配置文件后监听一个端口。默认监听 4000我在内网环境直接用了这个默认值。litellm --config ./config.yaml4.3 模型组的回退与负载均衡逻辑代理模式还有一个非常实用的能力把多个模型组成一个模型组并在组内做回退或负载均衡。这个特性在线上很有价值因为模型服务偶尔会不稳定或者出现速率限制如果没有回退机制请求就会直接失败。我在配置里加了一个模型组用通配符的方式把几个可互相替代的模型放进去。请求进来后litellm 会按配置顺序优先把请求发给第一个模型如果它报错或超时就自动转到下一个模型。这个逻辑对业务方完全透明业务方只知道自己请求的是某个模型组别名不会感知到背后有多个模型。示例如下model_list: - model_name: customer-service litellm_params: model: openai/gpt-4o-mini - model_name: customer-service litellm_params: model: anthropic/claude-3-5-sonnet api_key: os.environ/ANTHROPIC_API_KEY - model_name: customer-service litellm_params: model: another-llm/some-chat-model api_key: os.environ/ANOTHER_API_KEY同样叫customer-service的多个条目会被当成一个模型组。路由时默认会轮流选择组里的模型实现负载均衡也可以配置成有优先级的模式让第一顺位模型先被使用失败后再回退。我在客服场景里用的是回退优先保证大部分请求都打到效果最好的模型上一旦不稳定才落到备用模型。4.4 调用方只需要改一个地址跟代理对接时对业务应用而言本质上只是把原先指向模型服务商的 base_url 改成指向 litellm 代理再把自己的密钥换成代理认可的密钥。很多项目本来就是用 OpenAI 兼容协议封装的所以这里几乎就是改一行配置的事。我内部的服务大多用了一个公用的 SDK 或 HTTP 工具包。当时我只需要改工具包里的 base_url 常量所有接入项目就都自动切到网关上了。而且我可以把原来的密钥放在请求里代理看到后会用你配置的真实密钥去调用后端模型业务方不用持有任何厂商密钥。这种模式在安全上的好处是假如某个业务的密钥泄露我可以在代理侧立即撤销而不是满世界找哪个服务还在用旧密钥。5. 上线前倒逼我解决的四类问题5.1 超时与流式响应的取舍代理模式下最需要注意的问题是超时。各家模型的响应时长差异很大同一个模型在不同负载下也可能从几秒变成几十秒。如果代理层的超时设得太短容易误杀正常请求设得太长又会让用户等太久。我当时的做法是分场景设置超时。对于普通问答请求超时设置在 60 秒左右对于流式生成我会更关注首包时间并在客户端做异步等待。特别说一下流式代理模式下流式转发需要代理把客户端的流式请求和模型端的流式响应正确联系起来测试时一定要确认streamTrue时能正常返回增量内容而不是等全部生成完才吐出来。这个点我第一次没注意结果线上用户感觉模型没有实时输出体验很差。5.2 配额与限流网关不稳通常是没设保护代理刚上线时我把各模型服务的配额想象成了无限量。结果某个内部工具脚本写了一个循环短时间内发了几百个并发请求直接触发了模型服务商的速率限制。那一刻我才意识到代理层不能只是做转发还应该在入口处做好限流。litellm 允许在配置里设定速率限制和预算控制。简单的做法是给某个模型组限定每分钟调用次数超出的请求直接返回排队提示或错误。虽然这会让部分调用在高峰期失败但总比把后端模型服务打到触发严格处罚要强。惩罚式限流一旦触发整个团队的使用都会受影响修复成本更高。经验之谈给团队做模型网关时不要只想着尽可能多地转发更要想怎么控制突发流量。给每个业务方设置独立的配额也是防止某个项目写坏脚本拖垮所有人的关键。5.3 成本观测谁在哪个模型花了多少钱我们内部一直想搞清楚一个问题每个业务方到底消耗了多少模型调用量、花了多少钱。以前各家模型服务商的后台会有消耗数据但统计口径不统一而且不同密钥对应的项目归属容易混乱。用了代理模式后litellm 会把每次调用的模型、token 数、完成 token 数这些信息记录下来我可以用这些数据做统一成本核算。配置上它支持连数据库记录日志我用的是 PostgreSQL。日志表里可以看到每个请求用了哪个模型、谁的 key、输入输出 token 数。再结合运行时统计我能在内部报表里直接列出某某业务本周消耗了多少 token。这一点对于预算管控太重要了以前我只能靠厂商后台截图对账现在是一份统一数据源。5.4 模型不可用时的降级路线我遇到的另一个高频问题是某个模型在某段时间内不可用。这种不可用可能表现为超时、返回错误码或者反复触发限流。如果没有降级方案一次模型故障就等于一次线上事故。通过模型组回退我可以把不同质量层级的模型做成备用链路。比如第一优先用能力最强的模型它挂了就切到能力稍弱但稳定得多的模型再不行就切到本地部署的开源模型。在架构上这等于给业务方提供了故障隔离能力。业务方只需要指定一个逻辑模型名剩下的路由和切换全部由网关负责。这样即使某个服务商出问题用户的请求也不会直接断掉。6. 从调试到落地的若干细节与心得6.1 排查变慢的路径先看队列再看模型很多人一遇到代理响应变慢第一反应是跑到模型厂商后台去看负载。其实对网关类服务来说最常见的原因是请求堆积。我遇到过代理本身变成了瓶颈因为它要接收所有请求要逐个解析、转发、等待模型返回再回传给业务方。如果代理服务的进程数量或者线程池配置不够一个慢请求就能把整体并发拖垮。我的排查顺序一般是先看代理服务的实时日志和连接数检查有没有大量请求在排队然后看目标模型服务的响应耗时最后再分析是不是业务方自己发了过多请求导致限流。这三步下来大多数变慢都能定位到具体环节。不要一上来就升级机器先搞清楚卡点在哪。6.2 输出格式不一致时的兼容技巧虽然 litellm 会把各家模型返回统一成 OpenAI 结构但实际使用中还是会遇到一些细节差异。比如某些模型在拒绝生成时返回的内容为空或者返回一个特殊的停止原因。如果业务方只判断content is not None那空结果的语义就可能和真正正常的结果混淆。我的处理方式是在网关后面再做一层薄薄的后处理对空内容、异常停止原因、敏感词拦截等等做统一归类转成内部通用的业务状态码。这样一来上层业务面对的不是模型原始返回而是我们内部定义好的几种业务结果。这层后处理虽然简单但对业务稳定性帮助非常大。6.3 中小团队的上线方式一台机器一个服务关于部署方式我看到不少教程上来就是容器编排、多副本、负载均衡。说实话中小团队一开始完全不需要这么复杂。litellm 代理是一个无状态的转发服务我最初就在一台普通服务器上跑了一个进程连容器都没用。只要把配置文件备份好进程挂了重新拉起来就行。不过有一点要提前考虑就是日志。如果服务跑着跑着突然重启之前的调用记录会不会丢。为了让记录不丢我在接入前就把日志数据库配置好了。这样即使进程重启历史记录还在对账和排障都不受影响。等到真的需要横向扩展时再把代理做成多副本加负载均衡也不迟。最后再分享两个小建议如果你正在计划接入多个模型我建议从一开始就把接入层和业务层分开哪怕现在只有一个模型也值得这样做。因为我见过太多项目一开始觉得只有一个模型没必要封装后来发展到七八个模型时已经改不动了。litellm 最舒服的地方不在于它有多强的魔法而在于它把接入层这件本来就该独立出来的事情做成了开箱即用的标准件。另一个建议是定期检查模型调用日志观察不同模型在业务数据上的表现而不要只看响应速度和成本。因为同一类问题在不同模型上的表现差异可能很大只有数据积累下来你才知道哪条链路更可靠。我后来把一部分业务流量切到不同的模型上做 A/B 观察都是依赖代理层的统计能力才能这么轻松实现。