☰
Litellm实战:统一大模型API接入,搭建LLM网关与多模型路由
2026/10/14 8:59:21 网站建设 项目流程

我发现和很多团队聊大模型落地时,大家最头疼的往往不是模型能力不够强,而是同时接了太多家模型之后,代码里全是if-else适配逻辑。今天分享的这个开源项目,就是专门解决这类问题的:一个叫 litellm 的轻量级工具,核心功能就是把国内外主流大模型的API接入方式统一成一套规范,让调用端只面对一个接口,而底层想换哪家就换哪家。我用了快一年时间,从最初只是想省点事,到后来把它做成了自建的模型网关,整个过程踩了不少坑,也有些非常实用的心得,这篇文章就当作一次完整复盘,希望能给正在折腾多模型接入的朋友一些参考。

1. 先弄明白:litellm 到底在解决什么“痛点”

先说一个很典型的场景。某天老板跟你说:“现在这个聊天机器人用的模型太贵了,换个便宜点的试试。”你一听就知道麻烦来了。如果代码里直接调的是某一家厂商的官方SDK,那换模型就意味着改请求格式、改鉴权方式、改返回解析,运气不好还要逐个排查哪些字段不兼容。

这类痛点其实有几个共同点:第一,各家模型的API风格很不一样,有的用路径参数区分模型,有的要求自定义header,有的返回内容里藏着不同结构的元数据;第二,模型数量一多,密钥管理就变得棘手,谁用了哪个模型、花了多少钱很难统一统计;第三,一旦主力模型出故障或者被限流,想快速切到备用通道,完全没有现成机制。

litellm 的思路非常直接,它把大模型接入时的两个关键层级解耦了:一是面向调用端的接口层,二是面向模型提供方的适配层。调用端不需要关心背后连接的是哪家服务,只需要按照一套统一的格式发请求,剩下的映射、转发、鉴权、计费它全包了。再加上它本身支持以服务方式运行,团队里的其他人不需要装Python库,直接通过HTTP接口就能用,这就变成了一个真正意义上的模型网关。

我用下来最大的感受是,它不只是帮我在代码里省掉了几十个适配函数这么简单,更关键的是让“模型选型”这件事变得可以随时调整,而不是绑定在代码里。以前想比较两个模型的效果,要写两套调用逻辑,现在只需要改配置,几分钟就能出一组对比测试。这种灵活性,在快速迭代的AI应用开发中特别值钱。

市面上类似的方案不是没有,但大多要么绑定特定云平台,要么功能单一只做接口转换。litellm 把路由、负载均衡、成本追踪、流式转发这些都集成到了一起,才让它真正像个基础设施而不是一个小工具库。

2. 核心机制拆解:统一接口、路由策略和计费体系

2.1 统一接口是怎么设计的

要理解 litellm 的统一接口,最简单的方式是拿 OpenAI 的请求格式当基准。无论你用的是哪家模型,litellm 都会把请求转换成OpenAI风格的格式,再把各家返回的内容映射回OpenAI风格的结构。这样做的好处是生态兼容性极强,市面上绝大多数工具链、框架、开源项目都已经兼容OpenAI接口格式,你现有什么就能继续用什么。

比如在官方SDK里,调用一个模型的补全接口通常长这样:

from litellm import completion response = completion( model="gpt-3.5-turbo", messages=[ {"role": "user", "content": "你好,介绍一下你自己"} ], temperature=0.7 ) print(response.choices[0].message.content)

这段代码背后对应的是某家的模型,但当你把 model 换成其他模型时,比如改成某个开源模型的服务商接入,请求参数里那些共通的字段,比如 temperature、max_tokens、stream,litellm 会自动做转换。各家特殊的参数也保留,通过特定的键名透传,互不干扰。

类似地,文本嵌入模型的调用也是一套统一接口:

from litellm import embedding response = embedding( model="text-embedding-ada-002", input=["今天天气怎么样", "明天会下雨吗"] ) for item in response.data: print(len(item["embedding"]))

