Files
Aether/docs/architecture/gemini-api-endpoint-routing.md
2026-05-19 03:16:35 +08:00

27 KiB
Raw Blame History

Gemini API Endpoint Routing Design

状态: implementation design 最后更新: 2026-05-18 目标: 把 Gemini Developer API 和 Vertex AI 的端点语义在 Aether 内部做成明确、可测试、可审计的一等路由语义,根治 generativelanguage.googleapis.comaiplatform.googleapis.com 混用、批量 embedding 伪成功、provider 能力声明不完整等问题。


速查结论

Aether 里同一个 api_format 只描述请求/响应数据形态,不等于实际 Google 后端产品面。

Aether 语义 默认后端产品面 官方 host 主要认证形态 说明
Google / Gemini Developer API Gemini Developer API, 也就是 AI Studio 这条 Gemini API generativelanguage.googleapis.com API key 默认 Gemini provider 应走这里
Vertex AI Vertex AI Gemini API aiplatform.googleapis.com{region}-aiplatform.googleapis.com service account / Vertex API key provider_type = vertex_ai 应走这里

Aether 还必须区分 Google 官方的 OpenAI-compatible 表面。它们使用 OpenAI request/response schema但不等于 native generateContent / embedContent endpoint

OpenAI-compatible 表面 后端产品面 官方 API root 主要认证形态 Aether 处理原则
Gemini Developer API OpenAI compatibility Gemini Developer API / AI Studio https://generativelanguage.googleapis.com/v1beta/openai Gemini API key as Bearer 只在 provider format 是 openai:* 且显式配置该 root 时使用
Vertex AI OpenAI compatibility Vertex AI / Google Cloud https://aiplatform.googleapis.com/v1/projects/{project}/locations/{location}/endpoints/openapi Google Cloud access token / service account 只在显式 OpenAI-compatible endpoint 上使用;不得替代 native Vertex provider 主链

端点动作必须按后端产品面区分:

能力 Gemini Developer API Vertex AI Aether 处理原则
Generate Content models/{model}:generateContent projects/{project}/locations/{location}/publishers/google/models/{model}:generateContent 两边都支持,但 URL 构造不同
Stream Generate Content models/{model}:streamGenerateContent?alt=sse projects/{project}/locations/{location}/publishers/google/models/{model}:streamGenerateContent?alt=sse 两边都支持,但 URL 构造不同
Single Embedding models/{model}:embedContent projects/{project}/locations/{location}/publishers/google/models/{model}:predict Vertex 文本 embedding 使用 Predict contractinstances[] + parameters
Batch Embedding models/{model}:batchEmbedContents 同一个 :predict,由 instances[] 表达多输入 Aether 不切到 Developer API模型自身的批量限制由 Vertex 明确返回

工程不变量:

  1. 默认 Gemini provider 只能生成 Gemini Developer API URL不得因为模型名是 Gemini 就走 Vertex。
  2. provider_type = vertex_ai 或明确的 Vertex auth/host 只能生成 Vertex URL不得回退到 Gemini Developer API URL。
  3. Vertex embedding 请求必须使用 Vertex Predict contract不得把 Developer API 的 model/content/requests body 原样发给 :predict
  4. 任何“不支持”的情况必须在调度/URL 构造阶段显式暴露为不可用,不能伪成功。
  5. Provider 模板、runtime policy、URL builder、conversion policy、测试连接、live DB reconciliation 必须消费同一个语义模型。
  6. Google 官方 OpenAI-compatible root 已经包含 API rootAether 不得额外拼接 /v1,否则会生成 .../openai/v1/....../endpoints/openapi/v1/... 这类错误 URL。
  7. Native Gemini endpoint 与 Google OpenAI-compatible endpoint 不是互相 fallback 的关系。显式配置 openai:* 才能走 OpenAI-compatible显式配置 gemini:* 才能走 native Gemini REST。

官方资料依据

本节只记录影响工程设计的官方事实。实现前必须以这些来源为真源,而不是以旧代码行为为真源。

Gemini Developer API / AI Studio

