返回 AIPA 笔记
AIPA Day 43

自托管 AI gateway — LiteLLM/Bifrost/Portkey 与统一接入层

自托管 AI gateway — LiteLLM/Bifrost/Portkey 与统一接入层

2026-07-27
ai-gatewaylitellmcost-control

日期: 2026-07-27 阶段: Phase 2 - AI-native 参考架构 标签: #ai-gateway #litellm #cost-control

核心问题

agent-v2 现在直接在 src/agent/config/providers.ts 里硬编码了 5 个 provider(xiaomi/anthropic/openai/deepseek/custom),每个 sub-agent 各自 generateText() 直连 LLM API。这套「应用直连」模式在 demo 期没问题,但放到合规产品里有四个致命洞:成本不可控(没有跨 agent 的预算闸门,orchestrator 一次广度检索能烧 15× 普通 chat 的 token,Anthropic multi-agent 系统已实测过,2025-06)、故障不韧性(一个 provider 429 整条链就断)、密钥裸奔(API key 散在前端配置里)、可观测割裂(Day 22-25 建的 OTel 链路在「调 LLM」这一跳是黑盒)。

今天回答一个架构问题:该不该在应用和 LLM 之间插一层 AI gateway? 答案是「该」,但关键是想清楚 gateway 的五层职责边界、三个主流产品(LiteLLM / Bifrost / Portkey)的取舍,以及 agent-v2 统一走 gateway 后哪些代码该删、哪些该留

关键内容

A. AI gateway 的五层职责:从「代理」到「控制平面」

把 gateway 当成「一个转发 OpenAI 格式请求的反向代理」是低估它。它的本质是 LLM 调用的控制平面(control plane),职责自下而上分五层,每层解决「应用直连」的一个洞:

① 接入层(normalization):用 OpenAI 兼容格式统一 100+ provider。LiteLLM 把 Bedrock/Vertex/Anthropic/本地 vLLM 全收敛成同一个 /chat/completions 端点(LiteLLM docs,2026-06)。价值不是「省得学多个 SDK」,而是让上游应用对 provider 无感——切换底模不改业务代码。

② 缓存层(caching):精确缓存(OpenAI/Anthropic prompt caching 透传)+ 语义缓存(Day 45 详谈)。命中直接返回,不进 LLM。

③ 路由层(routing):跨 deployment 的负载均衡 + 按成本/延迟/语义选模型(Day 46 详谈)。

④ 韧性层(resilience):fallback 链、retry、cooldown。某 provider 429 或超时,自动降级到 fallback 模型,而不是把错误抛回 agent。

⑤ 治理层(governance):virtual keys(每个 team/project 一把虚拟密钥,真密钥只在 gateway 持有)、per-key budget(日/周/月/年预算,超支直接拒请求)、rate limit、spend tracking、guardrails(PII/越狱拦截)、统一 logging。

关键洞察——这五层必须在一个进程内串成一条请求流水线,因为它们共享同一份「请求上下文」(谁的 key、命中没命中缓存、走了哪个 fallback、花了多少钱)。LiteLLM 的实现是一套 hook 链(LiteLLM docs,2026-06):

client ──► [gateway proxy]
              │
   ① auth: 校验 virtual key、查 budget(超支 → 402 拒绝)
              │
   ② async_pre_call_hook: 改写/拒绝请求(PII mask、注入 system)
              │
   ③ cache lookup: 精确 hash 命中? ──是──► 直接返回(不计费、不进 LLM)
              │ 否
   ④ router.pick(): 按策略选 deployment(成本/延迟/健康度)
              │
   ⑤ 调 LLM ──► 失败? ──是──► fallback 链下一个 / retry(带 cooldown)
              │ 成功
   ⑥ async_post_call_hook: 流式审计、guardrail(streaming 仅 audit,不可拦)
              │
   ⑦ spend tracking + logging: 写 cost、写 OTel span
              │
            response ──► client

反直觉洞察①(gateway 的价值不在「省事」而在「把横切关注点从 N 个 agent 里抽出来」):很多人觉得 gateway 只是「少写几个 SDK adapter」,所以「我才 5 个 provider,不值得引入」。真正的价值是:预算、重试、密钥轮换、成本归因这些横切关注点,在「应用直连」模式下会被复制到每一个 sub-agent(knowledge/research/portfolio 各写一遍 retry),而 gateway 让它们收敛到一处。判断要不要上 gateway 的标准不是 provider 数量,而是 agent 数量 × 横切关注点数量——agent-v2 有 4 个 agent(1 orchestrator + 3 sub),横切关注点 5 个,等于 20 处潜在重复。

