litellm网关实践:多模型统一接入与成本追踪指南 最开始有这个需求是因为我在模拟项目X里接了三套大模型服务一套官方GPT接口、一套Claude兼容接口、还有一套自建的本地模型服务。它们的数据格式完全不同鉴权方式也不同结果就是业务代码里全是if/else分支今天调这个、明天调那个日志还混在一起成本也算不清楚。后来我把litellm作为统一的网关层放在前面所有模型请求都先走它由它来负责格式转换、负载均衡、密钥管理和成本统计。这篇文章就从一个实际使用者的角度把这套工具的搭建过程、核心能力、真实测出来的表现以及一批配置和运行阶段的坑完整地写一遍。1. litellm解决的问题与其改代码不如在代码前面加一层1.1 多模型接入的真实困境假设你的项目需要同时使用三个供应商的模型接口OpenAI格式、Anthropic格式、Google格式。你很快会发现三件事第一请求结构不一样。OpenAI用messages数组Anthropic要system字段单独拆出来Google的contents里又是另一套结构。同样的“用户和助手多轮对话”在三个SDK里要写三套构造逻辑。第二响应解析不一样。有的返回choices[0].message.content有的返回content[0].text流式输出时字段名也不统一。你不得不在主业务逻辑里写一堆关于“字段取哪里”的判断这套东西跟业务毫无关系纯属磨人。第三出错了各报各的。超时的错误码不同限流的返回结构不同重试策略也得针对不同接口单独调。我在项目里一度有超过四分之一的代码是在处理“接口格式适配”这显然是架构上有问题。1.2 网关层的思路litellm做的事情用一句话概括在你业务代码和多个模型服务之间加一个统一网关。所有模型请求发给它它负责按目标服务的格式做转换转发出去把响应再转回OpenAI格式返回。这个“OpenAI兼容”的定位很关键。OpenAI格式事实上有两重价值一是它是全网公开格式里文档最齐全的二是大量开源生态很多组件、SDK、工具链都把OpenAI兼容作为默认标准。用了litellm之后业务侧客户端不用换SDK所有代码围绕一个固定格式写后面要换模型或者新增模型只改服务端配置不动代码。做个类比你不希望每次出差都重学当地插座的形状于是随身带了个转换插头。litellm就是那个转换插头连接的是业务代码和各类模型服务。1.3 它到底是什么形态很多第一次接触litellm的人会把它当成“Python库”因为装的时候就是pip install litellm而且它确实提供litellm.completion()这种函数直接调用。但这只是一个“库模式”。它还有更重要的“服务模式”启动一个本地服务进程监听8000端口提供/v1/chat/completions这样的接口。这种模式下你的前端、后端、测试脚本、低代码平台、第三方工具都可以像调用OpenAI一样调用它由它内部再去路由到真正的大模型。所以准确地说litellm是一个可嵌入的大语言模型网关。它既能当SDK用也能当独立服务部署。我在实际项目里用的是服务模式这也是我最推荐的方式因为业务代码里可以直接配一个base_url就能切换到任意后端模型。2. 搭建与配置从安装到一次真实可用的请求2.1 最简安装与启动先把环境准备好。建议使用Python 3.9以上版本最好准备一个独立虚拟环境不要直接装到系统里python -m venv lm_gateway source lm_gateway/bin/activate pip install litellm[proxy][proxy]这个附加依赖会装齐所有跑代理服务需要的组件。如果你只拿它当SDK用直接pip install litellm就够但那样很多已有功能比如UI管理、数据库记录用不上。安装完成后可以先跑一个空配置启动一下litellm --port 8000如果能在浏览器里看到管理页面说明基本环境正常。查对应端口是litellm什么时候开始提供内置管理端的这个我们要到文章第三部分再展开。2.2 第一份配置文件的结构litellm的核心配置是一个YAML文件比较常见的启动方式是指定配置litellm --config config.yaml --port 8000一份最基础、能跑通的配置大概是这样的model_list: - model_name: gpt-sample litellm_params: model: openai/gpt-4o-mini api_key: os.environ/OPENAI_API_KEY - model_name: claude-sample litellm_params: model: anthropic/claude-sonnet-4-20250514 api_key: os.environ/ANTHROPIC_API_KEY这里有两个名字容易搞混model_name这是对外暴露的名字。你的业务代码里传的model参数写gpt-samplelitellm就看到这个名字然后去匹配内部配置。litellm_params.model这是真正要调用的模型完整名。格式是“提供商/模型名”比如openai/gpt-4o-mini、anthropic/claude-sonnet-4-20250514、google/gemini-2.0-flash。所以实际的意义是你可以对外暴露一个gpt-sample后端偷偷换成别的模型业务代码完全不用动。这个设计在厂商迁移、灰度切换的时候非常省事。2.3 密钥管理方式配置文件里不要直接写明文key。litellm支持从环境变量读取写法是os.environ/变量名启动前在系统里导入即可export OPENAI_API_KEYsk-xxx export ANTHROPIC_API_KEYsk-ant-xxx litellm --config config.yaml --port 8000也支持本地.env文件如果同时存在推荐用环境变量导入的方式在容器编排里更容易管理。我在本地开发时用一个.env文件生产环境直接在部署平台配置环境变量原理一样。2.4 用curl做第一次连通性验证服务启动后模拟一个用户请求确认路由和密钥都配置正确curl http://localhost:8000/v1/chat/completions \ -H Content-Type: application/json \ -d { model: gpt-sample, messages: [{role: user, content: 你好帮我做一下自我介绍}] }如果一切正常你会收到一个OpenAI格式的响应里面包含choices字段、model字段显示的是你对外暴露的名字usage字段则记录token消耗。到这一步你的业务代码就已经可以用一个统一的OpenAI SDK指向http://localhost:8000了。一个很小的但容易忽略的点默认情况下litellm代理不校验调用者身份谁拿到8000端口的地址都能调用配置里的模型。正式环境里一定要开启master_key下面会讲或者用Apikey管理模块。3. 路由、预算、成本追踪litellm真正值钱的三个能力3.1 多模型路由策略如果你的服务背后挂了多个相同能力的模型litellm可以做路由控制支持权重、优先级和fallback三种方式。权重轮询适用于“多个模型能力接近、希望按比例分摊流量”的场景model_list: - model_name: mix-model litellm_params: model: openai/gpt-4o-mini api_key: os.environ/OPENAI_API_KEY routing_strategy: weight: 70 - model_name: mix-model litellm_params: model: anthropic/claude-sonnet-4-20250514 api_key: os.environ/ANTHROPIC_API_KEY routing_strategy: weight: 30fallback则解决“主模型不可用/限流时自动换备用模型”的需求。注意fallback不是靠配置完成的而是在每次请求时指定from litellm import completion resp completion( modelopenai/gpt-4o-mini, messages[{role: user, content: 你好}], fallbacks[{model: anthropic/claude-sonnet-4-20250514}] )当主模型返回5xx、429、超时等情况时litellm会自动尝试备用模型。我在实际项目中把主模型设成走已训练私有化的模型备用模型设成通用云API。效果非常明显上游一抖动自动就兜底了业务方几乎感知不到。判断参数还有个priority它和权重是两套独立机制。权重只是分配流量比例priority则是在“该用哪条路由”这一排序问题上起作用比如同样一个对外model名配了多个主备模型按优先级顺序尝试。两套可以同时用但刚开始建议只用一个避免行为不符合预期。3.2 预算和配额管理给每个用户、每个项目一把独立的尺子如果litellm只做“多模型统一转发”那它跟普通的反向代理没太大区别。真正拉开差距的是它在“API Key管理”和“预算控制”上做得很完整。启动代理时加上--master_key并开启数据库记录litellm --config config.yaml --master_key sk-master-123456 --port 8000然后可以通过管理API创建分发给特定用户或项目的虚拟Keycurl http://localhost:8000/key/generate \ -H Authorization: Bearer sk-master-123456 \ -H Content-Type: application/json \ -d {models: [gpt-sample], max_budget: 5, duration: 30d}这里生成的Key是litellm自己的虚拟Key业务侧拿它来调用litellmlitellm再转用真实模型密钥。好处是你不需要把OpenAI的Key发给每个内部用户谁的Key超预算了就直接失效即使泄露了也只影响一个有限范围。预算控制可以分为几个维度Key级别给每个Key设置最大消费金额。User级别针对最终使用者的唯一标识。Team级别以团队或项目为单位限制总额。IP级别限制某个来源IP的调用量。我做模拟项目X的对外接口时只开放了key级别限制和一个30天过期时间所有内部调用都打在一个虚拟Key上月底一拉消费列表就能查出哪个业务线开销最大。3.3 成本追踪让每一笔token花销都有据可查成本追踪有两种很典型的用法实时查询每次请求完成后litellm会把spend本请求花费和total_tokens等参数写进数据库。通过/spend/logs接口输入一个Key就能看到这个Key的所有请求记录、模型、token数、消费金额、响应时间。定期报表通过/global/spend可以拿到所有Key在某个时间窗口内的总消费。做月度对账的时候我一般是把数据拉出来按model维度或者按key维度分组很快就知道钱主要花在哪块。价格数据来源上litellm内置了大量模型的价格信息也会定期更新。如果某个模型的价格没覆盖到可以在litellm_params里手动指定input_cost_per_token、output_cost_per_token或者在config里定义一个自有价格表。它在成本跟踪上的精细度对需要做内部结算和成本归因的团队很实用。3.4 模型别名与配置文件热加载除了路由、预算和成本另一个实际很有用的小功能叫“模型别名管理”。比如你内部约定一个别名叫fast-answer希望它背后的实际模型优先用低延迟模型如果效果不够好再切到更强模型。配置里可以这样model_list: - model_name: fast-answer litellm_params: model: openai/gpt-4o-mini - model_name: strong-answer litellm_params: model: anthropic/claude-sonnet-4-20250514然后业务侧永远传fast-answer。今天你想把fast-answer指向别的模型改配置重启全部归一。不改业务一行代码。当然要不要重启这件事得看你的部署方式。litellm支持通过管理API动态新增、修改、删除模型配置不需要重启进程但这个功能在需要持久化数据库的情况下才比较顺手。4. 实测记录在模拟项目X里的真实表现与数据4.1 跨厂商模型切换的测试效果我在模拟项目X里搭了一个简单的AI客服DEMO用户提问系统调模型回答同时记录token消耗。第一次配置时我故意在config里同时放了一个国内厂商模型和一个国外商用模型并让业务侧用同一个OpenAI SDK调用。效果是所有请求都通过http://localhost:8000/v1/chat/completions发进去请求体完全一样只是model字段不一样。服务端根据不同model名分别路由到不同厂商返回结构始终一致。整个切换过程中业务代码零改动。比较直观的体验是过去如果我从模型A切到模型B往往要改请求构造、改响应解析、改错误处理代码现在只需要在litellm配置里增删一条litellm_params然后测试一次就能继续用了。4.2 流式输出和工具调用的兼容情况模型在聊天场景和工具调用场景下返回格式稍有差异。litellm的默认目标是“看起来完全是OpenAI”所以它对不同厂商响应做了不少适配。流式输出把OpenAI SDK的streamTrue传给litellmlitellm会把上游流式响应转成OpenAI的data: [DONE]格式。这样前端用EventSource就能无痛对接。实测中一个接入Gemini流式响应的请求到了业务侧看到的字段和直接调OpenAI流式接口几乎没有差别。工具调用function calling主流模型现在都支持工具调用但参数结构略有差异。litellm在转换后会把工具调用的相关信息放在OpenAI格式的tool_calls里。实际测了一个用Claude模型做“天气查询工具调用”的流程业务侧完全按OpenAI的tool_calls标准解析没有改任何代码。不过在工具调用场景里有个要注意的点模型自身支持的工具域差异仍然存在。litellm能统一的是“传输和格式”不是“每个模型都有一模一样的原生能力”。比如某个模型限制了单次请求最大工具数量它在litellm转换后仍然受这个限制。业务侧最好还是对“传多个工具”的场景做好降级方案。4.3 成本追踪的实际数据我在这套测试跑了两周大概处理了6000多次请求总token消耗约4200万。最后拉成本报表时能看到按日期的消费分布哪天的调用量异常高。按模型汇总哪个模型实际占比最大。按虚拟Key维度哪个具体业务线消耗最多。有一次我发现某一类请求延迟突然升高查数据库日志看到是某个模型返回了429限流而我配置的fallback模型没有生效因为fallback只在completion()函数指定的时候才触发代理模式的模型路由则需要配置更细一层的routing_strategy。这个坑我会在下一节详细讲。4.4 延迟与稳定性分析延迟主要取决于三块litellm本身的转发开销、上游模型的响应速度、网络链路质量。在局域网内自测大部分请求从业务发起到业务收到完整响应litellm自身带来的额外耗时在50毫秒以内。对于一次动辄几秒钟的大模型推理来说这点开销基本无感。稳定性上litellm做对了一件事它默认维护了连接池上游断连时能快速重试。只要不是配置写错服务本身可以长期保持稳定我更推荐在生产环境用Docker容器来跑docker run -d --name litellm-gateway \ -e OPENAI_API_KEY$OPENAI_API_KEY \ -e ANTHROPIC_API_KEY$ANTHROPIC_API_KEY \ -v $(pwd)/config.yaml:/app/config.yaml \ -p 8000:8000 \ ghcr.io/某个镜像仓库/litellm:main-latest如果是从官方镜像拉拉下来后启动命令里指定--config /app/config.yaml。Docker化的最大价值不是节省安装时间而是让你能同时管理多个环境配置。5. 配置与运行阶段的常见坑5.1 最经典的混淆model_name和litellm_params.model很多人拿着文档学习的时候都会犯同一个错——把model_name当成“要调用的真实模型名”写进业务代码但业务代码向litellm发送请求时model字段必须填配置文件里的model_name而不是litellm_params.model里的完整名。举个例子如果你在业务代码里传了anthropic/claude-sonnet-4-20250514但配置里只定义了claude-sample你会收到一个“模型不存在”的错误。正确做法是业务侧只用model_name配置文件里才知道它背后映射到谁。一开始不习惯踩过一次之后就再也不容易忘记对外名称是配置自由定义的对内名称带提供商前缀。5.2 环境变量没有正确加载导致的重试风暴我在二次修改配置后出现过一次很隐蔽的问题key一直鉴权失败。查日志发现每次请求都触发litellm自动重试重试又在同一个问题上反复。原因就是我在config里写了api_key: os.environ/OPENAI_API_KEY但启动服务的那条终端没加载这个环境变量。更隐蔽的是服务启动时os.environ/OPENAI_API_KEY如果为空litellm不一定会立刻报错因为它可能自动尝试从别的地方找key。最终请求被转发到上游之后才失败失败后又重试整个链路看起来就是慢、不稳定。排查定位方法很简单启动前先确认能否在终端里直接用Python读取到对应环境变量结构。确保环境变量真的生效之后再启动代理服务。5.3 负载均衡权重和fallback行为上的误判有些配置看起来能用实际不会按预期走。比如你给同一个model_name配了两个模型一个权重90一个权重10你可能会以为“流量按90:10分”。结果最近突然发现90这个模型返了4xx错误另一条模型却没有自动接管。原因正如前文所说代理模式下“负载均衡”和“故障备援”是两回事。代理模式要实现自动故障转移可以在litellm_params里配置model_list: - model_name: primary-gpt litellm_params: model: openai/gpt-4o-mini api_key: os.environ/OPENAI_API_KEY routing_strategy: fallback_model: anthropic/claude-sonnet-4-20250514配置后当主模型返回某种特定错误时litellm会根据配置满足条件地切到fallback模型。初学阶段建议先把“轮询”和“故障备援”分开测试确认每种配置下各自的触发条件再组合使用。5.4 非对话模型与自定义API地址的接入litellm支持的不只是对话补全/v1/chat/completions还有/v1/completions、/v1/embeddings、/v1/moderations等接口。我在做检索增强生成里的向量化环节时直接用/v1/embeddings来调通embedding模型OpenAI SDK的embedding.create()不用改带一个模型名就能命中litellm配置。如果你要接本地或者自定义AI服务比如一个不通公网的自建推理服务可以在配置里指定api_basemodel_list: - model_name: local-llm litellm_params: model: openai/custom-model api_key: fake-key api_base: http://127.0.0.1:8888/v1注意模型前缀要写成openai/custom-model引导litellm走OpenAI兼容协议的解析方式。实际请求会转发到api_base指定地址。这个接法测试本地模型非常方便。5.5 日志与监控没日志的时候排查是最耗时的litellm默认的请求日志覆盖了每一次调用的完成时间、总token、状态码、缓存命中情况。如果你在容器里跑建议把日志挂出来用日志采集工具统一收集。暴露指标以后可以在Grafana里直接看到延迟趋势和费用趋势。我遇到过接口偶尔超时的问题单独调接口是好的就是某些时段慢。这种问题没有日志基本上只能靠猜。后来把平均响应时间和分位数响应时间都拉出来才定位到是上游模型服务在高峰期排队。litellm的日志在排查这类问题上帮助很大建议第一天上手的时候就养成看日志的习惯。6. 升级到生产环境的几个建议6.1 开数据库用好持久化能力本地测试时可以不接数据库重启后就丢了一些统计信息。但生产环境建议用Postgres作为存储这样虚拟Key、预算信息、消费记录都能持久化。配置里加一行database_url即可启动首次启动会自动建表。我的经验是一旦挂上数据库就相当于你拥有了一个内部所有模型调用的“审计日志库”。不管从成本角度还是稳定性排查角度价值都很直接。6.2 缓存策略减少重复开销针对重复提问litellm支持在网关层做缓存命中后不重新请求上游模型。可以在请求里带上caching: True并通过cache_args控制参数from litellm import completion resp completion( modelgpt-sample, messages[{role: user, content: 今天的天气怎么样}], cachingTrue )实测中对于完全相同的消息第二次响应时间从原来的两三秒降到了几十毫秒费用也变成了0。代价是缓存如果设置不当会给出过时答案所以只建议对“固定知识类、不强调时效性”的问题开。6.3 多环境管理dev、staging、prod三套环境建议用不同配置文件通过部署时传入不同config路径来切换。不要在同一个配置文件里用条件注释控制多套环境那会越来越难维护。规范做法是把config文件作为环境配置的一部分和代码一起做版本管理。6.4 监控告警与应用集成litellm的网关层天然能看到所有大模型调用的状态。如果出现特定错误码比如429限流、成本突然飙升、某个模型连续多次失败可以接入告警。个人小团队用简单的方式就可以轮询接口记录阈值触发就推提醒。大团队则建议直接接入现有监控体系配合自带的指标导出能力。这些告警在业务量上来之后尤其重要。因为大模型服务的SLA“看起来”由供应商保障但配置错误、Key失效、紧急限流这类问题在网关层反而是最容易提前发现和止损的。我在模拟项目X里跑通这套方案之后最大的体会是litellm并没有让任何一类模型变得“更强”它只是把“同时用多少个模型”这件事的成本从“改代码”变成了“改配置”。如果你现在还在代码里硬编码多个供应商的请求和解析逻辑建议尽早把网关层加上。刚开始会多一点服务要维护但一旦模型多起来、调用方多起来这笔投入的回报会非常明显。另外提醒一句每次升级litellm版本前记得看一眼变更日志这个项目迭代快偶尔会有配置字段调整养成“先查后升”的习惯能省不少事。