官方 Gemini API 文档把 Developer API 作为可直接用 API key 调用的产品面。其 REST API host 是 generativelanguage.googleapis.com,常见路径是 /v1beta/models/{model}:...

关键资料:

工程含义:

  • generateContentstreamGenerateContent 可以走 Developer API host。
  • embedContent 是单条 embedding。
  • batchEmbedContents 是 Developer API 的批量 embedding 方法;批量 body 形态是顶层 requests[],每项包含 modelcontent
  • Developer API key 不应被拼进 pathAether URL builder 应继续过滤或独立处理 key query避免 query 重复或泄露。
  • Developer API 的 OpenAI-compatible root 是 /v1beta/openai,其 chat / embedding path 是 /chat/completions/embeddings,不是 /v1/chat/completions/v1/embeddings

Vertex AI Gemini API

Vertex AI 的 Gemini API REST reference 使用 aiplatform.googleapis.com 或 region host路径包含 GCP project 与 location。

关键资料:

工程含义:

  • Vertex service account 路径必须包含 project 和 location
    • https://{region}-aiplatform.googleapis.com/v1/projects/{project}/locations/{region}/publishers/google/models/{model}:{action}
    • global location可使用 https://aiplatform.googleapis.com/v1/projects/{project}/locations/global/...
  • Vertex API key 路径可走:
    • https://aiplatform.googleapis.com/v1/publishers/google/models/{model}:{action}?key=...
  • Vertex 模型目录拉取不是推理请求,必须走 Model Garden publisher list
    • https://aiplatform.googleapis.com/v1beta1/publishers/{publisher}/models
    • 不得使用 projects/{project}/locations/{location}/publishers/{publisher}/modelsprojects.locations.publishers.models 资源没有 list 方法,只有 generate / stream / predict / embed 等动作。
  • Vertex 文本 embedding API 文档使用 :predict,请求体是 instances[],可选参数在 parameters 下;响应是 predictions[].embeddings.values
  • Vertex REST reference 也列出 embedContent,但 Aether 当前 text embedding 主链使用 text embeddings guide 和 Predict API 的 contract。
  • Vertex instances[] 是在线 Predict 请求体,不等同于异步 batch prediction job。模型级输入数量限制由 Vertex 返回Aether 不把超出限制的请求静默改走其他产品面。
  • Vertex OpenAI-compatible root 是 /v1/projects/{project}/locations/{location}/endpoints/openapi,其 OpenAI path 直接挂在这个 root 之后。
  • 自定义 Vertex OpenAI-compatible endpoint 可以使用 service account token 刷新,但只有 base URL 明确落在 /endpoints/openapi 时才能启用该 Vertex auth 语义。普通 aiplatform.googleapis.com + openai:* 不能被误判成 Vertex OpenAI compatibility。

Google Gen AI SDK 的后端切换语义

官方 SDK 同时支持 Gemini Developer API 与 Vertex AI但二者需要显式选择后端。SDK 层面的 vertexai=true / GOOGLE_GENAI_USE_VERTEXAI=true 说明:这不是“同一 URL 自动兼容”的关系,而是同一 SDK 下的两个后端产品面。

关键资料:

工程含义:

  • Aether 也应把“选择 Gemini Developer API 还是 Vertex AI”作为显式路由语义而不是让 URL builder 通过零散 host 字符串猜测。
  • api_format = gemini:generate_contentapi_format = gemini:embedding 只是数据格式。真正的后端产品面由 provider family / auth / endpoint host 决定。

Aether 当前相关链路

这一节说明在 Aether 内部,哪些对象共同决定一次 Gemini 请求实际打到哪里。

