ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Litellm实战:统一大模型调用网关,解决多API接入与密钥管理难题

Litellm实战:统一大模型调用网关,解决多API接入与密钥管理难题

做 AI 应用开发做到一定阶段,几乎都会遇到同一个尴尬:今天接 A 厂商的模型,明天换 B 厂商的接口,后天又要加一个私有化部署的模型。每个平台的鉴权方式不一样,请求格式有差异,返回结构也不同,连错误码都各说各话。项目里全是 if-else 分发逻辑,每加一个新模型就得改一轮代码。我当时为了解决这个问题,把市面上的工具翻了个遍,最后真正留下来一直用的,就是 litellm 这个开源项目。

litellm 做的事情一句话就能说清楚:它是一个统一的大模型调用网关,对外提供一套标准接口,帮你转发到 OpenAI、Anthropic、Azure、Google 以及各种兼容 OpenAI 格式的服务上,同时把响应也统一成固定的结构。你只需要写一份调用代码,就能在几十个模型之间自由切换。对于团队合作的项目,它还能做密钥统一管理、调用日志、预算控制这些事,相当于给 LLM 调用层加了一个标准的中间件。

这篇文章我会从实际使用的角度,把 litellm 的定位、核心机制、上手过程和踩过的坑串起来讲。不管你是刚开始折腾模型调用的新手,还是已经在生产环境里被多 API 维护折磨的开发者,应该都能找到有用的部分。

1. 为什么说 litellm 解决的是"真痛点"

先说个真实场景。我之前维护一个文本分析服务,需求是让业务方可以自由选择用哪个模型做摘要——因为不同模型在不同任务上各有所长,价格也差很多。于是我的代码里出现了这样的结构:一个 model_name 参数,然后一堆 if 分支,每个分支里是不同的 HTTP 请求构造逻辑、不同的 headers、不同的响应解析。这还只是调用层,后面还有鉴权配置、错误重试、token 统计。

这种写法撑到三五个模型就非常痛苦了。每次加新模型,要把请求格式、返回格式、鉴权文件、超时设置全走一遍,代码越来越难维护。而且所有 API 密钥散落在各个配置文件里,前端如果直接调用各家 API,密钥还面临泄露风险——浏览器里不能放密钥,必须先走一个后端代理。这些需求叠加在一起,你会发现你其实是在做一个 BFF 层(Backend For Frontend),而这个层大家都在重复造轮子。

litellm 就是把这一层提前做好了。它对外暴露的接口协议完全兼容 OpenAI 的 chat/completions 格式,你只要用标准的 OpenAI SDK 就能调用它,然后它内部根据你的配置把请求转发到任意后端。返回结果它也帮你统一成 OpenAI 的格式,不管是 Anthropic 还是 Google 的模型,你拿到的都是同一套结构。

这个概念现在听起来很自然,但当时我第一反应是:转发一次不会很慢吗?延迟会不会增加?实际测试下来,litellm 的代理层开销非常小,主要延迟还是花在模型本身的推理上。而且你会发现,把鉴权、限流、重试这些脏活从业务代码里剥离出去之后,业务代码变得非常干净,后续维护成本大幅下降。

1.1 各家 API 的格式差异到底有多大

很多人没实际接过多个平台,可能觉得"反正都是发 POST 请求,能差到哪去"。真去接一遍就发现差异比想象中大得多。OpenAI 用的是 messages 数组,里面 role 和 content 分明;Anthropic 的请求结构是 system 单独拆出来,然后 messages 的格式又和 OpenAI 不完全一样;有些国产模型虽然是"OpenAI 兼容"的,但某些参数的取值范围、返回字段的命名还是有自己的脾气。更别提有的服务还需要额外的 header 字段做企业鉴权。

这些差异单独看都不难处理,但组合起来就很烦。你需要在代码里维护一张"模型特性表":哪些模型支持 temperature,哪些支持 top_p,哪些工具(function calling)格式有差别,哪些返回的 usage 字段值得注意。每次升级 SDK 或者接入新模型,表格就要更新。litellm 帮你把大部分这类差异抹平了,你只需要用一套 OpenAI 风格的数据结构,它内部完成各平台的格式转换。

1.2 网关模式对团队协作的意义

如果是个人项目,也许写几个 if-else 忍一忍还能过去。但团队项目里,API 密钥管理就是个不小的麻烦。有些同事会把密钥写在代码里提交到仓库,后面泄露了都不知道是哪个环境出的问题。litellm 的代理模式让密钥只存在于服务端配置里,业务代码只认这个代理的地址和 key,前端的请求也走这里,天然规避了密钥暴露问题。

再加上它有日志、预算和频率控制,团队里不同成员可以分配不同的虚拟 key,谁调了多少、花了多少钱一目了然。我在实际项目中就把 litellm 部署在一个内网服务上,各业务线各自申请访问 key,再也不用互相拷密钥了。这部分我觉得是它真正提升团队效率的地方。

