☰
LiteLLM实战:用OpenAI格式统一调用100+大模型API
2026/10/13 19:33:57 网站建设 项目流程

做 AI 应用落地这一年多,我最大的感受是:模型本身反而不是最纠结的选项,真正磨人的是把各家服务商串起来这件事。今天聊的 LiteLLM,就是为这个痛点而生的开源项目——它既可以作为一个 Python 库嵌入你的代码,也可以部署成独立的代理网关,核心承诺只有一句话:用 OpenAI 的接口格式,统一调用 100 多家大模型服务商。

我第一次接触它在一次多模型评测项目里。当时需要对比 GPT、Claude、Gemini 和几个开源模型的回答质量,最原始的写法是每个模型都装一套官方 SDK,各自初始化客户端、各自拼 messages、各自处理错误,代码里全是 if-else,日志格式还不统一。换模型等于重写小半套调用逻辑。LiteLLM 把这一层完全抹平了:改一行 model 字符串就能切换供应商,流式、异步、工具调用、成本统计都走同一套接口。如果你也在做多模型产品或想给团队搭一个统一的模型网关,这篇文章应该能帮你省掉不少弯路。

1. 统一接入层存在的意义:各家 API 的差异远比你想的多

1.1 每次都重写一遍 SDK 适配,问题出在哪

很多人以为各家大模型 API 大同小异,不就是发个 prompt 拿个回复吗?真上手写过才发现差距。初始化客户端的方式就不一样:OpenAI 是 api_key 一把梭,Anthropic 需要 x-api-key 放在请求头里,AWS Bedrock 走的是 IAM 签名,Azure OpenAI 还得额外带 endpoint 和 api-version,Google Gemini 有时候要处理 project、location 这一串参数。这只是身份认证,后面还有更麻烦的:

  • 消息结构不同。OpenAI 把 system prompt 放在 messages 里的 system 角色,Anthropic 是独立的 system 参数,Gemini 的消息结构又是另一套 content/parts 嵌套。
  • 流式输出的格式不同。各家 SSE 事件里的字段命名不一样,有的给你 delta,有的给 content,解析代码没法复用。
  • 错误码语义不同。同样一个 429,有的服务是限流,有的是配额耗尽;5xx 里还分过载和网关错误,处理策略完全不同。
  • 用量字段不统一。有的返回 prompt_tokens/completion_tokens,有的返回 input_tokens/output_tokens,统计成本时还得各写一份换算逻辑。

我见过不少项目因为这些差异,把业务代码写得又厚又脆,模型厂商一升级接口或者换了个参数命名,就要跟着改业务。这就是我理解的"适配地狱"——模型越多,维护成本越高,而且这些代码基本没有业务价值,纯粹是搬运工。

1.2 LiteLLM 的思路:用 OpenAI 格式当"通用语言"

LiteLLM 的解法很直接:把 OpenAI 的 Chat Completions 风格作为统一输入输出格式,内部再做协议转换。你在代码里永远只写一套 messages、tools、stream 这些概念,LiteLLM 负责把它们翻译成各家 API 真正需要的结构,再把各家返回结果统一成 OpenAI 风格的对象。使用哪种模型,通过 model 字符串来控制,例如openai/gpt-4o、anthropic/claude-3-5-sonnet-latest、gemini/gemini-1.5-pro、bedrock/anthropic.claude-v3-sonnet,前缀就是服务商标识。

这个设计聪明在哪?OpenAI 格式几乎是当前事实标准,LangChain、LlamaIndex、各种开源工具链都兼容 OpenAI 风格 API。用 LiteLLM 等于让自己的代码天然呆在所有生态的连接器里。而且网关模式下它对外暴露的也是 OpenAI 兼容接口,这意味着你项目中已存在的 OpenAI SDK 客户端,只需要改一下 base_url 就能接入,不需要引入任何新的客户端依赖。

需要提醒的是,LiteLLM 不是把所有模型的能力都缩减成最小公分母。对于各家特有的高级能力,它也在不断补齐,但如果你依赖某个品牌刚发布、只在官方 SDK 里出现的独家参数,那就要先在 LiteLLM 的文档里确认支持情况,别默认"有手就行"。