代表文件 当前职责 设计要求
请求格式转换 crates/aether-ai-formats/src/formats/... OpenAI / Gemini / Claude 等格式互转 只负责 body 形态,不决定 Google 后端产品面
Provider 类型模板 crates/aether-provider-transport/src/provider_types.rs 固定 provider 默认 endpoint、runtime policy Vertex 模板必须声明 generate + embedding 能力
Runtime policy crates/aether-provider-transport/src/provider_types.rs 和 provider policy 判断 provider 是否本地可消费 Vertex embedding 必须进入支持矩阵
URL builder crates/aether-provider-transport/src/request_url/mod.rs 把 transport + mapped_model + api_format 转成 upstream URL 必须按后端产品面构造 URL
Vertex helpers crates/aether-provider-transport/src/vertex/url.rs 构造 Vertex 特有 URL 必须覆盖 generate / stream / embedding
Conversion policy crates/aether-provider-transport/src/conversion.rs 判定跨格式请求能否走某个 transport OpenAI embedding -> Gemini embedding 在 Vertex 上必须可判定、可认证、可 URL
Gateway 测试连接 apps/aether-gateway/src/handlers/public/support/test_connection/route.rs 测试 provider endpoint 是否可用 不得用过低 token 或伪成功规则误判 Gemini 3
Live provider reconciliation gateway admin/provider 初始化与 DB 把固定模板同步到 live DB 新 endpoint 不应只存在源码里,必须进入 live provider/endpoints

目标语义模型

新增或显式固化一个内部概念:GeminiEndpointFamily

enum GeminiEndpointFamily {
    DeveloperApi,
    VertexAi,
}

该概念不一定必须以公开 enum 落地,但所有相关函数必须在行为上遵守同一判定:

判定输入 结果 备注
provider_type == "vertex_ai" VertexAi 固定 provider 主判据
endpoint host 看起来是 aiplatform.googleapis.com{region}-aiplatform.googleapis.com VertexAi 支持自定义 Vertex provider但不可反客为主覆盖固定 provider
Vertex service account auth 可解析 VertexAi service account 是 Vertex 强语义
Vertex API key query auth 可解析 VertexAi Vertex API key 仍是 Vertex 后端
普通 Google/Gemini provider + generativelanguage.googleapis.com DeveloperApi 默认 Gemini API

禁止规则:

  • 不得因为 api_formatgemini:* 就默认走 Vertex。
  • 不得因为 Vertex 缺少某个 endpoint 就回退到 Developer API。
  • 不得在 URL builder 里用“host 像谁就算谁”覆盖固定 provider 的 provider_type。
  • 不得在 body converter 里偷偷决定 endpoint familybody converter 只能做数据形态转换。

URL 构造矩阵

Developer API URL

Aether api_format stream batch URL 形态
gemini:generate_content false 不适用 /v1beta/models/{model}:generateContent
gemini:generate_content true 不适用 /v1beta/models/{model}:streamGenerateContent?alt=sse
gemini:embedding false false /v1beta/models/{model}:embedContent
gemini:embedding false true /v1beta/models/{model}:batchEmbedContents

Developer API 的批量 embedding 支持顶层 requests[]。Aether 可以继续用 body 检测来决定单条还是批量 URL但该检测只允许影响 Developer API URL。

Vertex AI URL

Aether api_format stream batch URL 形态
gemini:generate_content false 不适用 /v1/projects/{project}/locations/{location}/publishers/google/models/{model}:generateContent
gemini:generate_content true 不适用 /v1/projects/{project}/locations/{location}/publishers/google/models/{model}:streamGenerateContent?alt=sse
gemini:embedding false false /v1/projects/{project}/locations/{location}/publishers/google/models/{model}:predict
gemini:embedding false true /v1/projects/{project}/locations/{location}/publishers/google/models/{model}:predict

Vertex text embedding 的模型由 URL path 承载body 不得重复携带顶层 model 字段,否则会触发 Vertex oneof field '_model' is already set 一类错误。Aether 在 Vertex transport context 下必须把 Gemini Developer API embedding body 转成 Predict body

{
  "instances": [
    { "content": "text", "task_type": "RETRIEVAL_QUERY", "title": "optional" }
  ],
  "parameters": {
    "outputDimensionality": 768,
    "autoTruncate": true
  }
}

如果输入已经是 Predict bodyAether 只移除重复的顶层 model。如果输入仍是 OpenAI body 或无法确定可转换,调度阶段必须显式失败,不得把未转换 body 发到 Vertex native endpoint。

Google OpenAI-Compatible URL

OpenAI-compatible URL 属于显式 passthrough root不参与 native Gemini URL builder。

