基于LiteLLM搭建多厂商AI网关:统一API、虚拟密钥与生产部署实践

发布时间:2026/10/3 3:03:17
基于LiteLLM搭建多厂商AI网关:统一API、虚拟密钥与生产部署实践
做AI应用开发这半年我最大的感受是模型厂商越来越多但把多个厂商的API真正用顺反而越来越难。OpenAI、Anthropic、Azure再加上本地的Ollama每家都有自己的鉴权方式、请求格式和限流策略。而把厂商原始密钥直接发给团队里的每个人跟把银行卡密码贴在工位上没什么区别。于是我基于LiteLLM搭了一个多厂商AI代理平台把上游模型统一成OpenAI兼容格式用虚拟密钥做权限隔离再挂上预算、限流和日志。从零到生产部署前后花了两周时间。这篇文章就是那次部署的完整记录配置和踩坑都在打算自建AI网关的朋友可以直接参考。开始之前先说清楚LiteLLM是个开源项目它提供两样东西一个是Python SDK负责把各家大模型API翻译成统一调用方式另一个是LiteLLM Proxy一个基于FastAPI的代理服务也就是大家常说的litellm proxy。我们团队日常用的主要是后者。它可以对外暴露一个OpenAI兼容接口内部把请求转发给不同的模型厂商顺便把密钥管理、预算控制、负载均衡这些事一起做了。我注意到社区里有些模型切换工具也会把litellm proxy当统一入口来用可见这个网关已经不只是个人玩具了。1. 先想清楚一个问题多厂商API直接调用到底卡在哪1.1 各家协议不统一代码里全是if分支如果你写过直接对接多家大模型API的代码一定熟悉这个场面OpenAI用Authorization: BearerAnthropic用x-api-keyAzure还要额外拼api_version和api_baseGoogle的Gemini是另一套generateContent协议。表面上大家都在做聊天补全实际请求体结构、stream事件格式、tool calling的定义都有差异。我最早接入三家厂商时业务代码里长满了类似这样的东西if vendor anthropic: headers {x-api-key: api_key, anthropic-version: 2023-06-01} body {...} elif vendor azure: headers {api-key: api_key} body {...}每个模型单独写一套调用逻辑厂商升级协议就要跟着改一遍。更麻烦的是客户端SDK不统一团队成员有人用openai库、有人用anthropic库、有人直接requests手搓维护成本全摊在业务侧。1.2 密钥越分越多权限越来越失控多厂商意味着多把主密钥。每来一个新人就得把OpenAI、Anthropic、Azure的key都发一遍。半年后这堆密钥散落在聊天记录、.env文件、CI变量里。有一次我扫描仓库发现一个同事不小心把含真实API key的.env提交到了GitHub。虽然马上轮换了但那一整天我都在想这个key有没有被别人拉走。密钥泄露通常不是技术问题是管理问题你没办法给某个人单独发一把只能访问某模型的key也没法单独撤销其中一个人的权限更没法核算这个月测试环境到底烧了多少钱。1.3 为什么选LiteLLM而不是自研网关我们也讨论过自研网关。需求列出来协议转换、虚拟密钥、限流、预算、日志、负载均衡、失败重试。真要做得能上生产一个全职工程师至少干三周还不算后续迭代。商业网关用过一阵子功能全但是黑盒而且按量计费对于模型调用量大的团队是一笔额外开销。LiteLLM出现在这个位置很合适开源、本地部署、支持100多家模型厂商核心代码是Python出问题能看源码。它解决的不是调用某个模型的问题而是如何统一管理所有模型调用的问题。你不需要它支持100家只需要它支持你常用的3到5家就够了。数据也留在自己的基础设施里请求路径、token消耗都能审计。2. 读懂LiteLLM Proxy的分层职责开跑前先做决策2.1 网关替你扛了哪五件事部署LiteLLM Proxy之前建议先理解它在整个架构里的位置客户端永远只跟网关对话网关再跟上游模型厂商对话。它至少帮你做了五件事协议归一客户端用OpenAI格式发请求网关翻译成Anthropic、Azure、Ollama等各家格式。认证与鉴权你给团队发的是虚拟key不是厂商主key。虚拟key可以被独立禁用以适配权限最小化原则。流量治理超时控制、重试、fallback、负载均衡这些都不需要业务方关心。预算与限流可以按key限制每分钟请求数、每分钟token数也能设置总预算上限。可观测性请求日志、token消耗、模型延迟集中在网关这一层采集。打个比方没有网关每个业务方都要自己跟多个上游厂商打交道各自记账有了网关上游模型是后端资源池业务方只面对一个统一入口。2.2 部署形态与周边依赖的现实选择部署方式取决于你的规模。我们团队一开始就是单机Docker Compose一台2核4G的服务器就够跑。数据库用PostgreSQLRedis视情况加Redis用来做分布式限流计数和缓存如果只是单实例、qps不高可以暂缓。这里有个重要决策数据库别用默认SQLite就直接上生产。LiteLLM默认用SQLite存虚拟key、预算、日志单机低并发没问题一旦多实例部署或者写入量大SQLite的并发写锁会变成明显瓶颈。我们是在压测阶段遇到database is locked错误后才切到PostgreSQL的。切换本身不麻烦把DATABASE_URL换成PostgreSQL连接串就行但建议从一开始就用PostgreSQL省得后面迁移。2.3 模型路由的工作模式一个入口N条出口LiteLLM Proxy的路由逻辑很多新手会搞混。客户端请求时传的model名是你在config.yaml里自定义的模型别名不是上游真实模型ID。网关拿到这个别名去model_list里找到对应的litellm_params再从这些参数里取出真实provider和真实model完成转发。这意味着你可以做很多灵活映射两个不同厂商的模型都叫gpt-4o不会冲突因为你在网关层给它们不同的别名同一个模型配多个上游key可以负载均衡某个模型不可用时可以配置fallback切到另一个模型。这些都是业务方无感知的他们只看到我请求了gpt-4o网关返回了结果。3. 本地跑通一个三厂商代理安装、配置与验证3.1 三分钟装好Proxy本地调试我直接用pip安装一条命令搞定pip install litellm[proxy]装完启动litellm --config config.yaml --port 4000如果你不想污染本地Python环境用Docker镜像也是一样的docker run --name litellm-proxy -p 4000:4000 \ -v $(pwd)/config.yaml:/app/config.yaml \ ghcr.io/berriai/litellm:main-latest注意镜像tag和版本的关系main-latest是滚动更新生产环境我更建议锁定一个具体版本号避免上游更新带来不兼容变更。3.2 config.yaml模型清单怎么写才不乱这是整个部署最核心的文件。我放一个比较完整的示例覆盖OpenAI、Anthropic、Azure和本地Ollama四类常见上游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: azure-gpt-4o litellm_params: model: azure/gpt-4o api_key: os.environ/AZURE_API_KEY api_base: https://your-resource.openai.azure.com/ api_version: 2024-06-01 - model_name: local-llama3 litellm_params: model: ollama/llama3.1 api_base: http://127.0.0.1:11434 general_settings: master_key: os.environ/LITELLM_MASTER_KEY几个关键点model字段里的前缀很重要openai/、anthropic/、azure/、ollama/告诉LiteLLM用哪套协议翻译器去处理这个模型。前缀写错是最常见的坑。model_name是暴露给调用方的名字你可以自定义。生产环境我倾向于在别名里带上业务含义比如gpt-4o-live、claude-sonnet-test一眼能看出用途。os.environ/XXX的意思是运行时从环境变量取值不要直接把密钥写进yaml。密钥应该放在.env或者密钥管理服务里。master_key是网关的管理员密钥用它来生成和管理虚拟key。这个key也要独立设置别跟任何上游厂商key共用。3.3 用OpenAI SDK直连Proxy完成首次验证配置好之后验证方法很简单客户端的base_url改成你的网关地址就行。from openai import OpenAI client OpenAI( api_keysk-xxx-virtual-key, base_urlhttp://localhost:4000/v1 ) resp client.chat.completions.create( modelclaude-3-5-sonnet, messages[{role: user, content: 你好介绍一下你自己}] ) print(resp.choices[0].message.content)看到这里你可能会问api_key不是上游的Anthropic key网关怎么知道该用哪个真实key答案是在config.yaml里LiteLLM用master_key生成了管理接口通过管理接口发放虚拟key虚拟key再关联到你允许访问的模型列表。请求到达网关时网关校验虚拟key查到这个key有权访问claude-3-5-sonnet就去model_list里找到对应上游key完成转发。如果想快速验证一条请求走的是哪条上游链路用curl也行curl http://localhost:4000/v1/chat/completions \ -H Authorization: Bearer sk-xxx-virtual-key \ -H Content-Type: application/json \ -d {model: claude-3-5-sonnet, messages: [{role: user, content: ping}]}然后看LiteLLM的启动日志它会打出转发目标、模型延迟、token用量。这一步确认日志能正常记录后面做成本核算就有依据了。4. 从能用到生产可用虚拟密钥、预算、限流与可观测性4.1 虚拟密钥把主密钥关进保险箱跑通Proxy只完成了第一步真正让团队用起来必须引入虚拟密钥体系。LiteLLM的管理接口是/key/generate用master_key调用给不同成员生成不同key。curl -X POST http://localhost:4000/key/generate \ -H Authorization: Bearer sk-master-key \ -H Content-Type: application/json \ -d { user_id: zhangsan, models: [gpt-4o, claude-3-5-sonnet], max_budget: 100.0, rpm_per_key: 60, tpm_per_key: 500000 }返回结果里有一个sk-开头的虚拟key。把这个虚拟key发给团队成员上游厂商的真实key永远留在网关服务器上。虚拟key的好处是粒度可控每个key能访问哪些模型清清楚楚写在授权里。独立撤销某个人的key泄露了单独禁用这个key就行不影响其他人。用量可查按key查预算和token消耗月底对账不用猜。我强烈建议在团队里推行一人一key一项目一key规范尽量不要共享同一个虚拟key。4.2 速率限制与预算防止深夜跑飞账单大模型API的账单是典型的事后才知道疼。白天调几回没感觉某天凌晨一个定时任务写了个死循环一觉醒来几百美元没了。LiteLLM的预算和限流机制就是干这个的。预算限制就写在虚拟key上max_budget字段指定这个key最多能花多少钱单位是美元。达到上限后网关直接拒绝请求返回429或403不会把请求转发给上游。除了总预算还有rpm_per_key每分钟请求数和tpm_per_key每分钟token数两个维度适合控制单个key的并发冲击。如果你希望整个团队共享一个总预算可以给多个key挂到同一个team或wallet下。一个常见的配置是每个成员有自己的key团队一个总budget谁烧得最多一目了然超了大家一起停。4.3 日志与追踪出了问题有人可查生产环境没有日志等于裸奔。LiteLLM本身记录每次请求的模型、token数、延迟、花费默认存在数据库里。配合管理接口/spend/logs可以按key、按模型、按时间范围查询调用详情。我们接的是PrometheusLiteLLM暴露/metrics端点可以直接拉取token消耗、请求延迟这类指标到Grafana面板。一个小团队不需要多复杂的监控但至少要做到某个key的调用量突然飙升时能及时发现能追溯到是哪个业务在调。一个实用的习惯每次接入新模型先用一个独立虚拟key跑几天测试流量观察它的延迟分布和token消耗是否符合预期再放量到生产。这个key天然就是一个隔离环境。5. 生产部署Docker systemd Nginx 的完整落地方案5.1 容器化一条命令拉起服务本地验证通过后我们把它搬到了服务器上。生产环境我用Docker Compose管理LiteLLM和PostgreSQLRedis暂时不需要因为单实例qps不高。services: litellm: image: ghcr.io/berriai/litellm:main-latest restart: always ports: - 4000:4000 volumes: - ./config.yaml:/app/config.yaml environment: - DATABASE_URLpostgresql://litellm:strongpasswordpostgres/litellm env_file: - .env depends_on: postgres: condition: service_healthy postgres: image: postgres:16 restart: always environment: - POSTGRES_USERlitellm - POSTGRES_PASSWORDstrongpassword - POSTGRES_DBlitellm volumes: - pgdata:/var/lib/postgresql/data healthcheck: test: [CMD-SHELL, pg_isready -U litellm] interval: 5s timeout: 3s retries: 5 volumes: pgdata:.env文件里放所有上游厂商的真实key注意文件权限至少是600。第一次启动后LiteLLM会自动初始化数据库表结构不需要手动建表。如果新版本提供了数据库迁移命令按官方文档走一遍更稳妥。5.2 用systemd托管进程开机自启与崩溃重启Docker Compose的restart: always只能保证容器挂了自动拉起来但机器重启后如果是手动docker compose up启动的它不会自动恢复。所以我用systemd托底。写一个/etc/systemd/system/litellm.service[Unit] DescriptionLiteLLM Proxy Afternetwork-online.target docker.service Requiresdocker.service [Service] Typeoneshot RemainAfterExityes WorkingDirectory/opt/litellm ExecStart/usr/bin/docker compose up -d ExecStop/usr/bin/docker compose down Restarton-failure RestartSec15 [Install] WantedBymulti-user.target然后systemctl daemon-reload systemctl enable --now litellm这样机器重启后docker服务起来litellm容器会自动跟着起来。注意Typeoneshot这种写法更适合把docker compose封装成systemd服务如果你更习惯直接管容器也可以用docker run --restartalways加systemd统一拉起docker服务的方案。5.3 反代与HTTPS让网关对外可服务LiteLLM默认监听4000端口生产环境不建议直接把端口裸奔在公网。我们用Nginx做反向代理顺手把HTTPS做了。server { listen 443 ssl http2; server_name llm.example.com; ssl_certificate /etc/nginx/certs/llm.example.com.pem; ssl_certificate_key /etc/nginx/certs/llm.example.com.key; location / { proxy_pass http://127.0.0.1:4000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_http_version 1.1; proxy_set_header Connection ; proxy_buffering off; proxy_read_timeout 600s; client_max_body_size 20m; } }这里有几个细节值得提proxy_buffering off一定要开。LLM响应是流式的如果Nginx开了缓冲它会攒一批数据才发给客户端前端看起来就像一直没响应体感很糟糕。proxy_read_timeout调到600秒。上游模型生成长文本时可能几十秒才返回第一个token默认60秒超时会让请求中断。client_max_body_size设大一点。有些请求可能携带较大的上下文历史默认1m可能会挡掉正常请求。6. 实测踩坑清单与性能调优文档没写的那些事6.1 最容易翻车的五个坑这个项目整体不难真正耗时间的都是细节问题。我把踩过的坑整理成一张表按频率排序坑现象解决办法model_name冲突两个厂商模型都叫gpt-4o路由串线在config.yaml里给不同上游起不同的model_nameSQLite并发写锁高并发时报database is locked生产环境换PostgreSQL不要用默认SQLite未配置超时上游模型卡住网关请求挂死在litellm_settings里配置request_timeout并保证Nginx的超时时间更大Nginx缓冲流式响应前端看着没响应其实数据已在缓冲区关闭proxy_buffering关掉Connection头部复用环境变量泄露docker inspect能看到容器环境变量里的key收紧.env文件权限或使用docker secret避免在compose里明文写key第一条值得展开说。有一次我们同时接入了OpenAI的gpt-4o和Azure的gpt-4oconfig里都叫gpt-4o结果发现同样的请求有时走OpenAI有时走Azure还不好复现。后来统一把Azure那个改名成azure-gpt-4o问题立刻消失。模型命名这事一定要在网关层规划好aliasing规则最好写进团队文档。6.2 性能与稳定性调优参数如果网关的并发量上来了有几点值得调首先是连接池。LiteLLM底层调用上游模型时如果没有连接池每次请求都要重新建立TCPTLS握手延迟和资源消耗都很亏。可以在配置里开启litellm_settings: connection_pool: true connection_pool_kwargs: pool_size: 100 max_retries: 3其次是worker数。单进程跑不满多核LiteLLM启动时可以用--num_workers 4开启多workerqps有明显提升。同时注意每个worker都有自己的数据库连接PostgreSQL连接数要留足余量。再就是客户端侧的keep-alive。业务方如果用的是OpenAI SDK底层requests库默认会复用连接但如果你在自己代码里每次new一个client连接复用就谈不上了。让业务方把client对象做成长生命周期单例对网关压力是质的区别。6.3 个人使用下来的补充体会活跃社区的好处是踩坑答案基本都能搜到但版本迭代快网上教程里的config写法可能和当前版本对不上。我的办法是遇到参数不生效第一反应去翻官方docs的config_settings页面第二反应去GitHub搜config示例。LiteLLM的配置文件结构一直在演进依赖旧教程容易白折腾。还有一件事升级版本前先看changelog。我们有过一次从旧版本升到新版本虚拟key生成接口的字段从models变成了model结果部分自动化脚本直接报错。好在影响面可控。现在我对关键配置文件和调用脚本都做了版本锁定升级前先在测试环境跑一遍完整的key生成、转发、查询流程。生产跑稳定之后这套网关联调的优势会越来越明显。新增一个模型就是往config.yaml里加一段reload服务然后给相应权限的人开个虚拟key全程不需要业务方改代码。有时候我甚至会想如果一开始就上LiteLLM之前那些散落在各处的if分支和头疼的密钥管理根本不会存在。