返回博客列表

litelm:2900 行的极简 LiteLLM,一个例子看懂 OpenAI 格式如何转换成各家格式

2026-09-13T11:30:00+08:00
litelmLiteLLMLLM APIOpenAI 格式格式转换开源

LiteLLM 统一了上百家 LLM 供应商的调用接口,但代码已经膨胀到十万行以上——代理服务器、缓存、成本追踪、负载均衡,一大堆功能大多数人根本用不到。新开源的 litelm(Python,MIT,pip install litelm)把最核心的调用路径抽了出来:模型路由、消息格式互转、流式输出、工具调用、Embedding,约 2900 行代码,只依赖 openaihttpx

对只想"一个客户端打全部模型"、却不想背上重依赖和常驻代理的团队,这是一个值得评估的极简替代。更重要的是,它是理解"OpenAI 格式如何转换成各家格式"的最佳教材——代码量小到可以完整读完。

这篇文章既介绍 litelm 这个项目,也借它讲清楚 LLM 统一接口背后的格式转换细节。

本文 outline

  1. litelm 是什么:把 LiteLLM 剥到只剩调用路径
  2. 核心:消息格式互转的三种情况
  3. 最难的一档:Anthropic 格式转换的细节
  4. 工具调用(Tool Use)的格式转换
  5. 迁移:全局替换 import 即可
  6. 代价:删掉了什么

1. litelm 是什么:把 LiteLLM 剥到只剩调用路径

litelm 的定位一句话:"litellm without the bloat"。它的设计哲学是只保留 5 件事:

  • 模型路由(openai/gpt-4o 这种 provider/model 格式)
  • 消息格式互转(OpenAI 格式 ↔ 各家原生格式)
  • 流式输出
  • 工具调用(function calling / tool use)
  • Embedding

约 2900 行、2 个运行时依赖(openaihttpx),没有 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_contentthinking_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(textimage 等)。转换要把 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_use content block
  • 执行结果:OpenAI 的 role: "tool" 消息 → Anthropic 的 tool_result block,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、工具调用、多输出)都已验证可用。异步用 acompletionaembedding

litelm 还提供完整异常体系(AuthenticationErrorRateLimitErrorContextWindowExceededError 等),和 LiteLLM 的异常类同名同构,错误处理代码不用改。

6. 代价:删掉了什么

litelm 的取舍很透明(README 有对比表):

保留了:模型路由、消息翻译、流式、工具调用、Embedding、text completion、Responses API、mock。

删掉了:Router(负载均衡/故障转移)、代理服务器、缓存/预算/成本追踪、token 计数、图像生成/音频/OCR/微调、agents/guardrails/scheduler。

这意味着:如果你需要负载均衡和故障转移,或者要常驻代理给整个团队用,litelm 帮不了你——那些正是 LiteLLM 的职责。litelm 的定位是单进程内的调用库,不是基础设施。

代价换来的东西:一个 ~2900 行、2 个依赖、可以完整审计的代码库。在 LLM 调用库这个信任敏感的场景,"能读完的代码"本身是价值。

要提醒的几点:

  1. Alpha 状态:没有 1.0,接口可能变
  2. 供应商覆盖:宣传的 19 家供应商里,只有 6 家(OpenAI、Anthropic、Groq、Mistral、xAI、OpenRouter)经过 live 验证;Gemini、Cohere 等走 OpenAI 兼容端点,与 LiteLLM 原生 handler 的等价性没有保证
  3. 单维护者:192 星、4 fork,但维护很勤——每周同步 LiteLLM 上游变更,最近一次审了 360 个核心路径 commit,262 个自有测试通过

对只想"一个客户端打全部模型"、用不到代理和缓存的团队,litelm 是一个把复杂度砍到极致的方案。更重要的是,读一遍它的 providers/_anthropic.py,你就彻底理解了"LLM 统一接口"到底在统一什么。

参考


作者: itech001 来源: 公众号:AI人工智能时代(the-ai-era) 网站: https://www.theaiera.top/ 关注每日最新AI新闻和技术博客,主页有更多的文章的AI 技术参考:https://www.theaiera.top

关注公众号,获取更多 AI 技术干货!

分享给朋友