Aether api_format Gemini Developer API OpenAI compatibility Vertex AI OpenAI compatibility Aether 处理原则
openai:chat /v1beta/openai/chat/completions /v1/projects/{project}/locations/{location}/endpoints/openapi/chat/completions root 已含 API 版本,不再补 /v1
openai:embedding /v1beta/openai/embeddings /v1/projects/{project}/locations/{location}/endpoints/openapi/embeddings root 已含 API 版本,不再补 /v1

这条链路的关键边界:

  1. openai:* provider format 走 OpenAI-compatible schema不做 OpenAI -> Gemini native body 转换。
  2. gemini:* provider format 走 native Gemini schema不因目标是 Google provider 就切到 OpenAI-compatible endpoint。
  3. 如果用户请求 openai:*、provider endpoint 是 gemini:*Aether 走格式转换后打 native Gemini endpoint。
  4. 如果用户请求 openai:*、provider endpoint 也是 openai:*Aether 保持 OpenAI schema 并打显式 OpenAI-compatible root。
  5. 以上两条都是正式主链,不能互相静默顶替。

请求体转换边界

aether-ai-formats 中的 Gemini embedding converter 当前负责:

  • 单条 input -> embedContent body
  • 多条 input -> Developer API batchEmbedContents body
  • dimensions -> outputDimensionality
  • embedding task -> Gemini taskType

设计要求:

  1. 该 converter 可以继续生成 Gemini Developer API 的 embedContent / batchEmbedContents body。
  2. Transport 层必须在 Vertex context 下把该 body 转成 Predict body并在无法转换时 fail closed。
  3. 所有 taskType / outputDimensionality 必须保持显式传递;不得默认注入会改变语义的 task 或维度。
  4. Vertex Predict 的 instances[] 只能表示在线 Predict 请求的一次调用;它不是异步 batch prediction job也不是 Developer API batchEmbedContents 的静默替身。
  5. Developer API 单条 embedding body 可以保留 modelVertex 单条 embedding 在 gateway transport 语义层必须删除顶层 model,因为 Vertex 模型已在 path 中指定。

格式转换矩阵

端点族和格式转换是两层语义:

  • 端点族决定请求发往 generativelanguage.googleapis.com 还是 aiplatform.googleapis.com
  • 格式转换决定客户端传入的 body 如何变成 provider 所需 body以及 provider response 如何变回客户端期望 body。

gemini:generate_content 在 Developer API 与 Vertex AI 上使用同一 Gemini generate-content body 形态,因此格式转换器不应区分这两个产品面。产品面差异只留给 URL/auth 层处理。

gemini:embedding 在格式层仍先表达为 Gemini Developer API 的 embedding body。进入 Vertex transport context 时transport 层再把它收敛到 Vertex Predict body。这样格式转换器不需要知道认证方式URL/body transport 也不会把 Developer API body 原样发给 Vertex。

客户端格式 Provider 格式 Developer API Vertex AI 处理要求
openai:chat gemini:generate_content 支持 支持 OpenAI chat -> Gemini contents / generationConfig
gemini:generate_content openai:chat 支持 支持 Gemini contents -> OpenAI messages
openai:embedding gemini:embedding 单条 支持 支持 OpenAI input string 或单项数组 -> Gemini embedContent bodyVertex transport 再转 instances[]
openai:embedding gemini:embedding 多条 支持 支持于 transport 层 Developer API -> batchEmbedContentsVertex transport -> Predict instances[],模型限制由 Vertex 返回
gemini:embedding 单条 openai:embedding 支持 支持 Gemini content.parts[].text -> OpenAI input string
gemini:embedding 批量 openai:embedding 支持 支持 Gemini requests[] -> OpenAI input[]Vertex Predict response 同样可转
gemini:embedding response openai:embedding response 支持 支持 Gemini embedding.values / embeddings[].values / Vertex predictions[].embeddings.values -> OpenAI data[].embedding
openai:embedding response gemini:embedding response 支持 支持于格式层 OpenAI data[] -> Gemini single embedding 或 batch embeddings[]
openai:chat openai:chat on Google OpenAI-compatible root 支持 支持 passthrough OpenAI schema不做 native Gemini 转换
openai:embedding openai:embedding on Google OpenAI-compatible root 支持 支持 passthrough OpenAI schema不做 native Gemini 转换

