﻿# 如何快速將 OpenAI 的 API 轉換成呼叫 Azure OpenAI Service (AOAI) 的 API

在 GitHub 上面有許多可以呼叫 GPT-3.5 或 GPT-4 的開源專案，但大多都僅支援 OpenAI 提供的 API 端點。如果你想要將這些專案改成呼叫 Azure OpenAI Service (AOAI) 的 API 端點的話，沒用過 AOAI 的人就會不知道怎樣調整。今天這篇文章我打算來整理一下幾個重要的背景知識，方便你快速的轉換過去。

![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/254c9ffb60d180709796-2b640e20-a9c4-46fc-8d54-0bf22b3394ce.webp)

### OpenAI API 與 AOAI 的相容性

其實 OpenAI 提供的 API 跟微軟 Azure OpenAI Service (AOAI) 提供的 API 是完全相容的，甚至於許多開源的 LLM 大語言模型也會刻意設計相容於 OpenAI 的 API 規格。所以，只要你能夠理解 Azure OpenAI Service (AOAI) 的一些基本概念，基本上很容易就可以切換過去。

### 呼叫 API 的注意事項

我們要呼叫一個 API 不外乎要注意以下事項：

1.  **Endpoint** (端點)
    
    OpenAI API 與 AOAI 的 API 端點位址肯定不太一樣。
    
2.  **API Version** (API 版本)
    
    OpenAI API 的「版本」資訊寫在網址列上，目前有都還是 `v1` 而已，所以不需要特別指定。
    
    但是 AOAI 的 API 有分版本，呼叫時必須明確加上，否則會呼叫不到。
    
3.  **API Key** (API 金鑰)
    
    OpenAI API 主要就是透過 API 金鑰來進行 API 驗證，並且搭配 Bearer Token 的方式傳輸，沒有其他方法。
    
    但是 AOAI 的 API 提供了 2 種驗證方法，分別是 `API Key authentication` 與 `Microsoft Entra ID authentication` 這兩種，所以呼叫的方式有點不太一樣。
    
    `API Key authentication` 是最簡單的方式，只需要在 HTTP Header 裡面加上 `api-key` 並放入 Acess Key 即可。
    
    `Microsoft Entra ID authentication` 是最安全的方式，但是需要透過 OAuth 2.0 的方式來取得 Access Token，並搭配 Bearer Token 的方式傳輸，這個方式比較複雜一些。
    
4.  **Request Body** (請求主體)
    
    這部分 OpenAI API 與 AOAI 的 API 並沒有太大的差異，基本上都是相容的。
    
5.  **Response Body** (回應主體)
    
    這部分 OpenAI API 與 AOAI 的 API 並沒有太大的差異，基本上都是相容的。
    

### API 呼叫時的差異之處

我以最常用的 Chat Completion API 為例：

-   **OpenAI**
    
    https://api.openai.com/v1/chat/completions
    
-   **Azure OpenAI Service (AOAI)**
    
    https://{your-resource-name}.openai.azure.com/openai/deployments/{deployment-id}/chat/completions?api-version={api-version}
    

從網址就可以看出，呼叫 OpenAI 真的簡單很多，沒有太多的認知負擔。但是呼叫 AOAI 的話，就需要多加上一些參數，這樣才能夠正確的呼叫到 API 端點。

當你在 Azure Portal 上面建立了一個 Azure OpenAI Service (AOAI) 之後，你就可以在該服務的概觀頁面上找到 **資源名稱**，也就是上述網址的 `{your-resource-name}` 部分，若以下圖為例，你的 `{your-resource-name}` 就要改成 `willh`：