post_call guardrail 在流式下只能审计、不能拦截(LiteLLM docs,2026-06):因为 chunk 已经逐个发给客户端了,等整个响应组装完再跑 guardrail,内容早就吐出去了。这意味着「输出侧合规拦截」对流式 SAR 草稿根本拦不住——合规拦截必须放在输入侧 pre_call(拒绝危险请求)+ 应用侧 HITL(人审后才提交),不能指望 gateway 输出 guardrail 兜底。

B. LiteLLM vs Bifrost vs Portkey:三产品定位对比

三个产品看似都是「AI gateway」,但定位差异巨大,选错会在生产期付出代价。核心分歧在语言/性能自托管 vs 托管两个轴:

维度LiteLLMBifrostPortkey
实现语言PythonGoNode/TS
延迟开销10-50ms(Py 解释器)~11µs @ 5k RPS低(边缘部署)
吞吐拐点~300-500 RPS 开始劣化5k+ RPS 稳定高(managed 弹性)
provider 数100+(2500+ 模型)1000+ 模型250+ 模型
语义缓存Redis 精确为主内置双层语义缓存(降本 60-85%)模糊匹配语义缓存
virtual key/budget✅ 成熟
自托管✅ 开源 Docker✅ 开源✅(2026-03 核心 Apache 2.0 开源)
MCP gateway通过插件原生支持
最佳场景自托管团队默认首选高 RPS、对延迟敏感要 managed + 内置 guardrail/PII

来源:TECHSY / FloTorch / getmaxim LLM gateway 对比(均 2026),Bifrost README(«50x faster than LiteLLM, <100µs @ 5k RPS»,2026-06)。

关键决策逻辑:

  • agent-v2 选 LiteLLM。理由:本项目是单租户教学+作品集 demo,RPS 个位数,根本碰不到 Python 的吞吐拐点(300 RPS);而 LiteLLM 的「100+ provider 统一 + virtual key + budget + Docker 自托管 + 开源免费」恰好覆盖五层全部职责,是「自托管团队 2026 默认首选」(FloTorch,2026)。
  • Bifrost 的 11µs 在我的场景是过度优化。它解决的是「5k RPS 下 Python gateway 崩了」——那是高并发推理服务的问题,不是教学 demo 的问题。

反直觉洞察②(性能数字要按「你的 RPS 区间」读,不能按「绝对值大小」读):「Bifrost 比 LiteLLM 快 50×」是真的,但这个差距只在 300+ RPS 才显现——低于这个量,两者对用户都是不可感知的毫秒级。被「快 50×」吓得不敢用 LiteLLM 是典型的用别人的瓶颈选自己的工具。先问「我的峰值 RPS 是多少」,再看延迟开销曲线在哪一段——本项目 RPS<10,LiteLLM 的 10-50ms 开销对一次 2-5s 的 LLM 调用是 <2% 的尾巴。

C. agent-v2 统一走 gateway 的改造:删什么、留什么

改造目标:让 4 个 agent 不再各自直连 provider,全部把 baseURL 指向一个 LiteLLM 网关地址,gateway 内部再扇出到真 provider。改造的本质是providers.ts 从「provider 注册表」降级为「gateway 端点配置」

改造前:  knowledgeAgent ─┐
         researchAgent  ─┼─► providers.ts(5个真URL+真key) ─► OpenAI/Anthropic/...
         portfolioAgent ─┘     (key 散落、预算各管各、retry 各写各)

改造后:  4个 agent ──► 单一 baseURL=http://gateway/v1 ──► [LiteLLM]
                       (前端只持 virtual key)            ├─ 真 key 只在网关
                                                          ├─ budget 统一闸门
                                                          ├─ fallback 链统一
                                                          └─ OTel span 统一

改造清单(计划,非已实现):

动作文件说明
providers.tsProviderDef 结构仍需 label/默认模型供 UI 选
defaultBaseURL 全部指向 gateway5 个真 URL → 1 个 GATEWAY_URL
真 API key 配置从前端 useAgentConfig 移除,迁到 gateway 的 config.yaml,前端只填 virtual key
sub-agent 里任何手写 retry/backoff韧性下沉到 gateway fallback 链
Budget 与 gateway budget 的对账orchestrator/budget.ts 现做应用层步数/成本闸门,gateway 做账本层硬闸门,两者双闸(应用闸先触发给好的 UX 报错,gateway 闸是兜底防超支)

设计要点/决策表

