Day 16 / 共 20 天 · 第 4 周 AI 网关/部署

AI 网关能力(ai-proxy)

第 4 周开始,进入 Higress 最"招牌"的部分。第 3 周你会写通用插件了(Day 12-14 的套路);今天看用同一套 Wasm 机制做出来的王牌 AI 插件 ai-proxy——把 39 家 LLM 厂商统一成 OpenAI 协议、模型路由、协议自动转换。这就是"AI 原生网关"名号的由来。

📍 你在第 4 周(AI 网关 / 部署)的位置
D16 ai-proxy D17 AI 增强插件 D18 hgctl CLI D19 部署 D20 收官
💡 先兜住今天("AI 网关 = 大模型统一收费前台"世界观) 本周把 Higress 当成一个大模型服务的统一前台:所有调 LLM 的请求都先到这个前台,前台帮你做统一登记、翻译、计费、限流。今天的 ai-proxy 是前台里的"万能翻译官 + 电源适配器":你的应用只会说一种"普通话"(OpenAI 协议),后端却站着几十家说不同方言的大模型(通义、文心、Claude、Gemini…)。ai-proxy 负责把你的普通话现场翻译成对方的方言、插上对方的插座——换一家大模型,你一个字代码都不用改,只改前台配置。
L01

ai-proxy 是什么

extensions/ai-proxy/(775 行 main + 60+ provider 文件)——把各家 LLM 统一成 OpenAI/Anthropic 协议的核心插件。

🤔 痛点:你的应用直接对接了 OpenAI,老板说"太贵,换成通义千问" 如果应用代码里写死了 OpenAI 的域名、鉴权头格式、请求体字段、模型名 gpt-4……换厂商就得改一大片代码、重新测试、重新上线。要是想同时接好几家做灰度对比,那更是灾难。
ai-proxy = 万能适配器:应用说"普通话",它翻成各家"方言" 你的应用 只说 OpenAI 协议 ai-proxy 翻译官 + 适配器 (39 家 provider) OpenAI(/v1/chat + Bearer) 通义千问(自家 URL / 鉴权 / 字段) Claude / Gemini / DeepSeek…
图注:应用只对接一种协议,换后端只改网关里的 provider 配置。这就是"屏蔽 LLM 厂商差异"的价值。
ai-proxy = "LLM 的万能适配器" 你的应用想调 LLM,但市面上有几十家(OpenAI/通义千问/DeepSeek/Claude/Gemini…),每家的 API 协议、鉴权、模型名都不同。如果应用直接对接,换一家就要改代码。ai-proxy 的价值:应用只用一种协议(OpenAI 格式)调 Higress,Higress 帮你翻译成目标厂商的真实协议、鉴权、格式。换厂商只改网关配置,应用零改动。好比一个万能电源适配器——你的设备用统一插头,适配器负责转成各国插座。这是"AI 网关"最核心的能力——屏蔽 LLM 厂商差异。
L02

Provider 架构

// ai-proxy/provider/provider.go:265 —— 基接口极简
type Provider interface { GetProviderType() string }
// providerInitializer 接口(:211):ValidateConfig + CreateProvider
// providerInitializers 注册表(:224):39 家厂商的 map
// CreateProvider(:920):按 typ 从注册表取 initializer 创建
读法:又是工厂 + 注册表模式(贯穿所有项目):providerInitializers 是"厂商名 → 构造器"的 map,CreateProvider 按配置的 type 取对应厂商实现。基接口 Provider 极简(只有 GetProviderType),能力靠可选接口组合(L04)。加一家新厂商 = 实现接口 + 注册进 map。
L03

39 家厂商

providerInitializersprovider.go:224)注册 39 家:openai/azure/qwen(通义千问)/claude/gemini/deepseek/moonshot(月之暗面)/doubao(豆包)/hunyuan(混元)/spark(讯飞星火)/baidu(文心)/minimax/bedrock(AWS)/vertex(Google)/ollama/vllm…

为什么能支持这么多? 因为架构好——每家厂商是一个 provider/<厂商>.go 文件,实现"把统一 OpenAI 请求翻译成本厂商格式"的接口。新增一家厂商,不用改核心代码,只写一个新文件 + 注册。所以社区能快速贡献新厂商支持——从最初几家到现在 39 家。国产大模型(通义/文心/星火/混元/豆包/月之暗面/DeepSeek)全覆盖——这是 Higress 在国内 AI 场景的巨大优势。"开闭原则"(对扩展开放)让 ai-proxy 能跟上 LLM 厂商爆发的速度。
L04

可选接口组合