这张矩阵的关键点:

  1. 格式层必须能双向理解 Gemini native embedding request/response 与 OpenAI embedding request/response。
  2. Vertex text embedding 使用 Predict contract多输入由 instances[] 表达,不构造不存在的 :batchEmbedContents
  3. 一旦 provider family 是 Vertex任何 embedding 请求都不能借格式转换之名回退到 Developer API。
  4. 对 OpenAI embedding 单项数组,转换器必须生成 Gemini 单条 body避免把“单条业务请求”误判成 Vertex batch。
  5. Google OpenAI-compatible passthrough 与 OpenAI -> Gemini native conversion 是两条显式路径。管理员通过 provider endpoint format 选择路径Aether 不得自动“择优”改路。

Provider 能力声明与调度

Vertex provider 的固定模板必须包含:

  • gemini:generate_content
  • gemini:embedding
  • claude:messages,如果当前上游 Vertex Claude 支持仍保留

Runtime policy 必须表达:

  • Vertex 能本地消费 Gemini generate content。
  • Vertex 能本地消费 Gemini embedding并在 transport 层生成 Predict URL/body。
  • Vertex text embedding 的模型级输入数量限制由 Vertex 返回Aether 不静默拆分、不静默降级到 Developer API。
  • 全局模型名与 Vertex 实际 provider 模型名必须可以分离。例如客户端继续请求全局 gemini-embedding-2-previewVertex provider model 可以映射到官方可用的 gemini-embedding-2调度、key allowed_models、URL builder 必须消费映射后的 provider model不得拿全局 preview 名直打 Vertex。

调度与 conversion policy 必须表达:

  • openai:embedding -> gemini:embedding 可以被 Vertex provider 接收transport 层负责把 Gemini Developer API body 转成 Vertex Predict body。
  • 对批量 input不能生成 Vertex :batchEmbedContents,也不能回退到 generativelanguage.googleapis.com
  • request_pair_direct_auth 对 Vertex API key 必须返回 key query authservice account auth 由 OAuth refresh path 处理,不能伪造成普通 bearer key。

测试设计

必须覆盖这些测试面:

  1. Developer API generate URL:
    • non-stream -> generativelanguage.googleapis.com/...:generateContent
    • stream -> ...:streamGenerateContent?alt=sse
  2. Developer API embedding URL:
    • 单条 body -> ...:embedContent
    • 多条 body -> ...:batchEmbedContents
  3. Vertex generate URL:
    • API key auth -> aiplatform.googleapis.com/v1/publishers/google/models/...
    • service account -> project/location path
  4. Vertex embedding URL:
    • API key auth -> ...:predict?key=...
    • service account -> project/location ...:predict
  5. Vertex embedding body:
    • 单条 model/content body -> instances[],且移除顶层 model
    • body 含顶层 requests[] 时 -> instances[]
    • body 已经是 instances[] 时只清理重复 model
    • 无法映射的 body 在调度/模型测试阶段显式失败
    • 不得生成 generativelanguage.googleapis.com
    • 不得生成 aiplatform.googleapis.com/...:batchEmbedContents
  6. Provider template:
    • Vertex fixed template 包含 gemini:embedding
    • provider embedding support 矩阵包含 Vertex -> Gemini embedding
  7. Conversion:
    • OpenAI embedding 可以被转换到 Gemini embedding provider format
    • Vertex embedding transport 可通过支持检查
    • Vertex embedding execution plan 的 URL 使用 mapped provider modelbody 不含顶层 model
    • Vertex embedding execution plan 的 body 使用 instances[] / parameters
  8. Gateway test connection:
    • Gemini generate content 测试不能强制 maxOutputTokens = 5
    • Google OpenAI-compatible openai:chat 测试不能强制 max_tokens = 5,否则 Gemini thinking 模型仍可能只返回 thought token / 空 visible content
    • Gemini 3 / thinking 模型返回 HTTP 200 但无 visible content 时必须判失败,不能写成成功
  9. Google OpenAI-compatible roots:
    • Developer API OpenAI root .../v1beta/openaiopenai:chat 时生成 .../chat/completions,不得生成 .../openai/v1/chat/completions
    • Developer API OpenAI root .../v1beta/openaiopenai:embedding 时生成 .../embeddings,不得生成 .../openai/v1/embeddings
    • Vertex OpenAI root .../endpoints/openapiopenai:chat / openai:embedding 时直接挂对应 OpenAI path
    • 自定义 Vertex OpenAI-compatible service account endpoint 必须进入 Vertex auth refresh 上下文;普通 aiplatform.googleapis.com + openai:* 不得被误认成 Vertex OpenAI-compatible
  10. Admin write validation:
  • Vertex API key key formats 允许 gemini:generate_contentgemini:embedding
  • Vertex service account key formats 允许 claude:messagesgemini:generate_contentgemini:embedding

