跳到正文
Ankole

添加 provider

AIGateway provider 是 Ankole 与上游 AI 服务通信的方式——LLM、embedding 模型、web 搜索 API。本页是贡献者 walkthrough:provider DSL、它产出的编译后 ProviderDefinition、provider 声明的设置与能力、拥有请求构造的 prepare 函数。它建立在 AIGateway 概念页之上;本页是如何添加一个 provider

先把决定性的性质说清楚:provider 模块在 Elixir 里拥有请求准备;Rust UniversalAIClient 拥有线上——传输、编码、响应归一化。编译后的定义只描述稳定的元数据、设置、以及哪个 prepare 函数拥有哪种能力。provider 不是完整的 HTTP 客户端;它是一个 prepare 函数加一份声明。

provider DSL

provider 模块 use Ankole.AIGateway.ProviderDSL 并用一个小型块结构 DSL 声明自己。DSL 记录元数据和能力归属;它刻意不描述请求体字段——每个 provider 的 prepare 函数是同一模块里的普通 Elixir 代码。

defmodule Ankole.AIGateway.Providers.MyProvider do
  use Ankole.AIGateway.ProviderDSL

  provider :my_provider do
    label(%{"default" => "My Provider"})
    base_url("https://api.example.com/v1")

    setting(:api_key, encrypted: true, scope: :credential)

    language_model do
      upstream(:sse)
      api_resolver(:openai_responses)
      prepare(:prepare_language_model)
      supports_parallel_tool_calls()
    end
  end

  def prepare_language_model(context) do
    # 普通 Elixir——从 context 构建准备好的请求
  end
end

provider 块编译成一个 ProviderDefinition 结构体,运行时被 AIGateway 注册表消费。

编译后的定义

ProviderDefinition 携带:

字段 含义
provider_kind 存储的 provider 行和模型绑定使用的稳定 id
label Console 用的本地化显示名
module provider 模块本身
base_url 默认上游 URL(运维者可覆盖)
settings 声明的 Setting 列表
capabilities 声明的 Capability 列表

注册表按 provider_kind 解析 provider、查找请求能力种类对应的能力、调用能力的 prepare 函数、把结果交给 UniversalAIClient

设置

Setting 声明一个运维者或请求选项:

setting(:api_key, encrypted: true, scope: :credential)
setting(:organization, advanced: true)
setting(:reasoningEffort, type: :select, default: "high",
       options: ["minimal", "low", "medium", "high", "xhigh"], scope: :request)
字段 含义
key 选项名(atom)
type :string:select:boolean:map 或 nil
default 默认值
options :select 的允许值
required? 运维者是否必须提供
encrypted? 凭据值静态加密所需的存储元数据
advanced? Console 表单的呈现元数据;不改变校验或运行时
scope :credential(按池成员)、:connection(按 Provider 行)或 :request(按 model profile)

每个 Provider 行都有凭据池,单成员也是池的退化情况。Resolver 在调用 prepare 函数前选择一个健康成员,并解密它的 :credential 设置。端点和自定义请求头等 connection 设置由整行共享;request 设置来自 model profile。prepare 函数从解析后的同一个 settings map 读取三种 scope,不实现池选择。

advanced? 仅控制呈现。它在 Console 中把字段放进高级设置,不改变校验或行为。

能力

Capability 把一个用户可见的模型能力绑定到 prepare 函数和线上形态:

language_model do
  upstream(:sse)
  api_resolver(:openai_responses)
  prepare(:prepare_language_model)
  supports_parallel_tool_calls()
  supports_native_image_generation()
end
字段 含义
kind :language_model:embedding_model:rerank_model:web_search:web_fetch:image_generate 之一
upstream 线上形态::sse:eventstream:websocket_text:json
api_resolver Rust 侧的 resolver atom(如 :openai_responses),拥有编码和传输
prepare 构建准备好的请求的 Elixir 函数
timeout_ms 可选的按能力超时
supports_parallel_tool_calls? Provider 是否接受并行工具调用
supports_native_image_generation? LLM 是否能在没有 hosted image_generate 档案时执行公共图像工具

六种能力映射到运行时使用的外部名:language_model"llm"embedding_model"embedding"rerank_model"rerank",三种 web/image 能力名不变。只提供 LLM 的 provider 只声明 language_model;提供 LLM 和 embedding 的 provider 两者都声明。

prepare 函数

prepare 函数是 provider 模块里的普通 Elixir 代码,由能力的 prepare 字段命名。它接收一个 PrepareContext(解析后的设置、模型请求、agent 的上下文)并返回一个 UniversalAIClient 发送的准备好的请求:

def prepare_language_model(%PrepareContext{} = context) do
  %UniversalAIRequest{}
  |> put_url(context.settings.base_url, "/responses")
  |> put_auth("Bearer", context.settings.api_key)
  |> put_body(context.request)
end

Provider 的差异住在这里——URL 构造、auth 头、体塑形和特定上游 API 的规则。prepare 函数是 Provider 的实际工作;DSL 声明说明这些工作如何被发现和路由。凭据重试不属于 prepare 函数:控制面可以改选池成员、重建请求,再让 kernel 执行一次新的传输尝试。

注册 provider

第一方 provider 编译进发布、被注册表发现。对于 plugin 贡献的 provider,在一个 Control Plane Plugin 的 adapter_declarations/0 里通过 ai_gateway.provider 契约声明,注册表从 plugin 的声明里捡起它——与 signal adapter 同一模型,不同契约 id。

Plugin 的注册方法见开发 Skill 与 Control Plane Plugin;Provider 契约是 ai_gateway.provider,kind ID 必须匹配 ~r/\A[a-z][a-z0-9_]{0,62}\z/

本指南不是什么

它不是 HTTP 客户端教程——prepare 函数构建准备好的请求,Rust 客户端做 HTTP。它不是绕过 kernel 传输的途径;api_resolverupstream 线上形态是 Rust UniversalAIClient 拥有的东西,provider 从已有 resolver 里选,而非发明自己的传输。它也不是读已有 provider 的替代;lib/ankole/ai_gateway/providers/ 是权威参考,最简单的一个(OpenAI 或 openai_compatible)是正确的起点。

下一步