// provider.go 一组可选能力接口(Go 鸭子类型/类型断言):
RequestHeadersHandler(:269)、RequestBodyHandler(:273)
StreamingResponseBodyHandler(:277)
TransformRequestHeadersHandler(:289)、TransformRequestBodyHandler(:293)
TransformResponseBodyHandler(:307)
// 各 provider 按需实现哪几个(不用全实现)
"能力接口"按需实现(优雅设计) 不同厂商差异大——有的只需改 URL/鉴权(简单),有的需要大改请求体(复杂)。如果基接口塞满所有方法,每家厂商都得实现一堆用不上的空方法。ai-proxy 的做法:基接口只有 GetProviderType,其余能力拆成一组可选接口——厂商需要哪个就实现哪个。运行时用 Go 的类型断言判断"这个 provider 实现了 TransformRequestBody 吗",实现了才调。(回想 Envoy 课 Day 13 的组合式 adapter——同一个思想!)这让简单厂商代码极少、复杂厂商能全定制——优雅的可扩展设计。

👶 小白:把所有方法都写进一个大接口,谁不需要就留空实现,不也行吗?为什么非要拆成一堆可选接口?

👨‍🏫 老师:能跑,但很脏。① 39 家厂商,每家都被迫写一堆空方法,代码噪音大、容易写错;② 加一个新能力,所有厂商都得改(哪怕用不上)。拆成可选接口后:厂商只实现自己需要的那几个,运行时用 Go 类型断言 if p, ok := provider.(TransformRequestBodyHandler); ok 判断"它到底会不会这招",会才调。简单厂商可能就实现 1 个接口、几十行;复杂厂商实现全套。各取所需,互不拖累。

📝 举个例子:加一家新厂商要动几个地方 想支持一家新大模型 foobar:① 新建 provider/foobar.go,实现 GetProviderType() + 需要的转换接口;② 在 providerInitializers map 里加一行 "foobar": &foobarInit{}核心代码一行不改——这就是"开闭原则"(对扩展开放、对修改关闭)为什么能让社区把厂商从几家堆到 39 家。
L05

统一 OpenAI 协议

// ai-proxy/main.go 路径→ApiName 映射(:51):
// /v1/chat/completions → ApiNameChatCompletion(覆盖 OpenAI/Anthropic/Qwen 各风格)
// onHttpRequestHeader(:217)取 activeProvider → 解析 apiName
//   Claude↔OpenAI 协议自动转换(:251,needClaudeResponseConversion)
// onHttpRequestBody(:314)→ provider.OnRequestBody 做模型映射 + 请求体改写
读法:对客户端永远暴露 OpenAI(或 Anthropic)协议。ai-proxy 识别请求路径(/v1/chat/completions 等)→ 找到目标 provider → 把统一请求翻译成厂商真实格式。甚至能自动转协议——客户端用 Claude 协议,后端是 OpenAI,自动转换(needClaudeResponseConversion)。这就是"万能适配器"的翻译现场。
L06

模型映射

// provider.go mapModel(:998)支持 前缀*/~正则/*
// defaultTransformRequestBody(:1396)用 sjson 只替换 model 名
//   避免整体反序列化以提性能
模型映射 + sjson 性能优化 模型映射:让你把客户端请求的模型名(如 gpt-4)映射到实际后端模型(如通义的 qwen-max),支持前缀/正则/通配。于是应用无感切换底层模型。性能细节:改请求体里的 model 名,用 sjson(只改 JSON 里那一个字段)而非"反序列化整个 body → 改 → 再序列化"——因为 LLM 请求体可能很大(长 prompt),整体反序列化慢。只动一个字段的 sjson 快得多。这是"热路径性能优化"的实战——大 body 场景避免不必要的全量解析。
L07

model-router

extensions/model-routermain.go:204):从 body 取 modelhigress/auto 时按最后一条 user 消息正则命中目标模型,否则把 provider/model 拆进 header(x-higress-llm-model),交给 Envoy 按 header 选上游集群。

model-router = "按模型路由到不同后端" ai-proxy 管"协议转换",model-router 管"路由决策"——根据请求的 model 字段,决定转发到哪个 LLM 后端集群。比如 gpt-* 走 OpenAI 集群、qwen-* 走通义集群。它把模型信息拆进 header,Envoy 按 header 匹配路由(回想 Istio 课 Day 08 的 header 路由)。甚至支持 higress/auto——按用户问题内容智能选模型(简单问题用便宜模型、复杂问题用强模型)。ai-proxy + model-router 配合:先路由选后端、再协议转换。
L08

今日小结 + 动手

🧠 今天你应该能回答

  • ai-proxy 解决什么?为什么叫"万能适配器"?
  • Provider 架构用什么模式?基接口为什么极简?
  • 为什么能支持 39 家厂商?(开闭原则)
  • 可选接口组合怎么工作?和 Envoy adapter 什么关系?
  • 模型映射是什么?sjson 为什么是性能优化?model-router 干什么?

✋ 动手

cd /Users/bitmart/work/codes/github/higress-group/higress/plugins/wasm-go/extensions/ai-proxy
grep -n 'providerInitializers\|type Provider interface\|func CreateProvider' provider/provider.go
ls provider/ | head -40   # 看有多少厂商文件
明天预告 · Day 17AI 增强插件——AI 网关不只代理:Token 限流(按真实 token 数扣减)、AI 统计(可观测)、语义缓存、内容安全。以及 Token 治理闭环。
← Day 15 golang-filter Day 17 · AI 增强插件 →