---
title: "添加 provider"
description: "如何声明一个 AIGateway provider——DSL、编译后的定义、设置、能力、prepare 函数、基于 plugin 的注册。"
url: "https://ankole.agentbull.com/zh-Hans-CN/docs/adding-a-provider/"
lang: "zh-Hans-CN"
---

> 面向 AI Agent 的文档索引：https://ankole.agentbull.com/zh-Hans-CN/llms.txt

# 添加 provider

AIGateway provider 是 Ankole 与上游 AI 服务通信的方式——LLM、embedding 模型、web 搜索 API。本页带贡献者走完整个流程：provider DSL、它产出的编译后 `ProviderDefinition`、provider 声明的设置与能力、拥有请求构造的 prepare 函数。它建立在 [AIGateway](https://ankole.agentbull.com/zh-Hans-CN/docs/ai-gateway/index.md) 概念页之上；本页是*如何添加一个 provider*。

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

## provider DSL

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

```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` 声明一个设置项（运维者设置或请求选项）：

```elixir
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 函数和线上形态：

```elixir
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` 发送的准备好的请求：

```elixir
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](https://ankole.agentbull.com/zh-Hans-CN/docs/writing-a-skill/index.md)；Provider 契约是 `ai_gateway.provider`，kind ID 必须匹配 `~r/\A[a-z][a-z0-9_]{0,62}\z/`。

## 本指南不是什么

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

## 下一步

- AIGateway 概念（路由、解析、统一边界），读 [AIGateway](https://ankole.agentbull.com/zh-Hans-CN/docs/ai-gateway/index.md)。
- Plugin 注册方法，读 [开发 Skill 与 Control Plane Plugin](https://ankole.agentbull.com/zh-Hans-CN/docs/writing-a-skill/index.md)。
- 首次配置 Provider，读 [快速开始](https://ankole.agentbull.com/zh-Hans-CN/docs/quickstart/index.md#3-添加模型提供商并创建-agent)。
