> For the complete documentation index, see [llms.txt](https://docs.maiagent.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.maiagent.ai/tech/ai-gateway/ai-gateway-openai-compatible.md).

# AI Gateway：OpenAI 相容 API 串接

用 OpenAI SDK 不改程式邏輯串接 MaiAgent AI Gateway：端點與 base URL、Bearer 認證、模型識別（別名、自建模型、備援群組）、思考等級、串流、語音轉文字與文字轉語音、錯誤格式、Credits 計費與限制。

本頁寫給要用 MaiAgent AI Gateway 呼叫語言模型的工程師，尤其是已經用 OpenAI SDK 寫好程式的軟體廠商（ISV）。Gateway 提供與 OpenAI 相容的介面，把 SDK 的 `base_url` 換成 MaiAgent、`api_key` 換成 MaiAgent API Key，就能透過同一個入口呼叫組織可用的各家模型，呼叫邏輯不必改。

Gateway 在來源、模型、資料、責任與紀錄五個面向提供哪些保障，請看 [AI Gateway 安全性](/tech/ai-gateway/ai-gateway-security.md)；本頁只講怎麼串接。

{% hint style="info" %}
AI Gateway 需要由 MaiAgent 平台為組織開通。組織未開通時，任何金鑰呼叫推論端點都會得到與無效金鑰相同的 `401`。
{% endhint %}

## 端點總覽 <a href="#endpoints" id="endpoints"></a>

| 方法     | 路徑                             | 認證 | 用途             |
| ------ | ------------------------------ | -- | -------------- |
| `POST` | `/api/v1/chat/completions`     | 必填 | 對話補全，支援串流與非串流  |
| `POST` | `/api/v1/embeddings`           | 必填 | 文字向量化          |
| `POST` | `/api/v1/audio/transcriptions` | 必填 | 語音轉文字（上傳整段音檔）  |
| `POST` | `/api/v1/audio/speech`         | 必填 | 文字轉語音（回傳完整音檔）  |
| `GET`  | `/api/v1/models`               | 選填 | 列出可呼叫的模型識別碼與定價 |

**Base URL**（給 OpenAI SDK 的 `base_url`）：

```
https://api.maiagent.ai/api/v1
```

SDK 會自己在後面接上 `/chat/completions`、`/embeddings`、`/audio/transcriptions`、`/audio/speech`、`/models`。路徑結尾**不加**斜線。

## 認證 <a href="#authentication" id="authentication"></a>

推論端點使用 Bearer 認證，與 OpenAI SDK 預設送出的標頭相同：

```
Authorization: Bearer <GATEWAY_API_KEY>
Content-Type: application/json
```

* 金鑰就是 MaiAgent 的 API Key，但 Gateway 端點只認 `Bearer` 前綴；MaiAgent 其他 API 使用的 `Api-Key <金鑰>` 格式在這裡會回 `401`。
* 以下情況一律回同樣的 `401 invalid_api_key`，不透露是哪一關擋下：金鑰不存在、已撤銷、已停用、已過期、綁定的成員或使用者已停用、組織未開通 AI Gateway。
* 請求本文只接受 JSON；唯一例外是語音轉文字，要用 `multipart/form-data` 上傳音檔（OpenAI SDK 會自動處理）。

### 取得 Gateway 金鑰 <a href="#get-api-key" id="get-api-key"></a>

在 MaiAgent 管理後台 <mark style="color:blue;">組織設定</mark> → <mark style="color:blue;">API Key 管理</mark> → <mark style="color:blue;">建立 API Key</mark>。建立精靈分三步：選擇成員、開通與限制、產生金鑰。

* 金鑰綁定一位成員，呼叫會記在該成員名下，並受該成員的 Credits 額度限制。
* 「開通與限制」可以設定：這把金鑰可呼叫的模型（只能在組織可用範圍內縮小，預設全開）、每分鐘請求數（RPM）、每分鐘 Token 數（TPM）。
* 完整金鑰只在建立當下顯示一次，請立即保存到您的密鑰管理系統。
* 同一位成員可以同時持有多把有效金鑰，輪替時先簽新金鑰、部署完成再撤銷舊的，服務不中斷。
* 使用 API Key 管理頁需要「API Key 管理權限」。沒有這個權限的成員，可以在右上角頭像 → <mark style="color:blue;">帳號</mark> → <mark style="color:blue;">API 金鑰</mark> 自行建立金鑰（同樣適用於 Gateway）；模型開通與 RPM／TPM 則由管理者在 API Key 管理頁設定。

後台左側選單的 <mark style="color:blue;">AI Gateway</mark> → <mark style="color:blue;">串接範例</mark> 會依您選的模型產生可複製的 cURL／Python／Node.js 範例；呼叫失敗時可到 <mark style="color:blue;">AI Gateway</mark> → <mark style="color:blue;">總覽</mark> 執行 <mark style="color:blue;">存取診斷</mark>，逐關檢查金鑰、成員、組織與模型開通狀態。看得到 AI Gateway 選單需要角色具備 AI Gateway 權限，設定方式見使用者手冊的 [讓指定角色使用 AI Gateway](https://docs.maiagent.ai/org/role-permission#enable-ai-gateway-for-role)。

## 最小範例 <a href="#minimal-examples" id="minimal-examples"></a>

先把金鑰放進環境變數，不要寫死在程式碼：

```bash
export GATEWAY_API_KEY="<GATEWAY_API_KEY>"
```

{% tabs %}
{% tab title="OpenAI Python SDK" %}

```python
import os

from openai import OpenAI

client = OpenAI(
    api_key=os.environ["GATEWAY_API_KEY"],
    base_url="https://api.maiagent.ai/api/v1",
)

resp = client.chat.completions.create(
    model="<vendor>/<model>",  # 從 GET /models 取得的 id，或備援群組 slug
    messages=[{"role": "user", "content": "Hello!"}],
)
print(resp.choices[0].message.content)
```

{% endtab %}

{% tab title="curl" %}

```bash
curl https://api.maiagent.ai/api/v1/chat/completions \
  -H "Authorization: Bearer $GATEWAY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "<vendor>/<model>",
    "messages": [
      { "role": "user", "content": "Hello!" }
    ]
  }'
```

{% endtab %}
{% endtabs %}

## 模型識別 <a href="#model-ids" id="model-ids"></a>

`model` 欄位填的是 **Gateway 模型識別碼**，不是各家供應商的原生 ID。識別碼一律是含 `/` 的形式，沒有 `/` 的值會直接回 `400`。

| 您要呼叫的           | `model` 填什麼                                     | 從哪裡取得                               |
| --------------- | ----------------------------------------------- | ----------------------------------- |
| MaiAgent 託管模型   | `{vendor}/{model}`（或平台設定的別名）                    | `GET /models` 回傳的 `id`              |
| 組織自建模型（BYOK／自架） | 系統產生的 Gateway 模型 ID，形如 `{供應商 slug}/{模型名}-{隨機碼}` | 後台 AI Gateway → 模型頁的「Gateway 模型 ID」 |
| 備援群組            | 系統產生的群組 slug，形如 `maiagent/fallbacks-{隨機碼}`      | 後台 AI Gateway → 備援模型群組              |

解析順序：先比對平台別名，再比對 `{vendor}/{model}`，接著是組織自建模型，最後是備援群組。平台模型（別名與 `{vendor}/{model}`）只在「組織可用 ∩ Gateway 開放 ∩ 這把金鑰開通」的範圍內找；自建模型與備援群組限本組織所有，並受這把金鑰的開通清單限制。範圍外的模型與不存在的模型回同樣的 `404 model_not_found`。

### 用 /models 列出可呼叫的模型 <a href="#list-models" id="list-models"></a>

```python
for m in client.models.list():
    print(m.id)
```

帶 Gateway 金鑰呼叫時，清單只列這把金鑰實際可呼叫的模型（含組織的封鎖設定與金鑰的開通清單）；不帶認證時回傳平台公開目錄，不含組織過濾。每筆除了 OpenAI 標準的 `id`、`object`、`created`、`owned_by`，還有：

| 欄位                            | 說明                                                                                                                                                              |
| ----------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name`                        | 顯示名稱                                                                                                                                                            |
| `type`                        | 語言模型為 `language`；語音模型為 `transcription`（語音轉文字）或 `speech`（文字轉語音），欄位差異見「語音轉文字與文字轉語音」                                                                               |
| `tags`                        | 能力標籤：`tool-use`、`multimodal`、`reasoning`、`prompt-caching`、`streaming`；`reasoning` 表示這個模型接受 `reasoning_effort`，見「思考等級」                                           |
| `pricing`                     | 託管模型：`input`／`output`，單位 `credits_per_1k_tokens`（帶 Gateway 金鑰呼叫時已含組織的 Gateway 服務費；不帶認證的公開目錄為未加服務費的基準單價）；自建模型：`traffic_fee_percent`，單位 `percent_of_total_tokens` |
| `providers`                   | 可承載這個模型的後端供應商清單，每筆有 `slug`，可搭配下方的 `provider.order` 指定                                                                                                           |
| `context_window`、`max_tokens` | 有設定時才出現                                                                                                                                                         |

### 組織隱藏中國模型時的串接行為 <a href="#hidden-china-models" id="hidden-china-models"></a>

組織開啟「隱藏中國模型」後，依平台登錄的模型產地排除中國平台模型，包含日後新上架的模型。這項限制會套用到 Gateway 的模型目錄與實際呼叫：

* **帶金鑰查詢目錄**：`GET /models` 不列出被隱藏的平台模型；不帶認證的公開目錄沒有組織資訊，不套用這項限制。
* **直接呼叫平台模型**：被隱藏的模型無法透過別名或 `{vendor}/{model}` 呼叫，回 `404 model_not_found`。以自帶金鑰呼叫同一個平台模型，也不會繞過組織的隱藏設定。
* **呼叫備援群組**：先排除被隱藏的平台模型步驟，再使用剩餘步驟；沒有剩餘步驟時回 `404 model_not_found`，該群組也不列入帶金鑰取得的目錄。
* **組織自建模型**：組織自己建立的 Gateway 模型不受這項產地隱藏設定限制，仍須符合原有的金鑰開通規則。

例如，企業的整合工程師原本將某個中國平台模型設為預設值；組織開啟隱藏後，原本的識別碼會回 `404`。工程師應用服務實際使用的金鑰重新取得 `/models` 清單，將預設值改成清單中的可用模型，再恢復呼叫；不要以未帶認證的公開目錄判斷該組織是否有權使用模型。

{% hint style="info" %}
隱藏設定不會清除既有的逐模型封鎖清單；關閉隱藏後，仍依原本的組織啟用狀態與金鑰開通範圍判斷。
{% endhint %}

### 指定後端供應商 <a href="#provider-order" id="provider-order"></a>

同一個模型識別碼可能由多家供應商承載（例如同一個模型同時在原廠與雲端平台上架）。要固定走某一家，在請求本文加上 `provider.order`，填 `/models` 回傳的 `providers[].slug`：

```python
resp = client.chat.completions.create(
    model="<vendor>/<model>",
    messages=[{"role": "user", "content": "Hello!"}],
    extra_body={"provider": {"order": ["<provider-slug>"]}},
)
```

目前只採用清單的第一個值；該供應商沒有這個模型時回 `404 model_not_found`。指定 `provider.order` 時不會解析到備援群組。

### 自帶金鑰（BYOK） <a href="#byok" id="byok"></a>

組織擁有者可以在後台 <mark style="color:blue;">AI Gateway</mark> → <mark style="color:blue;">模型供應商</mark> 登錄組織自己的供應商憑證。登錄後：

* 呼叫託管模型時，若組織有同一家供應商的有效憑證，Gateway 會改用組織的憑證呼叫上游（同家有多筆時用最近更新的一筆），呼叫端的 `model` 不用改。
* 也可以用這些憑證建立「自建模型」，以系統產生的 Gateway 模型 ID 呼叫。
* 上游以 401／403 拒絕組織憑證時，Gateway 回 `502 upstream_auth_error`，訊息會指出是組織的 BYOK 憑證被拒，請到 AI Gateway 設定更新憑證。

### 部分更新供應商憑證 <a href="#credential-partial-update" id="credential-partial-update"></a>

整合程式可用 `PATCH /api/v1/gateway/credentials/{id}/` 更新自己組織的供應商憑證。此管理介面需要登入、組織已開通 AI Gateway、具備 AI Gateway 存取權限，且寫入者為組織擁有者；認證與 [推論端點](#authentication) 的 Gateway Bearer 金鑰用途不同。

請求可包含 `name`、`isActive` 或 `credentials`；`credentials` 內使用 `apiBase`、`apiKey` 等 camelCase 欄位名稱。每個憑證欄位依下表處理：

| 送出的值     | 更新結果                     |
| -------- | ------------------------ |
| 省略欄位     | 保留原值                     |
| 空字串 `""` | 保留原值，代表沒有修改              |
| 非空的新值    | 取代原值，再依供應商規則驗證           |
| `null`   | 清空選填欄位；必填欄位無法清空，會回 `400` |

例如 vLLM 與 OpenAI 相容供應商的 `apiKey` 可用 `null` 清空，但 `apiBase` 為必填。Anthropic 的選填 `apiBase` 可用 `null` 清除憑證中的自訂端點；API Key 仍為必填。

{% hint style="warning" %}
已存有 API Key 時，把 `apiBase` 改成另一個非空端點，必須同時重新輸入 `apiKey`，或在該供應商允許時明確以 `null` 清空金鑰。省略金鑰或送空字串會回 `400`，錯誤位於 `credentials.apiKey`，原設定不變。只改名稱或重送相同端點不需重新輸入金鑰。
{% endhint %}

讀取回應只提供遮罩預覽 `credentialsPreview`，不會回傳原始憑證。後台的操作說明請見 [模型供應商設定](https://docs.maiagent.ai/build/ai-gateway#provider-credentials)，本節只說明整合程式的更新語意。

### 備援群組 <a href="#fallback-groups" id="fallback-groups"></a>

備援群組是組織擁有者在後台建立的有序模型清單。`model` 填群組 slug 時才會啟用整條備援鏈；單一模型的呼叫不會自動切換。

* **切換條件**（系統固定）：逾時、連線失敗、上游回 408／429／500／502／503／504／529。參數錯誤、金鑰無效這類請求端錯誤不切換，直接回錯誤。
* **串流**：Gateway 會先向上游取得第一段內容再開始回應，所以連線錯誤與限流在送出第一個位元組前就能切換；一旦開始回傳內容就不再切換。
* 回應的 `model` 欄位維持您送出的群組 slug，實際由哪個模型服務、是否觸發備援會記錄在後台的用量與請求紀錄。
* 模型資料已不存在，或組織自建模型已被刪除（停用）的步驟，會在呼叫前跳過；主模型也會改用清單中第一個仍可呼叫的步驟。**平台模型僅被停用時不適用這項跳過規則**。
* 整條鏈沒有可呼叫的步驟時，推論回 `400 invalid_request_error`，錯誤碼為 `fallback_group_unavailable`；請編輯群組加入可用模型，不要把它當作暫時的上游錯誤重試。
* `GET /api/v1/gateway/fallback-groups/{id}/` 讀取群組時，上述失效步驟會回 `modelId: null`、`modelName: ""`、`managedModel: null`、`customModel: null`。讀取管理介面需要登入、組織已開通 AI Gateway 與 AI Gateway 存取權限；只能讀取可見的平台群組與自己組織的群組。
* `/models` 中的組織自有備援群組，只有仍存在可呼叫步驟時才會列出。這項規則不代表每個平台虛擬模型會從模型目錄消失。

## 對話補全參數 <a href="#chat-completions-params" id="chat-completions-params"></a>

`POST /chat/completions` 接受的欄位：

| 欄位                             | 說明                                                                                                                                  |
| ------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------- |
| `model`                        | 必填，見上節                                                                                                                              |
| `messages`                     | 必填、不可為空。`role` 可為 `system`、`user`、`assistant`、`tool`；`content` 可為字串或 `text`／`image_url` 多模態陣列；支援 `tool_calls`、`tool_call_id`、`name` |
| `stream`                       | 預設 `false`                                                                                                                          |
| `stream_options.include_usage` | 預設 `false`，見「串流」                                                                                                                    |
| `temperature`                  | 數值                                                                                                                                  |
| `max_tokens`                   | 最小 1                                                                                                                                |
| `top_p`                        | 0 到 1                                                                                                                               |
| `stop`                         | 字串或字串陣列                                                                                                                             |
| `n`                            | 1 到 8，預設 1                                                                                                                          |
| `tools`                        | 只支援 `type: "function"`，函式名稱最長 64 字元                                                                                                 |
| `tool_choice`                  | `auto`、`none`、`required` 或指定函式物件                                                                                                    |
| `parallel_tool_calls`          | 預設 `true`                                                                                                                           |
| `response_format`              | `type` 為 `text`、`json_object` 或 `json_schema`；`json_schema` 需附 `json_schema` 物件                                                     |
| `reasoning_effort`             | `none`、`minimal`、`low`、`medium`、`high`、`xhigh`、`max`；只對 `/models` 帶 `reasoning` 標籤的模型生效，見「思考等級」                                     |
| `provider`                     | MaiAgent 擴充欄位，見「指定後端供應商」                                                                                                            |

注意事項：

* 目標模型不支援 `top_p` 或 `stop` 時，這兩個參數會被略過而不是報錯。
* `tools` 與 `response_format: json_schema` 不能同時使用，會回 `400`。
* `json_schema` 模式在串流時，完整 JSON 會以單一個 delta 一次送出，不會逐字串流。
* `json_object` 模式會在系統訊息中加上「只輸出合法 JSON」的指示。
* `json_object` 回覆若整段只有一個 Markdown `json` 程式碼區塊，且區塊內容是合法 JSON，Gateway 會移除區塊標記再回傳；一般回覆與串流回覆都適用。
* 若區塊外還有說明文字、區塊內容不是合法 JSON，或回覆截斷後不再構成完整的合法 JSON 區塊，Gateway 會保留模型原文。串接端仍須自行解析並處理無效 JSON，不能只靠 `json_object` 保證每次都取得可解析的物件。
* 串流的 `json_object` 回覆若以反引號開頭，Gateway 會等整段回覆結束後才送出內容；其他回覆照常逐段送出。上游中途失敗時，已收到但尚未送出的內容會照原文送出，再回報錯誤。

回應是標準 `chat.completion` 物件，`id` 形如 `chatcmpl-...`，`model` 回傳您送出的識別碼；回應標頭 `x-request-id` 與 `id` 相同，回報問題時請附上。

### 思考等級（reasoning\_effort） <a href="#reasoning-effort" id="reasoning-effort"></a>

`reasoning_effort` 用一組跨供應商通用的等級指定模型要想多深，由弱到強：

`none` < `minimal` < `low` < `medium` < `high` < `xhigh` < `max`

* **可送的值**：只接受上面七個值或 `null`。其他值（包括 `off`、數字形式的 token 預算）回 `400 invalid_request`。`none` 代表關閉思考。
* **不送或送 `null`**：Gateway 不指定等級。平台若為該模型設了預設思考等級就用它，否則不送思考參數，由供應商自己的預設決定。例外是少數 OpenAI 模型（例如 GPT-5.6 系列）不帶等級就不接受工具定義，平台會替這類模型送出 `none`。
* **模型沒有 `reasoning` 標籤**：這個模型沒有思考控制，`reasoning_effort` 會被略過，不會報錯。
* **轉換方式**：等級會先對應到目標模型支援的等級，再換成該供應商的原生參數（例如 OpenAI 的 `reasoning_effort`、Claude 的 effort 或思考 token 預算、Gemini 的思考預算）。每個模型支援哪些等級由平台依模型家族維護，`/models` 目前只提供 `reasoning` 標籤，不列出個別模型支援的等級。

模型不支援您送的等級時，Gateway 不報錯，而是依下列規則調整：

| 情況                 | 實際送出                                                            |
| ------------------ | --------------------------------------------------------------- |
| 模型支援這個等級           | 原樣送出                                                            |
| 不支援，但有較弱的等級        | 改用最接近、且不比您要求更強的等級（例如模型只到 `high`，送 `max` 會以 `high` 送出），不會因為調整而變貴 |
| 要求的等級低於模型的最低等級     | 改用模型的最低等級                                                       |
| 要求思考（`none` 以外）    | 不會被調整成關閉                                                        |
| 送 `none`，但模型無法關閉思考 | 不送思考參數，由供應商預設決定；這類模型仍會思考並依實際 token 計費                           |

{% hint style="info" %}
同一個等級在不同模型上代表的實際思考量不同。換模型時請重新評估延遲與 token 用量，不要假設相同等級的費用一致。
{% endhint %}

## 串流 <a href="#streaming" id="streaming"></a>

`stream: true` 時回應為 Server-Sent Events（`Content-Type: text/event-stream`），每個事件是一行 `data: {chat.completion.chunk JSON}`，正常結束以 `data: [DONE]` 收尾。OpenAI SDK 直接迭代即可：

```python
stream = client.chat.completions.create(
    model="<vendor>/<model>",
    messages=[{"role": "user", "content": "Hello!"}],
    stream=True,
    stream_options={"include_usage": True},
)
for chunk in stream:
    if chunk.choices and chunk.choices[0].delta.content:
        print(chunk.choices[0].delta.content, end="")
```

* `include_usage: true` 時，最後會多一個 `choices` 為空陣列、帶 `usage` 的事件。這個選項只影響是否回傳用量，不影響計費；不論是否開啟，Gateway 都會記錄並計費。
* 開始串流前發生的錯誤（認證、額度、模型不存在、上游連線失敗）以一般 HTTP 錯誤回應，不會是 SSE。
* 串流中途上游出錯時，HTTP 狀態已經是 `200`，Gateway 會送出一個 `data: {"error": {...}}` 事件後結束，不送 `[DONE]`。請在客戶端處理這種事件。

## 向量化 <a href="#embeddings" id="embeddings"></a>

`POST /embeddings` 與 OpenAI 相同：

```python
emb = client.embeddings.create(
    model="<embedding-model>",
    input=["第一段文字", "第二段文字"],
)
print(len(emb.data), len(emb.data[0].embedding))
```

* `input`：非空字串，或非空字串陣列。
* `encoding_format`：`float`（預設）或 `base64`。
* `model`：比對平台已對 Gateway 開放的向量模型識別值（其次比對名稱）。向量模型不在 `/models` 清單中，也不受 API Key 的模型開通清單限制；請向 MaiAgent 確認可用的向量模型識別碼。
* `usage.prompt_tokens` 由 Gateway 以 `cl100k_base` 分詞估算，這個數字也是計費依據。

## 語音轉文字與文字轉語音 <a href="#ai-gateway-audio" id="ai-gateway-audio"></a>

Gateway 提供兩支 OpenAI 相容的語音端點，OpenAI SDK 的 `client.audio.transcriptions.create()` 與 `client.audio.speech.create()` 只要換 `base_url` 與 `api_key` 就能呼叫。兩支端點都是同步呼叫：上傳整段音檔拿逐字稿，或送一段文字拿完整音檔，不支援串流。

### 可用的語音模型 <a href="#audio-models" id="audio-models"></a>

語音模型由 MaiAgent 平台管理，一律使用平台的供應商憑證，不支援 BYOK 或自建模型。目前公開目錄列出下列模型，實際可呼叫的清單以帶金鑰呼叫 `GET /models` 的結果為準：

| `model`                  | `type`          | 用途                                                                                                            |
| ------------------------ | --------------- | ------------------------------------------------------------------------------------------------------------- |
| `openai/gpt-transcribe`  | `transcription` | 語音轉文字                                                                                                         |
| `azure/speech-to-text`   | `transcription` | 語音轉文字                                                                                                         |
| `openai/gpt-4o-mini-tts` | `speech`        | 文字轉語音，聲音：`alloy`、`ash`、`ballad`、`coral`、`echo`、`fable`、`onyx`、`nova`、`sage`、`shimmer`、`verse`、`marin`、`cedar` |

語音模型在 `/models` 中的欄位與語言模型不同：

* `tags` 為空陣列，沒有 `providers`、`context_window`、`max_tokens`。
* `pricing` 為 `{"price": "…", "unit": "…", "currency": "credits"}`，`unit` 是 `credits_per_second`（語音轉文字，每秒）或 `credits_per_1k_characters`（文字轉語音，每千字元）。帶 Gateway 金鑰呼叫時已含組織的 Gateway 服務費；不帶認證的公開目錄為基準單價。
* `speech` 模型另有 `voices`（可用聲音）與 `response_formats`（可用音檔格式，第一個是預設值）。

{% hint style="warning" %}
**限定可呼叫模型的金鑰用不到語音模型。** API Key 的模型開通清單目前只能選語言模型；只要這把金鑰設了開通清單，`/models` 就不會列出語音模型，呼叫語音端點會回 `404 model_not_found`。要用語音請改用沒有設定模型開通清單的金鑰。
{% endhint %}

使用字數計費方案的組織不能使用語音端點：呼叫會回 `403`（`type` 為 `permission_error`、`code` 為 `billing_mode_not_supported`），帶金鑰呼叫的 `/models` 也不會列出語音模型。

### 語音轉文字 <a href="#audio-transcriptions" id="audio-transcriptions"></a>

`POST /audio/transcriptions`，本文為 `multipart/form-data`：

```python
with open("meeting.mp3", "rb") as f:
    result = client.audio.transcriptions.create(
        model="openai/gpt-transcribe",
        file=f,
        language="zh",
    )
print(result.text)
```

| 欄位                | 必填 | 說明                                                                                             |
| ----------------- | -- | ---------------------------------------------------------------------------------------------- |
| `file`            | 是  | 音檔，上限 25 MB（25 × 1024 × 1024 bytes）。格式依檔名副檔名判斷，接受 `mp3`、`mp4`、`mpeg`、`mpga`、`m4a`、`wav`、`webm` |
| `model`           | 是  | `type` 為 `transcription` 的模型識別碼                                                                |
| `language`        | 否  | 音檔語言；不填時由模型自動判斷，規則見下方                                                                          |
| `prompt`          | 否  | 轉錄提示，只送給 OpenAI 模型                                                                             |
| `temperature`     | 否  | `0` 到 `1`，只送給 OpenAI 模型                                                                        |
| `response_format` | 否  | `json`（預設）或 `text`；其他值回 `400`                                                                  |
| `stream`          | 否  | 只接受 `false`；送 `true` 回 `400`                                                                   |

`language` 的處理方式依模型而定：

* **`openai/gpt-transcribe`**：使用 ISO-639-1 兩碼（例如 `zh`、`en`）；填完整語系（例如 `zh-TW`）時只取 `-` 前面的語言碼。
* **`azure/speech-to-text`**：接受兩碼 `zh`、`en`、`ja`、`ko`、`th`、`vi`、`id`、`ms`、`fr`、`de`、`es`、`it`、`pt`、`ru`、`hi`（會轉成對應語系，例如 `zh` → `zh-TW`），或直接填完整語系（例如 `zh-TW`）；其他兩碼回 `400`。不填時在 `zh-TW` 與 `en-US` 之間自動辨識。

回應：

* `response_format: json`：`Content-Type: application/json`，`usage.seconds` 是這次計費的秒數。

  ```json
  {"text": "逐字稿內容", "usage": {"type": "duration", "seconds": 6}}
  ```
* `response_format: text`：`Content-Type: text/plain; charset=utf-8`，本文就是逐字稿。

### 文字轉語音 <a href="#audio-speech" id="audio-speech"></a>

`POST /audio/speech`，本文為 JSON：

```python
response = client.audio.speech.create(
    model="openai/gpt-4o-mini-tts",
    voice="alloy",
    input="歡迎使用 MaiAgent。",
    response_format="mp3",
)
response.write_to_file("welcome.mp3")
```

| 欄位                | 必填 | 說明                                                          |
| ----------------- | -- | ----------------------------------------------------------- |
| `model`           | 是  | `type` 為 `speech` 的模型識別碼                                    |
| `input`           | 是  | 要合成的文字，不可為空，上限 4,096 字元                                     |
| `voice`           | 是  | 必須是該模型 `voices` 中的值；不支援時回 `400` 並列出可用值                      |
| `response_format` | 否  | 必須是該模型 `response_formats` 中的值（目前為 `mp3`、`wav`）；不填用該模型的第一個格式 |
| `speed`           | 否  | 語速；目前列出的 OpenAI 模型為 `0.25`–`4.0`，超出範圍回 `400`                |
| `instructions`    | 否  | 語氣指示，只有 OpenAI 模型支援；對其他模型送出會回 `400`                         |
| `stream_format`   | 否  | 只接受 `audio`；不支援 SSE 串流                                      |

回應本文是一個完整的音檔，`Content-Type` 為 `audio/mpeg`（`mp3`）或 `audio/wav`（`wav`）。

### 語音端點的共通行為 <a href="#audio-behavior" id="audio-behavior"></a>

* **回應標頭**：成功的回應，以及通過欄位驗證之後才發生的錯誤，都帶 `x-request-id`，回報問題時請附上。
* **`Accept` 標頭**：Gateway 不依 `Accept` 協商格式，OpenAI SDK 預設送出的 `Accept: application/octet-stream` 不會造成錯誤。
* **呼叫供應商前的檢查**：認證、欄位驗證、模型、聲音／格式／語速／語言等參數、計費方案與 Credits 都在呼叫供應商之前檢查，任何一關擋下都不扣點。
* **上游逾時**：語音轉文字 300 秒、文字轉語音 120 秒；Gateway 不會自動重試。
* **上游錯誤**：供應商以 400／413／415／422 拒絕時回 `400 upstream_invalid_request`；拒絕平台憑證時回 `502 upstream_auth_error`；逾時與其他錯誤回 `502 upstream_error`。都不回傳供應商的原始錯誤訊息，也不扣點。
* **錯誤格式**：與其他 Gateway 端點相同，見「錯誤格式」。

## 錯誤格式 <a href="#errors" id="errors"></a>

所有錯誤都是 OpenAI 格式，OpenAI SDK 會轉成對應的例外：

```json
{
  "error": {
    "message": "Model 'vendor/model' not found or not enabled",
    "type": "invalid_request_error",
    "code": "model_not_found",
    "param": null
  }
}
```

| HTTP | `type`                  | `code`                       | 常見原因                                             | 建議處理                       |
| ---- | ----------------------- | ---------------------------- | ------------------------------------------------ | -------------------------- |
| 400  | `invalid_request_error` | `invalid_request`            | 欄位驗證失敗、`model` 沒有 `/`、`tools` 與 `json_schema` 併用 | 修正請求，不要重試                  |
| 400  | `invalid_request_error` | `upstream_invalid_request`   | 上游以 400／413／415／422 拒絕請求（參數、工具定義或輸入大小超出該模型限制）    | 依目標模型限制調整請求                |
| 400  | `invalid_request_error` | `fallback_group_unavailable` | 備援群組沒有任何可呼叫的步驟                                   | 編輯群組，加入可用模型                |
| 401  | `authentication_error`  | `invalid_api_key`            | 金鑰無效、停用、過期、撤銷，或組織未開通 Gateway                     | 檢查金鑰；用後台存取診斷定位             |
| 402  | `billing_error`         | `insufficient_credits`       | 組織 Credits 不足，或成員／群組額度用盡；在呼叫上游前就擋下               | 儲值或調整額度                    |
| 403  | `permission_error`      | `billing_mode_not_supported` | 字數計費方案的組織呼叫語音端點                                  | 改用 Credits 計費方案            |
| 404  | `invalid_request_error` | `model_not_found`            | 模型不存在、未開放給組織、未開通給這把金鑰，或指定的供應商沒有該模型               | 用 `/models` 確認可用 id        |
| 429  | `rate_limit_error`      | `rate_limit_exceeded`        | 超過金鑰的 RPM 或 TPM 限制                               | 退避後重試                      |
| 502  | `server_error`          | `upstream_auth_error`        | 上游拒絕憑證（BYOK 或平台憑證）                               | BYOK：更新憑證；平台憑證：聯繫 MaiAgent |
| 502  | `server_error`          | `upstream_error`             | 上游逾時、限流、5xx 或其他錯誤；上游原始錯誤訊息不會回傳                   | 退避後重試，或改用備援群組              |

## 用量與 Credits 計費 <a href="#billing" id="billing"></a>

每次成功呼叫都從組織的 Credits 扣點，對話補全以上游回報的 token 數為準，向量化以 Gateway 估算的 token 數為準（見「向量化」）：

* **託管模型**：依模型的輸入／輸出（含提示快取）token 單價計費，再加上組織的 Gateway 服務費比例。帶 Gateway 金鑰呼叫 `/models` 時，`pricing` 已是加上服務費後的單價；不帶認證的公開目錄顯示的是未加服務費的基準單價。
* **自建模型與 BYOK 憑證**：上游費用由貴公司直接付給供應商，MaiAgent 只收流量費，依該次呼叫的總 token 數（輸入＋輸出＋提示快取寫入與讀取）× 組織的流量費率計算，不收 LLM token 定價。使用者手冊的 [AI Gateway 自建模型的流量費](https://docs.maiagent.ai/org/credits#ai-gateway-traffic-fee) 說明如何在後台核對。
* **向量化**：依輸入 token 數計費，同樣加上 Gateway 服務費。
* **語音轉文字**：依供應商回報的音檔長度計費，無條件進位到整秒、最少 1 秒，乘上該模型的每秒單價，再加上 Gateway 服務費。回應的 `usage.seconds` 就是計費秒數。供應商沒有回報長度時，整筆以 `502` 失敗、不回逐字稿也不扣點。
* **文字轉語音**：依 `input` 的字元數 ÷ 1000 × 該模型的每千字元單價計費，再加上 Gateway 服務費。
* **語音呼叫的額度檢查**：呼叫供應商前，組織 Credits 必須足以支付最低費用（語音轉文字以 1 秒計，文字轉語音以這次 `input` 的完整費用計），不足回 `402`。語音呼叫失敗不扣點；語音模型使用平台憑證，沒有流量費。
* **串流中斷**：客戶端中途斷線或上游中途出錯時，已產生的 token 仍會計費。
* 組織 Credits 不足或成員額度用盡時，請求在呼叫上游前就回 `402`。

用量可在後台 <mark style="color:blue;">AI Gateway</mark> → <mark style="color:blue;">總覽</mark> 依模型、供應商與 API Key 查看，扣點明細在 Credits 消耗記錄。

## 限制 <a href="#limits" id="limits"></a>

* **RPM**：金鑰的每分鐘請求數上限作用在這把金鑰能呼叫的所有 MaiAgent API，不只 Gateway。
* **TPM**：每分鐘 Token 數上限只作用在 Gateway 的對話補全與向量化，不套用在語音端點（語音端點仍受 RPM 限制）。token 數要等上游回應後才知道，所以單一請求可能讓當分鐘超出一次，之後該分鐘內的請求都回 `429`，下一分鐘重置。
* 未設定 RPM／TPM 時不限速。
* 目前提供的 OpenAI 相容端點只有上表五個；OpenAI 的 Responses、Assistants、圖片等端點，以及語音的翻譯（`/audio/translations`）與即時語音，不在 Gateway 範圍內。

## 相關文件 <a href="#related" id="related"></a>

* [AI Gateway 安全性](/tech/ai-gateway/ai-gateway-security.md)：端點來源、模型把關、資料保護與稽核紀錄
* [快速上手](/tech/api-integration/quickstart.md)：MaiAgent 平台 API（Agent、對話、知識庫）的認證方式
* 使用者手冊 [Credits](https://docs.maiagent.ai/org/credits)：Credits 餘額、計費方式與消耗記錄


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.maiagent.ai/tech/ai-gateway/ai-gateway-openai-compatible.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