2. litellm 的核心机制与能力拆解

虽然它的用法看起来很简单,但内部机制还是值得了解一下的,至少要知道它能做到什么程度,遇到问题时也好判断该往哪个方向排查。

2.1 统一输出格式的底层逻辑

litellm 的外层接口是 OpenAI 兼容格式,但后端返回的格式五花八门。它做了几层转换:第一层是把 OpenAI 的请求体翻译成目标平台的请求体;第二层是调用目标平台后,把响应翻译回 OpenAI 的格式。这个翻译过程涉及非常多的细节,比如 Anthropic 的 content 可能是数组(里面有 text 类型和 tool_use 类型),litellm 会把它们映射成 OpenAI 的 message 结构;比如 usage 字段里 prompt_tokens、completion_tokens、total_tokens,各家统计口径不一样,litellm 都会做归一化。

这些逻辑如果你自己写,前期可能感觉不到什么,一旦开始接触流式输出、工具调用、多轮对话这些高级特性,每个都是一个深坑。我在最初接流式响应的时候就踩过坑:OpenAI 的流式返回是一段段 data: 开头的 chunk,而某些平台的流式格式完全不同,还有的流式模式下 usage 是在最后一个 chunk 里才返回。litellm 对这些细节都有处理,你拿到的始终是统一的结构。

2.2 模型路由与负载均衡

litellm 支持配置多个模型组,然后通过路由策略分发请求。具体场景是:你配置了两家提供商的同能力模型,指定权重各 50%,litellm 会根据权重做轮询分发;如果其中一个提供商挂了,它会自动把流量转移到另一个。这相当于给模型调用层加了一个简单的"故障转移 + 负载均衡"。

这个功能在实际运营中非常有价值。模型服务商偶尔会抽风——限流、超时、5xx 错误,单个供应商出问题的时候,你的服务不能跟着一起挂。用 litellm 的路由能力,可以把这类故障对用户的影响降到最低。配置时只需要在 model_list 里写好多个模型条目,然后通过 litellm.set_model_list 或 YAML 配置加载即可。

需要注意的一个点是:路由不只是简单的轮询,它还可以配合模型质量评估做动态路由。比如你可以在请求里指定 max_cost 之类的约束,litellm 会通过某种策略挑选满足条件的模型。不过这种高级用法通常需要搭配额外的模型评估服务,实际上用到的人不算多。我的经验是先把轮询和故障转移用好,这套已经能覆盖大多数场景。

2.3 预算管控与调用日志

litellm 有一个内置的数据库层,用来记录每次调用的 token 数、成本、模型、用户标识这些信息。打开方式是在配置里设置 database_url,或者用 litellm 自带的 UI 面板。它支持给不同的 key 设置预算上限,比如某个内部工具每天最多花 20 美元,一旦超了直接拒绝请求,会提示超预算信息。

这个功能对控制成本很有帮助。生成式模型接口的消费不像传统接口那样可控,一个循环写错就可能导致调用量暴涨。我在生产环境就是这么干的:每个业务线一个 key,各自设定月度限额,月底对账直接看面板就行,不用再自己统计日志算了。虽然也可以自己写 log 系统记录,但 litellm 已经做了现成的,没必要重复造轮子。

3. 从零跑通 litellm 的完整过程

说了这么多概念,直接上手跑一遍最直观。我演示的环境是 Linux 服务器,Python 3.10,实际在 macOS 上操作也完全一样。

3.1 安装、启动最基本的代理服务

litellm 用 pip 安装:

pip install 'litellm[proxy]'

注意官方文档里 proxy 是推荐安装的包,它包含代理服务器的依赖。如果只需要 SDK 功能,纯pip install litellm就够了,但做网关部署时还是装 proxy 版本省心。

安装完直接启动一个最简单的代理:

litellm --model openai/gpt-4o --port 4000

这条命令的意思是:在本地 4000 端口开一个代理,所有发给它的请求都转发到 OpenAI 的 gpt-4o 上。为了演示,我把密钥放在环境变量里:

export OPENAI_API_KEY=sk-xxxx

然后就可以用 OpenAI SDK 或者直接 curl 调它了。用 curl 验证最简单:

curl http://localhost:4000/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer sk-xxxx" \ -d '{ "model": "gpt-4o", "messages": [{"role": "user", "content": "你好,请做一句话自我介绍"}] }'

这里注意 Authorization 里的 key 是什么其实无所谓,litellm 默认允许任意字符串当 key 用,真正用于鉴权的是转发到上游时你配置的环境变量。如果你连这个都想跳过,后面配置里可以设master_key做代理层自己的校验。

