资讯动态

LiteLLM实战:统一大模型接口,轻松搭建AI代理网关

发布时间:2026/10/13 8:33:16 来源:尧图企业网站定制
前阵子和一个朋友聊各自手头的 LLM 应用他说最近最烦的不是模型效果不够而是接口问题。今天用的模型 A 是一套鉴权方式明天换成模型 B又是另一套 base_url、另一个请求体结构连 stream 事件都长得不一样。我说你怎么不试试现有的网关方案他说总觉得重一个内部小项目还得单独搭服务。其实就是没找对工具很多问题用一个叫 LiteLLM 的库就能解决得很干净。LiteLLM 把一个很复杂的问题拆得很简单它把主流大模型厂商的接口全部统一成一套风格你在代码里永远只面对同一个 completion 方法。换模型是改一个字符串的事想在多个模型之间自动切换、失败重试、负载均衡、控制预算、留请求日志它也都有现成方案。除了当 Python 库嵌进代码它还能作为独立的代理服务运行一个服务统一管所有上游。这篇文章我会从“为什么烦”开始把 LiteLLM 的核心设计、快速上手、代理网关搭建和踩坑经验都过一遍。不管你是个人开发者在写 Agent还是小团队想统一管理多个模型 Key都能在里面找到可以直接抄走的配置。1. 为什么要有 LiteLLM先搞懂它解决的是什么问题1.1 各家接口不统一这件事有多痛做 LLM 应用的人多少都被“模型切换”折磨过。每个大模型厂商都有自己的一套请求格式、鉴权头、参数命名和流式事件结构。今天接的厂商请求体里叫max_tokens换一家可能叫max_new_tokens上一家流式返回的是delta.content另一家则是嵌套在别处的字段。更不用说不同厂商对不同参数的支持程度还不一样传多了可能直接报错传少了效果又不对。看似是“稍微改改”的事真做起来却很烦。你自己的业务代码里会慢慢堆满 if-else 和适配函数比如“如果是 A 厂商走这个分支如果是 B 厂商走那个分支”。每次引入新厂商还得重新读一遍文档、写一遍测试。更麻烦的是模型效果对比通常不能只看一家需要同时调用两家的模型做评测如果连调用层都统一不了评测脚本都要写两份。LiteLLM 的思路不是发明一套新标准而是做一层“翻译官”。你在代码里调用它它再按你指定的厂商规则去请求真正的模型最终把返回结果统一成一套结构。这样业务代码只依赖 LiteLLM不依赖任何具体厂商。1.2 为什么 OpenAPI 兼容风格成了中转标准既然要统一总得选一个格式当“通用语言”。LiteLLM 选择的是很多开发者都已经熟悉的 OpenAI 兼容风格。这套风格之所以能承担这个角色不是因为它背后的公司有多强势而是因为生态足够大大量开源工具、Agent 框架、评测平台、向量数据库插件都默认兼容这套格式。对使用者来说这个选型带来一个直接好处几乎不用学新东西。你原来怎么调用某厂商 SDK用 LiteLLM 之后仍然是类似的写法连返回结构都几乎一致。唯一的区别是你在 model 参数里多了一个前缀告诉 LiteLLM 该走哪家适配器。顺带说一句这套兼容格式在国内外的模型服务里出现得越来越多很多自建的模型服务也直接宣布自己“兼容风格”。用 LiteLLM 做翻译相当于提前给自己留了一条宽松的切换通道以后不管上游怎么变你的调用方代码都不用动。1.3 统一入口与 Proxy 两种用法分别适合谁LiteLLM 有两种用法很多人一开始分不清其实对应两种完全不同的诉求。第一种是 SDK 嵌入模式。在你的 Python 项目里直接import litellm在进程内完成所有请求转发。这种模式适合单体应用、脚本、后端服务优点是无缝接入不增加额外运维组件。对个人开发者和中小项目来说这是最轻量的起步方式。第二种是 Proxy 代理模式。LiteLLM 启动成一个独立服务你的应用不再直接请求厂商而是统一请求本地代理地址由代理再转发给上游厂商。这适合多服务共享 Key、集中管控、跨语言接入的场景。比如你的后端是 Java中间件是 Go前端也需要偶尔直连但它们都只要访问一个代理地址就够了。Key 统一放在代理的环境变量里其他服务完全不接触真实密钥权限也更好收口。我把这个区分记成一句话SDK 模式升代码Proxy 模式升架构。后面章节我会先讲 SDK再重点讲 Proxy 怎么搭因为代理模式才是团队场景里的核心价值。2. 快速上手五分钟在项目里用起来2.1 安装与环境变量密钥到底放哪安装很简单一条命令pip install litellm装完之后不要急着写代码先把密钥管理好。我最开始图省事把 Key 直接写到 Python 文件里后来代码一提交到仓库就后悔了。正确做法是用环境变量LiteLLM 在解析 model 前缀时会自动去环境变量里找对应厂商的 Key。你可以创建一个.env文件PROV_A_API_KEY你的A厂商密钥 PROV_B_API_KEY你的B厂商密钥然后在入口代码里加载from dotenv import load_dotenv load_dotenv()变量名怎么定LiteLLM 内部对不同厂商有约定的前缀具体以官方文档为准。核心逻辑是你传的 model 参数前面带哪个前缀它就去获取对应前缀的 API Key。用环境变量而不是写死在文件里还能让同一套代码在不同环境切 Key 时不用改代码。2.2 最基础的统一调用先看一个最简单的例子import litellm response litellm.completion( modelprov-a/gpt-demo, messages[ {role: user, content: 你好介绍一下你自己} ], ) print(response[choices][0][message][content])这里的 model 参数可以分成两段斜杠前面的prov-a是目标厂商前缀后面的gpt-demo是真实的模型名。LiteLLM 拿到这个字符串后会找到对应的适配器把请求翻译成目标厂商熟悉的格式再发出去。返回的 response 结构仍然是你熟悉的风格choices、message、content。如果你用的是真正的厂商 Key那 model 参数里就要填对方文档里的真实模型名。比如某厂商的模型叫demo-v2那大概就是prov-a/demo-v2这种形式。别照抄我这里的占位符一定要换成你实际能用的模型名。除了文本生成LiteLLM 还有litellm.embedding用来做向量化litellm.image_generation处理图像类调用模型列表和用法都在官方文档里。不过日常用得最多的还是completion和它的异步版本。2.3 切模型、异步、流式这些高频操作既然统一了接口切模型就很自然了。把model参数换一个前缀和模型名就行。更进阶一点你还可以传一个列表response litellm.completion( model[prov-a/gpt-demo, prov-b/claude-demo], messages[...], num_retries2, )意思很直白优先用第一顺位的模型如果它失败自动换到第二个模型。这个能力在关键业务场景里特别有用。有一次我跑批处理任务上游模型因为限流连续失败如果没有 fallback整个批处理就会卡死配上列表之后失败的请求自动切到了另一家的模型任务最终跑完了。再看异步调用。如果你的服务是异步框架就别用同步阻塞版本的completion而是用response await litellm.acompletion( modelprov-a/gpt-demo, messages[...], )异步版本不会占着线程等你能在高并发场景下明显降低延迟抖动。官方推荐在 FastAPI 这类异步框架里使用这一套。流式输出也保留了一致的体验。设置streamTrue后拿到的不再是一个完整响应而是一个迭代器response litellm.completion( modelprov-a/gpt-demo, messages[...], streamTrue, ) for chunk in response: print(chunk[choices][0][delta].get(content, ), end)不同的上游厂商流式返回的事件格式并不相同LiteLLM 在内部做了归一化。你在业务代码里不用关心原始事件长什么样只需要处理统一的格式。这对前端逐字输出的体验优化很有帮助。这里提一个容易忽略的参数request_timeout。不设置的话某些上游接口长时间不返回你的请求会一直挂着浪费线程。建议显式设置比如 30 秒response litellm.completion( model..., messages[...], request_timeout30, )3. 从单机调用到服务化搭建一个 LLM 代理网关3.1 为什么需要代理层SDK 模式虽然方便但用在一堆服务上会很快失控。想象一下公司里有三个后端服务都要调模型如果每个服务各自配置厂商 Key那么 Key 至少散落在三个地方一旦要更换某个厂商的 Key需要同时修改三套环境。想给每个团队设置独立的调用额度SDK 模式不好搞想把所有请求日志统一存下来做审计SDK 模式也只能各记账各的。代理层就是把“调用模型”这件事收口。所有服务只认一个内部代理地址代理统一管理上游 Key、统一控制并发和预算、统一记录日志。换个更生活化的类比SDK 模式是自己跑到每个餐厅后厨点菜Proxy 模式是所有服务都把菜点给一个前台前台再分发给各个后厨。前台坏了会单点故障但换来的是集中管控这个取舍在团队场景里通常非常划算。3.2 用一份 YAML 定义多模型和路由LiteLLM Proxy 的配置核心是一份 YAML 文件。很多人第一次看官方示例会觉得字段多实际上只要抓住model_list这一块就理解了一半。下面是一个基础配置示例model_list: - model_name: fast-demo litellm_params: model: provider-a/gpt-demo api_key: os.environ.PROV_A_API_KEY - model_name: strong-demo litellm_params: model: provider-b/claude-demo api_key: os.environ.PROV_B_API_KEY litellm_settings: drop_params: true request_timeout: 30 general_settings: master_key: sk-your-master-key这里有个关键概念容易弄混model_name是暴露给外部调用方的“别名”而litellm_params.model才是真正路由到上游的“真实模型”。比如外部服务只需要知道fast-demo代理背后具体是哪个厂商的哪个模型调用方可以完全不用关心。以后你想把fast-demo从 A 厂商换成 B 厂商只需要改配置并重启代理调用方代码一行都不用动。drop_params: true是我比较推荐开启的选项。不同上游支持的参数集合不一样开启后LiteLLM 会自动丢弃当前上游不支持的参数而不是直接报错。比如模型 A 支持top_p模型 B 不支持你统一传了top_p默认情况下 B 会拒绝请求开启 drop 之后B 的请求里就会忽略top_p调用仍然能成功。这个开关能省掉很多不兼容的小烦恼。3.3 启动代理并用原生 SDK 接入配置文件准备好后启动代理只需要一行命令litellm --config config.yaml --port 4000启动后LiteLLM 会在http://localhost:4000上监听请求并在/v1路径下提供接口。master_key是访问代理时需要带上的管理密钥相当于代理自己的通行证。接着你完全可以用你熟悉的 SDK 来调用这个代理。例如from openai import OpenAI client OpenAI( api_keysk-your-master-key, base_urlhttp://localhost:4000/v1, ) response client.chat.completions.create( modelfast-demo, messages[{role: user, content: 你好}], ) print(response.choices[0].message.content)注意这里的modelfast-demo不是真实上游模型名而是你在配置里定义的对外别名。这个设计非常巧妙它让代理对外呈现一个稳定、统一的模型命名空间。你用主流 SDK 就能接不需要装额外的客户端库。很多团队喜欢这个模式还有一个隐藏原因网关层后面可以随时加模型加 Key做灰度切换而调用方始终感觉不到变化。我自己维护过一个长期项目半年内上游模型换过三次调用方一行代码都没改全靠代理层消化了变动。3.4 强化重试、fallback、负载均衡、预算与鉴权只做转发当然不够代理的价值更多体现在稳定性和治理能力上。我按配置顺序说。第一重试和 fallback 要用好。你可以在router_settings里配置失败重试次数和 fallback 列表router_settings: routing_strategy: simple-shuffle num_retries: 2 allowed_fails: 1 cooldown_time: 30 fallbacks: - model_name: fast-demo fallbacks: - strong-demo这里的含义是同一个模型最多重试 2 次如果上游连续失败 1 次以上就暂时把它冷却 30 秒如果fast-demo调用失败自动回退到strong-demo。我建议在正式环境里至少配一个 fallback 模型尤其是只有单一上游 Key 的时候。模型服务经常有偶发故障一个快速回退能避免很多线上告警。第二负载均衡适合多 Key 或多副本场景。在model_list里可以让同一个model_name对应多个litellm_params每个参数指向不同的上游 Key或者不同的上游地址。代理会根据配置的路由策略自动分配请求避免某个 Key 被打到限流。routing_strategy可以选simple-shuffle也可以选least-busy这类更智能的策略。实际选型时如果上游调用的延迟波动很大least-busy更稳如果只是纯粹随机分配simple-shuffle就够用。第三预算和额度治理。代理支持设置总体预算也可以按 key 设置预算general_settings: master_key: sk-your-master-key max_budget: 1000 budget_duration: 30d这表示代理在 30 天内最多消耗 1000 美元额度超过就会拒绝新请求。对团队来说更重要的是给不同成员发虚拟 Key并分别设置限额。这样单个 Key 超过预算后只影响使用者自己不会拖垮全团队的模型调用。这个功能在多人共用一个代理时很实用省去不少“到底是谁把额度用完了”的扯皮。第四日志和数据库。代理默认把日志打印到控制台但重启后就丢失了。如果需要审计和成本统计建议配置数据库地址比如general_settings: database_url: os.environ.DATABASE_URL接入数据库后代理会把每次请求的模型、Token 用量、请求来源、花费等都记录下来。这个数据做成本分摊和容量规划非常有用。4. 踩坑记录常见问题与排查思路4.1 模型名映射问题使用 LiteLLM 时最常遇到的就是模型名报错。常见场景是配置里的model_name是别名叫fast-demo但你在本地测试时直接传了真实上游模型名没带 provider 前缀或者反过来前缀写错了代理找不到对应的厂商适配器。排查顺序我建议这样先直接调用真实上游模型名加正确前缀确认上游没问题再看代理的日志定位是请求被拒绝还是适配器解析失败。如果某个参数导致上游报错而你又不想关掉该参数可以把drop_params打开让代理自动过滤不支持字段。遇到参数不兼容的情况这是最快的解法。如果是自己部署的开源模型服务可能还需要在配置里维护模型信息比如单次调用的最大上下文长度、价格信息等。否则一些依赖模型元数据的功能会算不准。4.2 限流与超时第二个高发问题是 429 限流和请求超时。如果你在日志里看到大量 429说明某个上游 Key 的并发已经触顶。解决方案不是单纯调低请求频率而是用好代理的冷却和 fallback。比如刚才配置里的cooldown_time就是为了让被限流的上游模型冷静一会儿避免频繁重试把限流打得更狠。我碰到过的一个典型现场是一批长文本任务同时跑某厂商 Key 的速率限制比较低一开始所有请求都挤在那一个 Key 上代理不断重试结果把限流窗口完全填满任务大面积失败。最后在配置里加了同一模型的多 Key 负载均衡同时配置了 fallback 到另一个厂商问题才真正解决。这个教训让我意识到重试不是越猛越好要与冷却和 fallback 配合。另外超时时间不要设得太长。有人为了让模型“尽量生成完”把超时设成 10 分钟结果线程全被卡住。更合理的做法是设置一个业务能接受的上限比如 30 到 60 秒配合重试策略让请求整体更健壮。4.3 日志与成本统计很多用户开了代理但没配数据库觉得日志控制台里有就行。等月底要看账单时才傻眼因为控制台日志早就被冲掉了。成本统计依赖完整的请求历史建议一开始就接数据库哪怕先用 SQLite 把请求落盘都好。还有一个隐含坑LiteLLM 内置的价格表是针对常见公开模型的但你接的私有化模型或者一些小众模型它并不认识。这时候成本统计会显示为 0 或者不准确。解决办法是在 model_list 中给对应模型补充价格信息比如按 token 用量、按请求次数计费等。维护价格的逻辑确实有很多细节但这是做成本治理绕不开的一部分。4.4 常见问题速查表现象可能原因解决建议调用时提示模型不存在model 参数没带 provider 前缀或者别名为配置确认前缀和真实模型名检查 model_list上游报参数不支持当前模型不接收某些参数开启 drop_params或移除多余参数大量 429 错误上游 Key 限流增加多 Key 负载均衡配置冷却时间请求长时间挂起没有设置 request_timeout显式设置 30 秒左右超时成本统计为 0模型价格信息缺失在配置中补充 model_info 价格设置重试导致雪崩重试次数过高且无冷却降低 retries配置 cooldown_time 和 fallback日志重启就丢没有接入持久化存储配置 database_url最后再分享点个人体会我用 LiteLLM 也算是从零折腾过来的。一开始图简单只在单机脚本里当 SDK 用确实很快解决问题后来服务多了、Key 散得到处都是才重新花时间搭代理。回头比较我会建议所有打算长期做 LLM 应用的团队不要等到被动转型而是在第一个模型接入稳定之后就把代理层搭起来。代理层的配置不算复杂一份 YAML 加一个启动命令但带来的集中管控和切换弹性越到后面越明显。如果你现在只是个人项目先用 SDK 模式跑通业务如果有一天你要在团队里共享模型资源直接升到代理模式代码里只改 base_url 和 key业务逻辑几乎不动。这种平滑升级的体验在基础设施工具里其实很难得。希望这篇文章能让你少走几步弯路。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