2. SDK 模式实际用起来的样子

2.1 最小调用代码与模型热切换

最基础的使用方式就是直接当普通库调用。安装很简单,SDK 模式一个 pip 命令就好:

pip install litellm

然后设置环境变量,比如OPENAI_API_KEY、ANTHROPIC_API_KEY、GEMINI_API_KEY,LiteLLM 会按 model 前缀自动读取对应的密钥。写一个最小调用:

from litellm import completion response = completion( model="gpt-4o", messages=[ {"role": "system", "content": "你是一个专业的中文写作助手。"}, {"role": "user", "content": "帮我写一段关于数据可视化的简介。"} ], temperature=0.7 ) print(response.choices[0].message.content)

这段代码的返回值结构和 OpenAI SDK 几乎一样,choices[0].message.content直接拿文本,response.usage拿 token 用量。真正让我觉得舒服的是换模型成本极低,把 model 改成:

response = completion( model="claude-3-5-sonnet-latest", messages=messages, )

别的代码一行不用动。做模型对比评测的时候,这个特性省下的时间非常可观。如果不带提供商前缀,比如直接写gpt-4o或claude-3-5-sonnet-latest,LiteLLM 会根据内置的模型注册表自动推断供应商,大多数知名模型都能正确猜到。但我的建议是生产环境务必写全前缀,让路由意图显式化,后面排查问题能少一层猜测。

2.2 流式、异步与并发场景

流式输出是大模型应用最常用的形态。LiteLLM 的流式接口依然向 OpenAI 靠拢:

response = completion( model="gpt-4o", messages=messages, stream=True ) for chunk in response: if chunk.choices[0].delta.content: print(chunk.choices[0].delta.content, end="")

流式场景里有个容易忽略的点:不同服务商的流式协议差异很大,LiteLLM 在 SDK 层统一成了 OpenAI 的 chunk 结构,但"流中途报错"的表现不一定完全一致。比如某些供应商在流式传输中断时会先发一个正常 chunk 再掐断连接,这会导致你的业务层看到的是一次"成功结束"而非错误。对这种行为,我在后面高可用章节会说处理思路。

异步调用也一样,把方法名加个 a 就行:

from litellm import acompletion import asyncio async def main(): response = await acompletion( model="gpt-4o", messages=messages ) return response asyncio.run(main())

生产环境处理批量任务时,我通常用asyncio.Semaphore控制并发:

sem = asyncio.Semaphore(10) async def safe_completion(**kwargs): async with sem: return await acompletion(**kwargs)

如果不做并发控制,脚本一跑起来很容易把供应商的 rate limit 打满,还得回头处理一堆限流报错。LiteLLM 自带重试能力,但重试并不意味着你可以无节制地开并发。

2.3 工具调用和多模态传参

工具调用是 Agent 应用的重点。LiteLLM 同样沿用了 OpenAI 的 tools 参数:

tools = [ { "type": "function", "function": { "name": "get_weather", "description": "查询指定城市的天气", "parameters": { "type": "object", "properties": { "city": {"type": "string", "description": "城市名称"} }, "required": ["city"] } } } ] response = completion( model="gpt-4o", messages=messages, tools=tools )

多模态输入则用 content 列表的方式传图:

response = completion( model="gpt-4o", messages=[ { "role": "user", "content": [ {"type": "text", "text": "这张图里有什么信息?"}, {"type": "image_url", "image_url": {"url": "https://example.com/test.png"}} ] } ] )

这里有个实操经验:别默认所有模型都支持所有模态。你把图片按 OpenAI 格式传给某个纯文本模型,LiteLLM 可能不会报错,但对端会直接忽略图片部分或者返回异常。动手前先确认目标模型的模态支持情况,在代码里按模型维度做一次参数校验,比事后排查诡异输出省心得多。

3. 网关模式:把密钥和路由收回到服务端

3.1 一条 config.yaml 定义全部模型