这种设计最省心的地方在于,业务代码可以一直保持稳定,模型变了,业务逻辑完全不用动。我见过很多团队迁移模型时因为返回字段结构差异导致线上报错,用 litellm 做中间层之后,这种无谓的返工几乎消失了。

2.2 路由、重试和故障转移的运作原理

litellm 提供的 Router 模块,相当于一个智能分发器。你可以预先设置多个模型通道,并给每个通道配置优先级、权重、最大并发。当请求进来时,Router 会根据这些配置决定发给哪个通道。实际使用中,最常见的是两类设置:优先使用性价比高的模型,当它不可用时自动切换到备用模型;或者在多个相同能力的模型之间做负载均衡。

看一下这段配置,就能很直观地理解工作方式:

from litellm import Router model_list = [ { "model": "gpt-4o-mini", "api_key": "sk-xxx1", "rpm": 1000, }, { "model": "claude-3-5-haiku", "api_key": "sk-xxx2", "rpm": 500, }, { "model": "gpt-4o-mini", "api_key": "sk-xxx3", "rpm": 300, }, ] router = Router(model_list=model_list) response = router.completion( model="gpt-4o-mini", messages=[{"role": "user", "content": "请给我一份周报总结"}] )

这里有个很实用的细节:同一个模型在列表里可以配置多个不同密钥,Router 会视为多条通道,自动分摊负载,这样能有效规避单密钥的速率限制。rpm 字段用来声明每分钟最大请求数,Router 在做分配时会把通道的剩余额度考虑进去,避免把某个通道打满。

故障转移也给了几种策略。最常见的是配置 fallback_models,当主模型调用失败时自动尝试备用模型:

response = router.completion( model="gpt-4o-mini", messages=[{"role": "user", "content": "生成一段产品描述"}], fallback_models=["claude-3-5-haiku", "gpt-3.5-turbo"] )

一旦主通道抛出异常或超时,Router 会立刻把请求转向下一个模型。背后有重试机制和退避策略,默认情况下会对限流错误和网络抖动做指数退避重试,避免连续故障造成二次雪崩。

从我自己的工程实践看,故障转移不是万能的,不是配了就永不报错。像字段级映射不兼容这种问题并不会触发路由切换,因为网关认为请求成功了。真正需要结合的效果是:在网关层做好通道健康监测,在应用层保留兜底逻辑,双层保险才稳妥。

2.3 预算、速率限制和成本追踪

模型网关一旦面向团队开放,成本和配额就是绕不开的问题。litellm 提供了以预算为中心的可观测体系,既能设定键级配额,也能按用户或项目维度限制,甚至可以在总预算耗尽时自动掐断服务。

比较常见的基础需要是三个:第一,限制某个项目的月度总花费;第二,限制某个用户每分钟调用次数;第三,追踪每个模型实际花了多少钱。litellm 内置了计费映射表,能自动根据模型、输入输出 token 数换算成本,不需要自己维护价格表。

代理模式下,这些能力可以直接通过接口暴露出来。比如设置预算上限并配置回调通知,当预算使用率超过某个阈值时发告警,这对有一定规模的团队很有价值。很多团队用了大模型后,成本完全不可见,月底对账才发现某条业务线烧掉了过多预算,这类问题如果从一开始就在网关层做了预算管控,是完全可以避免的。

2.4 嵌入模式与代理模式

litellm 有两种使用方式,需要先分清。嵌入模式就是前面演示的方式,在自己的 Python 进程里直接 import,API请求直接发出,不做额外的本地服务。这种方式适合个人项目、脚本、以及本身就在 Python 环境里跑的自动化流程。

代理模式则是在本地或服务器上跑一个独立的服务进程,所有请求都发到这个服务上,由它转发到真实模型厂商。代理模式有几个明显优点:第一,调用方可以是任何语言,只需支持 HTTP 和 OpenAI 兼容接口;第二,密钥和配置集中在网关一侧,不会散落在各个客户端;第三,可以在网关层统一做鉴权、限流、成本控制,不用每个调用方各自实现。

