LLM 路由层:LiteLLM、OpenRouter、Portkey 本节摘要:被一家厂商锁定代价高昂。不同的工具调用负载适合不同的模型。路由网关给你一个 API 表面、重试、故障转移、成本追踪与护栏。2026 年主导的三种原型是 LiteLLM(开源自托管)、OpenRouter(托管 SaaS)、Portkey(生产级,2026 年 3 月开源)。本节命名决策准则,并走一个 stdlib 路由网关。读完本节,你能区分自托管/托管/生产级路由选项、实现一条按优先级重试的故障转移链、跨厂商追踪逐请求成本,并针对给定的生产约束在三者之间做选择。 学习目标 阅读完本节,你应当能够: 区分自托管、托管、生产级路由选项。 实现一条按厂商失败、按定义优先级重试的故障转移链。
本节摘要:被一家厂商锁定代价高昂。不同的工具调用负载适合不同的模型。路由网关给你一个 API 表面、重试、故障转移、成本追踪与护栏。2026 年主导的三种原型是 LiteLLM(开源自托管)、OpenRouter(托管 SaaS)、Portkey(生产级,2026 年 3 月开源)。本节命名决策准则,并走一个 stdlib 路由网关。读完本节,你能区分自托管/托管/生产级路由选项、实现一条按优先级重试的故障转移链、跨厂商追踪逐请求成本,并针对给定的生产约束在三者之间做选择。
阅读完本节,你应当能够:
厂商路由在以下场景里都关键:
每个集成都手写这些,重复且易错。路由网关给你一个 OpenAI 兼容的 API,把剩下的都接管了。
大家都说 OpenAI 形状。路由网关暴露 /v1/chat/completions,接 OpenAI schema,内部代理到 Anthropic / Gemini / Cohere / Ollama / 任何东西。客户端不关心后端是谁。
代码里不写死快照 id,而写 our_smart_model。网关把别名映射到真实模型。厂商发新一代,你只改服务端别名;代码一行不动。
primary: openai/gpt-4o on 5xx: anthropic/claude-3-5-sonnet on 5xx: google/gemini-1.5-pro on 5xx: refuse
网关在配置里定义。重试计入预算,防止故障转移级联把成本炸飞。
ROUTES = { "our_smart_model": ["openai/gpt-4o", "anthropic/claude-3-5-sonnet", "google/gemini-1.5-pro"], } def route(alias, request): for provider_model in ROUTES[alias]: try: return call(provider_model, request) # 成功即返回 except ProviderError as e: if not is_5xx(e): raise # 4xx 不重试 continue # 5xx 试下一个 return refuse("所有提供商失败")
相同或近乎相同的提示命中缓存而非厂商。重复 Agent 循环上的节省可达 30%~60%。键基于嵌入,近相同提示共享一个缓存槽。
网关级:
Portkey 与 Kong 都提供有主见的护栏。LiteLLM 把它留作可选。
一个 API key = 一个团队。按 key 的预算防一个团队吃光共享配额。多数网关支持。
| 因素 | LiteLLM(自托管) | OpenRouter(托管) | Portkey(生产) |
|---|---|---|---|
| 代码 | 开源 Python | 托管 SaaS | 开源(2026-03)+ 托管 |
| 上手 | 部署一个代理 | 注册即用 | 两者皆可 |
| 厂商数 | 100+ | 300+ | 100+ |
| 计费 | 你自己的 key | OpenRouter 积分 | 你自己的 key |
| 可观测 | OpenTelemetry | 仪表盘 | 完整 OTel + PII 脱敏 |
| 最适合 | 想全控的团队 | 快速原型 | 有合规要求的生产 |
LiteLLM 在你有 SRE 团队、想要数据主权时胜出;OpenRouter 在你想要单一订阅、零基础设施时胜出;Portkey 在你想要开箱护栏与合规时胜出。
每次请求带 provider、model、input_tokens、output_tokens。乘以网关维护的价目表里逐模型逐 token 单价,按用户/团队/项目聚合。
def cost(provider_model, usage): rate = PRICE_SHEET[provider_model] return usage["input_tokens"] * rate["in"] + usage["output_tokens"] * rate["out"]
一个网关既能路由 LLM 调用,也能路由 MCP 采样请求。当采样请求的 modelPreferences 倾向某个模型,网关翻译到正确后端。这正是第 17 节(MCP 网关)与本节路由网关有时合并成一个服务的地方。
把上面的别名、回退、成本、PII 拼起来:
class Router: def __init__(self): self.routes = ROUTES self.spend = defaultdict(float) # 团队 → 累计花费 def chat(self, alias, request, team): request["messages"] = redact_pii(request["messages"]) # 护栏 for pm in self.routes[alias]: try: resp, usage = call(pm, request) self.spend[team] += cost(pm, usage) # 成本 return resp except FiveXX: continue return refuse()
设计要点:路由网关把「用谁」从代码里剥离出来,变成一份可热改的配置。代码只认别名,网关在厂商、成本、延迟、合规之间做实时取舍。故障转移计入预算,是为了防级联重试把成本烧穿。
| 维度 | LiteLLM | OpenRouter | Portkey | 自建 stdlib |
|---|---|---|---|---|
| 部署 | 自托管 | 托管 SaaS | 自托管/托管 | 自托管 |
| 厂商数 | 100+ | 300+ | 100+ | 手接 |
| 护栏 | 可选 | 基础 | 内置 | 手写 |
| 成本追踪 | 强 | 仪表盘 | 强 + PII 脱敏 | 手写 |
| OTel | 是 | 仪表盘 | 完整 | 手接 |
| 适合 | 想全控 | 零基础设施 | 有合规的生产 | 学习/原型 |
💡 心法:别把路由网关当成「再加一层延迟」。它换来的——故障转移、成本可见、合规护栏、统一埋点——远远超过那点开销。选型只看一个变量:你有没有 SRE 与数据主权要求。有就 LiteLLM/Portkey,没有就 OpenRouter。
本节产出 outputs/skill-routing-config-designer.md——给定一份负载画像(延迟、成本、合规),这个 skill 选 LiteLLM / OpenRouter / Portkey 并产出路由配置。
code/main.py 用约 150 行实现一个路由网关:接 OpenAI 形状请求、翻译到逐厂商 stub、跑一条优先级故障转移链、追踪逐请求成本、对输入做 PII 脱敏。跑三个场景:正常请求、主厂商故障触发故障转移、PII 泄露被脱敏拦下。可重点看:ROUTES 字典(别名 → 按优先级排列的具体厂商列表)、故障转移循环在 5xx 上重试、成本追踪器把 token 用量乘以逐模型费率、PII 脱敏器在转发前清洗 SSN 形态模式。
触发故障转移:运行 code/main.py,触发故障场景,确认故障转移落到第二厂商且成本归因正确。
加语义缓存:用提示的 SHA256 做查找键,缓存命中直接返回。测量重复调用上的成本节省。
加任务感知路由:加一个提示分类器,把「code …」路由到偏智能的别名,把「summarize …」路由到偏速度的别名。
设计按团队预算:每个团队有月度消费上限,网关在到顶后拒绝请求。选一种强制粒度(逐请求或滑窗)。
横向对比:并排读 LiteLLM、OpenRouter、Portkey 文档,说出各自有、另两者没有的那个特性。
下一节,我们看怎么把这些工具用可复用的工作流知识串起来——Skills 与 Agent SDK:AGENTS.md、SKILL.md,以及跨 Agent 的可移植打包。