手头同时维护好几家大模型API接入的时候最让人崩溃的不是哪个模型更聪明而是接口格式从来都不统一。有的厂商要求请求体里写max_tokens另一家偏要写max_output_tokens有的返回里直接把答案放在choices[0].message.content另一家塞在content[0].text里最要命的是鉴权方式还不一样。项目里的封装代码越堆越多但每换一个模型就要改一版请求逻辑谁都不敢轻易动那团“祖传代码”。后来我在一个新项目里把 litellm 接了进去用同一套代码同时调用不同厂商的模型密钥统一管理成本也能按项目拆分团队里再接新模型只需要在配置里加一行。litellm 本质上是一个大模型网关它把各家模型服务的接口翻译成统一的格式你只需要学会一种调用方式就能操控后面一串模型。它既能当 Python SDK 在代码里用也能起一个独立的代理服务让整个团队共享一套 API 入口。如果你是个人开发者、前后端团队或者正在做一个需要对接多家模型平台的系统这篇文章值得看完。1. 多模型接入的真实痛点为什么我最终选择加一个抽象层1.1 接口格式千奇百怪统一调用成了奢望我不止一次见过这样的代码项目里有一套call_gpt()函数然后复制粘贴出一套call_claude()再改一版call_gemini()三个函数内部逻辑高度相似却又各自维护各自的请求头、超时时间、错误码解析。这事在只有一两个模型时还能勉强撑着一旦模型数量超过三个你就要维护 N 套请求代码、N 套异常处理、N 种流式解析逻辑。更麻烦的是同一家厂商还经常升级模型参数旧的max_tokens被废弃、换成max_completion_tokens你压根不知道底层哪一次改动会让接口崩掉。而 litellm 做的事情非常“笨”但有效把所有厂商的请求统一转写成其原生格式再把他们返回的内容统一包装成你熟悉的结构。这意味着你的业务代码只需要关心response.choices[0].message.content不用关心后面到底是什么模型返回的。1.2 密钥和权限管理失控才是最可怕的隐患我见过某个团队把所有模型密钥直接灌进前端环境变量理由是“后端没时间封装”。也见过有人把三四个密钥写死在代码仓库里离职之后密钥跟着流出去直到某天账单暴涨才反应过来。这些场景的共同点是密钥的散落、不可轮换、没有审计。litellm 的做法很有意思它允许你定义“虚拟密钥”。你可以给每个项目、每个成员单独生成一个 key并且这个 key 只允许访问某几个模型、每个月只允许消耗一定额度。外部成员拿到的永远是这个虚拟 key即使泄露你也可以单独吊销而不影响其他密钥。这样一来密钥管理从“一锅乱炖”变成了“按人按项目隔离”。1.3 模型切换和容灾基本靠手写而且写得一塌糊涂很多团队嘴上说着“多模型容灾”实际上就是 catch 异常之后换下一个模型重试。但重试的逻辑远没有想象中简单什么时候算超时哪些错误需要换模型哪些错误换了也白换限流了是要等还是跳这些问题没有统一答案的时候每个业务方都会自己写一套最后行为各不相同。litellm 在 SDK 层面直接支持了 fallback 机制也就是一个模型请求失败后自动尝试下一个模型并且可以自定义触发 fallback 的条件。对于大部分场景这已经帮你把容灾这事从“业务代码”里抽离出来了。1.4 账单只能看总览没办法按项目拆分多模型时代的隐藏痛点还有成本归因。不同模型单价天差地别如果所有请求都走同一个 key月底账单上只有一条总费用你根本分不清是哪个业务线烧的钱。这个需求听起来不性感但实际做项目时非常折磨人。litellm 的代理模式自带用量统计和费用归因每一次请求都会记录模型、调用方 key、输入输出 token 数和估算费用你可以按 key、按模型、按时间维度去查。这笔账算清楚了任何业务方想接入新模型时都能当场评估成本而不是等月底被账单吓一跳。2. LiteLLM 的四层设计统一格式、转发代理、密钥池、用量日志2.1 统一格式层所有模型都长一个样litellm 把不同模型的 API 格式统一成 OpenAI 兼容风格。这不是说它强制要求你自建一套新协议而是让熟悉 OpenAI 接口的开发者几乎零成本上手。比如你想同时调用不同家的大模型代码可以写成下面这样from litellm import completion response completion( modelgpt-4o, messages[{role: user, content: 你好}], ) print(response.choices[0].message.content)如果换一个模型你只需要把model参数换成对应的模型名。litellm 内部通过模型名称前缀自动识别该走哪套协议比如带anthropic/前缀走 Anthropic 风格带gemini/前缀走 Gemini 风格。你还以指定每个模型具体的接入方式response completion( modelanthropic/claude-3-5-sonnet-20241022, messages[{role: user, content: 你好}], )这种设计最舒服的地方在于业务侧能看到的世界是收敛的。模型厂商改了内部参数由 litellm 去适配业务代码不用跟着改。2.2 转发代理层让团队只面对一个 API 地址SDK 模式适合单机脚本、后端微服务这种场景。但如果是多人协作的团队我更推荐部署 litellm 的代理服务器模式。它本质上是一个跑在服务器上的轻量服务监听一个端口对外暴露 OpenAI 兼容的/v1/chat/completions接口。这样做的效果是你团队里的所有后端服务只需要把 base_url 指向这台代理服务器把 api_key 设置为服务器上生成的虚拟密钥就能像调用 OpenAI 官方接口一样使用任意已接入模型。代理层在中间负责模型路由、负载均衡、密钥鉴权和日志记录。这种架构对前端和小团队特别友好因为他们根本不需要知道背后接入了哪些模型也不需要处理各家复杂的环境变量。2.3 密钥池层上游密钥与虚拟密钥隔离密钥池机制是 litellm 代理从“能用”到“好用”的关键分水岭。我可以在代理配置里注册多个真实的上游密钥比如各家模型平台的 key这些真实 key 对外不可见。然后我用管理接口创建一批虚拟 key发给团队成员或各个服务使用。虚拟 key 能绑定具体模型名单、设置额度上限、支持失效时间。假设某个合作方的 key 泄露了我直接在管理端吊销那一把虚拟 key真实 key 和其他人的 key 完全不受影响。这个机制比让每个人都拿着真实 key 到处跑安全得多。2.4 用量日志层每一分钱都花得明明白白日志层记录的核心字段包括请求时间、模型名、虚拟 key 标识、输入 token 数、输出 token 数、估算费用、响应延迟。litellm 会把不同厂商返回里的 token 使用量字段统一解析并落库然后按照模型单价计算出花销。实际项目中我会定期按虚拟 key 拉一次费用报表看看哪条业务线的 token 开销明显异常及时调整模型策略。如果没有这层数据多模型混用一段时间后成本基本就是一笔糊涂账。3. 从零接入实战Python SDK 把三个模型串在同一段代码里3.1 安装与基础配置安装非常简单直接用 pippip install litellm然后设置环境变量。不同厂商的密钥各用各的环境变量名称export OPENAI_API_KEYsk-... export ANTHROPIC_API_KEYsk-ant-... export GEMINI_API_KEYyour-gemini-key如果你用的是代理模式环境变量也可以不写在代码里而是统一写在服务器的配置文件中。SDK 模式则更灵活支持运行时直接传api_key参数。3.2 一个请求自动路由到多个模型最基础的多模型调用就是这样from litellm import completion models [ gpt-4o, anthropic/claude-3-5-sonnet-20241022, gemini/gemini-1.5-pro, ] for model in models: try: response completion( modelmodel, messages[{role: user, content: 用一句话解释什么是依赖注入}], ) print(f{model}: {response.choices[0].message.content}) except Exception as e: print(f{model} 调用失败: {e})这段代码的价值在于你不需要为每个厂商分别写请求函数也不需要关心各家消息格式的差异。litellm 会帮你把messages转译成目标模型需要的结构。3.3 给请求加上 fallback一个挂了自动切换容灾不是等到模型挂了才去改代码而是在调用前就声明好备胎列表from litellm import completion response completion( modelgpt-4o, messages[{role: user, content: 介绍一下云原生的核心思想}], fallbacks[ {model: anthropic/claude-3-5-sonnet-20241022}, {model: gemini/gemini-1.5-pro}, ], )当主模型遇到超时、服务不可用、鉴权失败等错误时litellm 会自动按顺序尝试 fallback 列表里的模型。你还可以通过参数控制重试次数和超时时间from litellm import completion response completion( modelgpt-4o, messages[{role: user, content: 你好}], num_retries2, request_timeout30, )这里的num_retries是指在单个模型上失败后的重试次数request_timeout是单次请求的秒数超时。这两个参数看起来简单但大多数手写容灾代码恰恰忽略了超时控制导致某个模型卡住时整个接口也跟着卡死。3.4 异步批量调用处理并发需求如果是服务端场景同步调用会阻塞线程性能堪忧。litellm 也提供了异步接口import asyncio from litellm import acompletion async def run(): response await acompletion( modelgpt-4o, messages[{role: user, content: 写一段异步处理消息的 Python 代码}], ) return response.choices[0].message.content async def main(): results await asyncio.gather(*[run() for _ in range(5)]) print(results) asyncio.run(main())配合信号量做并发限制会更稳妥避免一下子把上游 API 的配额打爆sem asyncio.Semaphore(10) async def limited_run(): async with sem: return await run()这段代码解决的实际问题是当你同时给多个模型发请求做对比评测时异步并发能节省大量时间。4. 把 LiteLLM 部署成团队级 AI 网关一个 config 文件管住所有模型4.1 配置文件里定义模型列表代理模式是我目前最推荐团队使用的形态。第一步写一个config.yaml把你的模型资源都登记进去model_list: - model_name: gpt-4o litellm_params: model: openai/gpt-4o api_key: os.environ/OPENAI_API_KEY - model_name: claude-sonnet litellm_params: model: anthropic/claude-3-5-sonnet-20241022 api_key: os.environ/ANTHROPIC_API_KEY - model_name: gemini-pro litellm_params: model: gemini/gemini-1.5-pro api_key: os.environ/GEMINI_API_KEYmodel_name是暴露给使用方的“逻辑模型名”也就是客户端那边填写的名字。litellm_params.model是 litellm 真正要调度的目标模型。这样设计的好处是底层模型随便换只要不换逻辑名调用方代码一行都不用改。同时可以加一些全局策略litellm_settings: drop_params: true num_retries: 2 request_timeout: 60drop_params: true的意思是当某个上游模型不支持某些参数时自动丢弃而不是报错。这个开关非常实用因为不同厂商对参数的支持范围差异很大。4.2 启动代理验证接口启动命令litellm --config config.yaml --port 8000启动后在另一个终端里用 curl 验证curl http://localhost:8000/v1/chat/completions \ -H Content-Type: application/json \ -H Authorization: Bearer sk-1234 \ -d { model: gpt-4o, messages: [{role: user, content: 你好}] }如果返回的结构和 OpenAI 原生一致说明网关已经跑通了。这里的sk-1234是代理默认的主密钥生产环境务必替换并通过管理接口创建虚拟密钥。4.3 模型别名与多模型负载均衡除了简单映射你还可以把多个上游模型配置成同一个“逻辑模型名”实现按权重轮询model_list: - model_name: my-assistant litellm_params: model: openai/gpt-4o api_key: os.environ/OPENAI_API_KEY - model_name: my-assistant litellm_params: model: anthropic/claude-3-5-sonnet-20241022 api_key: os.environ/ANTHROPIC_API_KEY这样客户端请求my-assistant时网关会在两个模型之间做轮询分发。这个功能很适合做 A/B 对比测试或者单纯想把流量分摊到不同模型上降低成本。如果你想控制流量比例还可以在 litellm_settings 里配置模型权重。实际团队里我更常用的是把“便宜且快”的模型和“贵但聪明”的模型配成一个逻辑名让网关自动分流业务侧无感知。4.4 创建虚拟密钥给成员或服务单独授权代理启动后可以用管理接口创建虚拟密钥curl -X POST http://localhost:8000/key/generate \ -H Authorization: Bearer sk-1234 \ -H Content-Type: application/json \ -d { models: [gpt-4o, claude-sonnet], max_budget: 20.0, budget_duration: 30d }返回结果里会有一个新的sk-开头字符串这个就是虚拟 key。你把这个 key 下发给对应的业务方它只能调用models里指定的模型并且 30 天内最多消费 20 美元。预算用完后这个 key 会自动失效不会影响其他 key。4.5 看日志和花费代理服务提供了一组管理端点比如/spend/logs可以按 key、按模型查询请求记录和费用。你会看到类似这样的表格虚拟 key模型输入 token输出 token估算费用请求时间sk-abc...gpt-4o15234210.021 USD2025-01-10 10:15:03sk-def...claude-sonnet8021650.012 USD2025-01-10 10:16:44这个表格我每周拉一次基本就能掌握各业务线的大模型成本走势。比起月底对着平台账单猜这个粒度要清楚得多。5. 实测两个月之后的踩坑清单与优化习惯5.1 模型名写错导致的神秘 404代理模式部署完之后最常遇到的报错不是配置错误而是“404 model not found”。排查下来绝大多数是因为客户端请求里的model名字没有对应 config 里的model_name。这里有个认知误区很多同事以为填的是上游模型名比如gpt-4o直接填进去但代理配置里定义的是别名gpt-4o看起来一样实际上订单的是逻辑名。如果我在 config 里把逻辑名改成my-gpt客户端就得填my-gpt而不用管上游真名叫什么。建议在 config 开头注释里写清楚每个逻辑名对应哪个上游模型省得后面维护的人靠猜。5.2 流式返回格式的差异litellm 的兼容性做得不错但流式模式stream的体验有些细微差别。不同的上游模型在流式返回时增量内容的字段不完全一致litellm 会尽量统一但我在使用某些小众模型时偶尔会遇到第一个 chunk 里content为null的情况。处理方式很简单客户端解析流式返回时遇到content为null的 chunk 直接跳过不要抛错。这个看似低级的问题实际开发时很容易被忽略导致线上偶发报错。5.3 超时参数的默认值坑litellm 的默认request_timeout可能比你预期的更短。某些长输出任务比如生成一篇长文模型思考时间加上生成时间很容易超过默认超时然后报超时错误。这时候你加大模型本身的能力并没有用得在配置里显式调大超时时间litellm_settings: request_timeout: 120另外重试次数不要盲目调大。对于限流429错误重试是有用的但对于上下文超长、参数非法这类 4xx 错误重试只会白白浪费请求。litellm 支持按错误类型做重试策略生产环境建议只对 429、5xx 这类瞬时错误重试。5.4 成本估算与实际账单存在偏差litellm 的费用估算基于模型单价和 token 数这个数字比较接近真实账单但不是绝对精确。一方面某些模型有缓存折扣价另一方面不同计费粒度会有差异。我的习惯是用 litellm 的报表做趋势分析和横向对比最终以各家平台账单为准。如果发现某个 key 的花费异常先用 litellm 的日志定位再去找平台账单核对。5.5 接入新模型前先做小流量验证这是我最想强调的优化习惯。任何新模型接入 litellm 之后先别急着全量切流量。我通常会在 config 里加一个新逻辑名先手动调用几次验证请求参数是否合适、返回内容是否完整解析、价格是否符合预期。确认没问题后再把流量切到新模型上。因为 litellm 的转发能力很强切换模型的成本变得很低所以反而容易让人草率上线。但从稳定性角度讲模型表现的差异只有真实验证过才敢放心。5.6 日常运维的几个小习惯最后分享几个我实践下来很有用的习惯。定期清理虚拟 key长时间不用的 key 及时吊销防止成为安全隐患。监控错误日志litellm 的日志里有各模型的错误码分布经常能看到某个模型频繁 5xx这时就该考虑把它移出主链路。模型路由配合缓存如果业务中有大量重复问题可以在 litellm 前面加一层响应缓存把相同请求直接命中缓存返回节省成本和延迟。不要把真实密钥直接写在 config 里尽量使用os.environ占位符由服务器环境变量注入避免配置文件泄露。我自己的感受是litellm 把“接多家模型”这件脏活累活从业务代码里剥离了出去。它不会让某个模型变聪明但能让你的系统更从容地面对模型更新、厂商故障和多模型切换。对于一个长期迭代的项目来说这层抽象省下的维护成本远比最开始配置它的那点时间多得多。