我怎么选这两种方式呢?如果是个人脚本、临时测试,嵌入模式最方便;如果是多个成员协作开发、产品要上线,强烈建议直接上代理模式。后期加个新模型,只需要在网关配置里加一条记录,客户端一行代码也不用改。想自建API网关服务的团队,其实用这个,低代码就能跑起来。

3. 快速部署:从零搭建一个自托管 LLM 网关

3.1 环境准备和安装

litellm 要求 Python 3.8 以上,建议直接用 Python 3.11 或更高版本,后面跑各种依赖更省心。安装方式很常规,直接通过 pip:

pip install 'litellm[proxy]'

如果安装时卡在一些编译步骤上,多半是当前环境缺少构建依赖,换个干净的 Python 环境一般能解决。也可以再用 docker 方式运行,官方镜像已经打包好了环境:

docker run \ -p 4000:4000 \ -v $(pwd)/litellm_config.yaml:/app/config.yaml \ ghcr.io/berriai/litellm:main-latest \ --config /app/config.yaml --port 4000

在本地环境验证安装是否成功:

python -c "import litellm; print(litellm.version)"

能看到版本号就说明装好了。接着要准备至少一家模型厂商的 API 密钥,最常见的做法是把密钥放到环境变量里,litellm 会自动读取标准方式命名的环境变量,比如某一家的密钥键一般以 OPENAI_API_KEY 形式命名。

3.2 配置 API 密钥与环境变量

在项目目录下创建一个 .env 文件,把各家密钥统一管理起来:

OPENAI_API_KEY=sk-your-openai-key ANTHROPIC_API_KEY=sk-your-anthropic-key AZURE_API_KEY=your-azure-key AZURE_API_BASE=https://your-resource.openai.azure.com AZURE_API_VERSION=2024-02-15-preview

启动代理服务时,加一个参数让它自动加载 .env 文件:

litellm --model gpt-3.5-turbo --port 4000

这样跑起来之后,本地 4000 端口就多了一个 OpenAI 风格接口,可用模型是 gpt-3.5-turbo。不过单个模型的代理配置很简单,实际生产环境我们会用 YAML 配置多个模型,一次配齐。

创建一个配置文件,比如 litellm_config.yaml:

model_list: - model_name: gpt-3.5-turbo litellm_params: model: gpt-3.5-turbo api_key: os.environ/OPENAI_API_KEY - model_name: claude-3-5-sonnet litellm_params: model: claude-3-5-sonnet api_key: os.environ/ANTHROPIC_API_KEY

用这份配置启动:

litellm --config /path/to/litellm_config.yaml --port 4000

需要留意的是,model_name 是暴露给外部调用方使用的别名,可以随意定义;而 litellm_params 里的 model 才是真正发往厂商的模型标识,不能乱填,要写厂商那边实际存在的模型名。

3.3 客户端库调用

代理起来了,验证一下是否正常工作。最简单的方式是用 OpenAI SDK 指向本地服务:

from openai import OpenAI client = OpenAI( api_key="任意字符串", base_url="http://localhost:4000" ) response = client.chat.completions.create( model="gpt-3.5-turbo", messages=[ {"role": "user", "content": "用一句话解释什么是递归"} ] ) print(response.choices[0].message.content)

这里 api_key 填什么都行,因为 litellm 默认在无网关密钥配置时不做强制校验。如果团队内部使用,建议开启虚拟密钥功能,把真实厂商密钥隐藏在网关后面,调用方只拿一个分配的子密钥,这样既能审计用量,又能及时吊销某个客户端的访问权。

代理模式的透明程度很高,原本直连厂商的代码,唯一的变化就是把 base_url 改成网关地址,其他完全不变。

3.4 代理服务器模式

不走 SDK,直接往 HTTP 接口发请求也能用。curl 测试一下:

curl --request POST \ --url http://localhost:4000/v1/chat/completions \ --header 'Authorization: Bearer sk-1234' \ --header 'Content-Type: application/json' \ --data '{ "model": "gpt-3.5-turbo", "messages": [ {"role": "system", "content": "你是一个耐心的老师"}, {"role": "user", "content": "请解释一下什么是大语言模型"} ] }'

返回结果和 OpenAI 官方结构一致。这套接口兼容性意味着,任何能调 OpenAI 接口的程序,都可以通过改 base_url 切换到自己的网关。

代理模式还有个很有价值的用途:给传统应用接入AI能力时,你不需要去改造那些已经写好的逻辑,只需在配置层面把请求转发到新地址,旧代码就像没改过一样照常运行。

4. 高级配置实测:多供应商回退、流式输出和成本控制

4.1 配置多供应商回退策略

把回退策略做进网关,是我认为值得优先落地的能力之一。线上业务最怕模型提供商故障导致服务不可用,而回退功能就是一道保险丝。

在代理配置里可以这样声明一个分组:

model_list: - model_name: primary-model litellm_params: model: gpt-4o api_key: os.environ/OPENAI_API_KEY - model_name: fallback-model litellm_params: model: claude-3-5-sonnet api_key: os.environ/ANTHROPIC_API_KEY router_settings: fallbacks: [ {"primary-model": ["fallback-model"]} ]

这段配置的意思很明确:当 primary-model 触发异常、限流或超时时,自动把请求转发给 fallback-model。我在实际生产中经历过一次某个模型通道持续超时的情况,当时主模型通道因为上游服务波动一直返回错误,回退机制立刻接管,客户端无感切换,整体业务没有中断。这种体验确实让人安心不少。

回退并不等同于零延迟,切换过程需要重新发起请求,会有额外消耗,所以回退模型最好选择能力相近但通道独立的服务,避免回退后性能明显下降。

4.2 流式输出和函数调用的注意点

大模型应用里流式输出几乎成了标配,聊天场景要一个字一个字地往外蹦才能有“在思考”的体验。litellm 对流式响应的处理非常直接,支持透传流式内容,无论底层是哪家模型,调用方都按 OpenAI 的流式格式接收。

response = client.chat.completions.create( model="gpt-3.5-turbo", messages=[{"role": "user", "content": "写一首关于大海的诗"}], stream=True ) for chunk in response: delta = chunk.choices[0].delta.content if delta: print(delta, end="", flush=True)

流式模式下要注意的一个坑是,有些模型不支持某些参数,比如有的模型不支持 logprobs,或对 max_tokens 有上限约束。网关做参数透传时不会拦截这类参数,实际请求就会报错。建议在配置层面对不同模型做参数映射或裁剪,把明显不兼容的字段提前过滤掉。

函数调用方面,OpenAI格式的 tools 声明能被正确传递给支持工具调用的模型,但不同模型对工具描述的解析效果有差异,需要针对目标模型微调提示词和参数格式。这样在网关层做一次适配,客户端只需要跟OpenAI格式保持对齐。

4.3 集成 LangChain 这类编排框架

如果你平时用 LangChain 这类流程编排框架,litellm 的适配也很平滑。很多框架本身就支持通过自定义 base_url 接入兼容接口,或者在 Python 上直接改用它的客户端类。我这里举个简单例子,通过 LangChain 的 ChatOpenAI 指定 base_url 来接入网关:

from langchain_openai import ChatOpenAI llm = ChatOpenAI( model="gpt-3.5-turbo", openai_api_key="any", base_url="http://localhost:4000" ) response = llm.invoke("请用三句话介绍你自己") print(response.content)

把网关作为统一出口之后,编排层的代码不再关心底层模型来源,只关心逻辑和提示词,整个架构的分工更清爽。团队里有人想做模型评估,也可以直接用网关对比多个模型的效果,数据收集和成本统计天然集中在一个层面。

4.4 预算、速率限制和异常处理

在一个真实的多项目环境中,预算控制需要非常具体的策略。litellm 允许按 key 或者按用户设置预算。例如给A团队分配一个月度20美元的子密钥,当这个密钥累计消耗超过阈值,后续请求就会被拒绝。