如果一切正常,你会拿到和 OpenAI 原生返回结构几乎一样的 JSON。现在换一个模型试试,比如改成--model anthropic/claude-3-5-sonnet-20241022,再配好 ANTHROPIC_API_KEY,同样的请求格式,只是把模型名换掉,litellm 就会帮你和 Anthropic 的 API 通信再转回来。这就是"一套代码,所有模型随便切"的体验。不过用命令行的方式只适合临时测试,真正做项目还是要用配置文件把模型都列好。

3.2 用配置文件管理多个模型

配置文件是 litellm 代理的核心。新建一个 config.yaml,内容大概这样:

model_list: - model_name: gpt-4o litellm_params: model: openai/gpt-4o api_key: os.environ/OPENAI_API_KEY - model_name: claude-3-5-sonnet litellm_params: model: anthropic/claude-3-5-sonnet-20241022 api_key: os.environ/ANTHROPIC_API_KEY - model_name: local-deepseek litellm_params: model: openai/deepseek-chat api_base: https://api.deepseek.com/v1 api_key: os.environ/DEEPSEEK_API_KEY general_settings: master_key: sk-your-master-key database_url: postgresql://user:password@localhost:5432/litellm

这里面有几个关键点:

  • model_name是暴露给你业务代码的"别名",你调 gpt-4o 还是 claude-3-5-sonnet 都走这个标识。
  • litellm_params.model是实际转发的目标,前缀openai/、anthropic/告诉 litellm 用什么适配器。
  • api_base在对接某些 OpenAI 兼容服务时非常重要,比如 DeepSeek、Moonshot、以及各类国内推理服务,它们大多提供 OpenAI 兼容接口,但 base url 不一样。

配置好之后启动:

litellm --config config.yaml --port 4000

请求时 model 参数用别名即可:

curl http://localhost:4000/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer sk-your-master-key" \ -d '{ "model": "gpt-4o", "messages": [{"role": "user", "content": "介绍一下你自己"}] }'

这时候你会看到 litellm 对 key 有了校验,必须是 master_key 才能在代理层通过。团队内部就可以给不同成员发不同的虚拟 key,然后在配置里做限额。

3.3 配置虚拟 Key 与预算控制

如果你想把 master_key 只留给管理员,其他成员各自用独立 key,可以在启动前先调用 admin API 生成。启动代理后,用 master_key 调/key/generate:

curl --location 'http://localhost:4000/key/generate' \ --header 'Authorization: Bearer sk-your-master-key' \ --header 'Content-Type: application/json' \ --data '{ "models": ["gpt-4o", "claude-3-5-sonnet"], "max_budget": 10 }'

返回的 JSON 里会有key字段,把这个 key 分给团队成员使用,litellm 会自动统计该 key 的花费,超过 10 美元就拒绝调用。预算粒度还可以按天、按请求数设置,具体字段可以查官方配置项。这块功能一开始我没当回事,直到有一次脚本死循环把一个月预算半小时花完,才发现预算控制的重要。

4. 生产环境落地的避坑与经验

litellm 上手快,但生产环境里有些细节还是要注意,我把实际踩过的坑按"症状—原因—解决办法"整理成表格,可能对你有直接参考价值。

4.1 常见报错与排查方向

症状可能原因解决办法
返回 401代理层 key 未配置,或上游密钥缺失检查 master_key 和对应模型的环境变量
返回 404 model not found配置文件里的 model_name 和请求里的 model 不一致核对model_list里的别名,注意大小写和空格
返回 429 rate limit上游限流或 litellm 内部限额触发查看具体是上游返回还是 litellm 自身日志,分别处理
流式响应卡住请求里没有传stream: true,或上游流式格式特殊确认请求参数,必要时对特殊模型关闭流式
中文乱码某些平台对 UTF-8 处理差异请求头显式加上Content-Type: application/json; charset=utf-8

这些排查思路和通用 API 网关差不多,关键是遇到问题时先分清楚是 litellm 的问题还是上游的问题。litellm 的日志在终端会打印,启动时加--detailed_log可以看到每个请求转发到哪个上游、耗时多少。我排障时习惯先把 detailed_log 打开,一眼就能看出卡在哪一环。

4.2 和现有代码库的迁移衔接

从一个直接调用各家 SDK 的项目迁移到 litellm 网关,其实难度不大。核心思路是:把原来直接给OpenAI(api_key=...)这类客户端传参的地方,改成指向 litellm 代理服务器的地址和 key。如果你的代码是基于 openai 库写的,迁移成本极低——只要改 base_url 和环境变量即可。

原来代码:

from openai import OpenAI client = OpenAI(api_key="原厂商key") response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "你好"}] )

迁移后:

from openai import OpenAI client = OpenAI( api_key="litellm的虚拟key", base_url="http://localhost:4000/v1" ) response = client.chat.completions.create( model="gpt-4o", # 这里用的是 litellm 配置里的别名 messages=[{"role": "user", "content": "你好"}] )

需要注意base_url要带/v1,因为 litellm 的端点路径是/v1/chat/completions。如果原来代码直接用了某个 SDK 特有的参数,迁移时要去掉,或者确认 litellm 支持。比如旧的 completions 接口和新的 chat completions 接口差异就很大,最好提前做好兼容检查。

如果是 Python 之外的语言,比如 Node.js,官方也有对应的 proxy 调用示例,因为 litellm 对外是标准 REST API,任何语言的 HTTP 客户端都能调。这一层统一之后,前端、后端、脚本就都可以走同一个入口,而不用各自维护一套调用逻辑。

4.3 关于自建内网代理与团队协作的实践细节

把一个 litellm 代理部署在内网之后,有几个容易被忽略的点值得提醒。第一是安全组和网络隔离——如果你在内网服务器上开 4000 端口,注意设置好访问控制,别让代理直接暴露到公网。尤其是有 master_key 的情况下,虽然没有 master_key 调不了接口,但能进到 /health 等只读端点也会暴露一些信息。

第二是日志保留策略。litellm 会把调用记录写入数据库,如果你配置了 postgres,这些数据会一直增长。我建议定期清理旧日志,或者用面板看趋势而不是堆全量明细。这一项针对数据量大的团队尤其重要,否则半年后数据库体积会很感人。

第三是密钥轮换流程。litellm 的配置读的是环境变量,所以轮换上游密钥时,重启进程让新环境变量生效即可。但要注意旧的请求还在处理的时候新配置会不会导致不一致——实际上 litellm 是在每次请求发起时读取环境变量的,重启只会影响后续请求,对处理中的请求没有影响。

5. 进阶用法与扩展方向

大多数人的使用止步于代理转发,但 litellm 的潜力远不止这些。我最后再聊几个我实测过、觉得值得关注的进阶场景。

5.1 用 litellm 做模型质量评估

litellm 有评估路由(evaluation)的概念,简单说就是当你有多个模型可选时,litellm 可以根据历史请求的成功率和响应质量,动态决定把请求路由到哪个模型。这个功能在生产环境里可以用来做 A/B 测试,或者作为服务降级的预案。

实际配置时可以给某个model_name设置多个上游,比如:

model_list: - model_name: gpt-4o litellm_params: model: openai/gpt-4o - model_name: gpt-4o-fallback litellm_params: model: anthropic/claude-3-5-sonnet-20241022

然后在路由策略里设定当主模型连续失败 N 次,切换到备用模型。这就实现了自动降级,用户感知不到你在不同模型之间切换。个人项目可能用不到,但对稳定性和成本都敏感的场景,这个机制能帮你省掉不少夜间救火的时间。

5.2 把 litellm 作为统一 OpenAI 兼容层

不只是代理,litellm 的 SDK 模式也可以直接嵌入到你的 Python 项目中使用:

from litellm import completion response = completion( model="openai/gpt-4o", messages=[{"role": "user", "content": "你好"}], )

SDK 模式的好处是不用额外部署服务,适合独立脚本、定时任务、内部小工具。和代理模式相比,它少了一个网络跳转,延迟略低,但没有团队共享、统一密钥管理这些能力。我个人的习惯是:能独立部署代理就用代理,临时脚本才用 SDK。

5.3 与现有框架的配合

litellm 提供了 OpenAI 兼容端点,所以几乎所有支持自定义 base_url 的框架都能直接接上。比如 LangChain 里的 OpenAI 类、LlamaIndex 里的 OpenAI 连接器,把 base_url 指向 litellm 即可。这样整个项目链路的模型调用统一由 litellm 接管,上层应用不需要关心模型从哪来。

遇到框架层面和 litellm 不兼容的情况时,优先检查该框架对 OpenAI 兼容协议的支持程度。有些框架内置了某厂商 SDK 的特定行为,可能绕过了标准弹窗,这种情况下就要看资料、查文档,或者绕开框架里的内置模型封装,直接用 HTTP 调用 litellm。

写在最后的实操心得

我从第一次接触 litellm 到现在,最大的体会是:它把一个"每家都要写一遍"的适配层做成了标准件,这件事本身就很值钱。尤其是几个模型并行、成本要控制、密钥要管理的场景,litellm 几乎是我目前试过最顺手的一套方案。

最后分享一个小技巧:如果实在不确定某个模型能不能被 litellm 正常适配,不用急着写配置,直接先跑一条最简单的 CLI 命令试试——litellm --model xxx能跑通,就说明基本通路没问题,再慢慢把配置模型列表的细节补齐。这个方法帮我省了不少时间,也希望对你快速验证需求有用。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进