⚡ BerriAI/litellm:给 AI 网关换上一颗 Rust 心脏 🚀
如果你在过去两年里接过三个以上的大模型 API,大概会经历过这种时刻:Anthropic 的错误结构和 OpenAI 不一样,Bedrock 的鉴权要走 SigV4,VertexAI 的参数得套一层 generationConfig,而某个自建的 vLLM 服务又希望你按它自己的格式发请求。
业务代码还没写几行,集成层已经长成了一片沼泽。更麻烦的是,当你想加一个"看看这个月到底花了多少钱"的需求时,会发现账单分散在五个控制台里,格式还各不相同。
今天登上 GitHub Trending 的 BerriAI/litellm,正是冲着这团沼泽去的。它的自我定位很直接:
The fastest, litest AI Gateway. Rust core with Python SDK. Call 100+ LLM APIs in OpenAI (or native) format with cost tracking, guardrails, load balancing, and logging.
网关这个位置,决定了它必须快
先想清楚 AI Gateway 站在架构的哪一格。它不是业务逻辑的一部分,而是所有模型调用都必须路过的一层。用户请求 → 你的服务 → 网关 → 模型厂商 → 回到用户。
这种"所有流量都经过"的位置有一个残酷的性质:网关增加的每一毫秒,都会被乘上调用总量。一个 SDK 慢一点,只有用它的那个服务受影响;一个网关慢一点,所有接入它的线上服务一起受影响。
所以"最快的、最轻的"这个说法并不只是宣传语,它其实是这个品类能否成立的先决条件。一个又厚又重的网关,很快就会被绕过。
Rust core + Python SDK:一次有意识的拆分
litellm 的技术选型里,最有意思的一点是它没有把整套东西写成一门语言,而是拆成了两层:Rust 核心 + Python SDK。
这两个选择各自对应的动机其实不太一样:
- Rust 放在核心——因为核心位于请求热路径上,它要做的事情是转发、路由、计数、限流、写日志。这些活儿本身逻辑不复杂,但量大、并发高,而且不能有停顿。Rust 没有 GC,内存模型让它在高并发转发场景下更容易把延迟压稳——这正是"网关"这个身份最需要的品质。
- Python 做成 SDK——因为模型生态几乎就是 Python 的主场。做 RAG 的、跑评测的、调 prompt 的、写 notebook 的,绝大多数都在 Python 里工作。让他们为了调一次模型去学一套新的客户端,是不现实的。
换句话说,这层拆分回答的是两个不同的问题:数据面(data plane)要快,所以往下走;控制面(control plane)和开发者界面要顺,所以往上走。网关产品如果只做其中一半,通常会在另一半上翻车。
你的应用 / Agent / Notebook
│
┌───────▼────────┐
│ Python SDK │ ← 开发者接触的部分
└───────┬────────┘
│
┌───────▼────────┐
│ Rust core │ ← 请求热路径:路由 / 计数 / 日志
└───────┬────────┘
│ 统一为 OpenAI 格式(或保留原生格式)
┌────────┬────┴────┬─────────┬────────┐
Bedrock Azure Anthropic VertexAI vLLM / NIM
它到底替开发者扛了什么
从项目描述来看,litellm 承担的不只是"协议转换"这一件事,而是一组在真实生产环境里迟早会撞上的需求:
1. 统一调用格式
用 OpenAI 格式调用 100+ 个 LLM API,同时允许走原生格式。这一点很关键——统一格式降低了心智负担,而保留原生格式意味着你不会因为用了网关就丢掉厂商的新特性。这个取舍是务实的:完全归一化看起来很优雅,但代价是永远追不上厂商的更新速度。
2. 成本追踪
这是很多团队上线后才意识到要做的事。当你有几个模型、十几个调用方、几十个 prompt 模板时,"这个月钱花在哪儿了"会变成一个需要专门排查的问题。把计量放在网关层,是唯一能覆盖全量的位置。
3. Guardrails 与负载均衡
护栏决定了什么请求不该出去、什么响应不该返回;负载均衡决定了同一个模型有多个后端(多个 key、多个 region、自建集群)时请求该去哪。这两件事都天然属于网关:它们需要对流量有全局视角。
4. 日志
可观测性的地基。没有它,前三条都只能靠猜。
Provider 矩阵里藏着的判断
项目描述里点名的后端是:Bedrock、Azure、OpenAI、Anthropic、VertexAI、vLLM、Nvidia NIM。
把这份名单横着看一遍,会发现它同时覆盖了三种形态:
- 闭源 API 直连:OpenAI、Anthropic
- 云厂商托管平台:Bedrock、Azure、VertexAI
- 自建推理引擎:vLLM、Nvidia NIM
第三类尤其值得注意。愿意支持 vLLM 和 NIM 这类自托管推理,意味着 litellm 假设的使用场景不是"我全用云 API",而是混合部署——生产流量可能在云端,敏感数据或成本敏感的部分跑在自己的 GPU 上,而调用方希望代码里看不出这个区别。
这也是网关这一层真正的价值所在:它让你可以在不修改业务代码的前提下,把一个模型从云上挪到自建集群,或者反过来。
开发者视角:把复杂度收进一行 import
对使用者来说,最直观的变化是调用形态。示意性地看,大致是这样一种感觉:
from litellm import completion
# 用统一的 OpenAI 格式调用不同厂商的模型
resp = completion(
model="<provider>/<model-name>",
messages=[{"role": "user", "content": "Hello"}],
)
print(resp.choices[0].message.content)
换成另一个 provider,多数情况下只需要改 model 这一行,而 messages 的结构、返回值的读取方式、上层业务代码都不用动,也不会多 import 一个厂商专属的 SDK。
成本追踪、护栏、日志这些能力则更像是"配置项"而不是"代码改动"——它们生效的位置在网关里,而不在每个调用点上。对于一个已经在生产里跑着几十处模型调用的团队来说,这个差别意味着:接入是集中式的,而不是地毯式的。
一点启发:薄,也是一种设计
litellm 这个名字本身就带点态度——"lite"。在基础设施领域,大家容易被"功能全"吸引,但对一个位于热路径上的组件来说,克制才是性能的一部分。
它给出的解题思路可以概括成三句话:
- 位置决定选型:因为站在所有流量的必经之路上,所以核心用 Rust;因为面向 AI 开发者,所以 SDK 用 Python。不是"哪个语言更酷"的问题。
- 统一但不吞掉差异:默认 OpenAI 格式,同时保留原生格式的出口。归一化解决 90% 的场景,剩下 10% 不该被牺牲。
- 把横切关注点放到横切层:成本、护栏、负载均衡、日志,这四件事没有一个属于业务逻辑,但每一个都需要全局视角——那它们就不该散落在业务代码里。
如果你手上正维护着一个需要对接多家模型的服务,或者正在纠结"要不要自己写一层适配",这个项目值得拆开看看它的架构取舍。毕竟,能让人少写几百行 if provider == ... 的东西,通常都值得多看一眼。🛠️