SDK 模式适合个人项目和单服务场景,一旦涉及团队协作、多人共用密钥,就要上网关模式了。LiteLLM 的代理网关是一个独立服务,你用一份 config.yaml 声明要暴露哪些模型,然后所有客户端只对着网关发请求,真正的上游密钥都留在服务端。

model_list: - model_name: gpt-4o litellm_params: model: openai/gpt-4o api_key: os.environ/OPENAI_API_KEY - model_name: claude-3-5-sonnet litellm_params: model: anthropic/claude-3-5-sonnet-latest api_key: os.environ/ANTHROPIC_API_KEY - model_name: gemini-1.5-pro litellm_params: model: gemini/gemini-1.5-pro api_key: os.environ/GEMINI_API_KEY litellm_settings: num_retries: 3 request_timeout: 600

这里model_name是暴露给调用方的别名,litellm_params.model才是实际上游模型。为什么要分两层?因为你可以用别名对外屏蔽真实模型细节,比如model_name: flagship映射到openai/gpt-4o,将来换更强的模型,改配置就好,客户端完全感知不到。这是网关模式一个很实用的工程价值。

启动前先用测试命令验证配置:

litellm --config config.yaml --test

它会逐条尝试配置里的模型,能通就输出成功信息,不通会给出错误原因。我每次改完配置都会跑一遍,比直接重启服务再发请求试错高效得多。

3.2 虚拟密钥与预算绑定

网关模式的核心管理对象是虚拟密钥。服务端通过主密钥管理一切,然后为每个成员或每个应用生成一把 sk- 开头的虚拟密钥:

curl -X POST http://localhost:4000/key/generate \ -H "Authorization: Bearer sk-my-master-key" \ -H "Content-Type: application/json" \ -d '{ "models": ["gpt-4o", "claude-3-5-sonnet"], "max_budget": 50, "budget_duration": "30d", "metadata": {"team": "simulation-project-a"} }'

这把虚拟密钥只能调用你指定的模型,额度是 50 美元、30 天有效。它背后对应某个真实上游密钥,但对调用方完全透明。好处很明显:同事拿到手的不是你的 OpenAI 主密钥,泄露了也只是泄露一张受限的卡;某个应用跑了异常消耗,你可以精确到是哪个 key 在烧钱。

预算维度还可以做到用户级、团队级。团队成员用同一个 key 也能区分到个人,管理层能看到"谁在用模型、花了多少钱",这在大组织里几乎是刚需。配置了 Postgres 之后,这些 key 和用量数据才有持久化保障,本地测试用 SQLite 可以,生产环境我强烈建议直接用 Postgres。

3.3 Docker 部署和现有应用的无缝衔接

网关部署最省事的方式是 Docker:

docker run -d --name litellm-proxy \ -p 4000:4000 \ -e LITELLM_MASTER_KEY=sk-my-master-key \ -e DATABASE_URL=postgresql://user:pass@host:5432/litellm \ -v $(pwd)/config.yaml:/app/config.yaml \ ghcr.io/berriai/litellm:main-latest \ --config /app/config.yaml

启动后用两个健康检查端点确认状态:

curl http://localhost:4000/health/liveliness curl http://localhost:4000/health/readiness

最让人心动的是接入成本。已有使用 OpenAI SDK 的应用,改为走 LiteLLM 网关只需要换 base_url:

from openai import OpenAI client = OpenAI( api_key="sk-虚拟密钥", base_url="http://localhost:4000" ) resp = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "你好"}] )

代码里没有引入任何 LiteLLM 依赖,却拿到了网关的密钥管理、多模型路由和成本统计能力。网关还提供一个/models端点,客户端可以随时查看当前可用的模型列表,配合 OpenAI 官方的模型列表动态发现机制,体验很顺。

4. 高可用配置:重试、兜底和负载均衡

4.1 重试与超时,以及哪些错误值得重试

大模型 API 的可用性再高,也架不住网络抖动、供应商过载和限流。LiteLLM 内置了重试机制,通过litellm_settings配置:

litellm_settings: num_retries: 3 request_timeout: 600 retry_after: 5