要点决策理由
是否引入 gateway引入(P2 规划)横切关注点(预算/韧性/密钥/可观测)收敛,agent 数×关注点数 = 20 处重复
选哪个LiteLLM 自托管RPS<10 碰不到 Py 拐点;五层职责全覆盖;开源 Docker
不选 Bifrost11µs 是高 RPS 优化本项目无 5k RPS 场景,过度优化
真密钥位置只在 gateway config.yaml前端持 virtual key,密钥不下发浏览器
预算闸门应用 Budget + gateway budget 双闸应用闸给 UX、gateway 闸防兜底超支
输出合规拦截不靠 gateway post_call流式下只能审计;改用 pre_call + HITL

对本项目的落地

  • 新建 infra/litellm/config.yaml(规划):声明 model_list(把现 PROVIDERS 的 5 项搬进去)、router_settings.fallbacks(如 claude-sonnet-4-6 → gpt-5 → mi-large)、general_settings 里挂 budget。这个文件是「provider 真相」的新单一来源,替代散落在前端的配置。
  • src/agent/config/providers.ts:新增一个 gateway provider(defaultBaseURL: process.env.GATEWAY_URL),并在 useAgentConfig.ts 默认选它;真 provider 定义降级为「gateway 内部路由目标」的文档注释。ProviderDef 结构保留不动,UI 无需改。
  • 对接 orchestrator/budget.ts:现有 Budget.assertCostOk() 是应用层估算闸;在 gateway 落地后,把 gateway 返回的 x-litellm-response-cost header 回填进 Budget 的真实花费,让应用层闸从「估算」升级为「网关回传的真实成本」,消除 Day 1-7 prototype 里成本估算偏差。
  • 对接 Day 22-25 OTel:LiteLLM 的 logging 层产 OTel span,把它接到现有 useTraceStore.ts 的 trace 树上,让「调 LLM」这一跳从黑盒变成带 token/cost/provider/fallback 命中的可观测 span,补齐 Day 24「全链路埋点」在 LLM 跳的缺口。
  • 诚实标注:gateway 接入为 P2 规划动作,W?? 落地时须重新确认 LiteLLM 当周版本与 config.yaml schema(schema 跨大版本会变);当前 agent-v2 仍是应用直连,本笔记描述的是目标架构。

参考资料

  1. LiteLLM — AI Gateway (LLM Proxy) docs:load balancing/routing/fallbacks、virtual keys、budgets+rate limits、spend tracking、guardrails(8项)、logging/alerting/metrics、traffic mirroring、Docker 自托管 (2026-06)
  2. LiteLLM — Modify/Reject Incoming Requests (call hooks) + Custom Guardrail docsasync_pre_call_hook 改写/拒绝输入、post_call 流式仅 audit 不可拦、guardrail_information 写入 logging payload (2026-06)
  3. FloTorch — LLM Gateway Comparison 2026: Enterprise Buyer's Guide:LiteLLM 自托管默认首选、Python 300-500 RPS 拐点 (2026)
  4. maximhq/bifrost README + getmaxim 5 Best AI Gateways 2026:Bifrost Go 实现 ~11µs @ 5k RPS、«50x faster than LiteLLM»、语义缓存降本 60-85%;Portkey 2026-03 核心 Apache 2.0 开源、原生 MCP gateway、250+ 模型 (2026-06)
  5. 本仓库 src/agent/config/providers.tssrc/agent/orchestrator/budget.tssrc/agent/orchestrator/orchestratorAgent.tssrc/agent/trace/useTraceStore.ts (2026-06)

SOTA 检查 (2026-06-11)

  • 「自托管 AI gateway」在 2026-06 是 agent 生产化的标配层:LiteLLM(自托管开源默认)、Bifrost(高性能 Go)、Portkey(managed+guardrail,2026-03 核心开源)三分天下,对比口径在 FloTorch/getmaxim/TECHSY 多份 2026 报告里一致。
  • 语义缓存是 gateway 之间的主要差异化战场:Bifrost 主打内置双层语义缓存(降本 60-85%),Portkey 模糊匹配,LiteLLM 以 Redis 精确缓存为主、语义需外接——这条决定 Day 45「语义缓存实测」该不该换 gateway,留待 Day 45 深挖。
  • 过时认知警示:「应用直连足够、不需要 gateway」在多 agent + 合规场景已过时——预算硬闸、密钥隔离、统一成本归因是合规产品的 table-stakes,2026 工程口径普遍把 gateway 当默认层而非可选项。
  • 待跟踪:LiteLLM 大版本 config.yaml schema 演进、Portkey 开源核心是否补齐自托管语义缓存;执行落地当周须按顶层时效硬规则重新 WebSearch 确认 LiteLLM 最新稳定版本号。