测试断言必须检查具体 URL、具体 action、具体 body contract 和具体失败原因,不能只检查 Some(url) 或状态码。


Live 迁移与验证

上线后必须做四类验证:

  1. 源码测试:
    • cargo test -p aether-provider-transport --lib
    • 必要时补 cargo test -p aether-ai-formats --lib
    • 必要时补 gateway 相关 test
  2. Live DB reconciliation
    • vertex_ai provider 的 endpoints 中必须出现 gemini:embedding
    • Google/Gemini provider 的 embedding endpoint 仍指向 Developer API不被 Vertex 改写
  3. Live HTTP smoke
    • Developer API embedding 单条可用
    • Developer API embedding 批量可用
    • Vertex generate content 返回 visible content 才算成功
    • Vertex embedding 单输入可用
    • Vertex embedding 多输入如果模型拒绝,必须暴露 Vertex 原始失败,不能降级成 Developer API 成功
  4. 接入方地址核验:
    • astrbot plugin ltm
    • codex cli config
    • 其它容器中引用 Aether 的配置

接入方默认应使用容器网络内稳定地址:

http://aether-app:8084/v1

只有在调用方不在 edge-stack-aether-internal 这类 Docker 内网、或需要从宿主机/外网访问时,才使用宿主机映射地址或域名。


明确不做的事

  1. 不把 Vertex embedding 多输入写成隐藏循环。隐藏 fan-out 会改变成本、延迟、断路器行为和重试语义,必须另开设计。
  2. 不为了测试通过把 Vertex 请求降级到 Developer API。
  3. 不为了让 HTTP 200 看起来成功而接受空 candidate / MAX_TOKENS 无 visible content。
  4. 不改前端视觉定制、字体、品牌名、landing page 设计。
  5. 不用旧 provider endpoint 继续承担新主链。
  6. 不把 Google OpenAI-compatible endpoint 当成 native Gemini endpoint 的隐藏 fallback它只能通过显式 openai:* endpoint 进入。

施工顺序

  1. 固化 endpoint family 判定与 URL helper。
  2. 为 Vertex gemini:embedding 补齐 provider template、runtime policy、conversion policy。
  3. 让 request URL builder 对 Vertex embedding 走 Vertex Predict helper。
  4. 让 transport body semantics 对 Vertex embedding 生成 instances[] / parameters,无法转换时 fail closed。
  5. 移除测试连接中对 Gemini generate content 的过低 maxOutputTokens 硬编码,防止 Gemini 3 thinking 被预算挤空。
  6. 移除公开 test-connection 对 OpenAI-compatible chat 的过低 max_tokens 硬编码,防止 Google OpenAI-compatible root 复发同类空输出。
  7. 跑 red/green 测试。
  8. 部署 live。
  9. 校验 live DB provider endpoints 与外部接入方地址。

后续可选增强

如果 7 天 embedding 重算必须在 Vertex 上高吞吐完成,建议后续单独实现 VertexEmbeddingFanoutExecutor

  • 输入 OpenAI embedding 数组。
  • 按配置分片,每片发单条或有限并发 Vertex predict
  • 合并为 OpenAI embedding response。
  • 将每个子请求的失败、重试、成本、断路器状态独立记录。

这项增强不能混入本次 endpoint 语义修复,否则会扩大风险面。