1. 从一个真实困境说起:多模型调用的碎片化之痛如果你正在做AI应用开发,大概率遇到过这样的场景:项目初期用某家的大模型API跑通了原型,效果不错,上线后想加一个备用模型做降级容错,结果发现两家的接口参数格式完全不一样。好不容易把第二家接进来,产品又提了新需求——某些场景要用便宜的小模型,某些场景要用推理能力强的大模型,甚至还要支持本地部署的开源模型。这时候你打开代码一看,光是不同厂商的SDK调用逻辑就散落在十几个文件里,每家的鉴权方式、请求体结构、返回格式、错误码定义全都不一样。这不是个别现象。我在过去一年多的项目里,前后对接过七八家不同的大模型服务,从闭源商业API到本地推理服务,每一次切换或新增都意味着一轮重复的适配工作。直到我开始系统性地使用litellm这个工具,才真正把这块的维护成本降下来。litellm的核心价值用一句话概括:它用一套统一的OpenAI格式接口,屏蔽了背后几十家模型服务商的差异。你只需要学会一种调用方式,就能在多家模型之间自由切换、组合、降级。这篇文章适合三类人看:一是正在做AI应用但被多模型适配搞得焦头烂额的开发者;二是想给现有系统加模型容错能力但不知道从哪下手的工程师;三是单纯想找一个轻量方案来统一管理多个模型调用的技术负责人。我会从实际使用角度出发,把litellm的安装配置、核心用法、代理模式、踩坑经验都讲清楚,尽量做到你看完就能直接上手。2. litellm到底解决了什么问题:统一接口的底层逻辑2.1 没有litellm之前,多模型调用有多麻烦先还原一下没有统一层时的典型代码结构。假设你要调用三家不同的模型服务,代码大概长这样:# 服务商A import vendor_a_sdk client_a = vendor_a_sdk.Client(api_key="xxx") resp_a = client_a.chat(model="model-a-v2", messages=[...], temperature=0.7) # 服务商B import vendor_b_sdk client_b = vendor_b_sdk.Client(token="yyy") resp_b = client_b.generate(model="model-b-pro", prompt=..., max_tokens=1000) # 服务商C(本地部署) import requests resp_c = requests.post("http://localhost:8000/v1/chat/completions", json={...})这还只是最简化的版本。实际项目中,你需要处理的东西包括但不限于:不同厂商的鉴权header格式、流式输出的解析方式差异、function calling的参数结构差异、token计数方式不同、错误码体系不统一、重试策略要分别实现。每接一家新服务,这些工作就要重来一遍。更麻烦的是,当你想做模型路由——比如根据请求内容自动选择最合适的模型,或者主模型超时后自动切换到备用模型——你需要在业务代码里写大量if-else判断,逻辑很快就会变得不可维护。2.2 litellm的统一抽象层是怎么设计的litellm的思路很直接:把所有模型服务商的接口都翻译成OpenAI的格式。它内部维护了一套映射关系,你传入model="gpt-4"或者model="claude-3-opus"或者model="ollama/llama3",litellm会自动识别这是哪家服务,用对应的SDK或HTTP请求去调用,然后把返回结果统一转换成OpenAI格式的响应对象。这个设计的好处在于:你只需要学一套API,就能调用所有支持的模型。切换模型时,改一个字符串参数就行,业务代码完全不用动。对于团队协作来说,这意味着新人只需要理解一种调用范式,不需要为每个服务商单独写文档。从架构上看,litellm做了几层关键抽象:Provider层:识别模型名称对应的服务商,处理鉴权和请求发送Transform层:把统一的OpenAI格式请求转换成各服务商要求的格式,再把响应转回来Router层:在代理模式下提供负载均衡、降级、重试等高级路由能力Cache层:可选的缓存机制,避免重复请求相同内容这种分层设计让litellm既能作为轻量SDK嵌入代码,也能作为独立代理服务部署,适配不同规模的项目需求。2.3 哪些场景最适合用litellm根据我的实际经验,以下几类场景用litellm收益最明显:多模型降级容错。主模型服务出现波动时,自动切换到备用模型,业务无感知。这个在线上环境特别重要,我遇到过好几次某家API临时不可用的情况,有了降级机制后用户完全没感觉到异常。成本优化路由。简单请求走便宜的小模型,复杂请求走大模型。litellm的Router支持基于规则或标签的路由策略,不需要在业务层写判断逻辑。本地与云端混合部署。开发环境用本地Ollama跑开源模型省钱,生产环境用商业API保证效果。litellm让这两者的切换只需要改配置。统一日志和监控。所有模型的调用都经过同一层,方便统一记录token消耗、延迟、错误率等指标,不用为每家单独埋点。注意:litellm虽然支持很多模型,但不同模型的参数支持程度有差异。比如某些开源模型不支持function calling,某些商业API不支持自定义temperature范围。使用前最好查一下对应provider的文档说明。3. 从零开始:安装、配置与第一个可运行示例3.1 安装方式选择与依赖管理litellm的安装本身很简单,但有几个细节值得注意。最基础的安装:pip install litellm这个命令装的是核心包,支持大部分商业API的调用。但如果你要用本地模型或者某些特定provider,可能需要额外依赖。比如用Ollama的话,需要确保本地Ollama服务在运行;用某些云服务商的SDK,可能需要单独安装对应的包。我的建议是:先装核心包,用到哪个provider再补哪个依赖。不要一上来就装litellm[all],那会拉一大堆你可能永远用不到的包,拖慢环境构建速度。版本管理方面,litellm迭代比较快,建议在requirements.txt里锁定具体版本号,避免不同环境行为不一致。我一般会写litellm==1.x.x这样的精确版本,升级时再手动测试。3.2 API Key的配置策略litellm读取API Key的方式很灵活,支持环境变量、代码传参、配置文件三种方式。推荐用环境变量,既安全又方便切换环境:export OPENAI_API_KEY="sk-xxx" export ANTHROPIC_API_KEY="sk-ant-xxx" export AZURE_API_KEY="xxx" export AZURE_API_BASE="https://your-resource.openai.azure.com"litellm会自动读取这些标准环境变量。如果你用的是非标准命名的key,也可以在代码里显式传入:import litellm response = litellm.completion( model="gpt-4", messages=[{"role": "user", "content": "你好"}], api_key="sk-xxx" # 显式传入 )提示:生产环境千万不要把API Key硬编码在代码里。用环境变量或者密钥管理服务,这是基本的安全底线。对于需要同时管理多个key的场景,比如不同团队用不同的key,litellm支持在代理模式下配置多个key并做轮询或负载均衡,这个后面讲代理模式时会展开。3.3 第一个可运行的调用示例配置好key之后,最简单的调用长这样:from litellm import completion response = completion( model="gpt-4o", messages=[ {"role": "system", "content": "你是一个简洁的助手"}, {"role": "user", "content": "用一句话解释什么是统一接口"} ] ) print(response.choices[0].message.content)如果你要调用其他模型,只需要改model参数:# 调用Claude response = completion(model="claude-3-5-sonnet-20241022", messages=[...]) # 调用本地Ollama response = completion(model="ollama/llama3", messages=[...], api_base="http://localhost:11434") # 调用Azure OpenAI response = completion(model="azure/gpt-4", messages=[...])返回的对象结构是统一的OpenAI格式,response.choices[0].message.content拿到的就是模型输出文本。流式输出也支持,加stream=True即可,返回的是一个迭代器,逐块吐出内容。这个统一性带来的最大好处是:你的业务代码只依赖litellm的接口,不依赖任何具体厂商的SDK。将来换模型、加模型,业务层几乎不用改。4. 核心功能拆解:路由、降级与流式处理4.1 用Router实现多模型负载均衡单次调用用completion就够了,但生产环境往往需要更复杂的路由逻辑。litellm提供了Router类来处理这类需求:from litellm import Router router = Router( model_list=[ { "model_name": "primary-model", "litellm_params": { "model": "gpt-4o", "api_key": "sk-xxx" } }, { "model_name": "backup-model", "litellm_params": { "model": "claude-3-5-sonnet-20241022", "api_key": "sk-ant-xxx" } } ], fallbacks=[{"primary-model": ["backup-model"]}], num_retries=2 ) response = router.completion( model="primary-model", messages=[{"role": "user", "content": "测试降级"}] )这段配置的含义是:优先调用primary-model,如果失败(超时、限流、服务错误等),自动降级到backup-model,并且每个模型最多重试2次。整个过程对调用方透明,你拿到的还是统一的响应对象。model_list里可以配置多个同名的模型实例,Router会自动做负载均衡。比如你有三个不同账号的OpenAI key,可以配三个model_name相同的条目,Router会轮询使用,避免单个key触发限流。4.2 降级策略的配置细节与实测表现降级策略的配置有几个关键参数需要理解:参数作用推荐值fallbacks定义降级链按模型能力从强到弱排列num_retries单模型重试次数2-3次,太多会拖长响应时间timeout单次请求超时根据业务容忍度设,一般30-60秒allowed_fails触发熔断的失败次数3-5次cooldown_time熔断后冷却时间30-60秒实测下来,降级机制在应对API临时波动时非常有效。我做过一个测试:故意把主模型的key设成无效的,观察降级是否触发。结果是主模型报鉴权错误后,Router在几百毫秒内就切换到了备用模型,用户侧只感觉到响应稍慢了一点,没有报错。但要注意一个坑:不是所有错误都应该触发降级。比如请求参数本身有问题(消息格式错误),这种错误换模型也没用,重试只是浪费时间。litellm默认会对大部分错误做重试,你可以通过retry_policy参数精细控制哪些错误码才重试。4.3 流式输出的统一处理流式输出是聊天类应用的标配,litellm对流的处理也做了统一。不管底层模型是SSE格式还是WebSocket,经过litellm之后都变成标准的迭代器:response = completion( model="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)这里有个细节:不同模型的流式返回中,delta字段的结构可能略有差异,但litellm会尽量归一化。不过某些模型在流式模式下不支持function calling,或者返回的tool_calls格式有差异,这些边界情况需要实测确认。注意:流式模式下如果发生降级,已经吐出的内容不会回滚。所以对于流式请求,降级策略要更保守一些,或者在前端做相应的处理。5. 代理模式:把litellm变成统一网关5.1 什么时候需要代理模式SDK模式适合Python项目直接集成,但如果你的团队有多种技术栈——比如前端直接调、Java服务调、Go服务也调——那在每个项目里都集成litellm SDK就不太现实了。这时候代理模式就派上用场了。代理模式的做法是:把litellm作为一个独立服务启动,暴露一个OpenAI兼容的HTTP接口。所有客户端都用标准的OpenAI SDK或HTTP请求来调用,完全不感知底层用的是哪家模型。这相当于在你的系统和各家模型服务之间加了一个统一网关。启动代理服务很简单:litellm --model gpt-4o --port 8000或者用配置文件启动,支持更复杂的路由和鉴权配置:model_list: - model_name: gpt-4o litellm_params: model: gpt-4o api_key: os.environ/OPENAI_API_KEY - model_name: claude litellm_params: model: claude-3-5-sonnet-20241022 api_key: os.environ/ANTHROPIC_API_KEY general_settings: master_key: sk-your-master-keylitellm --config config.yaml --port 8000启动后,任何支持OpenAI接口的客户端都可以直接连过来,把base_url指向http://localhost:8000就行。5.2 代理层的鉴权与多租户管理代理模式下的鉴权是两层:一层是客户端到代理的鉴权,用master_key或虚拟key;另一层是代理到模型服务商的鉴权,用各家的真实key。这样做的价值在于:真实key只存在于代理服务端,客户端拿不到,安全性更高。litellm支持生成虚拟key,并给每个key设置预算、速率限制、可用模型范围:# 创建一个只能访问gpt-4o、每月预算10美元的虚拟key curl -X POST http://localhost:8000/key/generate \ -H "Authorization: Bearer sk-your-master-key" \ -d '{"models": ["gpt-4o"], "max_budget": 10}'这个能力在多团队共用模型资源的场景下特别实用。每个团队拿自己的虚拟key,用量和费用独立核算,超预算自动拒绝,不需要人工盯着。5.3 代理模式下的日志与可观测性代理层天然是个做统一日志的好位置。litellm支持把每次调用的详细信息写到数据库或日志系统,包括:请求时间、模型名称、输入输出token数、延迟、费用、是否命中缓存、是否触发降级等。这些数据对于运营和优化非常关键。比如你可以分析出哪些请求其实用便宜模型就够了,从而调整路由策略;或者发现某个模型的错误率异常升高,及时切换。litellm内置了和几个主流日志系统的集成,也支持自定义callback把数据推到自己的监控平台。我在实际项目里会把代理层的日志接到内部的监控看板上,每天看一眼各模型的调用量、费用分布、P95延迟。这些指标比单纯的系统监控更能反映AI功能的真实健康度。6. 踩坑实录:那些文档里不会写的细节6.1 模型名称的坑:同名不同物litellm用模型名称来识别provider,但模型名称的命名规则有时候会让人困惑。比如gpt-4和openai/gpt-4在某些版本里行为可能不同,前者依赖自动识别,后者显式指定provider。当自动识别出错时,显式加provider前缀是最稳妥的做法。另一个坑是:不同provider可能有同名模型。比如多个平台都提供llama3,如果你只写llama3,litellm可能不知道你要调哪个。这时候必须写成ollama/llama3或together/llama3这样的完整形式。我的经验是:在生产配置里永远写完整的provider前缀,不要依赖自动识别。多打几个字符,省去很多排查时间。6.2 Token计数与费用估算的偏差litellm提供了token计数和费用估算功能,但实测下来,这个估算和实际账单有时会有偏差。原因有几个:不同provider的token计算方式不同(比如中文的切分方式差异),某些模型有额外的系统token开销,缓存命中的计费规则也不一样。所以我的建议是:litellm的费用估算用来做实时监控和预算控制没问题,但不要把它当成财务对账的依据。精确的费用还是以各provider的账单为准。如果对费用敏感,可以在代理层记录每次调用的token数,定期和实际账单做校准。6.3 超时与重试的连锁反应超时和重试配置不当会导致请求堆积。我踩过的一个坑是:把timeout设得太长(比如120秒),同时num_retries设了3次,结果一个慢请求最坏情况下要等6分钟才返回,期间占用的连接资源一直不释放。高并发时这会迅速耗尽连接池。正确的做法是:超时时间要结合业务容忍度设置,重试次数要克制。对于实时聊天场景,单次超时15-30秒比较合理,重试1-2次就够了。如果模型在30秒内没响应,大概率是服务端有问题,重试也未必能解决,不如快速降级。6.4 流式模式下降级的边界情况前面提过流式模式下降级的问题,这里展开说一下。流式请求一旦开始返回内容,如果中途出错,litellm的降级机制会尝试用备用模型重新发起请求。但这时候前端已经收到了一部分内容,重新发起的请求会从头开始生成,导致内容重复或逻辑断裂。处理方案有两种:一是在流式模式下关闭降级,出错就让前端提示用户重试;二是在应用层做处理,检测到降级时清空已显示内容,重新渲染。我倾向于第一种,简单可靠,用户体验上偶尔重试一次也能接受。7. 进阶玩法:缓存、回调与自定义Provider7.1 缓存策略的选择与命中率优化litellm支持多种缓存后端,包括内存、Redis、磁盘等。缓存的逻辑是:如果相同的请求之前已经调用过,直接返回缓存结果,不再请求模型。这对于重复性高的场景(比如FAQ问答、固定模板生成)能显著降低成本。配置Redis缓存:import litellm litellm.cache = litellm.Cache( type="redis", host="localhost", port=6379 ) response = completion( model="gpt-4o", messages=[{"role": "user", "content": "公司退货政策是什么"}], caching=True )缓存命中率取决于请求的重复度。对于开放域聊天,命中率可能很低;但对于客服问答、文档摘要这类场景,命中率可以做到相当可观。我的做法是:先开缓存观察一段时间,看命中率和成本节省数据,再决定是否长期开启。注意:缓存是基于请求内容做key的,如果请求里包含时间戳、随机数等每次都不同的内容,缓存永远不会命中。设计请求时要考虑这一点。7.2 用Callback做统一埋点litellm的callback机制允许你在每次调用的前后插入自定义逻辑,这是做统一埋点的最佳位置:from litellm.integrations.custom_logger import CustomLogger class MyLogger(CustomLogger): def log_success_event(self, kwargs, response_obj, start_time, end_time): # 记录成功调用的信息 print(f"模型: {kwargs['model']}, 耗时: {end_time - start_time}") def log_failure_event(self, kwargs, response_obj, start_time, end_time): # 记录失败调用 print(f"调用失败: {kwargs['model']}") litellm.callbacks = [MyLogger()]通过callback,你可以把每次调用的模型、token数、延迟、错误信息统一收集起来,推到自己的监控系统。这比在每个业务代码里手动埋点要干净得多。7.3 接入自定义Provider的思路litellm内置支持的provider已经很多,但如果你的模型服务不在列表里(比如公司内部自研的推理服务),也可以通过自定义provider接入。核心是实现一个转换类,把OpenAI格式的请求转成你的服务要求的格式,再把响应转回来。自定义provider需要实现的方法包括:completion(同步调用)、acompletion(异步调用)、streaming(流式处理)等。litellm的代码结构比较清晰,照着已有provider的实现改一改就能跑通。这块的文档相对简略,建议直接看源码里某个简单provider的实现作为参考。8. 生产环境部署的几点实战建议8.1 代理服务的部署形态与扩容代理服务本身是无状态的(除非开了缓存),所以可以水平扩容。部署形态上,小规模用单实例加进程管理就够了;规模大了之后,前面挂负载均衡,后面跑多个代理实例,共享同一个Redis做缓存和限流计数。容器化部署时注意:代理服务的启动依赖配置文件和环境变量,把这些通过配置管理注入,不要打进镜像里。健康检查接口litellm有提供,接到编排系统的探针上就行。8.2 密钥轮换与安全实践生产环境的API Key要定期轮换。litellm支持配置多个key并做轮询,轮换时可以先把新key加进去,观察一段时间确认没问题,再把旧key移除,实现无缝切换。代理层的master_key更要保护好,它相当于整个网关的管理权限。建议用强随机字符串,定期更换,并且只在内网访问代理服务,不要直接暴露到公网。如果必须公网访问,前面加一层网关做IP白名单和额外的鉴权。8.3 监控指标与告警设置生产环境至少要监控这几个指标:各模型的调用成功率、P95延迟、token消耗速率、费用日累计、降级触发次数。成功率下降或延迟飙升通常意味着某个provider出了问题,需要及时切换。费用异常增长可能是被滥用或者路由策略失效,也要及时告警。litellm代理层暴露了Prometheus格式的指标接口,可以直接接到现有的监控体系里。如果没有Prometheus,用callback把数据推到自己的告警系统也行。8.4 版本升级的注意事项litellm迭代快,升级前一定要看changelog,特别关注breaking changes。我遇到过升级后某个provider的参数名变了导致调用失败的情况。稳妥的做法是:先在测试环境升级验证,跑一遍核心调用场景,确认没问题再上生产。生产环境升级用滚动方式,保留旧版本实例直到新版本验证通过。另外,litellm的某些功能依赖特定版本的依赖包,升级litellm时注意检查依赖冲突。用虚拟环境或容器隔离是最省心的方式。9. 我个人的使用体会用litellm这一年多,最大的感受是它把"多模型管理"这件事从脏活累活变成了配置工作。以前每接一个新模型都要写适配代码、测试、修bug,现在大部分情况改几行配置就搞定了。省下来的时间可以花在真正有价值的业务逻辑上。当然它也不是银弹。litellm的抽象层在带来便利的同时,也意味着你对底层细节的控制力会弱一些。某些provider的高级特性可能还没被litellm完全支持,或者支持得不够及时。遇到这种情况,要么等社区更新,要么在litellm之外单独处理那部分逻辑。我的建议是:把litellm当成统一入口,但保留绕过它的能力。核心的、通用的调用走litellm,特殊需求单独处理。这样既享受了统一管理的便利,又不会被抽象层限制住。对于大多数团队来说,这个平衡点是比较好把握的。
1. 从一个真实困境说起:多模型调用的碎片化之痛如果你正在做AI应用开发,大概率遇到过这样的场景:项目初期用某家的大模型API跑通了原型,效果不错,上线后想加一个备用模型做降级容错,结果发现两家的接口参数格式完全不一样。好不容易把第二家接进来,产品又提了新需求——某些场景要用便宜的小模型,某些场景要用推理能力强的大模型,甚至还要支持本地部署的开源模型。这时候你打开代码一看,光是不同厂商的SDK调用逻辑就散落在十几个文件里,每家的鉴权方式、请求体结构、返回格式、错误码定义全都不一样。这不是个别现象。我在过去一年多的项目里,前后对接过七八家不同的大模型服务,从闭源商业API到本地推理服务,每一次切换或新增都意味着一轮重复的适配工作。直到我开始系统性地使用litellm这个工具,才真正把这块的维护成本降下来。litellm的核心价值用一句话概括:它用一套统一的OpenAI格式接口,屏蔽了背后几十家模型服务商的差异。你只需要学会一种调用方式,就能在多家模型之间自由切换、组合、降级。这篇文章适合三类人看:一是正在做AI应用但被多模型适配搞得焦头烂额的开发者;二是想给现有系统加模型容错能力但不知道从哪下手的工程师;三是单纯想找一个轻量方案来统一管理多个模型调用的技术负责人。我会从实际使用角度出发,把litellm的安装配置、核心用法、代理模式、踩坑经验都讲清楚,尽量做到你看完就能直接上手。2. litellm到底解决了什么问题:统一接口的底层逻辑2.1 没有litellm之前,多模型调用有多麻烦先还原一下没有统一层时的典型代码结构。假设你要调用三家不同的模型服务,代码大概长这样:# 服务商A import vendor_a_sdk client_a = vendor_a_sdk.Client(api_key="xxx") resp_a = client_a.chat(model="model-a-v2", messages=[...], temperature=0.7) # 服务商B import vendor_b_sdk client_b = vendor_b_sdk.Client(token="yyy") resp_b = client_b.generate(model="model-b-pro", prompt=..., max_tokens=1000) # 服务商C(本地部署) import requests resp_c = requests.post("http://localhost:8000/v1/chat/completions", json={...})这还只是最简化的版本。实际项目中,你需要处理的东西包括但不限于:不同厂商的鉴权header格式、流式输出的解析方式差异、function calling的参数结构差异、token计数方式不同、错误码体系不统一、重试策略要分别实现。每接一家新服务,这些工作就要重来一遍。更麻烦的是,当你想做模型路由——比如根据请求内容自动选择最合适的模型,或者主模型超时后自动切换到备用模型——你需要在业务代码里写大量if-else判断,逻辑很快就会变得不可维护。2.2 litellm的统一抽象层是怎么设计的litellm的思路很直接:把所有模型服务商的接口都翻译成OpenAI的格式。它内部维护了一套映射关系,你传入model="gpt-4"或者model="claude-3-opus"或者model="ollama/llama3",litellm会自动识别这是哪家服务,用对应的SDK或HTTP请求去调用,然后把返回结果统一转换成OpenAI格式的响应对象。这个设计的好处在于:你只需要学一套API,就能调用所有支持的模型。切换模型时,改一个字符串参数就行,业务代码完全不用动。对于团队协作来说,这意味着新人只需要理解一种调用范式,不需要为每个服务商单独写文档。从架构上看,litellm做了几层关键抽象:Provider层:识别模型名称对应的服务商,处理鉴权和请求发送Transform层:把统一的OpenAI格式请求转换成各服务商要求的格式,再把响应转回来Router层:在代理模式下提供负载均衡、降级、重试等高级路由能力Cache层:可选的缓存机制,避免重复请求相同内容这种分层设计让litellm既能作为轻量SDK嵌入代码,也能作为独立代理服务部署,适配不同规模的项目需求。2.3 哪些场景最适合用litellm根据我的实际经验,以下几类场景用litellm收益最明显:多模型降级容错。主模型服务出现波动时,自动切换到备用模型,业务无感知。这个在线上环境特别重要,我遇到过好几次某家API临时不可用的情况,有了降级机制后用户完全没感觉到异常。成本优化路由。简单请求走便宜的小模型,复杂请求走大模型。litellm的Router支持基于规则或标签的路由策略,不需要在业务层写判断逻辑。本地与云端混合部署。开发环境用本地Ollama跑开源模型省钱,生产环境用商业API保证效果。litellm让这两者的切换只需要改配置。统一日志和监控。所有模型的调用都经过同一层,方便统一记录token消耗、延迟、错误率等指标,不用为每家单独埋点。注意:litellm虽然支持很多模型,但不同模型的参数支持程度有差异。比如某些开源模型不支持function calling,某些商业API不支持自定义temperature范围。使用前最好查一下对应provider的文档说明。3. 从零开始:安装、配置与第一个可运行示例3.1 安装方式选择与依赖管理litellm的安装本身很简单,但有几个细节值得注意。最基础的安装:pip install litellm这个命令装的是核心包,支持大部分商业API的调用。但如果你要用本地模型或者某些特定provider,可能需要额外依赖。比如用Ollama的话,需要确保本地Ollama服务在运行;用某些云服务商的SDK,可能需要单独安装对应的包。我的建议是:先装核心包,用到哪个provider再补哪个依赖。不要一上来就装litellm[all],那会拉一大堆你可能永远用不到的包,拖慢环境构建速度。版本管理方面,litellm迭代比较快,建议在requirements.txt里锁定具体版本号,避免不同环境行为不一致。我一般会写litellm==1.x.x这样的精确版本,升级时再手动测试。3.2 API Key的配置策略litellm读取API Key的方式很灵活,支持环境变量、代码传参、配置文件三种方式。推荐用环境变量,既安全又方便切换环境:export OPENAI_API_KEY="sk-xxx" export ANTHROPIC_API_KEY="sk-ant-xxx" export AZURE_API_KEY="xxx" export AZURE_API_BASE="https://your-resource.openai.azure.com"litellm会自动读取这些标准环境变量。如果你用的是非标准命名的key,也可以在代码里显式传入:import litellm response = litellm.completion( model="gpt-4", messages=[{"role": "user", "content": "你好"}], api_key="sk-xxx" # 显式传入 )提示:生产环境千万不要把API Key硬编码在代码里。用环境变量或者密钥管理服务,这是基本的安全底线。对于需要同时管理多个key的场景,比如不同团队用不同的key,litellm支持在代理模式下配置多个key并做轮询或负载均衡,这个后面讲代理模式时会展开。3.3 第一个可运行的调用示例配置好key之后,最简单的调用长这样:from litellm import completion response = completion( model="gpt-4o", messages=[ {"role": "system", "content": "你是一个简洁的助手"}, {"role": "user", "content": "用一句话解释什么是统一接口"} ] ) print(response.choices[0].message.content)如果你要调用其他模型,只需要改model参数:# 调用Claude response = completion(model="claude-3-5-sonnet-20241022", messages=[...]) # 调用本地Ollama response = completion(model="ollama/llama3", messages=[...], api_base="http://localhost:11434") # 调用Azure OpenAI response = completion(model="azure/gpt-4", messages=[...])返回的对象结构是统一的OpenAI格式,response.choices[0].message.content拿到的就是模型输出文本。流式输出也支持,加stream=True即可,返回的是一个迭代器,逐块吐出内容。这个统一性带来的最大好处是:你的业务代码只依赖litellm的接口,不依赖任何具体厂商的SDK。将来换模型、加模型,业务层几乎不用改。4. 核心功能拆解:路由、降级与流式处理4.1 用Router实现多模型负载均衡单次调用用completion就够了,但生产环境往往需要更复杂的路由逻辑。litellm提供了Router类来处理这类需求:from litellm import Router router = Router( model_list=[ { "model_name": "primary-model", "litellm_params": { "model": "gpt-4o", "api_key": "sk-xxx" } }, { "model_name": "backup-model", "litellm_params": { "model": "claude-3-5-sonnet-20241022", "api_key": "sk-ant-xxx" } } ], fallbacks=[{"primary-model": ["backup-model"]}], num_retries=2 ) response = router.completion( model="primary-model", messages=[{"role": "user", "content": "测试降级"}] )这段配置的含义是:优先调用primary-model,如果失败(超时、限流、服务错误等),自动降级到backup-model,并且每个模型最多重试2次。整个过程对调用方透明,你拿到的还是统一的响应对象。model_list里可以配置多个同名的模型实例,Router会自动做负载均衡。比如你有三个不同账号的OpenAI key,可以配三个model_name相同的条目,Router会轮询使用,避免单个key触发限流。4.2 降级策略的配置细节与实测表现降级策略的配置有几个关键参数需要理解:参数作用推荐值fallbacks定义降级链按模型能力从强到弱排列num_retries单模型重试次数2-3次,太多会拖长响应时间timeout单次请求超时根据业务容忍度设,一般30-60秒allowed_fails触发熔断的失败次数3-5次cooldown_time熔断后冷却时间30-60秒实测下来,降级机制在应对API临时波动时非常有效。我做过一个测试:故意把主模型的key设成无效的,观察降级是否触发。结果是主模型报鉴权错误后,Router在几百毫秒内就切换到了备用模型,用户侧只感觉到响应稍慢了一点,没有报错。但要注意一个坑:不是所有错误都应该触发降级。比如请求参数本身有问题(消息格式错误),这种错误换模型也没用,重试只是浪费时间。litellm默认会对大部分错误做重试,你可以通过retry_policy参数精细控制哪些错误码才重试。4.3 流式输出的统一处理流式输出是聊天类应用的标配,litellm对流的处理也做了统一。不管底层模型是SSE格式还是WebSocket,经过litellm之后都变成标准的迭代器:response = completion( model="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)这里有个细节:不同模型的流式返回中,delta字段的结构可能略有差异,但litellm会尽量归一化。不过某些模型在流式模式下不支持function calling,或者返回的tool_calls格式有差异,这些边界情况需要实测确认。注意:流式模式下如果发生降级,已经吐出的内容不会回滚。所以对于流式请求,降级策略要更保守一些,或者在前端做相应的处理。5. 代理模式:把litellm变成统一网关5.1 什么时候需要代理模式SDK模式适合Python项目直接集成,但如果你的团队有多种技术栈——比如前端直接调、Java服务调、Go服务也调——那在每个项目里都集成litellm SDK就不太现实了。这时候代理模式就派上用场了。代理模式的做法是:把litellm作为一个独立服务启动,暴露一个OpenAI兼容的HTTP接口。所有客户端都用标准的OpenAI SDK或HTTP请求来调用,完全不感知底层用的是哪家模型。这相当于在你的系统和各家模型服务之间加了一个统一网关。启动代理服务很简单:litellm --model gpt-4o --port 8000或者用配置文件启动,支持更复杂的路由和鉴权配置:model_list: - model_name: gpt-4o litellm_params: model: gpt-4o api_key: os.environ/OPENAI_API_KEY - model_name: claude litellm_params: model: claude-3-5-sonnet-20241022 api_key: os.environ/ANTHROPIC_API_KEY general_settings: master_key: sk-your-master-keylitellm --config config.yaml --port 8000启动后,任何支持OpenAI接口的客户端都可以直接连过来,把base_url指向http://localhost:8000就行。5.2 代理层的鉴权与多租户管理代理模式下的鉴权是两层:一层是客户端到代理的鉴权,用master_key或虚拟key;另一层是代理到模型服务商的鉴权,用各家的真实key。这样做的价值在于:真实key只存在于代理服务端,客户端拿不到,安全性更高。litellm支持生成虚拟key,并给每个key设置预算、速率限制、可用模型范围:# 创建一个只能访问gpt-4o、每月预算10美元的虚拟key curl -X POST http://localhost:8000/key/generate \ -H "Authorization: Bearer sk-your-master-key" \ -d '{"models": ["gpt-4o"], "max_budget": 10}'这个能力在多团队共用模型资源的场景下特别实用。每个团队拿自己的虚拟key,用量和费用独立核算,超预算自动拒绝,不需要人工盯着。5.3 代理模式下的日志与可观测性代理层天然是个做统一日志的好位置。litellm支持把每次调用的详细信息写到数据库或日志系统,包括:请求时间、模型名称、输入输出token数、延迟、费用、是否命中缓存、是否触发降级等。这些数据对于运营和优化非常关键。比如你可以分析出哪些请求其实用便宜模型就够了,从而调整路由策略;或者发现某个模型的错误率异常升高,及时切换。litellm内置了和几个主流日志系统的集成,也支持自定义callback把数据推到自己的监控平台。我在实际项目里会把代理层的日志接到内部的监控看板上,每天看一眼各模型的调用量、费用分布、P95延迟。这些指标比单纯的系统监控更能反映AI功能的真实健康度。6. 踩坑实录:那些文档里不会写的细节6.1 模型名称的坑:同名不同物litellm用模型名称来识别provider,但模型名称的命名规则有时候会让人困惑。比如gpt-4和openai/gpt-4在某些版本里行为可能不同,前者依赖自动识别,后者显式指定provider。当自动识别出错时,显式加provider前缀是最稳妥的做法。另一个坑是:不同provider可能有同名模型。比如多个平台都提供llama3,如果你只写llama3,litellm可能不知道你要调哪个。这时候必须写成ollama/llama3或together/llama3这样的完整形式。我的经验是:在生产配置里永远写完整的provider前缀,不要依赖自动识别。多打几个字符,省去很多排查时间。6.2 Token计数与费用估算的偏差litellm提供了token计数和费用估算功能,但实测下来,这个估算和实际账单有时会有偏差。原因有几个:不同provider的token计算方式不同(比如中文的切分方式差异),某些模型有额外的系统token开销,缓存命中的计费规则也不一样。所以我的建议是:litellm的费用估算用来做实时监控和预算控制没问题,但不要把它当成财务对账的依据。精确的费用还是以各provider的账单为准。如果对费用敏感,可以在代理层记录每次调用的token数,定期和实际账单做校准。6.3 超时与重试的连锁反应超时和重试配置不当会导致请求堆积。我踩过的一个坑是:把timeout设得太长(比如120秒),同时num_retries设了3次,结果一个慢请求最坏情况下要等6分钟才返回,期间占用的连接资源一直不释放。高并发时这会迅速耗尽连接池。正确的做法是:超时时间要结合业务容忍度设置,重试次数要克制。对于实时聊天场景,单次超时15-30秒比较合理,重试1-2次就够了。如果模型在30秒内没响应,大概率是服务端有问题,重试也未必能解决,不如快速降级。6.4 流式模式下降级的边界情况前面提过流式模式下降级的问题,这里展开说一下。流式请求一旦开始返回内容,如果中途出错,litellm的降级机制会尝试用备用模型重新发起请求。但这时候前端已经收到了一部分内容,重新发起的请求会从头开始生成,导致内容重复或逻辑断裂。处理方案有两种:一是在流式模式下关闭降级,出错就让前端提示用户重试;二是在应用层做处理,检测到降级时清空已显示内容,重新渲染。我倾向于第一种,简单可靠,用户体验上偶尔重试一次也能接受。7. 进阶玩法:缓存、回调与自定义Provider7.1 缓存策略的选择与命中率优化litellm支持多种缓存后端,包括内存、Redis、磁盘等。缓存的逻辑是:如果相同的请求之前已经调用过,直接返回缓存结果,不再请求模型。这对于重复性高的场景(比如FAQ问答、固定模板生成)能显著降低成本。配置Redis缓存:import litellm litellm.cache = litellm.Cache( type="redis", host="localhost", port=6379 ) response = completion( model="gpt-4o", messages=[{"role": "user", "content": "公司退货政策是什么"}], caching=True )缓存命中率取决于请求的重复度。对于开放域聊天,命中率可能很低;但对于客服问答、文档摘要这类场景,命中率可以做到相当可观。我的做法是:先开缓存观察一段时间,看命中率和成本节省数据,再决定是否长期开启。注意:缓存是基于请求内容做key的,如果请求里包含时间戳、随机数等每次都不同的内容,缓存永远不会命中。设计请求时要考虑这一点。7.2 用Callback做统一埋点litellm的callback机制允许你在每次调用的前后插入自定义逻辑,这是做统一埋点的最佳位置:from litellm.integrations.custom_logger import CustomLogger class MyLogger(CustomLogger): def log_success_event(self, kwargs, response_obj, start_time, end_time): # 记录成功调用的信息 print(f"模型: {kwargs['model']}, 耗时: {end_time - start_time}") def log_failure_event(self, kwargs, response_obj, start_time, end_time): # 记录失败调用 print(f"调用失败: {kwargs['model']}") litellm.callbacks = [MyLogger()]通过callback,你可以把每次调用的模型、token数、延迟、错误信息统一收集起来,推到自己的监控系统。这比在每个业务代码里手动埋点要干净得多。7.3 接入自定义Provider的思路litellm内置支持的provider已经很多,但如果你的模型服务不在列表里(比如公司内部自研的推理服务),也可以通过自定义provider接入。核心是实现一个转换类,把OpenAI格式的请求转成你的服务要求的格式,再把响应转回来。自定义provider需要实现的方法包括:completion(同步调用)、acompletion(异步调用)、streaming(流式处理)等。litellm的代码结构比较清晰,照着已有provider的实现改一改就能跑通。这块的文档相对简略,建议直接看源码里某个简单provider的实现作为参考。8. 生产环境部署的几点实战建议8.1 代理服务的部署形态与扩容代理服务本身是无状态的(除非开了缓存),所以可以水平扩容。部署形态上,小规模用单实例加进程管理就够了;规模大了之后,前面挂负载均衡,后面跑多个代理实例,共享同一个Redis做缓存和限流计数。容器化部署时注意:代理服务的启动依赖配置文件和环境变量,把这些通过配置管理注入,不要打进镜像里。健康检查接口litellm有提供,接到编排系统的探针上就行。8.2 密钥轮换与安全实践生产环境的API Key要定期轮换。litellm支持配置多个key并做轮询,轮换时可以先把新key加进去,观察一段时间确认没问题,再把旧key移除,实现无缝切换。代理层的master_key更要保护好,它相当于整个网关的管理权限。建议用强随机字符串,定期更换,并且只在内网访问代理服务,不要直接暴露到公网。如果必须公网访问,前面加一层网关做IP白名单和额外的鉴权。8.3 监控指标与告警设置生产环境至少要监控这几个指标:各模型的调用成功率、P95延迟、token消耗速率、费用日累计、降级触发次数。成功率下降或延迟飙升通常意味着某个provider出了问题,需要及时切换。费用异常增长可能是被滥用或者路由策略失效,也要及时告警。litellm代理层暴露了Prometheus格式的指标接口,可以直接接到现有的监控体系里。如果没有Prometheus,用callback把数据推到自己的告警系统也行。8.4 版本升级的注意事项litellm迭代快,升级前一定要看changelog,特别关注breaking changes。我遇到过升级后某个provider的参数名变了导致调用失败的情况。稳妥的做法是:先在测试环境升级验证,跑一遍核心调用场景,确认没问题再上生产。生产环境升级用滚动方式,保留旧版本实例直到新版本验证通过。另外,litellm的某些功能依赖特定版本的依赖包,升级litellm时注意检查依赖冲突。用虚拟环境或容器隔离是最省心的方式。9. 我个人的使用体会用litellm这一年多,最大的感受是它把"多模型管理"这件事从脏活累活变成了配置工作。以前每接一个新模型都要写适配代码、测试、修bug,现在大部分情况改几行配置就搞定了。省下来的时间可以花在真正有价值的业务逻辑上。当然它也不是银弹。litellm的抽象层在带来便利的同时,也意味着你对底层细节的控制力会弱一些。某些provider的高级特性可能还没被litellm完全支持,或者支持得不够及时。遇到这种情况,要么等社区更新,要么在litellm之外单独处理那部分逻辑。我的建议是:把litellm当成统一入口,但保留绕过它的能力。核心的、通用的调用走litellm,特殊需求单独处理。这样既享受了统一管理的便利,又不会被抽象层限制住。对于大多数团队来说,这个平衡点是比较好把握的。