1. 项目概述LiteLLM 是什么它解决的到底是什么问题LiteLLM 这个名字乍一听像某个轻量级编程语言或小众框架但其实它是一个在大模型应用开发圈子里迅速走红的开源工具库——准确地说它是一个统一 API 抽象层。我第一次在某跨平台智能体项目中见到它时团队正被十几个不同厂商的模型接口折磨得焦头烂额OpenAI 的/v1/chat/completions要带api_key和model字段Anthropic 的/messages要用system单独传、max_tokens改叫max_tokens_to_sampleGoogle 的 Gemini 接口连temperature都不叫这个名而是temperaturetop_ptop_k三参数联动更别说国内几家主流模型平台有的要求stream是布尔值有的要求是字符串true有的甚至把流式响应包装成自定义 SSE 格式……当时我们一个基础对话模块光是适配接口就写了 470 行 if-else 分支每次新增一个模型就得改三处请求构造、响应解析、错误码映射。上线后只要某家模型更新了字段名整个服务就报 500。LiteLLM 就是为终结这种“接口碎片化”而生的。它不训练模型不优化推理速度也不做 UI它的核心价值就一句话让你用一套代码调通所有主流大模型 API。你写litellm.completion(modelgpt-4, messages[{role: user, content: 你好}])它自动识别这是 OpenAI 模型转成标准请求发过去你换modelclaude-3-haiku-20240307它立刻切到 Anthropic 协议你填modelgemini-pro它无缝对接 Google Vertex AI 或直接调 Gemini Web API甚至你本地跑一个 Ollama 的llama3:8b只要加个ollama/llama3:8b前缀它也能识别并走本地 HTTP 调用。这不是简单的“转发代理”而是做了深度协议对齐字段名标准化全部统一为 OpenAI 风格、参数范围归一化比如把max_output_tokens映射到max_tokens、流式响应统一为标准 chunk 格式、错误码统一为litellm.RateLimitError/litellm.AuthenticationError等可捕获异常。它背后不是魔法是一套持续维护的“模型能力矩阵表”——每个支持的模型都明确标注了是否支持function calling、是否支持vision、最大上下文长度、token 计数规则、流式是否真正逐 token 返回等。这些信息不是靠猜而是通过真实压测官方文档交叉验证得来的。所以当你看到 LiteLLM 支持 120 模型时不是简单列了个名字而是意味着这 120 个模型你都能用同一套逻辑去调度、熔断、降级、打日志、算账单。它最适合三类人第一类是正在快速验证产品想法的 MVP 团队没时间也没人力去为每个模型写适配器第二类是需要多模型路由的智能体系统比如根据问题类型自动选 GPT-4 处理复杂推理、用 Claude 做长文档摘要、用本地 llama3 做隐私数据脱敏第三类是企业内部的 AI 中台建设者要给几十个业务线提供统一的模型调用 SDK同时还要管控成本、监控延迟、做灰度发布。它不是替代你深入理解模型而是把你从重复的 HTTP 胶水代码里解放出来让你真正聚焦在 prompt 工程、RAG 架构、Agent 编排这些高价值环节上。如果你还在手写requests.post(url, json{...})并对着各家文档 copy-paste 字段名那 LiteLLM 就是你今天最该花 15 分钟装上的那个 pip 包。2. 核心设计思路与方案选型逻辑为什么是抽象层而不是代理或网关很多人第一次接触 LiteLLM会下意识把它和 API 网关如 Kong、Traefik或反向代理Nginx划等号觉得“不就是个转发中间件吗”——这是最大的认知偏差。LiteLLM 的本质不是网络层的流量调度而是应用层的语义对齐。这个定位决定了它从架构设计之初就彻底放弃了“零改造接入”的幻想也避开了网关方案必然面临的性能损耗和运维复杂度陷阱。先说为什么不用纯网关方案。假设你用 Nginx 做一个通用代理所有请求都打到https://your-api-gateway/v1/chat/completions然后靠proxy_pass转发到不同后端。表面看很美但立刻撞上三堵墙第一堵是请求体变形。OpenAI 要{model:gpt-4,messages:[...]}Anthropic 要{model:claude-3-haiku,system:...,messages:[...]}Gemini 要{contents:[{parts:[{text:你好}]}]}。Nginx 的map指令和sub_filter根本处理不了这种嵌套 JSON 的字段重命名和结构重组你得写 Lua 脚本而 Lua 脚本一旦复杂调试和维护成本直线上升。第二堵是响应体解析。OpenAI 返回{choices:[{message:{content:...}}]}Claude 返回{content:[{type:text,text:...}]}Gemini 返回{candidates:[{content:{parts:[{text:...}]}}]}。网关层想统一成一种格式要么用正则硬匹配极不可靠要么引入 JSONPath 解析器增加依赖和延迟。第三堵是状态一致性。流式响应里OpenAI 的data: {delta:{content:a}}和 Claude 的data: {delta:{text:a}}字段名不同网关没法做无损透传你必须在网关里做字段映射而映射规则又得随模型版本更新——这已经不是网关该干的活了。LiteLLM 的解法非常务实它选择成为 SDK而不是基础设施。这意味着它运行在你的应用进程内天然拥有完整的上下文它知道你调用时传的是modelgpt-4就能提前查表确认这是 OpenAI 兼容接口它知道你设置了streamTrue就能在收到原始流响应后用预置的 parser 把{delta:{content:a}}统一转换成标准{choices:[{delta:{content:a}}]}它甚至能在发送前就把max_tokens1024自动转换成 Anthropic 的max_tokens_to_sample1024或者把 Gemini 的temperature0.7同时设置top_p0.9来模拟相近效果。这种“知情权”是网关永远无法获得的。更关键的是它把所有模型的差异点都显式建模成了配置项。比如litellm.get_supported_openai_params(modelclaude-3-sonnet-20240229)会返回一个字典明确告诉你这个模型支持哪些参数、哪些不支持、哪些有别名。这种设计让“适配新模型”变成了一个可预测、可测试、可版本化的工程任务而不是一次充满未知的黑盒调试。另一个常被问到的问题是为什么不直接用各家官方 SDK答案是组合爆炸。假设你用 OpenAI 官方 SDK 处理 GPT 系列用 Anthropic SDK 处理 Claude用 Google Generative AI SDK 处理 Gemini那么你的代码里就会出现from openai import OpenAI、from anthropic import Anthropic、from google.generativeai import GenerativeModel三套完全不兼容的 client 初始化、调用方式、异常体系。一旦你要做 A/B 测试比如对比 GPT-4 和 Claude 在同一问题上的输出质量就得写三套几乎一样的逻辑只改 client 实例。而 LiteLLM 提供的是单一入口litellm.completion()所有模型共用同一套参数签名、同一套返回结构、同一套异常类型。这带来的不仅是代码简洁更是架构清晰——你的业务逻辑层完全不知道底层调用的是哪家模型所有模型相关的策略路由、降级、缓存都可以集中在一个地方管理。我见过最典型的案例是一家做法律文书生成的公司他们用 LiteLLM 实现了“模型热插拔”法务部门认为某个模型在合同条款识别上准确率更高运营同学只需在配置中心把default_model从gpt-4-turbo切成claude-3-opus5 秒钟后全量请求就自动切换无需发版、无需重启。这种敏捷性是任何网关或多 SDK 方案都无法提供的。3. 核心功能拆解与实操要点从安装到生产级部署的完整链路LiteLLM 的使用门槛极低但要真正用好、用稳尤其在生产环境必须吃透几个核心模块的设计逻辑和实操细节。下面我按实际落地顺序把从pip install到高可用部署的关键环节掰开揉碎讲清楚。3.1 安装与基础调用三行代码跑通第一个请求安装本身毫无难度pip install litellm。但要注意两个隐性前提一是 Python 版本需 ≥3.8因为部分模型适配器用了typing.TypedDict等新特性二是如果要用到本地模型如 Ollama、LM Studio需确保对应服务已启动且可访问。安装后最简调用如下import litellm # 设置环境变量推荐方式避免密钥硬编码 import os os.environ[OPENAI_API_KEY] sk-xxx # OpenAI 密钥 os.environ[ANTHROPIC_API_KEY] xxx # Anthropic 密钥 # 一行代码调用任意模型 response litellm.completion( modelgpt-4-turbo, messages[{role: user, content: 用一句话解释量子纠缠}] ) print(response.choices[0].message.content)这段代码看似简单背后却完成了至少五件事第一自动识别gpt-4-turbo属于 OpenAI 模型族加载 OpenAI 适配器第二从环境变量读取OPENAI_API_KEY构造标准 Authorization Header第三将messages数组按 OpenAI 规范序列化补全缺失字段如role默认为user第四发起 HTTPS 请求并处理重试逻辑默认 3 次指数退避第五解析响应 JSON将choices[0].message.content提取为标准属性。你完全不需要关心 URL 是https://api.openai.com/v1/chat/completions还是https://openai-proxy.example.com/v1/chat/completionsLiteLLM 会根据model名称自动匹配 base_url。提示首次运行建议加debugTrue参数如litellm.completion(..., debugTrue)它会在控制台打印出实际发出的请求 URL、Headers、Body 和响应 Body这是排查 401/404 错误的最快方式。我踩过最深的坑是某次用错环境变量名写成OPENAI_KEY而非OPENAI_API_KEYdebug 日志里直接显示Authorization: Bearer None5 秒定位问题。3.2 模型路由与动态调度如何让一个 API 对接百家模型LiteLLM 的核心价值在“路由”。它支持三种粒度的路由策略按推荐度排序第一种静态模型映射最常用适用于模型列表固定、无需运行时变更的场景。通过litellm.set_verbose(True)开启日志后你可以看到 LiteLLM 内置的模型映射表。但更推荐的方式是显式注册from litellm import Router router Router( model_list[ { model_name: gpt-4-turbo, # 你在代码里写的 model 名 litellm_params: { # 实际调用时的参数 model: gpt-4-turbo, api_key: os.getenv(OPENAI_API_KEY), api_base: https://api.openai.com/v1 } }, { model_name: claude-3-haiku, litellm_params: { model: claude-3-haiku-20240307, api_key: os.getenv(ANTHROPIC_API_KEY) } } ] ) # 调用时仍用 model_name response router.completion( modelgpt-4-turbo, # 注意这里用的是 model_name不是 litellm_params.model messages[...] )这种写法的好处是解耦业务代码只认gpt-4-turbo这个逻辑名具体由哪个服务商、哪个 endpoint、哪个密钥提供全在Router初始化时配置。上线后想把gpt-4-turbo切到 Azure OpenAI只需改litellm_params里的api_base和api_version业务代码零修改。第二种权重轮询Load Balancing当同一个逻辑模型有多个实例比如你部署了 3 台 Ollama 服务器跑 llama3可以用model_list配置多个相同model_name的条目并设置weight{ model_name: llama3-8b, litellm_params: {model: ollama/llama3:8b, api_base: http://host1:11434}, weight: 1 }, { model_name: llama3-8b, litellm_params: {model: ollama/llama3:8b, api_base: http://host2:11434}, weight: 2 # host2 承担 2/3 流量 }LiteLLM 内置的负载均衡器会按权重分配请求且自动剔除连续失败的节点健康检查间隔默认 60 秒。第三种规则路由Rule-based Routing这是最高阶用法适合复杂业务策略。比如“用户等级 VIP 用 GPT-4普通用户用 Claude问题含‘代码’关键词用 CodeLlama长文档摘要强制用 Gemini”。LiteLLM 提供Router.route_request()方法允许你传入自定义函数def custom_routing_rule(*args, **kwargs): messages kwargs.get(messages, []) last_msg messages[-1][content] if messages else if 代码 in last_msg: return codellama-7b elif len(last_msg) 5000: # 长文本 return gemini-pro else: return gpt-4-turbo router.route_request custom_routing_rule注意规则路由函数必须是纯函数不能有副作用如写数据库否则并发下会出问题。我建议把路由逻辑抽成独立服务LiteLLM 只负责调用这样更易测试和灰度。3.3 生产级关键配置超时、重试、熔断、监控一个都不能少LiteLLM 默认配置是为开发体验优化的生产环境必须调整。以下是我在三个不同规模项目中验证过的黄金配置配置项推荐值为什么这么设实操心得timeout30.0秒OpenAI 官方 SLA 是 60 秒但实际 95% 请求 15 秒。设 30 秒既能覆盖慢请求又不会让前端无限等待如果你的业务对延迟极度敏感如实时客服可降到 15 秒但需配合降级策略num_retries2网络抖动常见1 次重试不够3 次又太耗时。2 次重试 指数退避默认 1s, 2s是性价比最优解务必关闭streamTrue时的重试流式请求重试会导致重复 chunkLiteLLM 会自动禁用但你要心里有数fallbacks[gpt-3.5-turbo, claude-3-haiku]当主模型如 gpt-4-turbo因限流或故障不可用时自动降级到备用模型。LiteLLM 会按顺序尝试直到成功fallback 模型必须和主模型有相似能力否则降级后输出质量断崖下跌。我曾把 gpt-4 降级到 text-davinci-003结果 JSON 输出全乱码cachingTrue开启内存缓存LRU对相同messagesmodel的请求直接返回缓存结果省 API 调用和钱缓存 key 默认包含messages全内容如果 message 里有时间戳或随机 ID会导致缓存失效需用cache_keys参数定制监控方面LiteLLM 提供success_callback和failure_callback钩子推荐集成 Prometheusfrom litellm import success_callback, failure_callback import time def log_success(kwargs, response_obj, start_time, end_time): duration (end_time - start_time) * 1000 # 上报到 Prometheusmodel_latency_seconds{modelgpt-4-turbo} 123.45 # 上报model_requests_total{modelgpt-4-turbo,statussuccess} 1 def log_failure(kwargs, exception, start_time, end_time): # 上报model_requests_total{modelgpt-4-turbo,statuserror} 1 # 上报model_errors_total{modelgpt-4-turbo,error_typeRateLimitError} 1 success_callback [prometheus] failure_callback [prometheus]实操心得一定要监控model_requests_total的statusrate_limit标签。我有个客户在 Black Friday 当天GPT-4 的 rate limit 错误突增 20 倍但业务指标没报警——因为降级到了 Claude用户无感知但成本翻了 3 倍。后来加了 rate limit 错误率告警才及时发现并扩容。4. 实操过程详解从零搭建一个支持多模型的问答 API 服务现在我们动手实现一个真实的生产级服务一个/v1/chat接口接收标准 OpenAI 格式请求支持 GPT-4、Claude、Gemini 三模型动态路由并具备熔断、缓存、监控能力。整个过程分四步每步都附可直接运行的代码。4.1 步骤一初始化 Router 与全局配置创建app.py先完成核心路由配置import os from litellm import Router from litellm.proxy._types import UserAPIKeyAuth from fastapi import FastAPI, Request, Depends from fastapi.responses import StreamingResponse import asyncio # 1. 从环境变量读取密钥生产环境务必用 secrets manager os.environ[OPENAI_API_KEY] os.getenv(OPENAI_API_KEY, sk-xxx) os.environ[ANTHROPIC_API_KEY] os.getenv(ANTHROPIC_API_KEY, xxx) os.environ[GEMINI_API_KEY] os.getenv(GEMINI_API_KEY, xxx) # 2. 构建 model_list定义每个逻辑模型的真实后端 model_list [ { model_name: gpt-4-turbo, litellm_params: { model: gpt-4-turbo, api_key: os.environ[OPENAI_API_KEY], timeout: 30.0, num_retries: 2 } }, { model_name: claude-3-haiku, litellm_params: { model: claude-3-haiku-20240307, api_key: os.environ[ANTHROPIC_API_KEY], timeout: 45.0, # Claude 通常比 GPT 慢 num_retries: 2 } }, { model_name: gemini-pro, litellm_params: { model: gemini/gemini-pro, api_key: os.environ[GEMINI_API_KEY], timeout: 60.0, # Gemini Web API 延迟波动大 num_retries: 1 # Gemini 重试成本高慎用 } } ] # 3. 创建 Router 实例启用缓存和 fallback router Router( model_listmodel_list, redis_hostos.getenv(REDIS_HOST), # 可选用 Redis 做分布式缓存 redis_portint(os.getenv(REDIS_PORT, 6379)), cache_responsesTrue, # 启用缓存 fallbacks[{gpt-4-turbo: [claude-3-haiku, gemini-pro]}] # 主模型失败时的降级链 ) # 4. FastAPI 应用初始化 app FastAPI(titleMulti-Model Chat API)这段代码完成了 80% 的核心工作密钥管理、模型注册、超时重试、缓存开关、降级策略。注意fallbacks是字典结构gpt-4-turbo是主模型名[claude-3-haiku, gemini-pro]是降级候选列表LiteLLM 会按顺序尝试。4.2 步骤二实现标准 OpenAI 兼容的/v1/chat/completions接口FastAPI 的路由逻辑要严格遵循 OpenAI API 规范这样才能被现有 SDK 无缝集成from pydantic import BaseModel, Field from typing import List, Optional, Dict, Any class Message(BaseModel): role: str Field(..., description角色user/assistant/system) content: str Field(..., description消息内容) class ChatCompletionRequest(BaseModel): model: str Field(..., description逻辑模型名如 gpt-4-turbo) messages: List[Message] Field(..., description对话历史) stream: bool Field(False, description是否流式响应) temperature: Optional[float] Field(0.7, ge0.0, le2.0) max_tokens: Optional[int] Field(None, ge1, le4096) top_p: Optional[float] Field(1.0, ge0.0, le1.0) app.post(/v1/chat/completions) async def chat_completions(request: ChatCompletionRequest): try: # 1. 构造 litellm.completion 参数 litellm_kwargs { model: request.model, messages: [{role: m.role, content: m.content} for m in request.messages], stream: request.stream, temperature: request.temperature, max_tokens: request.max_tokens, top_p: request.top_p } # 2. 调用 Router注意不是 litellm.completion而是 router.completion if request.stream: # 流式响应返回 StreamingResponse async def generate(): try: response await router.acompletion(**litellm_kwargs) # LiteLLM 的流式响应是异步生成器需逐 chunk yield async for chunk in response: yield fdata: {chunk.json()}\n\n except Exception as e: yield fdata: {json.dumps({error: str(e)})}\n\n return StreamingResponse(generate(), media_typetext/event-stream) else: # 非流式直接返回 JSON response await router.acompletion(**litellm_kwargs) return response.dict() except Exception as e: # 统一错误处理 return {error: {message: str(e), type: type(e).__name__}}这里有两个关键细节第一router.acompletion()是异步方法必须用await且 FastAPI 路由函数要声明为async第二流式响应必须手动构造 SSE 格式data: {...}\n\nLiteLLM 返回的chunk是标准ChatCompletionChunk对象调用.json()即可序列化。这个接口完全兼容openai-pythonSDK你可以用openai.ChatCompletion.create(modelgpt-4-turbo, ...)直接调用它。4.3 步骤三添加熔断与健康检查LiteLLM 内置的熔断基于tenacity库但默认不开启。我们要手动集成目标是当某个模型连续 3 次失败超时/认证失败自动将其从路由池中剔除 5 分钟from tenacity import retry, stop_after_attempt, wait_exponential, retry_if_exception_type from litellm import RateLimitError, AuthenticationError, Timeout # 定义熔断装饰器 retry( stopstop_after_attempt(3), waitwait_exponential(multiplier1, min1, max10), retryretry_if_exception_type((RateLimitError, AuthenticationError, Timeout)) ) async def robust_completion(**kwargs): return await router.acompletion(**kwargs) # 在路由中使用 app.post(/v1/chat/completions) async def chat_completions(request: ChatCompletionRequest): try: # ... 前面的参数构造 ... if request.stream: # 流式调用 robust_completion response await robust_completion(**litellm_kwargs) # ... 流式生成逻辑 ... else: response await robust_completion(**litellm_kwargs) return response.dict() except Exception as e: # 熔断触发时e 是最后一次异常可记录 return {error: {message: fMelted: {str(e)}}}实操心得熔断阈值要结合业务容忍度设。我们做过压测GPT-4 在 100 QPS 下rate limit 错误率约 0.5%设 3 次失败就熔断太激进会频繁误伤最终定为 5 次失败 10 分钟冷却既保证稳定性又避免过度保守。4.4 步骤四部署与监控集成最后一步是容器化部署和监控。Dockerfile 很简单FROM python:3.11-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . CMD [uvicorn, app:app, --host, 0.0.0.0:8000, --port, 8000, --reload]requirements.txt至少包含litellm1.42.0 fastapi0.110.0 uvicorn0.29.0 prometheus-client0.17.1 redis4.6.0启动命令docker run -p 8000:8000 \ -e OPENAI_API_KEYsk-xxx \ -e ANTHROPIC_API_KEYxxx \ -e GEMINI_API_KEYxxx \ -e REDIS_HOSTredis-host \ multi-model-api监控方面LiteLLM 原生支持 Prometheus只需在启动时加参数litellm --host 0.0.0.0 --port 8000 --api_key sk-123 --database_url sqlite:///./litellm.db --use_azure_key_vault False然后访问http://localhost:8000/health查看各模型健康状态http://localhost:8000/metrics获取 Prometheus 指标。关键指标包括litellm_request_total{modelgpt-4-turbo,statussuccess}成功请求数litellm_request_duration_seconds_bucket{modelgpt-4-turbo,le30.0}30 秒内完成的请求占比litellm_token_usage_total{modelgpt-4-turbo,typeprompt}提示词 token 消耗实操心得上线前必做三件事1用litellm --test命令测试所有模型连通性2用ab或hey工具压测验证熔断是否生效3故意停掉一个模型的 API看 fallback 是否自动触发。我们有个项目就是因为忘了测 fallback上线后 GPT-4 故障整个服务直接 500损失了 2 小时订单。5. 常见问题与独家避坑指南那些文档里不会写的实战经验LiteLLM 文档写得不错但有些坑只有踩过才知道。我把过去一年在多个项目中遇到的典型问题整理成速查表并附上根因分析和独家解决方案。5.1 问题速查表问题现象根本原因解决方案我的实操记录调用返回401 Authentication Error但密钥确认正确LiteLLM 会优先读取os.environ如果环境变量名拼错如OPENAI_KEY它会静默 fallback 到空字符串导致 401用litellm.completion(..., debugTrue)打印实际请求头检查Authorization字段值某次部署到 KubernetesConfigMap 挂载时文件权限错误导致环境变量未加载debug 日志里Authorization: Bearer None一眼定位流式响应卡住前端收不到第一个 chunk某些模型如早期 Gemini的流式接口不遵守 SSE 规范返回data: {...}但缺少\n\n结尾或返回非标准 JSON在generate()函数里加容错解析try: json.loads(line.strip(data: )) except: pass我们封装了一个safe_sse_parser工具函数已开源在内部工具库max_tokens设置无效模型总是返回超长内容不同模型对max_tokens的解释不同OpenAI 是“最大生成 token 数”Claude 是“最大总 token 数含 prompt”Gemini 是“最大输出 token 数”。LiteLLM 默认按 OpenAI 语义映射显式指定max_completion_tokensClaude或max_output_tokensGeminiLiteLLM 会自动转换在 Router 的litellm_params里加max_completion_tokens: 1024比全局设max_tokens更精准本地 Ollama 模型调用失败报Connection refusedLiteLLM 默认用http://localhost:11434但 Docker 容器内localhost指向容器自身而非宿主机在litellm_params中显式设api_basehttp://host.docker.internal:11434Mac/Windows或api_basehttp://172.17.0.1:11434Linux这个 IP 地址要记牢我们贴在团队共享文档首页缓存命中率极低cache_key总是不同messages数组里如果包含时间戳、UUID、随机数等动态字段每次请求的messages内容都不同导致缓存 key 失效用cache_keys参数定制缓存 key 生成逻辑例如只取messages[-1].content的 hash我们写了个hash_last_message函数缓存命中率从 12% 提升到 89%5.2 三个必须知道的冷知识冷知识一LiteLLM 的model参数支持“别名链”你以为modelgpt-4就只能调 GPT-4错。LiteLLM 允许你定义别名映射litellm.model_alias_map { my-best-model: gpt-4-turbo, cheap-model: claude-3-haiku }然后代码里写modelmy-best-model它会自动转成gpt-4-turbo。这个特性在 A/B 测试时神了运营同学改个配置就能把 10% 流量切到新模型完全不用动代码。冷知识二litellm.embedding()支持跨模型向量对齐Embedding 模型的向量维度和分布差异巨大text-embedding-ada-002 是 1536 维bge-large-zh 是 1024 维直接比较 cosine similarity 无意义。LiteLLM 内置了向量归一化逻辑调用litellm.embedding(modeltext-embedding-ada-002, input[hello])返回的向量已做过 L2 归一化可直接用于跨模型相似度计算。我们用这个特性实现了“混合 embedding 检索”用 GPT 的 embedding 做粗筛用本地 bge 做精排QPS 提升 3 倍。**冷知识三litellm.image_generation()