1. 项目概述与核心定位
litellm 这个项目,第一次接触是在一个多模型混用的内部工具里。当时团队需要同时对接好几家不同厂商的大模型接口,每家的 SDK、鉴权方式、请求体结构、返回格式都不一样,代码里到处是 if-else 分支,维护起来非常头疼。后来有人推荐了 litellm,用下来确实解决了不少问题。简单来说,litellm 是一个统一的大模型调用层,它把不同厂商的 API 抽象成一套 OpenAI 兼容的接口,让你用同一套代码就能调用多种模型。
这个项目能做什么?核心就一件事:抹平不同大模型服务商之间的接口差异。你不需要为每个厂商单独写适配代码,也不需要记住每家的参数命名规则。litellm 提供了一层代理,对外暴露统一的接口格式,对内负责把请求翻译成各家能识别的形式,再把返回结果统一成标准格式。它解决的是多模型场景下的调用碎片化问题,适合需要频繁切换模型、做模型对比评测、或者构建多模型应用的开发者和团队。
从定位上看,litellm 不是模型本身,也不是推理框架,而是一个中间层。它的价值在于解耦:你的业务代码只依赖 litellm 的接口,底层换模型、加模型、删模型,业务代码基本不用动。这个思路和数据库领域的 ORM 有点像,你写一套代码,底层换数据库只需要改配置。litellm 做的就是大模型领域的这层抽象。
我最初以为它只是个简单的请求转发工具,深入用下来发现它的功能比想象中丰富:支持流式输出、函数调用、嵌入向量、图像生成、成本追踪、负载均衡、失败重试等。这些能力在实际项目中非常实用,尤其是成本追踪和失败重试,省了不少自己造轮子的时间。
2. 核心架构与设计思路拆解
2.1 为什么需要统一调用层
多模型混用的痛点,不实际做一遍很难体会。假设你的应用需要同时支持三家厂商的模型,每家都有自己的 Python SDK,鉴权方式不同,请求参数命名不同,返回结构也不同。更麻烦的是,有些厂商支持流式输出,有些支持函数调用,有些两者都支持但格式不一样。如果你在业务代码里直接调用各家 SDK,代码会变得非常臃肿,每加一个模型就要改一遍业务逻辑。
litellm 的设计思路是在业务代码和模型服务之间加一层抽象。业务代码只调用 litellm 的 completion 函数,传入模型名称和消息列表,litellm 内部根据模型名称路由到对应的厂商适配器,完成请求转换、发送、响应解析,最后返回统一格式的结果。这样业务代码就与具体厂商解耦了。
这个设计的好处很明显:第一,降低切换成本,换模型只需要改一个字符串;第二,统一功能接口,流式、函数调用、嵌入等能力在不同模型上表现一致;第三,便于集中管理,鉴权信息、重试策略、成本统计都可以在这一层统一处理。
2.2 适配器模式的具体落地
litellm 内部为每个支持的厂商实现了一个适配器,适配器负责三件事:请求转换、响应转换、异常映射。请求转换是把统一的 OpenAI 格式请求转成厂商特定的格式,比如某厂商可能用prompt而不是messages,适配器就负责这层翻译。响应转换是把厂商返回的结果转回 OpenAI 格式,保证业务代码拿到的数据结构一致。异常映射是把厂商特有的错误码转成统一的异常类型,方便上层统一处理。
这种适配器模式在工程上很常见,好处是新增厂商支持时不需要改动核心逻辑,只需要新增一个适配器类并注册到路由表里。我翻过它的源码,适配器的注册机制比较清晰,扩展起来不算复杂。
2.3 代理模式与 SDK 模式的选择
litellm 提供两种使用方式:一种是作为 Python SDK 直接集成到代码里,另一种是作为独立代理服务运行。这两种方式的选择取决于你的场景。
SDK 模式适合单体应用或者脚本工具,直接pip install litellm然后在代码里调用,简单直接。代理模式适合多服务共享的场景,你启动一个 litellm 代理服务,所有需要调用大模型的服务都通过 HTTP 请求访问这个代理,代理再转发到各家厂商。代理模式的好处是鉴权信息集中管理,不需要每个服务都配置一遍 API Key,而且可以做统一的限流、日志、成本统计。
我个人的经验是,如果是个人项目或者小团队内部工具,SDK 模式足够了;如果是多服务架构或者需要给多个团队共享,代理模式更合适。代理模式还有一个好处是语言无关,任何能发 HTTP 请求的语言都可以调用,不限于 Python。
3. 核心功能与实操要点解析
3.1 基础调用:从单模型到多模型切换
litellm 最基础的用法就是调用 completion 接口。你只需要指定模型名称和消息列表,它就会返回结果。模型名称的格式通常是厂商/模型名,比如openai/gpt-4、anthropic/claude-3-opus这样的形式。这种命名方式的好处是一眼就能看出用的是哪家的模型,同时也方便 litellm 路由到对应的适配器。
实际使用中,我建议把模型名称放在配置文件或环境变量里,而不是硬编码在代码中。这样切换模型时不需要改代码,只需要改配置。litellm 支持从环境变量读取 API Key,你只需要设置对应的环境变量,比如OPENAI_API_KEY、ANTHROPIC_API_KEY等,litellm 会自动读取。
这里有个细节需要注意:不同厂商的 API Key 环境变量名称不一样,litellm 的文档里有完整的对照表。我踩过的坑是,有些厂商的环境变量名称比较长,容易拼错,建议直接复制文档里的名称,不要凭记忆手写。
3.2 流式输出与异步调用
流式输出是大模型应用里很常见的需求,尤其是对话类产品,用户希望看到逐字输出的效果。litellm 支持流式输出,只需要在调用时设置stream=True,返回的就是一个迭代器,你可以逐块获取内容。
异步调用是另一个实用功能。litellm 提供了acompletion异步接口,配合asyncio可以并发调用多个模型。这在做模型对比评测时特别有用,你可以同时向多个模型发问,然后对比它们的回答。我实测下来,异步调用的性能提升很明显,尤其是需要调用多个模型时,总耗时取决于最慢的那个模型,而不是所有模型耗时之和。
需要注意的是,异步调用对代码结构有要求,如果你的代码是同步的,需要改成异步或者用线程池包装。另外,流式输出和异步调用可以组合使用,litellm 支持异步流式调用,返回一个异步迭代器。
3.3 函数调用与结构化输出
函数调用(Function Calling)是构建 Agent 类应用的基础能力。litellm 统一了不同厂商的函数调用接口,你只需要按照 OpenAI 的格式定义函数,litellm 会负责转换成各厂商支持的格式。不过这里有个现实问题:不是所有模型都支持函数调用,有些模型支持但格式有差异,litellm 会尽量抹平,但偶尔还是会有兼容性问题。
我的经验是,如果要用函数调用,优先选择原生支持较好的模型,比如 OpenAI 系列的模型。对于支持不完善的模型,litellm 可能会降级处理或者报错,这时候需要看具体错误信息来判断是模型不支持还是适配器的问题。
结构化输出是函数调用的一个变种,你定义一个 JSON Schema,让模型按照这个结构返回数据。这在信息抽取、数据整理等场景下很有用。litellm 支持通过response_format参数指定返回格式,但同样受限于底层模型的能力。
3.4 成本追踪与预算控制
成本追踪是我觉得 litellm 最实用的功能之一。每次调用完成后,litellm 会返回本次调用的 token 使用量和预估成本。你可以在回调函数里收集这些数据,做成本统计和预算控制。
成本计算的原理是:litellm 内置了各模型的价格表,根据输入和输出的 token 数量乘以对应的单价,得出本次调用的成本。需要注意的是,价格表可能不是实时更新的,如果厂商调价了,litellm 的价格表可能滞后。所以成本数据只能作为参考,不能作为财务依据。
预算控制可以通过设置max_budget参数实现,当累计成本超过预算时,litellm 会拒绝后续请求。这个功能在多团队共享代理服务的场景下很有用,可以防止某个团队过度使用导致成本失控。
4. 代理服务部署与配置实战
4.1 代理服务的启动与配置
litellm 代理服务的启动方式比较简单,安装后通过命令行工具启动,指定配置文件路径即可。配置文件是 YAML 格式,里面定义模型列表、鉴权信息、路由策略等。
配置文件的核心是model_list,每个条目定义一个模型,包括模型名称、实际调用的厂商模型、API Key 等。你可以给同一个底层模型定义多个条目,用不同的名称区分,比如一个用于高优先级请求,一个用于低优先级请求,配合不同的超时和重试策略。
代理服务启动后,会监听一个 HTTP 端口,对外暴露 OpenAI 兼容的接口。你的业务代码只需要把 base_url 指向代理服务地址,API Key 用代理服务配置的 Key,就可以像调用 OpenAI 一样调用其他模型了。
4.2 多模型路由与负载均衡
代理模式的一个优势是可以做多模型路由和负载均衡。你可以在配置文件里定义路由策略,比如按权重分配请求到不同的模型,或者按优先级依次尝试,前一个失败后自动切换到下一个。
负载均衡的典型场景是:你有多个相同模型的 API Key,想要轮询使用以分散限流压力。litellm 支持这种配置,你只需要在模型条目里配置多个 API Key,它会自动轮询。
失败重试是另一个实用功能。你可以配置重试次数和重试间隔,当某个模型调用失败时,litellm 会自动重试或者切换到备用模型。这个功能在网络不稳定或者厂商服务波动时很有用。我建议至少配置一次重试,并且设置合理的超时时间,避免请求长时间挂起。
4.3 鉴权与访问控制
代理服务支持多种鉴权方式,最简单的是静态 API Key,你在配置文件里定义一组 Key,客户端请求时带上其中一个即可。这种方式适合内部服务之间的调用,简单够用。
如果需要更细粒度的控制,litellm 支持基于 Key 的权限管理,你可以为每个 Key 设置可访问的模型列表、预算上限、速率限制等。这在多团队共享代理服务时很有用,可以防止某个团队越权访问或者过度使用。
需要注意的是,代理服务本身的安全配置很重要,如果暴露在公网,一定要配置鉴权,否则任何人都可以调用你的模型,消耗你的额度。我建议至少配置静态 API Key,并且定期轮换。
5. 常见问题与排查技巧实录
5.1 模型调用失败的排查思路
模型调用失败是最常见的问题,原因可能有很多种。我的排查顺序通常是:先看错误信息,确定是鉴权问题、参数问题还是网络问题;然后检查 API Key 是否正确配置,环境变量是否生效;接着确认模型名称是否正确,有些厂商的模型名称比较长,容易拼错;最后检查网络连通性,有些厂商的接口在国内访问可能不稳定。
litellm 的错误信息通常比较清晰,会告诉你具体是哪一步出了问题。如果错误信息不够明确,可以开启调试日志,litellm 支持设置日志级别,把详细的请求和响应信息打印出来。我调试的时候经常用这个功能,能看到实际发送的请求体和收到的响应体,定位问题很快。
5.2 流式输出的常见坑
流式输出有几个常见的坑。第一个是流式和非流式的返回结构不同,流式返回的是迭代器,每个块包含部分内容,你需要自己拼接。第二个是流式输出的结束标志,有些厂商会在最后一个块里带上结束原因,有些不会,你需要自己判断。第三个是流式输出的错误处理,流式过程中如果出错,错误可能在中途抛出,你需要捕获并处理。
我的建议是,如果业务不强制要求流式,优先用非流式,简单可靠。如果必须用流式,一定要做好错误处理和内容拼接,并且测试不同模型的流式行为,确保兼容。
5.3 成本统计不准确的处理
成本统计不准确通常有两个原因:一是价格表滞后,二是 token 计数方式不同。价格表滞后只能等 litellm 更新,或者自己维护一份价格表覆盖默认值。token 计数方式不同是因为不同厂商对 token 的定义有差异,同样的文本在不同模型上 token 数可能不一样,这是正常现象,只能作为参考。
如果成本统计对你很重要,建议定期核对厂商的账单和 litellm 的统计,发现偏差及时调整。另外,可以在回调函数里记录每次调用的详细信息,方便后续分析。
5.4 常见问题速查表
| 问题现象 | 可能原因 | 排查方法 |
|---|---|---|
| 调用返回鉴权错误 | API Key 未配置或配置错误 | 检查环境变量和配置文件 |
| 模型名称报错 | 模型名称拼写错误或格式不对 | 对照文档确认模型名称格式 |
| 流式输出中断 | 网络波动或厂商限流 | 检查网络,增加重试 |
| 成本统计为零 | 模型不在价格表中 | 检查模型名称是否在价格表覆盖范围 |
| 代理服务无法启动 | 端口被占用或配置格式错误 | 检查端口和 YAML 格式 |
| 函数调用不生效 | 模型不支持或格式不兼容 | 换用支持函数调用的模型 |
6. 个人实操心得与建议
用 litellm 有一段时间了,踩过一些坑,也总结了一些经验。首先,不要把它当成万能药,它解决的是接口统一的问题,不解决模型能力差异的问题。不同模型对同一个提示词的响应可能差别很大,切换模型后需要重新调优提示词。
其次,代理模式虽然方便,但增加了一层网络开销,对延迟敏感的场景要评估是否值得。如果只是单服务调用,SDK 模式更直接。代理模式更适合多服务共享或者需要集中管理的场景。
第三,成本追踪功能很实用,但不要完全依赖。我习惯在业务层也记录一份调用日志,包括模型名称、token 数、耗时等,方便后续分析和对账。litellm 的统计作为参考,业务层的日志作为依据。
第四,版本更新要关注,litellm 迭代比较快,新版本可能修复了旧版本的 bug,也可能引入新的不兼容改动。升级前建议在测试环境验证,确认没问题再上生产。
最后分享一个小技巧:如果你需要频繁切换模型做对比,可以写一个简单的封装函数,把模型名称作为参数传入,内部调用 litellm,这样切换模型只需要改一个参数。配合配置文件管理模型列表,维护起来会很轻松。这个项目后续还可以扩展的方向包括:自定义适配器支持内部模型、结合缓存层减少重复调用、集成监控告警等,根据实际需求逐步完善就好。