从一个真实的周一早晨讲起
某个周一早晨,产品同学在群里甩出一张图:"GPT-4o 这个月账单 12 万美元,CFO 问能不能把客服分类任务切到 Gemini Flash,据说便宜 20 倍。"
工程师盯着代码仓库,心里默默算账。这套客服系统三个月前刚上线,用的是 OpenAI Python SDK。"切 Gemini"意味着什么呢?让我们一条一条列出来:
- SDK 不一样。OpenAI 用
from openai import OpenAI,Gemini 用google.generativeai,调用方法、参数名、返回结构全部不同。 - 消息格式不一样。OpenAI 的
messages是[{role, content}],Gemini 用contents,role 的取值只有 user/model,没有 system。 - system prompt 不一样。OpenAI 把它塞在 messages 数组第一条,Gemini 4 月以前要放在
system_instruction字段,后来又加了新写法。 - 流式协议不一样。OpenAI 是 SSE 的
chunk.choices[0].delta.content,Gemini 是chunk.text,且错误处理完全不同。 - 工具调用不一样。OpenAI 叫
tools+tool_calls,Claude 叫tools+tool_use,Gemini 叫function_declarations+function_call;参数 schema 的嵌套结构、strict 模式、并行调用能力都有细微差异。 - 计费不一样。OpenAI 返回
usage.prompt_tokens / completion_tokens,Gemini 返回usage_metadata.prompt_token_count / candidates_token_count,价格表要重新维护。 - 错误不一样。OpenAI 的
RateLimitError状态码是 429,Gemini 可能给你 500 还带一个加密的错误串,还有的厂商 200 里藏着 error 字段。
这还只是两家。加上 Anthropic Claude、Azure OpenAI(和 OpenAI 不完全一样)、AWS Bedrock、Vertex AI、Ollama、Together、Groq、国内的通义/DeepSeek/智谱,你至少要维护 10 套客户端封装代码。
更要命的是,真正的生产系统不是"换一家模型",而是"每个任务选最合适的一家":
一个真实的 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 处改动,且每一处都要回归测试。
代价二:运维层面的能力天花板
更隐蔽的代价是"失去了一些选择的自由"。比如:
- 想做 A/B 测试?同一个业务场景 50% 流量走 GPT-4o、50% 走 Claude,收集胜率——没有抽象层,你要在每个调用点 if/else 分发。
- 想做 fallback?主模型限流了,自动降级到备用模型——没有抽象层,你要自己写重试循环并记得两家 API 格式差异。
- 想统一做缓存?把语义相近的问题命中同一个缓存——没有抽象层,缓存 key 怎么算?GPT 的 messages 和 Claude 的 messages 根本不是同一个结构。
- 想统一审计?公司合规要求所有 LLM 调用都要脱敏记录——没有抽象层,你要在每家 SDK 外包一层,且永远担心漏了某个新加的 provider。
代价三:组织层面的谈判筹码
这是最少人提到、但最致命的一点。如果你的业务 100% 跑在 OpenAI 上,当 OpenAI 下一次涨价 30%、或者遗憾地告诉你某个模型 deprecated 了、或者某天服务挂了 8 小时,你什么都做不了。
反之,如果你的代码是 provider-agnostic(provider 无关) 的,你可以在任何时候把流量在几家之间切换。这种"随时可以离开"的能力,本身就是和供应商谈判时最硬的筹码。
为什么 OpenAI 格式成了事实标准
既然要抽象,那以哪家的格式作为"统一语言"?这件事没什么悬念——行业选择了 OpenAI 的 /v1/chat/completions。原因有三:
- 时间窗口。2022 年 11 月 ChatGPT 发布,2023 年初 OpenAI 的 API 独占市场一年多,几乎所有早期 LLM 应用都围绕它写代码。
- 生态惯性。LangChain、LlamaIndex、Semantic Kernel、AutoGen、OpenAI Agents SDK、几乎所有 Agent 框架最早都是对准 OpenAI SDK 开发的。后发的厂商为了接入这个生态,纷纷提供"OpenAI 兼容模式"。
- 格式足够"通用"。
messages = [{role, content}]这个对话结构简单、表达力够、容易映射到其他家。
于是今天你看到的局面是:
| 厂商 | 原生 API 格式 | 是否提供 OpenAI 兼容 |
|---|---|---|
| OpenAI | /v1/chat/completions | —(本家) |
| Anthropic | /v1/messages (自家格式) | 有 beta 兼容端点 |
| Google Gemini | generateContent | 官方 OpenAI 兼容 endpoint |
| Azure OpenAI | OpenAI 格式(部分微调) | 本身就是 |
| AWS Bedrock | Converse API(2024 新) | 需要网关 |
| Ollama | 自家 /api/chat | 提供 /v1/chat/completions |
| Together / Groq / Fireworks | 直接 OpenAI 兼容 | 是 |
| DeepSeek / 智谱 / 通义 / Moonshot | 原生 OpenAI 兼容 | 是 |
| vLLM / SGLang / LMDeploy | —(开源推理服务) | 提供 OpenAI 兼容端点 |
有些厂商(特别是 OpenAI 兼容做得好的)你甚至可以直接用 OpenAI SDK,只要改 base_url 和 api_key。但这只能解决"调用层"的一半问题,剩下一半是:
- 不同厂商的工具调用格式细节差异(parallel、strict、schema 限制)
- 多模态输入的 encoding 规范(Claude 要 base64,Gemini 要 fileData)
- 错误码归一
- 计费和 token 计算
- 不完全兼容的参数(Anthropic 的
max_tokens必填、Gemini 的topK独有)
这些"最后一公里"的差异,就是 LiteLLM 这类抽象层真正的价值所在。
LiteLLM 的核心主张
LiteLLM 是 BerriAI 维护的开源项目(Apache 2.0),目前支持 100+ 家 provider。它的核心主张一句话:
让所有 LLM 都看起来像 OpenAI。
不管后端是 Claude、Gemini、Bedrock 还是 Ollama,你写的永远是 litellm.completion(model="...", messages=[...]);返回的永远是一个 OpenAI 风格的对象。LiteLLM 在中间做完了所有的脏活累活——参数映射、流式归一、工具调用翻译、token 计算、错误分类。
它解决的三件事
把上面那些痛点抽象成三个核心能力:
messages,它给 Gemini 变成 contents;你写 response_format={"type":"json_object"},它给 Claude 改成 tool use 的强制 JSON schema;你写 tools,它给每家翻译成各自的函数调用约定。RateLimitError / AuthenticationError / BadRequestError / ContextWindowExceededError / ...)。你 try/except litellm.RateLimitError,不管底层谁限流都能抓到。model_prices.json,实时从社区更新价格。返回里附带 response_cost 字段,你不用关心每家的定价单位(有的按百万 token、有的按千 token、有的区分 input/output/cached)。它不做什么
值得提前说清楚,LiteLLM 不是万能药,它有清晰的边界:
- 不是 Agent 框架。它不管"智能决策",你还是要用 LangGraph / OpenAI Agents SDK / Pydantic AI 等框架来编排多步流程。
- 不是 RAG 框架。检索、索引、chunk 切分这些 LlamaIndex 或 LangChain 管。
- 不是 prompt 管理。prompt 版本化、灰度、A/B 交给 Langfuse 或 Prompt Hub。
- 不是评估框架。跑 eval 看 recall/precision 交给 Braintrust / Langfuse / Promptfoo。
它就是一层"通用 LLM 调用层",往上对接 Agent/RAG/prompt 框架,往下对接各家 provider。位置示意:
两种用法: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 章你能做到什么
把这门教程当作一份工程地图,学完之后:
- 任何时候切换 LLM provider,不用改业务代码,只改一个 model 字符串
- 给 AI 应用加上 fallback、retry、缓存、限流,而不是自己重复造轮子
- 看懂 LiteLLM 每一行 config.yaml 在做什么,能精准调优 cooldown / priority / tag 路由
- 部署一套内部的 LLM Proxy,给公司所有团队发 Virtual Key,集中预算和审计
- 把 LiteLLM 接进 Langfuse / Prometheus / OpenTelemetry,拿到全链路可观测
- 理解在什么场景 LiteLLM 不是最优解——比如需要极低延迟、需要 streaming 极致优化时
本章小结
- 多模型是 AI 产品的常态;为每家写客户端 = 在代码里埋一颗定时炸弹
- 厂商锁定有三重代价:代码耦合、运维天花板、组织谈判筹码
- OpenAI
/v1/chat/completions格式已成为事实标准,但"最后一公里"的差异仍然需要抽象层 - LiteLLM 的核心能力 = 参数映射 + 错误归一 + 计费翻译
- 两种用法:SDK(In-Process)适合单体;Proxy(Gateway)适合多团队中央网关
- 它不替代 Agent / RAG / Eval 框架,它只是最底层的"LLM 调用层"