这个能力在代理模式里会有独立的管理接口,可以通过调用管理API来创建子密钥、设置限额、查看消耗明细。注意子密钥和上游厂商密钥不同,子密钥是网关自己生成的,用来管理调用方身份,和厂商那边无关。

速率限制同样可以在网关层做。对每个子密钥设置最大并发请求数或每分钟请求数,能有效防止某个调用方把网关占满。这里的关键是:速率限制尽量在客户端也做一层,网关的限制只能兜底,不能完全依赖。

合理设置预算和速率限额,能让网关在实际运行中处于比较健康的状态,避免个别调用方出现突发超量请求影响整体服务。

5. 避坑手册:我实际踩过的几个错误

5.1 模型名称映射不一致

去查文档里提到的 aliases 功能。这个功能用来给模型起别名,但比别名更重要的是理解真实模型名的映射关系。我遇到过一个情况:某模型的接入点在 OpenAI 兼容模式下要求模型名带特定前缀,如果不加,服务端直接返回404错误。后来我通过配置层把模型名补齐了,客户端不感知,问题就解决了。

在配置里给同一模型映射两个不同入口时,也要小心。比如想同时用同一个厂商的基础版和增强版,映射名称不能含糊,不然容易把一个请求路由到不期望的那个版本上。

5.2 密钥权限过大

我在内测阶段图省事,给所有客户端都用同一个子密钥。后来排查一个异常消耗时才发现,某条测试脚本写了个死循环疯狂请求,因为共享密钥没有细颗粒度限制,直接烧掉了大笔费用。从那以后我严格要求每个调用方甚至在细分功能维度都使用独立子密钥,单独设限,异常波动一眼就能锁定是谁的问题。

5.3 自定义接口和本地模型

好多人只用 litellm 接云厂商,其实它还支持接入本地或自建的推理服务。比如企业内部部署了一些开源的模型,只要暴露兼容接口,就能配置进去。

配置方式是在 litellm_params 里指定 api_base:

model_list: - model_name: local-llama litellm_params: model: openai/llama-3.1-8b-instruct api_base: http://127.0.0.1:8000/v1 api_key: not-needed

这里 model 前缀用了 openai/ 表示走 OpenAI 兼容协议调用本地接口。理解这个机制后,可以把云厂商和本地服务统一纳入一套路由体系,按价格或优先级灵活调度。很多数据敏感场景会保留本地模型通道,先用本地方案快速响应,不满足时再路由到外部高能力模型。

这类自定义接入点最容易出的问题是参数兼容性。本地服务通常只实现了部分标准参数,遇到不支持的参数会被忽略甚至报错。遇到这种问题就优先精简参数集,把无关字段过滤掉,再做透传。

5.4 日志、监控与性能平衡

网关在团队内部成为流量中枢之后,使用进口数据很大,默认日志可能不够用。litellm 支持对接多种可观测性工具,比如 Prometheus 指标、Langfuse 追踪等,把成本、延迟、错误率这些关键指标打到统一面板上。

但我一开始把所有请求的内容都打了全量日志,导致存储暴涨,后续查询也很慢。后来调整策略:只记录请求元数据,比如模型名、token数、耗时、成功与否,全量内容只针对重点链路保留一段时间。这个平衡点要根据自己的排查需求和存储成本来摸索。

最后再分享一个小技巧

如果你打算长期维护一个模型网关,我建议从第一天就把配置和密钥分离管理。密钥放在环境变量或密钥管理服务里,配置放在代码仓库中,跟随版本管理。这样每次调整模型通道都像发一次代码变更,可回溯、可评审,不会变成“谁在服务器上手动改了下配置”之类的悬案。

litellm 是一个我很看好的项目,因为它在复杂的大模型生态里,提供了一个足够简单又足够灵活的中间层。从个人脚本到团队基础设施,它都能发挥作用。至少在我这边,接入统一网关之后,“换个模型试试”终于变成了一件很轻量的小事。

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

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

立即咨询