num_retries是重试次数,retry_after是重试间隔的基准值,内部会做指数退避。单独调用时也可以在completion()里传timeout参数。这里有一个经验:不是所有错误都值得重试。401 鉴权失败重试一万次也没用,那是密钥配置问题;429 和 5xx 才值得重试,因为大概率是暂时性过载。LiteLLM 的默认重试逻辑主要面向这些可恢复错误,但你在自己的业务代码里也应该加一道判断:错误类型决定要不要重试,而不是闭着眼睛 for 循环。

网关模式下,你可以通过/health/readiness这种探活端点接入容器编排系统的健康检查,让无状态实例在异常时被自动替换。LiteLLM 网关本身无状态(状态都存在数据库),这让横向扩容变得很简单。

4.2 兜底模型与多部署轮询

比重试更高级的是兜底切换。比如主模型用 GPT-4o,一旦请求失败或超时,自动切到 Claude 或 Gemini:

litellm_settings: fallbacks: [ {"gpt-4o": ["claude-3-5-sonnet-latest", "gemini/gemini-1.5-pro"]} ]

SDK 模式也能用:

response = completion( model="gpt-4o", messages=messages, fallbacks=["claude-3-5-sonnet-latest", "gemini/gemini-1.5-pro"] )

这个能力带来的不只是容灾,还有成本策略空间。比如把某个高成本模型设为优先,兜底的是低成本模型,在非核心链路上能省不少钱。反过来也可以把低价模型设为主力,兜底是高价强模型,保证体验下限。

负载均衡方面,同一模型可以配置多个上游部署,网关按策略分发请求:

model_list: - model_name: gpt-4o litellm_params: model: openai/gpt-4o api_key: os.environ/OPENAI_API_KEY model_info: id: deployment-1 - model_name: gpt-4o litellm_params: model: openai/gpt-4o api_key: os.environ/OPENAI_API_KEY_2 model_info: id: deployment-2

多个同名校验互相留作备份,配合 round-robin 或 least-busy 策略,即使某个 key 被限流也能自动切换。流式场景要格外注意:如果流已经开始传输,中途报错往往是没法触发兜底的,因为网关已经向下游吐了一部分内容。对流式任务,我一般会在客户端做"收到完整结果才算成功"的校验,必要时整段重试,而不是把希望全押在服务端兜底上。

5. 成本与可观测性:每一分钱都要有据可查

5.1 单次请求的成本核算

大模型项目最容易被忽视的问题就是成本失控。LiteLLM 在成本计量上下过功夫,每次请求后可以直接拿到金额:

from litellm import completion_cost response = completion(model="gpt-4o", messages=messages) cost = completion_cost(completion_response=response) print(f"本次请求成本: ${cost:.4f}")

它的背后是一份内置的价格清单,涵盖了各家模型最新的 token 单价。每次调用按输入/输出 token 分别计价,再汇总成总成本。这个能力对做模型性价比评测尤其有用——同样的任务,不同模型跑一遍,成本差异一目了然。

自建或私有化模型没法用内置清单,可以自定义计价:

model_list: - model_name: my-local-llama litellm_params: model: ollama/llama3 model_info: custom_pricing: true input_cost_per_token: 0.000001 output_cost_per_token: 0.000002

设好之后,网关依然会按同样的统计口径给你算钱。把内部成本核算统一到同一条体系里,财务报表和优化决策就都有据可依了。

5.2 预算、限额和告警

前面提到的虚拟密钥,最核心的价值就是预算控制。max_budget定义上限,budget_duration定义周期,还能设置单次请求最大预算之类的更细粒度限制。用户级和团队级预算则是更上层的管理维度,适合给不同项目组划独立额度。某个组额度烧完了,不会影响其他组。

这些预算数据要真实起作用,前提是把用量数据落库。网关会把每次调用的 token 数、费用、模型、耗时都记录到数据库里,通过/spend相关端点可以按 key、按用户、按天维度拉取明细。没有这套记录,所谓的成本管控就是纸上谈兵。

告警方面,LiteLLM 支持通过回调把异常和预算变化推送到内部 IM 工具,例如配置alerting后,超额、连续失败、限流频繁等事件都能自动通知到人。我们团队的实际做法是:预算阈值的 50%、80%、100% 各设一道告警,前两道是提醒,第三道直接触发人工介入。

