1. 从一个真实困境说起:多模型调用为什么让人头疼
如果你正在做AI应用开发,大概率遇到过这样的场景:产品需要同时接入好几个不同厂商的大模型,有的擅长长文本理解,有的在代码生成上表现突出,还有的性价比高适合做批量任务。于是你的代码里开始出现各种SDK的初始化逻辑、不同的鉴权方式、五花八门的请求参数格式、各不相同的返回结构。每接一个新模型,就要翻一遍文档、改一遍调用层、测一遍异常处理。
更麻烦的是,当你想把某个任务从A模型切换到B模型做效果对比时,发现两者的调用方式完全不兼容,光是适配层就得写半天。如果团队里还有其他人维护同一套代码,风格不统一带来的沟通成本更是雪上加霜。
这就是litellm要解决的核心问题。它本质上是一个统一的大模型调用中间层,用一套OpenAI风格的接口去调用市面上几乎所有主流模型服务。你不需要为每个厂商单独写适配代码,只需要改一个模型名称字符串,就能在多个模型之间自由切换。对于需要做模型对比、多模型路由、成本优化的团队来说,这个价值非常直接。
这篇文章我会从实际使用角度出发,把litellm的安装配置、核心用法、代理服务部署、成本追踪、踩坑经验完整讲一遍。不管你是刚接触大模型API调用的新手,还是已经在生产环境里维护多模型接入的老手,应该都能找到有用的东西。
2. litellm到底做了什么:统一接口的底层逻辑
2.1 它解决的不是"调用"问题,而是"适配"问题
很多人第一次看到litellm的介绍,会觉得"不就是个封装库吗"。但真正用起来才发现,它做的事情比简单封装要深得多。
大模型API调用看起来简单——发个HTTP请求,传几个参数,拿回结果。但实际工程中,不同厂商之间的差异远比想象中大。举几个例子:OpenAI的对话接口用messages数组,某些厂商用prompt字符串;OpenAI的返回结构里内容在choices[0].message.content,有的厂商放在output.text;流式返回的SSE格式各家也不完全一致;函数调用(Function Calling)的参数定义方式更是千差万别。
litellm的做法是在中间做一层标准化映射。它定义了一套统一的输入输出格式(基本对齐OpenAI的规范),然后在内部为每个支持的模型服务实现对应的转换逻辑。你传入统一格式的请求,它负责翻译成目标厂商需要的格式,拿到响应后再翻译回统一格式返回给你。
这意味着你的业务代码只需要面向一套接口编程,新增模型支持时只需要改配置,不需要动业务逻辑。这个设计思路在工程上叫做"适配器模式",但litellm把它做到了足够广的覆盖面。
2.2 支持的模型范围与调用方式
litellm目前支持的模型服务商覆盖了国内外主流选择,包括OpenAI系列、Anthropic系列、Google系列、Meta系列、Mistral系列,以及国内多家厂商的模型服务。此外它还支持通过Ollama调用本地部署的模型,通过HuggingFace接入开源模型。
调用方式极其简洁。核心就是completion函数:
from litellm import completion response = completion( model="gpt-4o", messages=[{"role": "user", "content": "用一句话解释什么是递归"}] ) print(response.choices[0].message.content)如果要切换到另一个模型,只需要改model参数:
response = completion( model="claude-sonnet-4-20250514", messages=[{"role": "user", "content": "用一句话解释什么是递归"}] )代码结构完全不变。这就是litellm最核心的价值——用字符串切换模型,用统一接口屏蔽差异。
2.3 模型命名规则与自动路由
litellm的模型命名有一套约定:provider/model-name的格式。比如openai/gpt-4o、anthropic/claude-sonnet-4-20250514、ollama/llama3。如果你只写模型名不写前缀,litellm会根据内置的映射表自动推断服务商。
这个自动推断在大多数情况下够用,但在一些边界场景下可能猜错。我的建议是始终显式写全前缀,避免因为模型名冲突导致路由到错误的服务商。比如某些厂商的模型名称和OpenAI的很像,不写前缀就容易出问题。
另外,litellm支持通过环境变量配置各家的API Key。比如OPENAI_API_KEY、ANTHROPIC_API_KEY等,它会自动读取。这样你就不需要在代码里硬编码密钥,部署时通过环境变量注入即可。
3. 环境搭建与基础调用:从零跑通第一个请求
3.1 安装与依赖管理
litellm的安装非常轻量:
pip install litellm如果你需要用到代理服务功能(后面会详细讲),可以安装带proxy的完整版:
pip install 'litellm[proxy]'需要注意的是,litellm本身依赖不算多,但它会按需引入各厂商的SDK。如果你只用OpenAI,不会安装Anthropic的包。这种按需加载的设计对部署体积比较友好。
我建议在虚拟环境里安装,避免和系统Python环境里的其他包产生版本冲突。特别是如果你同时用着其他AI框架,依赖冲突的概率不低。
3.2 API Key配置的几种方式
配置密钥有三种常见方式,各有适用场景:
第一种是环境变量,适合本地开发和容器化部署:
export OPENAI_API_KEY="your-key-here" export ANTHROPIC_API_KEY="your-key-here"第二种是代码里直接传参,适合快速测试:
response = completion( model="openai/gpt-4o", messages=[{"role": "user", "content": "你好"}], api_key="your-key-here" )第三种是通过配置文件,适合代理服务模式。这个后面讲代理部署时再展开。
注意:无论用哪种方式,都不要把密钥硬编码到提交到代码仓库的文件里。我见过太多因为密钥泄露导致账单暴涨的案例,这个坑一定要避开。
3.3 第一个完整调用示例
跑通一个最小可用示例,需要确认三件事:模型名写对、密钥配好、网络能通。下面是一个完整的测试脚本:
import os from litellm import completion os.environ["OPENAI_API_KEY"] = "your-key-here" try: response = completion( model="openai/gpt-4o", messages=[ {"role": "system", "content": "你是一个简洁的助手"}, {"role": "user", "content": "用三句话介绍Python的装饰器"} ], temperature=0.7, max_tokens=500 ) print(response.choices[0].message.content) print(f"本次消耗token: {response.usage.total_tokens}") except Exception as e: print(f"调用失败: {e}")这段代码里几个关键点值得说明。temperature控制输出的随机性,0.7适合大多数对话场景;max_tokens限制输出长度,防止意外产生超长响应导致费用失控;response.usage里包含了token消耗统计,这是做成本追踪的基础数据。
3.4 流式输出的处理方式
对话类应用通常需要流式输出,让用户看到逐字生成的效果。litellm的流式调用也很直接:
response = completion( model="openai/gpt-4o", messages=[{"role": "user", "content": "写一首关于秋天的短诗"}], stream=True ) for chunk in response: content = chunk.choices[0].delta.content if content: print(content, end="", flush=True)这里有个细节要注意:流式返回的每个chunk里,delta.content可能是None(比如第一个chunk通常只包含role信息)。不做判空处理会报错。另外流式模式下usage统计默认不返回,需要显式设置stream_options={"include_usage": True}才能拿到token消耗数据。
4. 代理服务模式:把litellm变成团队共用的网关
4.1 为什么需要代理模式
如果你只是个人项目里用litellm,直接import调用就够了。但如果是团队协作场景,代理模式的价值就体现出来了。
代理模式是把litellm跑成一个独立的HTTP服务,对外暴露OpenAI兼容的API接口。团队成员不需要在各自的项目里安装litellm、配置各家密钥,只需要把请求指向这个代理服务就行。密钥统一在服务端管理,调用日志统一记录,成本统一追踪,模型列表统一维护。
这个架构特别适合以下场景:多个项目共用多个模型服务、需要统一管控API密钥、需要做调用审计和成本分摊、需要在不改业务代码的前提下切换底层模型。
4.2 配置文件的结构与关键字段
代理服务的核心是一个YAML配置文件。下面是一个覆盖常见需求的配置示例:
model_list: - model_name: gpt-4o litellm_params: model: openai/gpt-4o api_key: os.environ/OPENAI_API_KEY - model_name: claude-sonnet litellm_params: model: anthropic/claude-sonnet-4-20250514 api_key: os.environ/ANTHROPIC_API_KEY - model_name: local-llama litellm_params: model: ollama/llama3 api_base: http://localhost:11434 general_settings: master_key: sk-your-master-key database_url: os.environ/DATABASE_URLmodel_list定义了对外暴露的模型名称和实际路由目标。注意model_name是对外名称,客户端请求时用的就是这个;litellm_params.model是实际的服务商标识。两者可以不同,这给了你一层抽象——客户端看到的是gpt-4o,你可以在后端随时把它指向不同的实际模型。
general_settings里的master_key是代理服务的访问密钥,客户端需要带上这个key才能调用。database_url用于持久化调用日志和成本数据,支持PostgreSQL等数据库。
4.3 启动服务与客户端接入
启动代理服务:
litellm --config config.yaml --port 4000启动后,代理服务监听4000端口,提供OpenAI兼容的接口。客户端调用方式:
from openai import OpenAI client = OpenAI( api_key="sk-your-master-key", base_url="http://localhost:4000" ) response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "你好"}] )注意这里用的是标准OpenAI SDK,只是把base_url指向了代理服务。这意味着任何已经用OpenAI SDK的项目,只需要改一个base_url就能接入,迁移成本几乎为零。
4.4 多实例部署与负载均衡
生产环境下,单个代理实例可能扛不住高并发。litellm支持在配置里为同一个model_name配置多个后端实例,自动做负载均衡:
model_list: - model_name: gpt-4o litellm_params: model: openai/gpt-4o api_key: os.environ/OPENAI_API_KEY_1 - model_name: gpt-4o litellm_params: model: openai/gpt-4o api_key: os.environ/OPENAI_API_KEY_2两个配置项的model_name相同,litellm会自动在它们之间轮询。这既能分摊并发压力,也能在某一个key触发限流时自动切换到另一个。
实操心得:负载均衡策略可以通过
router_settings调整,支持轮询、加权、最低延迟等多种模式。如果你的多个后端延迟差异大,用最低延迟模式体验会好很多。
5. 成本追踪与预算控制:让每一分钱都花得明白
5.1 token统计的获取方式
成本控制的前提是能准确统计消耗。litellm在每次调用的响应里都会返回usage信息:
response = completion( model="openai/gpt-4o", messages=[{"role": "user", "content": "你好"}] ) print(response.usage.prompt_tokens) # 输入token数 print(response.usage.completion_tokens) # 输出token数 print(response.usage.total_tokens) # 总token数对于流式调用,需要显式开启usage统计:
response = completion( model="openai/gpt-4o", messages=[{"role": "user", "content": "你好"}], stream=True, stream_options={"include_usage": True} )最后一个chunk里会包含完整的usage数据。这个细节很容易被忽略,导致流式场景下成本统计缺失。
5.2 内置成本计算与自定义价格
litellm内置了主流模型的价格表,可以自动把token消耗换算成费用:
from litellm import completion_cost response = completion( model="openai/gpt-4o", messages=[{"role": "user", "content": "你好"}] ) cost = completion_cost(completion_response=response) print(f"本次调用费用: ${cost:.6f}")如果你用的是自定义部署的模型,或者内置价格表没有覆盖的模型,可以在配置里手动指定价格:
model_list: - model_name: my-custom-model litellm_params: model: openai/my-model input_cost_per_token: 0.000001 output_cost_per_token: 0.000002这个功能在做成本对比时特别有用。你可以用同一批请求跑不同模型,直接对比费用差异,为选型提供数据支撑。
5.3 预算限制与告警配置
代理模式下可以设置预算上限,超过阈值自动拒绝请求:
general_settings: max_budget: 100 budget_duration: 30d这表示30天内总预算100美元,超出后拒绝新请求。对于团队共用场景,还可以按用户或按key设置独立预算,避免某个项目把整体额度用光。
注意:预算控制是"硬限制",触发后请求会直接失败。生产环境建议配合告警使用,在接近阈值时提前通知,而不是等到请求被拒才发现。
6. 踩坑实录:那些文档里不会写的细节
6.1 模型名称写错导致的诡异报错
最常见的问题就是模型名写错。litellm的报错信息有时候不够直观,比如你把anthropic/claude-sonnet-4-20250514写成了anthropic/claude-sonnet-4,可能得到一个"model not found"的错误,但不会告诉你正确的名称是什么。
我的做法是先用litellm提供的模型列表接口确认可用名称:
from litellm import model_list print(model_list)或者在代理模式下访问/models接口获取完整列表。养成先查再写的习惯,能省下不少排查时间。
6.2 超时与重试的默认行为
litellm默认有重试机制,但默认参数不一定适合所有场景。我遇到过因为默认超时太短,长文本生成任务频繁失败的情况。解决办法是显式设置超时和重试次数:
response = completion( model="openai/gpt-4o", messages=[{"role": "user", "content": "写一篇2000字的文章"}], timeout=120, num_retries=3 )timeout单位是秒,长文本任务建议设大一些。num_retries控制失败重试次数,但要注意重试会重复计费,对于已经产生部分输出的请求要谨慎。
6.3 不同模型对参数的支持差异
虽然litellm做了统一接口,但不同模型对参数的支持程度不同。比如某些模型不支持temperature,某些不支持top_p,某些对max_tokens有上限要求。传入不支持的参数时,有的厂商会报错,有的会静默忽略。
litellm的做法是尽量做参数映射,但无法保证100%兼容。我的经验是:先用最小参数集跑通,再逐步添加参数测试。如果某个参数在切换模型后报错,先去掉它确认问题来源。
6.4 代理模式下的并发瓶颈
代理服务默认是单进程运行的。在高并发场景下,可能会成为瓶颈。解决办法是用多worker模式启动:
litellm --config config.yaml --port 4000 --num_workers 4或者用gunicorn等WSGI服务器托管。具体worker数量根据CPU核心数和请求特征调整。IO密集型的场景可以多开一些,CPU密集型的则要控制数量避免上下文切换开销。
7. 进阶玩法:路由策略、缓存与可观测性
7.1 基于规则的路由策略
litellm支持根据请求内容自动路由到不同模型。比如根据问题长度、关键词、用户等级等条件选择模型。这个功能在成本优化上很有价值——简单问题用便宜模型,复杂问题用强模型。
配置方式是在model_list里定义路由规则,或者在代码里用Router类做更灵活的控制。实际使用中,我建议先从简单的场景开始,比如按token长度分流,验证效果后再逐步细化规则。
7.2 响应缓存减少重复调用
对于重复性高的请求(比如相同的FAQ问答),开启缓存可以显著降低成本。litellm支持内存缓存和Redis缓存两种模式:
litellm_settings: cache: true cache_params: type: redis host: localhost port: 6379缓存命中时直接返回历史结果,不产生API调用费用。但要注意缓存的有效期设置——对于时效性强的场景(比如查询实时数据),缓存时间要设短一些。
7.3 调用日志与可观测性
代理模式下,所有调用都会记录到数据库。可以查询每次调用的模型、token消耗、费用、延迟、状态等信息。这些数据对于排查问题、优化成本、分析使用模式都很有价值。
如果团队有现成的可观测性平台,litellm也支持把日志推送到外部系统。具体集成方式参考官方文档的callback配置部分。
8. 我个人的使用建议
用litellm有一段时间了,最大的感受是它确实解决了多模型接入的痛点,但也不是银弹。几点体会分享出来:
第一,不要为了统一而统一。如果你的项目只用一家模型服务,直接用它官方SDK可能更简单。litellm的价值在多模型场景下才体现得明显。
第二,代理模式适合团队,直调模式适合个人。团队协作时代理模式的统一管理优势很大,但个人项目直接import更轻量。
第三,成本追踪要尽早做。很多人是等到账单出来才发现费用超预期。litellm的usage统计和成本计算功能用起来不复杂,建议一开始就接上。
第四,版本升级要谨慎。litellm迭代很快,新版本可能引入不兼容变更。生产环境升级前先在测试环境验证,确认关键功能正常再上线。
第五,配置文件要纳入版本管理。代理模式的YAML配置是核心资产,模型列表、路由规则、预算设置都在里面。用Git管理起来,变更可追溯,回滚也方便。
最后说一个实际使用中的小技巧:如果你需要频繁在多个模型之间做效果对比,可以写一个简单的批量测试脚本,用同一组prompt跑不同模型,把结果和成本数据汇总成表格。这个脚本用litellm实现起来非常快,对于选型决策帮助很大。