拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

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

基于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: Bearer,Anthropic用x-api-key,Azure还要额外拼api_version和api_base,Google的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的服务器就够跑。数据库用PostgreSQL,Redis视情况加,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_key="sk-xxx-virtual-key", base_url="http://localhost:4000/v1" ) resp = client.chat.completions.create( model="claude-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、按模型、按时间范围查询调用详情。

我们接的是Prometheus,LiteLLM暴露/metrics端点,可以直接拉取token消耗、请求延迟这类指标到Grafana面板。一个小团队不需要多复杂的监控,但至少要做到:某个key的调用量突然飙升时,能及时发现,能追溯到是哪个业务在调。

一个实用的习惯:每次接入新模型,先用一个独立虚拟key跑几天测试流量,观察它的延迟分布和token消耗是否符合预期,再放量到生产。这个key天然就是一个隔离环境。

5. 生产部署:Docker + systemd + Nginx 的完整落地方案

5.1 容器化:一条命令拉起服务

本地验证通过后,我们把它搬到了服务器上。生产环境我用Docker Compose管理LiteLLM和PostgreSQL,Redis暂时不需要,因为单实例qps不高。

services: litellm: image: ghcr.io/berriai/litellm:main-latest restart: always ports: - "4000:4000" volumes: - ./config.yaml:/app/config.yaml environment: - DATABASE_URL=postgresql://litellm:strongpassword@postgres/litellm env_file: - .env depends_on: postgres: condition: service_healthy postgres: image: postgres:16 restart: always environment: - POSTGRES_USER=litellm - POSTGRES_PASSWORD=strongpassword - POSTGRES_DB=litellm 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] Description=LiteLLM Proxy After=network-online.target docker.service Requires=docker.service [Service] Type=oneshot RemainAfterExit=yes WorkingDirectory=/opt/litellm ExecStart=/usr/bin/docker compose up -d ExecStop=/usr/bin/docker compose down Restart=on-failure RestartSec=15 [Install] WantedBy=multi-user.target

然后:

systemctl daemon-reload systemctl enable --now litellm

这样机器重启后,docker服务起来,litellm容器会自动跟着起来。注意Type=oneshot这种写法更适合把docker compose封装成systemd服务,如果你更习惯直接管容器,也可以用docker run --restart=always加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_name
SQLite并发写锁高并发时报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-4o,config里都叫gpt-4o,结果发现同样的请求有时走OpenAI有时走Azure,还不好复现。后来统一把Azure那个改名成azure-gpt-4o,问题立刻消失。模型命名这事,一定要在网关层规划好,aliasing规则最好写进团队文档。

6.2 性能与稳定性调优参数

如果网关的并发量上来了,有几点值得调:

首先是连接池。LiteLLM底层调用上游模型时,如果没有连接池,每次请求都要重新建立TCP+TLS握手,延迟和资源消耗都很亏。可以在配置里开启:

litellm_settings: connection_pool: true connection_pool_kwargs: pool_size: 100 max_retries: 3

其次是worker数。单进程跑不满多核,LiteLLM启动时可以用--num_workers 4开启多worker,qps有明显提升。同时注意每个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分支和头疼的密钥管理,根本不会存在。

返回列表