5.3 日志、审计与指标采集

可观测性是网关模式相对 SDK 模式的最大优势。LiteLLM 通过回调机制对接外部系统:

litellm_settings: success_callback: ["prometheus", "langfuse"] failure_callback: ["prometheus"]

配置 Prometheus 之后,网关会暴露/metrics端点,请求延迟、token 用量、错误率都可以直接配置到监控大盘上。Langfuse 等链路追踪工具则能帮你把一次业务请求从进入网关到模型返回的完整过程串起来,非常适合排查"为什么这个回答这么慢"这类问题。

还有一点容易被忽略的是审计日志。网关模式涉及密钥创建、预算修改、模型配置变更这类敏感操作,打开audit_logs后,谁在什么时间做了什么操作都有记录。团队规模一大,没有审计日志,出了事故都定位不到责任人。这些能力单靠各家 SDK 很难凑齐,也是我推荐团队场景用网关模式的主要原因。

6. 实战中踩过的坑,以及什么场景别用它

6.1 model 字符串的前缀与别名陷阱

我踩过最隐蔽的坑是 model 字符串的前缀。SDK 模式下,不带前缀的知名模型名通常能自动识别;但在网关 config 里,litellm_params.model必须带着正确前缀,否则路由经常解析失败。比如某个部署里我写的是model: gpt-4o,网关会尝试猜测供应商,猜错就报模型不存在。后来统一改成openai/gpt-4o,问题消失。

另一个坑是虚拟密钥的模型限制。虚拟密钥生成时指定的models列表,对应的是 config 里的model_name别名,而不是上游模型名。如果你给调用方一把只能用gpt-4o的虚拟密钥,但系统里同时存在gpt-4o和gpt-4o-mini的别名,调用方以为自己在用同一个模型,实际可能因为别名不一致而请求失败。建议把别名命名规范写进团队文档,比如统一用provider-model的格式,从源头避免歧义。

6.2 版本迭代快,配置漂移防不胜防

LiteLLM 的迭代速度很快,新模型、新参数、新配置项几乎周周有更新。好处是能力跟得紧,坏处是旧配置可能无声无息失效。我就遇到过某个版本升级后,原先的 fallback 配置格式不再被识别,但服务不报错,只是兜底不再触发,直到线上故障才发现。从那以后我养成了一个习惯:升级版本前先看 release notes,升级后必须执行litellm --config config.yaml --test,然后跑一轮包含流式、非流式、工具调用的冒烟用例。

还有一点是生产环境别追最新版。LiteLLM 的 main-latest 镜像适合本地体验,线上部署我会锁定一个经过验证的版本号,等到有明确的升级收益再统一迁移。很多团队线上出问题,往往不是功能不够,而是版本漂移得太快,文档和实际行为对不上。

6.3 我的选型建议与上手路径

说了这么多,也得泼点冷水:不是所有场景都需要 LiteLLM。如果你的项目只有一个模型、一个供应商,直接用官方 SDK 是最简单可靠的选择,引入任何中间层都是过度设计。如果你大量依赖某家供应商的独家能力,比如最新发布的非标准参数,那也应该先用官方 SDK 验证可行性,再考虑怎么接入 LiteLLM。

需要它的信号其实很明确:你的代码里开始出现第二种模型供应商的 SDK,或者团队里有超过三个人需要共享模型密钥,或者你对成本核算有了横跨多模型的需求。满足任意一条,LiteLLM 的价值就体现出来了。

如果让我给一个最朴素的上手路径,还是那句话:先在本地pip install litellm,把你现有 OpenAI SDK 的那段请求改成 LiteLLM 调用,跑通后再把另一个模型加进来做个切换对比。当你发现配置多模型真的只是改字符串的时候,你对这个项目的理解就到位了。等密钥要分给同事时才上代理,一个 Docker 容器的事,把之前的代码 base_url 指过去就行。这两步走完,你就会知道 LiteLLM 在自己的技术栈里到底该站在哪个位置。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询