首页 / 资讯中心 / 文章详情

litellm实战:统一大模型API调用与代理服务部署

litellm实战:统一大模型API调用与代理服务部署 ★ FEATURED ARTICLE
现在不少做AI应用的朋友应该都有同感模型越来越多接口格式却五花八门今天调这家明天调那家代码里全是if-else分支维护起来让人头大。我之前也被这个事折腾得不轻直到后来用上litellm这个开源工具才算真正把这事儿捋顺了。这篇文章就结合我自己实际跑项目的经历把litellm的用法、思路和踩过的坑从头到尾聊一遍希望能帮到正在被多模型API集成折磨的人。litellm本质上是一个统一大模型API调用的代理层和工具库。用大白话说它就是把你和各家模型厂商比如OpenAI、Anthropic、Google、Azure、国内各家厂商等之间的通信封装成一个统一格式。你只需要按它定义的接口去写代码它负责在背后把请求转发到真正的模型服务商再把结果转回来。这样做带来的直接好处是你代码里的模型调用逻辑可以做到“改配置不改代码”换模型、加模型就像换插座一样简单。如果你正在做RAG应用、Agent应用、多租户SaaS服务或者只是单纯想在不同模型之间快速做对比评测litellm都非常合适。它既是一个命令行工具也可以直接在Python代码里当SDK用还能部署成一个独立的代理服务和OpenAI的API格式完全兼容。这意味你原来用OpenAI SDK写的代码几乎不用改就能跑到其他模型上。下面我从整体设计、核心功能、实际部署和常见问题几个角度把litellm讲细一点。1. 内容整体设计与思路拆解1.1 litellm解决的核心痛点先说一个我实际遇到的场景。早前我做一个客服问答机器人的时候需要同时测试好几家大模型的效果。最开始天真地以为各家都提供了SDK各写一套调用代码就是了。结果真做起来才发现这根本不是一个量级的事有的SDK是同步的有的是异步的有的返回带tool_calls有的返回是raw text有的超时设置叫timeout有的叫request_timeout有的错误码是401有的返回429但含义还不太一样。这些细节单独看都不难但只要你要维护两套以上代码里就会开始出现大量脏逻辑。后来我在一个开源项目里看到了litellm发现它的核心设计思路特别好它把所有模型提供商的API都映射成同一个格式这个格式就是OpenAI的message格式。也就是说你用litellm调什么模型写起来都像在调OpenAI一样。这套设计在工程上有个很大的优势团队内部可以统一定义一套工具函数和错误处理逻辑不用去分别维护多家SDK的兼容层。后续模型要升级、要替换也只需要改配置代码层几乎不动。1.2 代理模式与服务化思路litellm除了作为代码库使用还能当作一个代理服务部署这一点我觉得是它最值得用的地方。部署起来之后它会暴露一个和OpenAI API完全兼容的端点你原来代码里base_url指向OpenAI的地方改成指向你的litellm服务就行。这种代理模式的价值主要体现在几个方面统一鉴权与密钥管理你不需要把各家模型的API Key散落在各个服务里而是在litellm服务端统一管理调用方只需要知道你分配的虚拟Key。统一预算和限流每个Key可以设置预算上限、每分钟请求数防止某个应用把费用跑飞。统一日志与监控所有请求都经过litellm你可以在一个地方看到调用了哪些模型、延迟多少、成功率多少、花费了多少钱。从架构上看这其实是一种很典型的门面模式把背后复杂的多渠道适配全部藏起来给上层一个干净的接口。对于前后端分离、多团队协作、或者做企业内部AI网关的场景这个思路非常值得借鉴。1.3 为什么选litellm而不是自研适配层肯定有人会说这套东西我自己写一个适配层不就行了我最初也这么想过但实际走一遍就会发现维护一个适配层的工作量远超预期。首先是模型参数差异。同样一个temperature参数不同厂商的取值范围不一样有的叫temperature有的叫temp有的甚至不支持。生成token数量的上限各家也不一样有的用max_tokens有的用max_tokens_to_sample。单单把这些参数统一处理好就需要大量测试。其次是模型列表的更新速度。大模型行业现在是一周一个新模型厂商API也在不断调整。litellm有专门的维护团队和社区能很快跟上更新。我自己维护的话根本做不到这种时效性。再者是工具的成熟度。litellm已经支持超过100家模型提供商而且内置了重试、超时、流式输出、函数调用、预算管理、日志记录等一大堆功能。如果自己写这些功能没几个月根本沉淀不下来。基于成熟工具来构建自己的系统是更务实的选择。2. 核心细节解析与实操要点2.1 基础调用方式与参数映射先看最简单的用法。安装litellm后在Python里调用模型的代码是这样的import litellm response litellm.completion( modelgpt-4o, messages[ {role: system, content: 你是一个有用的助手}, {role: user, content: 你好介绍一下你自己} ], temperature0.7 ) print(response.choices[0].message.content)这个写法几乎和OpenAI原生SDK一模一样因为litellm就是按照OpenAI的格式来设计的。如果我想换成Anthropic的模型只需要把model参数改成claude-3-5-sonnet-20241022其他代码不用动。这里要特别说一下参数映射的问题。litellm在内部会把统一的参数转换成各个厂商的格式。比如你写max_tokens256对于不同厂商它会自动映射成对应的字段。这就解决了前面提到的参数差异问题。但要注意不是所有参数都能完全映射。有些厂商的特殊参数litellm也提供了透传机制用litellm.completion(..., anthropic_version2023-06-01)这种方式直接把额外参数传给后端厂商。2.2 流式输出的正确姿势流式输出做聊天机器人几乎是必选项不然用户要等好几秒才能看到一句话的开头体验很差。litellm支持所有接入模型流式输出但不同厂商的流式格式不一样litellm帮你统一成了OpenAI的流式格式。以streamTrue为例response litellm.completion( modelgpt-4o, messagesmessages, streamTrue ) for chunk in response: delta chunk.choices[0].delta if delta.content: print(delta.content, end, flushTrue)在实际项目中我通常会把流式响应包一层异步生成器方便在FastAPI里做SSE推送。有一点要提醒的是不同模型的流式行为略有差异比如有些模型会把内容分得特别碎有些会一次性返回很长一段。如果需要前端打字机效果可能要自己在服务端做一下缓冲聚合按标点或按固定长度切分后再推送这样体验更稳定。2.3 函数调用与工具使用的统一格式大模型应用中函数调用Function Calling或者叫工具调用Tool Use越来越重要。Agent系统全靠这个能力来调用外部工具。litellm同样对此做了统一。看一个简单的例子tools [ { type: function, function: { name: get_weather, description: 获取指定城市的天气, parameters: { type: object, properties: { city: {type: string, description: 城市名} }, required: [city] } } } ] response litellm.completion( modelgpt-4o, messagesmessages, toolstools ) # 模型返回的tool_call tool_calls response.choices[0].message.tool_calls对于支持该能力的模型比如OpenAI系列、Claude系列、Gemini系列litellm会把tool_calls统一成这个格式。你在业务侧只需要处理这种统一的tool_calls结构然后执行对应工具再把结果作为message传回去即可。这一整套流程在不同模型上跑起来表现基本一致。2.4 异步调用与批量场景在处理批量任务或并发请求时异步是必须的。litellm提供了acompletion方法用法和同步版几乎一样import asyncio import litellm async def ask_model(session_id, question): response await litellm.acompletion( modelgpt-4o, messages[ {role: user, content: question} ] ) return response.choices[0].message.content async def main(): questions [介绍一下Python, 介绍一下Rust, 介绍一下Go] tasks [ask_model(i, q) for i, q in enumerate(questions)] results await asyncio.gather(*tasks) print(results)我实际测下来在需要同时处理几十个请求的时候异步写法能显著降低总耗时。不过要留意的是并发量上来了之后厂商那边可能会给429限流。这时候litellm内置的重试机制就发挥作用了。它默认会对某些可重试的错误比如429、5xx进行指数退避重试。你可以通过num_retries和max_retries等参数控制重试策略。我自己踩过坑的地方是把重试次数设得太大导致线上请求堆积。我的建议是总的重试等待时间不要超过30秒超过之后最好直接返回错误给前端让用户决定是否重试。3. 实操过程与核心环节实现3.1 代理服务部署与配置litellm单独作为代理服务部署时操作非常直接。首先安装并启动pip install litellm litellm --model gpt-4o --port 4000启动后访问http://localhost:4000/v1/chat/completions就可以用OpenAI API的格式调用gpt-4o了。但生产环境不会这么简单你往往需要配置多个模型、多个团队、不同的预算和权限。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-20241022 api_key: os.environ/ANTHROPIC_API_KEY - model_name: llama3-8b litellm_params: model: ollama/llama3:8b api_base: http://localhost:11434配置里model_name是你暴露给使用方的别名litellm_params里则是真正传给litellm的参数。可以看到本地用Ollama跑的模型也能接入同一个网关这一点对于统一管理本地模型和云端模型特别方便。启动代理时指定配置文件litellm --config config.yaml --port 4000然后你就可以这样调用了from openai import OpenAI client OpenAI( base_urlhttp://localhost:4000/v1, api_keysk-your-virtual-key ) response client.chat.completions.create( modelclaude-3-5-sonnet, messages[{role: user, content: 你好}] )注意这里model参数用的是代理里的别名claude-3-5-sonnet而不是原始模型名。这样使用方根本不用知道真正的模型是哪个后续换模型也只改配置文件。3.2 多租户与虚拟Key管理如果做企业内部平台或者服务于多个客户litellm的预算和Key管理功能能派上大用场。启动代理后你可以调用litellm的管理接口来创建虚拟Keycurl -X POST http://localhost:4000/key/generate \ -H Content-Type: application/json \ -H Authorization: Bearer sk-admin-key \ -d { models: [gpt-4o, claude-3-5-sonnet], max_budget: 100.0, budget_duration: 30d, max_parallel_requests: 100 }响应里会得到一个虚拟Key这个Key只能调用指定的模型且总预算不超过100美元。这在实际运营中非常有用你给每个项目、每个租户发一个独立的虚拟Key就算这个Key泄露了损失也只有这个Key的限额。我用这个功能接待过一个多项目团队的情况每个项目组有自己的虚拟Key和预算各自在后台看自己的调用量和花销。出现问题的时候也能精确追溯到是哪个Key、哪个请求、调的哪个模型。这种可观测性对线上系统的帮助特别大排查问题不再需要翻半天日志。3.3 成本追踪与异常监控除了管理Keylitellm还会记录每一次调用的token数、模型单价并统计总费用。在代理模式下你可以在控制台里看到实时的请求记录包括状态码、响应时间、使用的模型、消耗的token等。我自己通常还会配合一个有用的做法把litellm的日志接进公司的监控系统。litellm支持通过litellm_settings配置回调callback比如litellm_settings: callbacks: [prometheus]开启之后litellm会暴露一堆Prometheus指标比如litellm_total_requests、litellm_total_cost、litellm_success_rate等等。你用Prometheus抓取这些指标再配上Grafana看板就能实时看到服务的整体健康状况。这里补充一个我实际操作中的心得光看成功率是不够的一定要看分模型的延迟和错误分布。有些阶段波动特别大如果没看分模型数据很难定位是哪个上游出了问题。litellm在响应对象里会带上model字段这个字段保留的是真实模型名可以按它做维度拆解。3.4 路由策略与多供应商容灾litellm的另一个值得深入了解的功能是模型路由。简单来说你可以在litellm里定义一组同类型的模型然后让litellm根据策略自动选择用哪个模型来响应请求。举例来说你可以配置一个gpt-4o的模型组里面包含openai/gpt-4o和azure/gpt-4o再配上az作为备用。当主供应商出故障或者限流严重时litellm会自动把流量切换到备用的azure/gpt-4o。这个策略配置在config.yaml里router_settings: routing_strategy: simple-shuffle fallbacks: [ {gpt-4o: [azure/gpt-4o]} ] cooldown_time: 300这里simple-shuffle是默认的轮询策略litellm还提供了least-busy、latency-based-routing等更高级的策略。cooldown_time表示某个模型出错后需要冷却多久才会重新尝试。这个容灾机制在线上非常有用因为模型厂商的稳定性和限流情况是不受你控制的有自动切换这个兜底至少不会因为上游波动导致服务整体不可用。我自己建议不要把路由策略配置得过于复杂。刚开始用轮询或者基于延迟的策略就够了。等积攒了足够的调用数据再切换到更复杂的策略。否则一开始就上最复杂的方案出了问题反而难排查。4. 常见问题与排查技巧实录4.1 模型名格式错误这是新手最容易犯的错。很多人直接写modelgpt-4o在某些情况下没问题但如果你同时装了多个平台的Key或者某些平台里存在同名模型就会出现奇怪的报错。litellm的模型名格式通常包含供应商前缀比如openai/gpt-4o、anthropic/claude-3-5-sonnet-20241022、gemini/gemini-1.5-pro。如果没带前缀litellm会尝试根据你的环境变量推断但推断结果不一定是你想要的那个。我遇到过一次很诡异的情况代码里写了modelgpt-4o但请求被转发到了Azure OpenAI因为我环境变量里设置了AZURE_API_KEY和AZURE_API_BASElitellm就自动优先走了Azure通道。解决方法是显式指定modelopenai/gpt-4o来锁定供应商。排查建议看到”model not found”或者“Unknown model”这类报错时先检查你的模型名是否带对供应商前缀再看环境变量里有没有设置多个平台的Key。4.2 超时与重试设置不当生产环境中模型调用超时是很普遍的事。litellm默认超时时间是600秒这个值对于很多在线应用来说太长。比如你做一个APP的聊天功能用户不可能等10分钟才看到响应。所以一定要显式设置超时litellm.request_timeout 30或者按单个请求设置response litellm.completion( modelgpt-4o, messagesmessages, timeout15 )我个人的经验值是普通对话场景15秒Agent多轮工具调用场景20到30秒。如果超过这个时间还没返回前端基本就该提示“稍后再试”了。重试也要注意一个细节litellm默认对429和5xx进行重试这是好事。但如果你在异步场景下开了很多并发重试会加剧上游压力反而更容易触发限流。比较稳妥的方式是控制好总并发数并给litellm设置num_retries为2到3次同时开启fallbacks来做跨供应商容灾而不是单纯依赖重试。4.3 context_window与token溢出不同模型的上下文窗口大小不一样有的16K有的32K有的200K。当请求的总token数超过模型上限时会直接报错ContextWindowExceededError或者类似的错误。我在做一个长文档问答功能时就频繁撞上这个问题。用户上传的文档太长超出了模型窗口。解决思路有几个先用文本切分器把文档切成小块只把相关块发给模型。用带检索能力的RAG方式而不是全量塞进上下文。在litellm侧也可以开启自动截断功能在确保不破坏消息结构的前提下把超出的部分丢弃。litellm提供了一种自动处理方式设置litellm.drop_params True之后它会自动丢弃一些不支持的参数但token溢出这种问题是靠调整参数解决不了的还是得从源头上控制输入长度。所以我对长文本场景的建议是一定要自己做文档切分和检索别指望模型硬扛。4.4 代理模式下鉴权配置缺失如果你部署了litellm代理但没有配置master_key那任何人只要知道你的服务器地址就能调用你的模型这会带来严重的安全和财务风险。官方建议生产环境必须设置master_keygeneral_settings: master_key: sk-your-master-key设置之后所有管理接口和模型调用请求都需要在Header中带这个Keycurl http://localhost:4000/v1/chat/completions \ -H Authorization: Bearer sk-your-master-key \ -H Content-Type: application/json \ -d {model: gpt-4o, messages: [{role: user, content: hi}]}如果你是在公司内部用建议再加上一个子Key用/key/generate接口生成这样可以精细控制每个内部项目的额度。不要所有地方都用同一个master_key不然出事的时候不好定位到底是谁调用的。4.5 不同provider返回结构差异导致的兼容问题虽然litellm统一了大部分接口但个别模型返回的某些字段仍然有细微差别。比如有的模型不返回usage字段有的返回的content可能为空但tool_calls有值。如果你的代码没有对这些边界情况做保护很容易出现NoneType相关的报错。最典型的场景是当你把消息传给模型模型认为不需要调用工具直接回了文本。这时tool_calls就是None反过来模型如果决定调用工具content可能是None。我在代码里都会这样处理message response.choices[0].message if message.tool_calls: # 处理工具调用 pass if message.content: # 处理文本回复 pass宁可多做一层空值判断也不要在线上跑挂了再去补。4.6 配置管理混乱最后说一个比较隐蔽但很常见的问题随着接入的模型越来越多config.yaml可能变得很长维护起来越来越困难。如果几个团队共用一个代理这个配置文件就成了一个易碎品。我的处理经验是在litellm代理这层按团队拆分不同的配置文件然后用脚本把公共的部分抽出来。另外把厂商的API Key统一放在环境变量或者密钥管理服务里别直接写在config.yaml里。毕竟配置文件是要入库的一旦泄露所有Key都完了。如果你用的是Kubernetes部署建议把配置放到ConfigMap或者K8s的Secret中并使用环境变量引用。每次更新配置后平稳地重启litellm服务就可以生效。5. 从使用到落地的一些个人体会litellm这个项目我从单纯在脚本里调用到部署成公司内部的统一模型网关实际走了不少弯路。现在回头看有几个认知层面的体会想分享出来。第一个体会是关于标准化的价值。很多人一开始觉得接入多家模型无非是多写几个函数但真到了多模态、流式、函数调用、并发、预算这些都叠加在一起的时候靠手写适配代码几乎是不可维护的。litellm的价值不在于帮你省了第一遍接入的功夫而在于让你后续的系统演化过程中不用反复在兼容层上打补丁。第二个体会是配置先行。我强烈建议刚上手litellm的时候就算是只用一个模型也把config.yaml建起来走代理模式调用。因为从“直接调用SDK”切换到“代理模式”成本很低但收益很大。等以后要加Key、加租户、加预算的时候你已经有一个跑通的基础设设施不用临时抱佛脚补架构。第三个体会是成本和延迟要一起盯。我们之前只盯着模型返回质量忽略成本监控结果月底账单出来吓了一跳。litellm的成本统计功能虽然能让你看到花了多少钱但更关键的是要建立“每次请求都绑定业务场景”的意识。你可以在传入请求时带上metadata标签比如标注是哪个功能、哪个用户群这样在成本统计分析时就能知道每个业务花了多少钱这个数据对产品决策特别有帮助。另外这个项目本身迭代速度很快社区也很活跃。你会发现它不只是个API封装还在往AI网关的方向走包括模型路由、预算管理、请求日志、甚至还有多模态支持。所以如果你所在团队已经开始做多个AI应用或者多租户AI平台花点时间研究litellm会是性价比很高的事情。最后抛一个我踩过坑之后养成的习惯每当个版本升级我都会先在测试环境把新的config.yaml和旧的config.yaml做对比然后把关键路径的调用都回归一遍。因为litellm更新快某些参数名或者默认行为可能有变化直接生产环境升级风险还是蛮大的。这个经验分享给大家参考。
阅读完成 · 觉得有帮助?
咨询建站