Chapter 01

为什么需要 provider 抽象层

你以为把 OpenAI API Key 换成 Anthropic 就是换模型?真到了生产线上才会发现,换模型其实是一次小型考古。

从一个真实的周一早晨讲起

某个周一早晨,产品同学在群里甩出一张图:"GPT-4o 这个月账单 12 万美元,CFO 问能不能把客服分类任务切到 Gemini Flash,据说便宜 20 倍。"

工程师盯着代码仓库,心里默默算账。这套客服系统三个月前刚上线,用的是 OpenAI Python SDK。"切 Gemini"意味着什么呢?让我们一条一条列出来:

这还只是两家。加上 Anthropic Claude、Azure OpenAI(和 OpenAI 不完全一样)、AWS Bedrock、Vertex AI、Ollama、Together、Groq、国内的通义/DeepSeek/智谱,你至少要维护 10 套客户端封装代码。

更要命的是,真正的生产系统不是"换一家模型",而是"每个任务选最合适的一家":

分类 / 路由
不需要推理能力,要的是便宜、快。 → Gemini Flash / DeepSeek / 本地 Llama 3.2
复杂对话 / 推理
需要强推理和多轮一致性。 → GPT-4o / Claude Sonnet / DeepSeek R1
长上下文摘要
动辄 50 万 token。 → Gemini 1.5 Pro / Claude 3.5 Sonnet(200K)
图片理解
多模态输入。 → GPT-4o / Claude / Gemini
代码补全 / 结构化生成
要 JSON 严格模式、工具调用准确率高。 → OpenAI structured outputs / Claude
合规 / 私有数据
禁止出境或要求私有化。 → Azure OpenAI / Bedrock / Ollama 自建

一个真实的 AI 产品,后台挂着 5-8 家模型是常态。如果每家都写一份客户端、一份错误处理、一份流式解析、一份计费脚本,代码库两年就会腐烂成"谁也不敢动"的状态。

厂商锁定(vendor lock-in)的三重代价

软件工程里有个老词叫 vendor lock-in——厂商锁定。很多团队以为 AI 领域不存在这个问题,因为"反正都是调 HTTP API 嘛"。实际情况是 AI 领域的锁定比传统云厂商严重得多,主要有三重代价。

代价一:代码层面的隐性耦合

当你写下这一行:

from openai import OpenAI
client = OpenAI()
resp = client.chat.completions.create(
    model="gpt-4o",
    messages=[{"role": "user", "content": "hi"}],
)
print(resp.choices[0].message.content)

每一个字符都在和 OpenAI 的对象结构深度耦合:client.chat.completions.create 是方法路径,messages 是参数名,choices[0].message.content 是返回路径。换到 Anthropic 就变成:

from anthropic import Anthropic
client = Anthropic()
resp = client.messages.create(
    model="claude-sonnet-4-5",
    max_tokens=1024,                     # Anthropic 必填, OpenAI 选填
    messages=[{"role": "user", "content": "hi"}],
)
print(resp.content[0].text)          # 结构不同, content 是数组

看起来只是语法不同,但任何读过 resp.choices[0].message.content 的代码都要重写。如果你有 50 个调用点,就是 50 处改动,且每一处都要回归测试。

代价二:运维层面的能力天花板

更隐蔽的代价是"失去了一些选择的自由"。比如:

代价三:组织层面的谈判筹码

这是最少人提到、但最致命的一点。如果你的业务 100% 跑在 OpenAI 上,当 OpenAI 下一次涨价 30%、或者遗憾地告诉你某个模型 deprecated 了、或者某天服务挂了 8 小时,你什么都做不了。

反之,如果你的代码是 provider-agnostic(provider 无关) 的,你可以在任何时候把流量在几家之间切换。这种"随时可以离开"的能力,本身就是和供应商谈判时最硬的筹码。

一个真实案例 2024 年某个月 OpenAI 上调了 gpt-4 turbo 的价格,一批用户发现自己的日账单跳涨 40%。有抽象层的团队当天把不敏感任务切到 Claude Haiku,成本回落;没有抽象层的团队花了两周改代码,期间账单继续烧钱。

为什么 OpenAI 格式成了事实标准

既然要抽象,那以哪家的格式作为"统一语言"?这件事没什么悬念——行业选择了 OpenAI 的 /v1/chat/completions。原因有三:

  1. 时间窗口。2022 年 11 月 ChatGPT 发布,2023 年初 OpenAI 的 API 独占市场一年多,几乎所有早期 LLM 应用都围绕它写代码。
  2. 生态惯性。LangChain、LlamaIndex、Semantic Kernel、AutoGen、OpenAI Agents SDK、几乎所有 Agent 框架最早都是对准 OpenAI SDK 开发的。后发的厂商为了接入这个生态,纷纷提供"OpenAI 兼容模式"。
  3. 格式足够"通用"。messages = [{role, content}] 这个对话结构简单、表达力够、容易映射到其他家。

于是今天你看到的局面是:

厂商原生 API 格式是否提供 OpenAI 兼容
OpenAI/v1/chat/completions—(本家)
Anthropic/v1/messages (自家格式)有 beta 兼容端点
Google GeminigenerateContent官方 OpenAI 兼容 endpoint
Azure OpenAIOpenAI 格式(部分微调)本身就是
AWS BedrockConverse API(2024 新)需要网关
Ollama自家 /api/chat提供 /v1/chat/completions
Together / Groq / Fireworks直接 OpenAI 兼容
DeepSeek / 智谱 / 通义 / Moonshot原生 OpenAI 兼容
vLLM / SGLang / LMDeploy—(开源推理服务)提供 OpenAI 兼容端点

有些厂商(特别是 OpenAI 兼容做得好的)你甚至可以直接用 OpenAI SDK,只要改 base_urlapi_key。但这只能解决"调用层"的一半问题,剩下一半是:

这些"最后一公里"的差异,就是 LiteLLM 这类抽象层真正的价值所在。

LiteLLM 的核心主张

LiteLLM 是 BerriAI 维护的开源项目(Apache 2.0),目前支持 100+ 家 provider。它的核心主张一句话:

让所有 LLM 都看起来像 OpenAI。

不管后端是 Claude、Gemini、Bedrock 还是 Ollama,你写的永远是 litellm.completion(model="...", messages=[...]);返回的永远是一个 OpenAI 风格的对象。LiteLLM 在中间做完了所有的脏活累活——参数映射、流式归一、工具调用翻译、token 计算、错误分类。

它解决的三件事

把上面那些痛点抽象成三个核心能力:

① 参数映射(Parameter Mapping)
把 OpenAI 格式的入参翻译成各 provider 的原生格式。你写 messages,它给 Gemini 变成 contents;你写 response_format={"type":"json_object"},它给 Claude 改成 tool use 的强制 JSON schema;你写 tools,它给每家翻译成各自的函数调用约定。
② 错误归一(Error Normalization)
把 40+ 种原生错误映射到 OpenAI 的 7 种标准异常(RateLimitError / AuthenticationError / BadRequestError / ContextWindowExceededError / ...)。你 try/except litellm.RateLimitError,不管底层谁限流都能抓到。
③ 计费翻译(Cost Tracking)
维护一份 model_prices.json,实时从社区更新价格。返回里附带 response_cost 字段,你不用关心每家的定价单位(有的按百万 token、有的按千 token、有的区分 input/output/cached)。

它不做什么

值得提前说清楚,LiteLLM 不是万能药,它有清晰的边界:

它就是一层"通用 LLM 调用层",往上对接 Agent/RAG/prompt 框架,往下对接各家 provider。位置示意:

┌──────────────────────────────┐ │ 你的 Agent / RAG / 业务代码 │ └─────────────┬────────────────┘ │ litellm.completion(...) ┌─────────────▼────────────────┐ │ LiteLLM 层 │ │ ───────────────────────── │ │ · 参数映射(messages/tools) │ │ · 错误归一 │ │ · 计费翻译 │ │ · Router/Fallback/Cache │ │ · Proxy Server (可选) │ └──┬──────┬──────┬─────┬───────┘ │ │ │ │ ┌────▼──┐ ┌─▼──┐ ┌─▼──┐ ┌▼──────┐ │OpenAI │ │Claude│ │Gemini│ │ ... │ └───────┘ └─────┘ └──────┘ └──────┘

两种用法:SDK 模式 vs Proxy 模式

LiteLLM 有两种形态,对应两个不同的工程场景。理解它们的分水岭,是后面所有章节的基础。

① SDK 模式(In-Process)

  • 直接 pip install litellm,在 Python 代码里 import litellm
  • 调用方就是 LLM 调用方,一个进程内完成
  • 适合单体应用、脚本、Jupyter notebook
  • 秘钥放在进程的环境变量里
  • 路由/缓存/限流都在本进程配置

② Proxy 模式(Gateway)

  • 独立部署一个 HTTP 服务 litellm --config config.yaml
  • 对外暴露 OpenAI 兼容的 /v1/chat/completions
  • 客户端(任何语言/任何团队)用 OpenAI SDK 指向这个 URL
  • 秘钥集中管理,发 Virtual Keys 给各团队
  • 路由/缓存/限流/预算/日志中心化

一般的演进路径是这样:初期用 SDK 模式快速开发;流量起来、团队变多、各部门都开始调 LLM 时,升级到 Proxy 模式做中央网关。第 2-9 章讲 SDK,第 10 章开始切到 Proxy。

一个"哇!"时刻的快速演示

先放一段代码让你直观感受 LiteLLM 的魔力。三家模型,三种 provider,一套调用,一套返回处理:

from litellm import completion
import os

os.environ["OPENAI_API_KEY"]    = "sk-..."
os.environ["ANTHROPIC_API_KEY"] = "sk-ant-..."
os.environ["GEMINI_API_KEY"]    = "..."

messages = [{"role": "user", "content": "用一句话介绍光合作用"}]

# 三个 provider, 三种模型, 一套代码
for model in ["gpt-4o-mini", "claude-haiku-4-5", "gemini/gemini-2.0-flash"]:
    resp = completion(model=model, messages=messages)
    print(f"[{model}]")
    print("  内容:", resp.choices[0].message.content)
    print("  用量:", resp.usage.prompt_tokens, "+", resp.usage.completion_tokens)
    print("  成本:", round(resp._hidden_params["response_cost"], 6), "USD")

三个模型,一段循环。每次返回都是 OpenAI 格式的对象,choices[0].message.content 都能取到内容,usage 都能读到 token 数,_hidden_params["response_cost"] 甚至帮你算好这一次花了多少美元。

这就是 provider 抽象层的威力——它让"模型"从一种绑死的依赖变成了一个可替换的参数

学完这 12 章你能做到什么

把这门教程当作一份工程地图,学完之后:

  1. 任何时候切换 LLM provider,不用改业务代码,只改一个 model 字符串
  2. 给 AI 应用加上 fallback、retry、缓存、限流,而不是自己重复造轮子
  3. 看懂 LiteLLM 每一行 config.yaml 在做什么,能精准调优 cooldown / priority / tag 路由
  4. 部署一套内部的 LLM Proxy,给公司所有团队发 Virtual Key,集中预算和审计
  5. 把 LiteLLM 接进 Langfuse / Prometheus / OpenTelemetry,拿到全链路可观测
  6. 理解在什么场景 LiteLLM 不是最优解——比如需要极低延迟、需要 streaming 极致优化时

本章小结