![image](https://stwillblogassets.blob.core.windows.net/files/images/external/github.com/1490e97319a449f3fc60-81219f01-d1f9-4686-939e-24e66605db9f.png)

接下來你必須要建立一個 Deployment 並取得 **Deployment name** 來替換掉 API 端點上的 `{deployment-id}` 內容，你要點擊上圖的 `Model deployments` 並點擊 `Managem Deployments` 按鈕，進入 [Azure OpenAI Studio](https://oai.azure.com/) 介面，你要在這裡建立一個 Deployment 資源。如下圖示，當你建立完成後，這裡的 Deployment name 就是你要置換掉 API 端點上的 `{deployment-id}` 內容：

![image](https://stwillblogassets.blob.core.windows.net/files/images/external/github.com/7f2ca7e57202a346e867-dcc0a67e-f996-4d35-b375-5a0019b4b276.png)

在點擊 `Create new deployment` 按鈕建立時，必須選擇一個 `model` (模型名稱)，然後自行設定一組 `Deployment name`，這樣就可以建立一個 Deployment 資源了。

![image](https://stwillblogassets.blob.core.windows.net/files/images/external/github.com/fc04d71c4ef10d903d19-ad161d40-630b-4196-bd24-6c8019152217.png)

基本上，**模型名稱**跟**部署名稱**不一定要一樣，但我個人習慣設定成一樣，這樣比較好理解。在呼叫 API 的時候，**部署名稱**就是網址列上的 `{deployment-id}` 部分，但是**模型名稱**則是用在 Request Body 的 `model` 屬性中，千萬不要搞混了。

最後，還有個 AOAI 的網址列上還有個 `{api-version}` 需要指定，要找目前支援的 `{api-version}` 版本，要到 [Azure OpenAI Service REST API reference](https://learn.microsoft.com/en-us/azure/ai-services/openai/reference?WT.mc_id=DT-MVP-4015686#api-specs) 才找的到。我以 AOAI 的 [Chat completions](https://learn.microsoft.com/en-us/azure/ai-services/openai/reference?WT.mc_id=DT-MVP-4015686#chat-completions) 為例，你要找 `Data plane - inference` 的 `Latest GA release` 欄位，才能知道目前最新的 API 版本是什麼。

### 結論

只要有了上述知識，我們就可以很輕鬆的將 OpenAI 的 API 呼叫，順利轉換成 Azure OpenAI Service (AOAI) 的 API 呼叫了！

以下是透過發出 OpenAI API 呼叫的範例：

```
curl https://api.openai.com/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -d '{
     "model": "gpt-3.5-turbo",
     "messages": [{"role": "user", "content": "Say this is a test!"}],
     "temperature": 0.7
   }'
```

如果要成功呼叫 AOAI 的 API 的話，就必須要將上述的呼叫改成以下這樣：

```
curl https://willh.openai.azure.com/openai/deployments/gpt-35-turbo/chat/completions?api-version=2024-02-15-preview \
  -H "Content-Type: application/json" \
  -H "api-key: $AOAI_API_KEY" \
  -d '{
     "model": "gpt-35-turbo",
     "messages": [{"role": "user", "content": "Say this is a test!"}],
     "temperature": 0.7
   }'
```

如此一來就可以順利轉換成功啦！👍

### 啟用 Stream 串流模式的注意事項

在將 OpenAI API 轉換為 Azure OpenAI Service (AOAI) API 時，如果您打算使用**串流模式** (`stream=true`)，需要特別注意一些重要事項。**串流模式**可以讓回應內容逐步返回，提供更好的使用者體驗，但在 AOAI 中有一些特殊的行為與 OpenAI API 不太一樣，必須特別注意。

首先，就是串流模式的 HTTP 要求大概長這樣：

```bash
curl -X POST "https://willh-swedencentral.openai.azure.com/openai/deployments/gpt-4.1/chat/completions?api-version=2025-01-01-preview" \
    -H "Content-Type: application/json" \
    -H "api-key: $AZURE_API_KEY" \
    -d '{
        "messages": [
            {
                "role": "user",
                "content": "1 + 1 = ? Do not provide explaination. Just tell me the final answer."
            }
        ],
        "stream": true,
        "max_completion_tokens": 800,
        "temperature": 1,
        "top_p": 1,
        "frequency_penalty": 0,
        "presence_penalty": 0,
        "model": "gpt-4.1"
    }'
```

然而 HTTP 回應的內容大致上是：

```json
data: {"choices":[],"created":0,"id":"","model":"","object":"","prompt_filter_results":[{"prompt_index":0,"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"jailbreak":{"filtered":false,"detected":false},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}}}]}

data: {"choices":[{"content_filter_results":{},"delta":{"content":"","refusal":null,"role":"assistant"},"finish_reason":null,"index":0,"logprobs":null}],"created":1744728511,"id":"chatcmpl-BMbsNfxlf7GVhCcjPaF6cWy7gc8ha","model":"gpt-4.1-2025-04-14","object":"chat.completion.chunk","system_fingerprint":"fp_3dfb47c1f3"}

data: {"choices":[{"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}},"delta":{"content":"2"},"finish_reason":null,"index":0,"logprobs":null}],"created":1744728511,"id":"chatcmpl-BMbsNfxlf7GVhCcjPaF6cWy7gc8ha","model":"gpt-4.1-2025-04-14","object":"chat.completion.chunk","system_fingerprint":"fp_3dfb47c1f3"}

data: {"choices":[{"content_filter_results":{},"delta":{},"finish_reason":"stop","index":0,"logprobs":null}],"created":1744728511,"id":"chatcmpl-BMbsNfxlf7GVhCcjPaF6cWy7gc8ha","model":"gpt-4.1-2025-04-14","object":"chat.completion.chunk","system_fingerprint":"fp_3dfb47c1f3"}

data: [DONE]
```

使用 Azure OpenAI Service 的串流模式時，您會遇到第一個回應項目中 `choices` 陣列為空的情況。這是因為 Azure OpenAI 在回應開始時會先傳送**內容過濾結果** (`prompt_filter_results`)，而不是立即開始生成文字。

這與標準 OpenAI API 的行為不同，可能會導致程式碼出現錯誤，特別是當您嘗試存取 `res.choices[0].delta` 時，會引發 `IndexError: list index out of range` 錯誤。

解決方案也很簡單，你不能略過這個行為，而是必須在處理 AOAI 串流回應時，您需要增加檢查邏輯，跳過那些 `choices` 陣列為空的回應項目。

其他串流模式注意事項還有：

1.  **API 版本相容性**：確保您使用的 API 版本支援串流功能。目前 `2024-02-15-preview` 和更新版本都支援串流模式。
    
2.  **回應格式**：AOAI 的串流回應格式與 OpenAI 相同，都是以 Server-Sent Events (SSE) 格式傳送，每個事件以 `data:` 開頭。
    
3.  **工具呼叫 (Tool Calls)**：如果您在使用工具呼叫功能時啟用串流模式，需要特別注意處理工具呼叫的回應格式，因為它們可能會分散在多個串流片段中。
    
4.  **錯誤處理**：在串流模式下，錯誤處理變得更加複雜。確保您的程式碼能夠優雅地處理連線中斷或其他錯誤情況。
    
5.  **內容過濾**：Azure OpenAI 的內容過濾機制可能會在串流過程中觸發，導致串流提前終止。確保您的程式碼能夠處理這種情況。
    

在使用 Azure OpenAI Service 的串流模式時，處理空的 `choices` 陣列是一個關鍵步驟。透過適當的檢查和錯誤處理，您可以確保您的應用程式能夠順利地從 OpenAI API 遷移到 Azure OpenAI Service API，同時保持良好的使用者體驗。

### 相關連結

-   [OpenAI API Reference](https://platform.openai.com/docs/api-reference/introduction)
-   [Azure OpenAI Service REST API reference](https://learn.microsoft.com/en-us/azure/ai-services/openai/reference?WT.mc_id=DT-MVP-4015686)
-   為何 Azure OpenAI API 在串流模式下第一個回應項目的 choices 為空
