litelm:2900 行的极简 LiteLLM,一个例子看懂 OpenAI 格式如何转换成各家格式
LiteLLM 统一了上百家 LLM 供应商的调用接口,但代码已经膨胀到十万行以上——代理服务器、缓存、成本追踪、负载均衡,一大堆功能大多数人根本用不到。新开源的 litelm(Python,MIT,pip install litelm)把最核心的调用路径抽了出来:模型路由、消息格式互转、流式输出、工具调用、Embedding,约 2900 行代码,只依赖 openai 和 httpx。
对只想"一个客户端打全部模型"、却不想背上重依赖和常驻代理的团队,这是一个值得评估的极简替代。更重要的是,它是理解"OpenAI 格式如何转换成各家格式"的最佳教材——代码量小到可以完整读完。
这篇文章既介绍 litelm 这个项目,也借它讲清楚 LLM 统一接口背后的格式转换细节。
本文 outline
- litelm 是什么:把 LiteLLM 剥到只剩调用路径
- 核心:消息格式互转的三种情况
- 最难的一档:Anthropic 格式转换的细节
- 工具调用(Tool Use)的格式转换
- 迁移:全局替换 import 即可
- 代价:删掉了什么
1. litelm 是什么:把 LiteLLM 剥到只剩调用路径
litelm 的定位一句话:"litellm without the bloat"。它的设计哲学是只保留 5 件事:
- 模型路由(
openai/gpt-4o这种provider/model格式) - 消息格式互转(OpenAI 格式 ↔ 各家原生格式)
- 流式输出
- 工具调用(function calling / tool use)
- Embedding
约 2900 行、2 个运行时依赖(openai、httpx),没有 Router 类、没有代理服务器、没有缓存层、没有成本追踪。当前状态 Alpha,但 API 面完全镜像 LiteLLM——函数名、参数、返回类型一致。
代码结构很清晰:核心是 _completion.py(572 行)+ _types.py(413 行),各家格式转换在 providers/ 目录下,其中 Anthropic 的转换是最复杂的一个文件(924 行)。这个比例本身就说明了问题:统一接口的复杂度主要不在路由,而在格式转换。
2. 核心:消息格式互转的三种情况
litelm 的路由逻辑:parse_model() 把 "provider/model-name" 拆开(裸的 "gpt-4o" 默认 openai),然后在 PROVIDERS 注册表里查 provider 对应的 base_url 和环境变量名。22 个 provider 前缀注册在案。
但转发方式分三种,复杂度完全不同:
第一档:OpenAI 兼容(大多数)。直接把 OpenAI SDK 的 base_url 换成 provider 的地址,消息格式原样透传,零转换。这覆盖了 OpenRouter、Groq、xAI、DeepSeek、Together、Fireworks、vLLM、Ollama、LM Studio 等等——只要对方提供 OpenAI 兼容端点。OpenAI 格式实际上成了事实标准,兼容它就能接入大多数服务。
第二档:原生 SDK 转换(4 家)。Anthropic、Bedrock、Cloudflare、Mistral 这四家不提供(或不完全提供)OpenAI 兼容端点,需要真正的格式翻译。Cloudflare 其实走 OpenAI 兼容 /ai/v1,只处理一些 legacy 路径;Bedrock 走 boto3 原生;Mistral 主要做一件事——把 reasoning_content、thinking_blocks 这类仅输出端的字段在回放历史前剥掉。
第三档(最复杂的):Anthropic。这就是 924 行代码的存在理由,值得单独一节。
3. 最难的一档:Anthropic 格式转换的细节
OpenAI 和 Anthropic 的消息模型表面相似,细节差异巨大。litelm 的 Anthropic 处理器(_anthropic.py,924 行)要处理这些差异:
System prompt 的抽取。OpenAI 允许 messages 里任意位置有 role: "system",Anthropic 要求 system 是独立顶层字段。转换时必须把 OpenAI 的 system 消息抽出来放到 Anthropic 的 system 参数里。
Content block 的转换。OpenAI 的消息 content 可以是字符串或数组;Anthropic 用结构化的 content blocks(text、image 等)。转换要把 OpenAI 的数组形式映射成 Anthropic 的 block 结构。
Schema 差异处理。OpenAI 工具定义的 JSON Schema 和 Anthropic 的有兼容性差异,litelm 要处理 _enum_conflicts_with_type(枚举和类型冲突)、_inline_schema_refs(内联引用)等边界情况。
Reasoning / thinking 处理。Claude 的推理块(thinking)和 OpenAI 的 reasoning 参数需要互相映射,包括"adaptive thinking 模型检测"——自动判断模型是否支持自适应思考,按模型设 max_tokens 和 reasoning effort。
Structured output。Claude 系列模型原生支持结构化输出,转换时要把 OpenAI 的 response_format 翻译过去。
一句话总结:OpenAI 格式是"平铺的、宽松的",Anthropic 格式是"嵌套的、严格的",转换的难点全在把宽松格式塞进严格格式时的边界情况。
4. 工具调用(Tool Use)的格式转换
工具调用是 Agent 的核心能力,也是格式差异最坑的地方。OpenAI 和 Anthropic 的工具调用模型是两套命名体系:
| 概念 | OpenAI | Anthropic |
|---|---|---|
| 工具定义 | tools(JSON Schema) |
tools(带 input_schema) |
| 模型请求工具 | tool_calls |
tool_use block |
| 工具执行结果 | role: "tool" 消息 |
tool_result block |
| 选择策略 | tool_choice: "auto/none/required" |
tool_choice 不同语义 |
litelm 的转换逻辑:
- 工具定义:OpenAI 的
tools→ Anthropic 的tools,支持cache_control缓存标记 - 模型发出的请求:OpenAI 的
tool_calls→ Anthropic 的tool_usecontent block - 执行结果:OpenAI 的
role: "tool"消息 → Anthropic 的tool_resultblock,ID 要清洗对齐 - 选择策略:
tool_choice: "required"正确映射到 Anthropic 对应语义,支持指定某个具名工具
对流式输出,Anthropic 的原生事件流被转换成 OpenAI 格式的 chunk(_build_stream_chunk),这样调用方拿到的是统一的 OpenAI 流式接口——这是"格式互转"最典型的价值:底层各家的流式协议不同,但顶层统一成一种。
5. 迁移:全局替换 import 即可
litelm 的迁移方式简单到不可思议:
# 之前
import litellm
# 之后
import litelm
response = litelm.completion(
"openai/gpt-4o",
messages=[{"role": "user", "content": "Hello!"}],
)
print(response.choices[0].message.content)
# 流式
for chunk in litelm.completion("groq/llama-3.1-70b-versatile", messages=[...], stream=True):
print(chunk.choices[0].delta.content or "", end="")
# Embedding
response = litelm.embedding("openai/text-embedding-3-small", input=["hello world"])
# 自定义端点(本地 vLLM / Ollama)
litelm.completion("openai/gpt-4o", messages=[...], api_key="sk-...", api_base="http://localhost:8000/v1")函数签名、参数名、返回类型和 LiteLLM 完全一致。__init__.py 里甚至写了它最初是为 DSPy 做 drop-in 替换的——DSPy 的全部 7 条执行路径(Predict、CoT、typed signatures、流式、Embedding、工具调用、多输出)都已验证可用。异步用 acompletion、aembedding。
litelm 还提供完整异常体系(AuthenticationError、RateLimitError、ContextWindowExceededError 等),和 LiteLLM 的异常类同名同构,错误处理代码不用改。
6. 代价:删掉了什么
litelm 的取舍很透明(README 有对比表):
保留了:模型路由、消息翻译、流式、工具调用、Embedding、text completion、Responses API、mock。
删掉了:Router(负载均衡/故障转移)、代理服务器、缓存/预算/成本追踪、token 计数、图像生成/音频/OCR/微调、agents/guardrails/scheduler。
这意味着:如果你需要负载均衡和故障转移,或者要常驻代理给整个团队用,litelm 帮不了你——那些正是 LiteLLM 的职责。litelm 的定位是单进程内的调用库,不是基础设施。
代价换来的东西:一个 ~2900 行、2 个依赖、可以完整审计的代码库。在 LLM 调用库这个信任敏感的场景,"能读完的代码"本身是价值。
要提醒的几点:
- Alpha 状态:没有 1.0,接口可能变
- 供应商覆盖:宣传的 19 家供应商里,只有 6 家(OpenAI、Anthropic、Groq、Mistral、xAI、OpenRouter)经过 live 验证;Gemini、Cohere 等走 OpenAI 兼容端点,与 LiteLLM 原生 handler 的等价性没有保证
- 单维护者:192 星、4 fork,但维护很勤——每周同步 LiteLLM 上游变更,最近一次审了 360 个核心路径 commit,262 个自有测试通过
对只想"一个客户端打全部模型"、用不到代理和缓存的团队,litelm 是一个把复杂度砍到极致的方案。更重要的是,读一遍它的 providers/_anthropic.py,你就彻底理解了"LLM 统一接口"到底在统一什么。
参考
- kennethwolters/litelm GitHub — MIT,~2900 行,2 依赖
- litelm PyPI — v0.5.2,Python 3.10+
- LiteLLM GitHub — 原始项目,统一 100+ 供应商
- Anthropic Messages API — 对比 Anthropic 原生格式
- OpenAI Chat Completions API — OpenAI 格式基准
作者: itech001 来源: 公众号:AI人工智能时代(the-ai-era) 网站: https://www.theaiera.top/ 关注每日最新AI新闻和技术博客,主页有更多的文章的AI 技术参考:https://www.theaiera.top
关注公众号,获取更多 AI 技术干货!