OpenAIとAzureAIのSDKを整理する

# はじめに

AI アプリを作ろうとすると、沢山の種類のSDKが出てきて混乱します。2025年4月の時点での整理をしたのでメモっておきます。立場上、AzureのAIリソースのSDKについても整理します。

# 準備

Azure AI Foundry 上にOpenAIのGPT-4o 2024-11-20をデプロイしてあります。基本的にこのGPT-4oへ通信します。（一部異なるバージョンでないと動かないものあり）

Python, TypeScript, C#の３種類について調べています。、ランタイムやVS Codeのプラグインなど各環境に必要なセットアップはご自身で行ってください。

<details><summary>Python</summary>

## Python

Pythonのバージョンは3.13.2です。
VSCode でコードを格納するフォルダを開きます。フォルダで仮想環境を設定しておくことをお勧めします。venvなどお好きなやり方でどうぞ。
</details>

<details><summary>TypeScript</summary>

## TypeScript

Node.jsのバージョンは23.11.0です。
VSCode でコードを格納するフォルダを開き、次の手順でTypeScript環境を作成します。

### 1. ターミナルを開き次のコマンドを入力

@type/nodeの後ろの@22は、私のマシンのNode.jsのバージョンに合わせたものです。ご自身の環境に併せて数字を変更してください。

```bash
npm init -y
npm install --save-dev typescript @types/node@22
npx tsc --init
```

### 2. tsconfig.jsonを修正

次の内容にします。ソースを./srcに配置し、tsファイルをコンパイルした結果を./distに置く設定を追加しています。また、一番下のincludeでコンパイル対象のファイルを、excludeでコンパイル対象外のフォルダを指定しています。ごく一般的な設定だと思います。

```json:tsconfig.json
{
  "compilerOptions": {
    "target": "es2018", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
    "module": "commonjs", /* Specify what module code is generated. */
    "rootDir": "./src", /* Specify the root folder within your source files. */
    "outDir": "./dist", /* Specify an output folder for all emitted files. */
    "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
    "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
    "strict": true, /* Enable all strict type-checking options. */
    "skipLibCheck": true /* Skip type checking all .d.ts files. */
  },
  "include": [
    "src/**/*"
  ],
  "exclude": [
    "node_modules"
  ]
}
```

### 3. package.jsonを編集

コンパイルとビルドを行うためのコマンドを追加します。

```diff_json:package.json
"scripts": {
+    "build": "tsc",
+    "start": "node ./dist/index.js",
    "test": "echo \"Error: no test specified\" && exit 1"
}
```

### 4. テスト

srcフォルダをプロジェクト（VSCodeで開いたフォルダ）直下に作成し、その中にindex.tsファイルを作成し、次の内容を貼り付けます。

```typescript:src/index.ts
const greet = (name: string) => {
    console.log("Hello, " + name + "!");
}

greet("World");
```

ターミナルで次のコマンドを入力します。

```bash
npm run build
```

プロジェクトにdistフォルダが作成され、中を開くとindex.jsファイルがいると思います。実行します。

```bash
npm start
```

Hello World!が表示されれば準備OKです。

が、人によっては毎回ビルドと実行コマンドを叩くのが面倒な方もいるでしょう。その場合はHotReloadできるようにしておくこともできます。

ts-node-dev をインストールします。

```bash
npm install --save-dev ts-node-dev  
```

package.jsonのstartコマンドを変更します。

```diff_json:package.json
  "scripts": {
    "build": "tsc",
    "start": "node ./dist/index.js",
+    "start:dev": "ts-node-dev --respawn --transpile-only src/index.ts",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
```

次のコマンドを実行します。

```bash
npm run start:dev
```

実行しっぱなしになると思います。試しにindex.tsファイルを変更し、保存してみてください。すると即時にコンパイルして実行してくれることがわかるかと思います。
</details>


<details><summary>C#</summary>

## C#

.NET のバージョンは .NET 9 を使用しています。.NET8以降であれば問題なく動作すると思います。
</details>

# 1. OpenAI Completion APIのSDK

このSDKはWebAPIとして公開されているGPT-4oなどのOpenAI社のモデルと通信するためのものです。同じWebAPI仕様を備えている場合はOpenAI社以外のモデルをこのSDKで操作可能な場合もあります。

モデルと直接通信するこのSDKは最初にリリースされたものですが、これから新規アプリケーションを作成する場合は後述のOpenAI Response APIの使用が推奨されています。

<details><summary>Python</summary>

[参考情報：クイックスタート: Azure OpenAI Service で GPT-35-Turbo と GPT-4 を使い始める](https://learn.microsoft.com/ja-jp/azure/ai-services/openai/chatgpt-quickstart?tabs=command-line%2Ckeyless%2Ctypescript-keyless%2Cpython-new&pivots=programming-language-python#create-a-sample-application)

依存関係をpipでインストールします。レスポンスはStreamingとして受け取るにしました。

```text:requirements.txt
openai
```

```bash
pip install -r requirements.txt
```

次の実装でAzure上のOpenAIとやり取りします。

```python:main.py
from openai import AzureOpenAI

api_version = "2024-12-01-preview"
endpoint = "<YOUR_OPENAI_ENDPOINT>"
apikey = "<YOUR_OPENAI_APIKEY>"

client = AzureOpenAI(
    api_version=api_version,
    azure_endpoint=endpoint,
    api_key=apikey
)

stream_response = client.chat.completions.create(
    messages=[
        {
            "role": "system",
            "content": "You are a helpful assistant.",
        },
        {
            "role": "user",
            "content": "シアトルに行きます。お勧めの観光スポットは？",
        }
    ],
    max_tokens=4096,
    temperature=1.0,
    top_p=1.0,
    model="gpt-4o",
    stream=True
)

for chunk in stream_response:
    if chunk.choices and (content := chunk.choices[0].delta.content) is not None:
        print(content, end="", flush=True)

client.close()
```

</details>

<details><summary>TypeScript</summary>

npmでパッケージをインストールします。

```bash
npm install openai @azure/openai
```

次の実装でAzure上のOpenAIとやり取りします。

```typescript:index.ts
import { AzureOpenAI } from "openai";

export async function main() {
    const endpoint = "<YOUR_OPENAI_ENDPOINT>";
    const apiKey = "<YOUR_OPENAI_APIKEY>";
    const deployment = "gpt-4o";
    const modelName = "gpt-4o";
    const apiVersion = "2024-10-21";
    const client = new AzureOpenAI({ endpoint, apiKey, deployment, apiVersion });

    const response = await client.chat.completions.create({
        messages: [
            { role: "system", content: "You are a helpful assistant." },
            { role: "user", content: "シアトルに行きます。お勧めの観光スポットは？" }
        ],
        max_tokens: 4096,
        temperature: 1,
        top_p: 1,
        model: modelName,
        stream: true
    });

    if (response?.error !== undefined && response.status !== "200") {
        throw response.error;
    }

    for await (const chunk of response) {
        process.stdout.write(chunk?.choices?.[0]?.delta?.content ?? '');
    }

}

main().catch((err) => {
    console.error("The sample encountered an error:", err);
});
```
</details>


<details><summary>C#</summary>

Nuget でパッケージをインストールします。

```bash
dotnet add package Azure.AI.OpenAI
dotnet add package Azure.Core
```

次の実装でAzure上のOpenAIとやり取りします。

```csharp:Program.cs
using Azure;
using Azure.AI.OpenAI;
using OpenAI.Chat;

var endpoint = new Uri("<YOUR_MODEL_API_ENDPOINT>");
var deploymentName = "gpt-4o";
var apiKey = "<YOU_API_KEY>";

AzureOpenAIClient azureClient = new(
    endpoint: endpoint,
    credential: new AzureKeyCredential(apiKey));

var chatClient = azureClient.GetChatClient(deploymentName);

var requestOptions = new ChatCompletionOptions()
{
    MaxOutputTokenCount = 4096,
    Temperature = 0.7f,
//    TopP = 1.0f,
};

List<ChatMessage> messages =
[
    new SystemChatMessage("You are a helpful assistant."),
    new UserChatMessage("シアトルに行きます。お勧めの観光スポットは？")
];

await foreach(var update in chatClient.CompleteChatStreamingAsync(messages, requestOptions))
{
    System.Console.Write(update.ContentUpdate.FirstOrDefault()?.Text);
}
```
</details>


# 2. Azure AI Inferernce SDK（Azure AI Foundry SDK）

Azureは次の２つの環境にデプロイされたモデルに対して共通のAPIを提供しています。

1. [Azure AI Foundry](https://learn.microsoft.com/ja-jp/rest/api/aifoundry/modelinference/)
1. [Azure Machin Learning Studio](https://learn.microsoft.com/ja-jp/azure/machine-learning/reference-model-inference-api?view=azureml-api-2&tabs=python)

その共通のAPIのことをAzure AI Model Inference APIと呼びます。日本語ではモデル推論APIとも呼ばれます。このAPIはOpenAI社のモデルだけではなく、上記2つの環境にデプロイされた**OpenAI以外のモデルとの通信も行える**のがOpenAI社のSDKを使用する場合との大きな違いです。

Azure AI **Model** Inference APIはREST APIです。これを便利に扱えるようにしたのがAzure AI Inference SDKです。なぜかSDK名からは「**Model**」が欠落しています。

[Supported programming languages for models in Azure AI Model Inference](https://learn.microsoft.com/en-us/azure/ai-foundry/model-inference/supported-languages)

<details><summary>Python</summary>

依存関係をpipでインストールします。共通のAPI仕様なので**openaiパッケージは必要ありません。**

```text:requirements.txt
azure-ai-inference
```

```bash
pip install -r requirements.txt
```

次の実装でAzure上のOpenAIとやり取りします。

```python:main.py
from azure.ai.inference import ChatCompletionsClient
from azure.ai.inference.models import SystemMessage, UserMessage
from azure.core.credentials import AzureKeyCredential

endpoint = "<YOUR_OPENAI_ENDPOINT>"
apikey = "YOUR_OPENAI_APIKEY"

client = ChatCompletionsClient(endpoint=endpoint, credential=AzureKeyCredential(apikey))

response = client.complete(
    max_tokens=4096,
    temperature=0.7,
    top_p=1.0,
    model="gpt-4o",
    messages=[
        SystemMessage("You are a helpful assistant."),
        UserMessage("シアトルに行きます。お勧めの観光スポットは？"),
    ],
    stream=True,
)

for chunk in response:
    if chunk.choices and (content:= chunk.choices[0].delta.content):
        print(content or "", end="", flush=True)

client.close()
```

</details>

<details><summary>TypeScript</summary>

npmで4つパッケージをインストールします。

```bash
npm install @azure-rest/ai-inference @azure/core-auth @azure/core-sse @azure-rest/core-client
```

このうち、後ろの2つ @azure/core-sse @azure-rest/core-client は Streaming 用のライブラリなので必須ではありません。

次の実装でAzure上のOpenAIとやり取りします。

```typescript:index.ts
import ModelClient from "@azure-rest/ai-inference";
import { createSseStream } from "@azure/core-sse";
import { createRestError } from "@azure-rest/core-client";
import { AzureKeyCredential } from "@azure/core-auth";

const endpoint = "<YOUR_OPENAI_ENDPOINT>";
const modelName = "gpt-4o";
const apiKey = "<YOUR_OPENAI_APIKEY>";

export async function main() {

    const client = new ModelClient(endpoint, new AzureKeyCredential(apiKey));

    const response = await client.path("/chat/completions").post({
        body: {
            messages: [
                { role: "system", content: "You are a helpful assistant." },
                { role: "user", content: "シアトルに行きます。お勧めの観光スポットは？" }
            ],
            max_tokens: 4096,
            temperature: 1,
            top_p: 1,
            model: modelName,
            stream: true
        }
    }).asNodeStream();

    const stream = response.body;
    if (!stream) {
        throw new Error("The response stream is undefined");
    }

    if (response.status !== "200") {
        throw createRestError(response);
    }

    const sses = createSseStream(stream as IncomingMessage);

    for await (const event of sses) {
        if (event.data === "[DONE]") {
            return;
        }
        for (const choice of JSON.parse(event.data).choices) {
            process.stdout.write(choice.delta?.content ?? "");
        }
    }
}

main().catch((err) => {
    console.error("The sample encountered an error:", err);
});
```

Streaming用の実装がちょっとわかりにくいのは残念ですね。
</details>


<details><summary>C#</summary>

Nuget で次のパッケージをインストールします。共通のAPI仕様なので**Azure.AI.OpenAIパッケージは必要ありません。**

```bash
dotnet add package Azure.AI.Inference
dotnet add package Azure.Core
```

次の実装でOpenAIのモデルとやり取りします。

```csharp:Program.cs
using Azure;
using Azure.AI.Inference;

var endpoint = new Uri("<YOUR_MODEL_API_ENDPOINT>");
var credential = new AzureKeyCredential("<YOU_API_KEY>");
var model = "gpt-4o";

var client = new ChatCompletionsClient(
    endpoint,
    credential);
    
var requestOptions = new ChatCompletionsOptions()
{
    Messages =
    {
        new ChatRequestUserMessage("シアトルに行きます。お勧めの観光スポットは？")
    },
    MaxTokens = 4096,
    Temperature = 1.0f,
//    NucleusSamplingFactor = 1.0f,
    Model = model
};

StreamingResponse<StreamingChatCompletionsUpdate> response = await client.CompleteStreamingAsync(requestOptions);

await foreach (var update in response)
{
    System.Console.Write(update.ContentUpdate);    
}
```
</details>

# 3. Microsoft.Extensions.AI

2024年の秋ごろに突如出てきた.NETのSDKです。名前からAzureが消えている点がポイントで、このSDKはかなり意欲的なものです。現在（2025年4月）は次の4種類のサービスとの通信を抽象化するレイヤーを提供しています。

1. OpenAI
1. Azure OpenAI
1. Azure AI Inference
1. Ollama（SLM・LLMローカル実行環境）

近い将来、この4種類に加えて[様々なモデル、他社ISVホスティングのモデルもこのSDKで扱えるようになる見込み](https://devblogs.microsoft.com/semantic-kernel/microsoft-extensions-ai-simplifying-ai-integration-for-net-partners/)です。（Semantic Kernelのモデル抽象化機能をこちらへ持っていくるため）

このSDKはただモデルと通信するだけではなくて様々な便利機能があり、Semantic Kernelを置き換える勢いですが、ここではただモデルと通信だけしてみます。

※.NET専用のSDKです。

<details><summary>C#</summary>

Nuget で次のパッケージをインストールします。

```bash
dotnet add package Azure.AI.OpenAI
dotnet add package Azure.Core
dotnet add package Microsoft.Extensions.AI.OpenAI
```

AI Inference SDKの時はOpenAI仕様を気にしなくてよかったのですが、Microsoft.Extensions.AIの場合は使用するモデル用のパッケージが必要となります。

次の実装でOpenAIのモデルとやり取りします。

```csharp:Program.cs
using Azure;
using Azure.AI.OpenAI;
using Microsoft.Extensions.AI;

var endpoint = new Uri("<YOUR_MODEL_API_ENDPOINT>");
var deploymentName = "gpt-4o";
var apiKey = "<YOU_API_KEY>";

IChatClient chatClient =
    new AzureOpenAIClient(endpoint, new AzureKeyCredential(apiKey))
        .AsChatClient(deploymentName);

var chatOptions = new ChatOptions()
{
    MaxOutputTokens = 4096,
    Temperature = 0.7f,
    //    TopP = 1.0f,
};

await foreach(var update in chatClient.GetStreamingResponseAsync("シアトルに行きます。お勧めの観光スポットは？", chatOptions))
{
    Console.Write(update.Text);
}
```
</details>

# 4. Azure AI Projects SDK（Azure AI Foundry SDK）

Azure AI Foundryは**プロジェクト**という単位でデータやデプロイしたAIモデル、Azure AI Searchなどの外部リソースへの接続をひとまとめにします。そして**Azure AI Foundry SDK**は、次の3つをひとまとめに総称したものです。

1. Azure AI Projects SDK（プロジェクトの管理, Azure AI Agent Serviceの操作）
1. 前述の[Azure AI Inference SDK](https://learn.microsoft.com/en-us/rest/api/aifoundry/modelinference/#inference-sdk-support)
1. 評価用のAzure AI Evaluation SDK
 
[Get started with projects](https://learn.microsoft.com/en-us/azure/ai-foundry/how-to/develop/sdk-overview?tabs=sync&pivots=programming-language-python#get-started-with-projects)


AIモデルと通信するSDKではないのですが、ここではAzure AI Projects SDKを使ってプロジェクトから情報を取得し、そのプロジェクトにデプロイしたモデルとの通信についてみてみます。

※ TypeScript用のSDKはリリースされていません。(2025年4月）

<details><summary>Python</summary>

```text:requirements.txt
azure-ai-projects
azure-identity
openai
aiohttp
```

aiohttpは非同期アクセスをするためにインストールしています。

```python:main.py
import asyncio
from azure.identity.aio import DefaultAzureCredential
from azure.ai.projects.aio import AIProjectClient

async def main():
    project_connection_string="<YOUR_PROJECT_CONNECTION_STRING>"

    project: AIProjectClient = AIProjectClient.from_connection_string(
        conn_str=project_connection_string,
        credential=DefaultAzureCredential())

    openai = await project.inference.get_azure_openai_client(api_version="2025-01-01-preview")
    response = await openai.chat.completions.create(
        model="gpt-4o",
        messages=[
            {"role": "system", "content": "You are a helpful writing assistant"},
            {"role": "user", "content": "シアトルに行きます。お勧めの観光スポットは？"},
        ],
        stream=True
    )

    async for chunk in response:
        if chunk.choices and (content:= chunk.choices[0].delta.content):
            print(content or "", end="", flush=True)

    await openai.close()
    await project.close()


asyncio.run(main())
```

OpenAIのSDKではなく、Azure AI Inference SDKを使うこともできます。

```text:requirements.txt
azure-ai-projects
azure-identity
aiohttp
azure-ai-inference
```

```python:main.py
import asyncio
from azure.identity.aio import DefaultAzureCredential
from azure.ai.projects.aio import AIProjectClient

async def main():
    project_connection_string="<YOUR_PROJECT_CONNECTION_STRING>"

    project: AIProjectClient = AIProjectClient.from_connection_string(
        conn_str=project_connection_string,
        credential=DefaultAzureCredential())

    chat = await project.inference.get_chat_completions_client()

    # run a chat completion using the inferencing client
    response = await chat.complete(
        model="gpt-4o",
        messages=[
            {"role": "system", "content": "You are a helpful writing assistant"},
            {"role": "user", "content": "シアトルに行きます。お勧めの観光スポットは？"},
        ],
        stream=True,
    )

    async for chunk in response:
        if chunk.choices and (content:= chunk.choices[0].delta.content):
            print(content or "", end="", flush=True)

    await chat.close()
    await project.close()

asyncio.run(main())
```

</details>


<details><summary>C#</summary>

```bash
dotnet add package Azure.AI.OpenAI
dotnet add package Azure.AI.Projects
```

```csharp:Program.cs
using Azure;
using Azure.AI.OpenAI;
using Azure.AI.Projects;
using Azure.Identity;
using OpenAI.Chat;

var deploymentName = "gpt-4o";
var connectionString = "<YOUR_PROJECT_CONNECTION_STRING>";
var projectClient = new AIProjectClient(connectionString, new DefaultAzureCredential());

var connections = projectClient.GetConnectionsClient();
ConnectionResponse connection = connections.GetDefaultConnection(ConnectionType.AzureOpenAI, true);
var properties = connection.Properties as ConnectionPropertiesApiKeyAuth;

if (properties == null) throw new Exception("Invalid auth type, expected API key auth");

// Create and use an Azure OpenAI client
AzureOpenAIClient azureOpenAiClient = new(
    new Uri(properties.Target),
    new AzureKeyCredential(properties.Credentials.Key));

// This must match the custom deployment name you chose for your model
var chatClient = azureOpenAiClient.GetChatClient(deploymentName);

var requestOptions = new ChatCompletionOptions()
{
    MaxOutputTokenCount = 4096,
    Temperature = 0.7f,
//    TopP = 1.0f,
};

List<ChatMessage> messages =
[
    new SystemChatMessage("You are a helpful assistant."),
    new UserChatMessage("シアトルに行きます。お勧めの観光スポットは？")
];

await foreach (var update in chatClient.CompleteChatStreamingAsync(messages, requestOptions))
{
    Console.Write(update.ContentUpdate.FirstOrDefault()?.Text);
}


```

やけに長ったらしくなりましたが、よくよく見てみると、AI Foundryのプロジェクトに接続して、AzureOpenAIのAPI KEYを取得して、そのKEYでOpenAI用のSDKを使ってモデルに接続してるだけです。

ということは別にOpenAI用のSDKじゃなくても、AI Infrerence SDKでも Microsoft.Extensions.AIのどちらを使用することも可能だ、ということです。AI Inference SDKを使う場合、便利な拡張メソッドが用意されていて、もっと簡単に実装することができる・・・はずなんですが、バグで動きません。

Issue登録されており、StatusはClosedになっていますが解決していません。（ver 1.0.0-beta.6時点）

[[BUG]: No serverless connection error on AIProjectClient.GetChatCompletionsClient](https://github.com/Azure/azure-sdk-for-net/issues/49064)

```bash
dotnet add package Azure.AI.Inference
dotnet add package Azure.AI.Projects
```
この実装で動くはずなのですが、動きませんので注意しましょう。

```csharp:Program.cs
using Azure.AI.Inference;
using Azure.AI.Projects;
using Azure.Identity;

var deploymentName = "gpt-4o";
var connectionString = "<YOUR_PROJECT_CONNECTION_STRING>";
var projectClient = new AIProjectClient(connectionString, new DefaultAzureCredential());

ChatCompletionsClient chatClient = projectClient.GetChatCompletionsClient();

var requestOptions = new ChatCompletionsOptions()
{
    Messages =
    {
        new ChatRequestSystemMessage("You are a helpful assistant."),
        new ChatRequestUserMessage("シアトルに行きます。お勧めの観光スポットは？"),
    },
    MaxTokens = 4096,
    Temperature = 0.7f,
//    NucleusSamplingFactor = 1.0f,
    Model = deploymentName
};

var response = await chatClient.CompleteStreamingAsync(requestOptions);

await foreach (var update in response)
{
    Console.Write(update.ContentUpdate);
}
```

</details>

# 5. OpenAI Response API

2025年3月に登場したAssistant APIとChat Completion API を１つにまとめた感じのAPIです。Chat Completion APIの上位互換に相当するそうなので、今後はResponse APIを使うことが推奨されるようです。

[OpenAI Developer quickstart](https://platform.openai.com/docs/quickstart?api-mode=responses)
[Azure OpenAI Responses API](https://learn.microsoft.com/en-us/azure/ai-services/openai/how-to/responses)

※ C#用のSDKはリリースされていません。(2025年4月)

実装の前に、デプロイ済みのGPT-4oの[リージョン](https://learn.microsoft.com/en-us/azure/ai-services/openai/how-to/responses?tabs=python-key#region-availability)と[バージョン](https://learn.microsoft.com/en-us/azure/ai-services/openai/how-to/responses?tabs=python-key#model-support)に注意してください。

<details><summary>Python</summary>

```text:requirements.txt
openai
```

```bash
pip install -r requirements.txt
```

既にopenaiインストール済みの場合はUpgradeが必要です。

```bash
pip install --upgrade openai
```

```python:main.py
from openai import AzureOpenAI

client = AzureOpenAI(
    api_key="<YOUR_OPENAI_APIKEY>",  
    api_version="2025-03-01-preview",
    azure_endpoint = "<YOUR_OPENAI_ENDPOINT>"
    )

stream_response = client.responses.create(
    max_output_tokens=4096,
    temperature=1.0,
#    top_p=1.0,
    model="gpt-4o", # replace with your model deployment name 
    input="シアトルに行きます。お勧めの観光スポットは？",
    stream=True,
)

for event in stream_response:
    if event.type == 'response.output_text.delta':
        print(event.delta, end="", flush=True)

stream_response.close()
client.close()
```
</details>

<details><summary>TypeScript</summary>

```bash
npm install openai @azure/openai
```

```typescript:index.ts
import { AzureOpenAI } from "openai";

export async function main() {

    const endpoint = "<YOUR_OPENAI_ENDPOINT>";
    const apiKey = "YOUR_OPENAI_APIKEY";
    const deployment = "gpt-4o";
    const modelName = "gpt-4o";
    const apiVersion = "2025-03-01-preview";
    const client = new AzureOpenAI({ endpoint, apiKey, deployment, apiVersion });

    const response = await client.responses.create({
        input: "シアトルに行きます。お勧めの観光スポットは？",
        max_output_tokens: 4096,
        temperature: 1,
        //        top_p: 1,
        model: modelName,
        stream: true
    });

    for await (const event of response) {
        if (event.type === 'response.output_text.delta') {
            process.stdout.write(event.delta ?? '');
        }

    }
}

main().catch((err) => {
    console.error("The sample encountered an error:", err);
});

```

</details>

Python、TypeScriptのどちらもstreamの結果はイベントオブジェクトなので、どのイベントが返ってきているのか判定が必要になっています。

# Agentを扱うSDK

Agentの機能は多いので簡単に整理することはできません。ここでAgent越しにモデルと通信するだけのシンプルな場合についてやり方を見てみましょう。

## OpenAI Agents SDK

OpenAI Agents SDKは、Agentを定義し、それをRunnerに渡すことで処理を実行します。今回、Azure上にデプロイされたOpenAIのモデルを扱う実装をします。Azure AI Agent Serviceを使用する必要はありません。

※ OpenAI Agents SDKにTypeScript, C#版はありません。（2025年4月）

<details><summary>Python</summary>

```text:requirements.txt
openai-agents
```

```bash
pip install -r requirements.txt
```

```python:main.py
from openai import AsyncAzureOpenAI
from openai.types.responses import ResponseTextDeltaEvent
from agents import set_default_openai_client, Agent, Runner
import asyncio

api_version = "2025-03-01-preview"
endpoint = "<YOUR_OPENAI_ENDPOINT>"
apikey = "<YOUR_OPENAI_APIKEY>"

async def main():
    openai_client = AsyncAzureOpenAI(
        api_key=apikey,
        api_version=api_version,
        azure_endpoint=endpoint,
    )

    # Set the default OpenAI client for the Agents SDK
    # set trace off because of recomendation from OpenAI
    set_default_openai_client(openai_client, use_for_tracing=False)

    agent = Agent(
        name="Assistant",
        instructions="You are a helpful assistant.",
        model="gpt-4o"
    )

    result = Runner.run_streamed(agent, "シアトルに行きます。おすすめの観光スポットは？")

    async for event in result.stream_events():
        if event.type == "raw_response_event" and isinstance(event.data, ResponseTextDeltaEvent):
            print(event.data.delta, end="", flush=True)

if __name__ == "__main__":
    asyncio.run(main())
```

```set_default_openai_client(openai_client, use_for_tracing=False)```の```use_for_tracing=False```が気になると思います。こちらはplatform.openai.com以外でのLLMを使用する場合はtraceをoffにすることがOpenAI社から推奨されているためです。

推奨を無視すると、Traceできないエラーがたくさん表示されてしまいますので、実質必須設定です。

[Using other LLM providers](https://openai.github.io/openai-agents-python/models/#using-other-llm-providers)

</details>

## Azure AI Agent Serviceを扱うSDK

Azure AI Agent Serviceは、Azure AI Foundryにプロジェクトを作成し、モデルを事前にデプロイしておく必要があります。使用可能なモデルとリージョンは限定されていますので、ご確認ください。

[Azure AI エージェント サービスでサポートされているモデル](https://learn.microsoft.com/ja-jp/azure/ai-services/agents/concepts/model-region-support)

Azure (AI Project) SDKのサンプルコードではGPT-4o 2024-05-13をデプロイ済みの環境に対して動作確認しています。サンプルコード内でのデプロイ名は"gpt-4o-0513"です。

OpenAI SDKはGpt-4o 2024-11-20で動作確認しています。(2024-05-13では動きません)

### 1. Azure (AI Project) SDK

Azure AI Project SDKは Azure AI Agent Serviceを操作するSDKでもあります。名前が悪いのでいずれ分離されるかもしれません。MS Learn の [Azure AI Agent Serviceのクイックスタート](https://learn.microsoft.com/ja-jp/azure/ai-services/agents/quickstart?pivots=programming-language-python-azure)で**Azure SDK**と記載されているのはAzure AI Project SDK のことです。

<details><summary>Python</summary>

```text:requirements.txt
azure-ai-projects
azure-identity
```

```bash
pip install -r requirements.txt
```

```python:main.py
from azure.ai.projects import AIProjectClient
from azure.ai.projects.models import MessageDeltaChunk, AgentStreamEvent
from azure.identity import DefaultAzureCredential

project_client = AIProjectClient.from_connection_string(
    credential=DefaultAzureCredential(), conn_str="<YOUR_PROJECT_CONNECTION_STRING>"
)

with project_client:
    try:
        agent = project_client.agents.create_agent(
            name="MyAgent",
            model="gpt-4o-0513",
            instructions="You are a helpful assistant.",
        )
        print(f"Created agent, agent ID: {agent.id}")

        thread = project_client.agents.create_thread()
        print(f"Created thread, thread ID: {thread.id}")

        message = project_client.agents.create_message(
            thread_id=thread.id,
            role="user",
            content="シアトルに行きます。おすすめの観光スポットは？"
        )
        print(f"Created message, message ID: {message.id}")

        with project_client.agents.create_stream(thread_id=thread.id, agent_id=agent.id) as stream:

            for event_type, event_data, _ in stream:

                if isinstance(event_data, MessageDeltaChunk):
                    print(event_data.text, end="", flush=True)

                # elif isinstance(event_data, ThreadMessage):
                #     print(f"ThreadMessage created. ID: {event_data.id}, Status: {event_data.status}")

                # elif isinstance(event_data, ThreadRun):
                #     print(f"ThreadRun status: {event_data.status}")

                # elif isinstance(event_data, RunStep):
                #     print(f"RunStep type: {event_data.type}, Status: {event_data.status}")

                elif event_type == AgentStreamEvent.ERROR:
                    print(f"An error occurred. Data: {event_data}")

                elif event_type == AgentStreamEvent.DONE:
                    print("Stream completed.")
                    break

                # else:
                #     print(f"Unhandled Event Type: {event_type}, Data: {event_data}")

    finally:
        if thread:
            project_client.agents.delete_thread(thread.id)
        if agent:
            project_client.agents.delete_agent(agent.id)
```

</details>

<details><summary>TypeScript</summary>

```bash
npm install @azure/ai-projects @azure/identity
```

```typescript:index.ts
import type { AgentOutput, AgentThreadOutput, MessageDeltaChunk, MessageDeltaTextContent } from "@azure/ai-projects";
import { AIProjectsClient, DoneEvent, ErrorEvent, MessageStreamEvent, RunStreamEvent } from "@azure/ai-projects";
import { DefaultAzureCredential } from "@azure/identity"

const connectionString = "eastus2.api.azureml.ms;6e5bc704-5645-462f-8368-0d98b1fcced5;rg-openai;admin-9739";

export async function main(): Promise<void> {

    const client = AIProjectsClient.fromConnectionString(connectionString, new DefaultAzureCredential());
    let agent: AgentOutput | undefined;
    let thread: AgentThreadOutput | undefined;

    try {

        agent = await client.agents.createAgent("gpt-4o-0513", {
            name: "my-agent",
            instructions: "You are a helpful agent"
        });

        thread = await client.agents.createThread();

        await client.agents.createMessage(
            thread.id, {
            role: "user",
            content: "シアトルに行きます。おすすめの観光スポットは？",
        });

        const streamEventMessages = await client.agents.createRun(thread.id, agent.id).stream();

        for await (const eventMessage of streamEventMessages) {
            switch (eventMessage.event) {
                case MessageStreamEvent.ThreadMessageDelta:
                    {
                        const messageDelta = eventMessage.data as MessageDeltaChunk;
                        messageDelta.delta.content.forEach((contentPart) => {
                            if (contentPart.type === "text") {
                                const textContent = contentPart as MessageDeltaTextContent;
                                process.stdout.write(textContent.text?.value || "");
                            }
                        });
                    }
                    break;

                // case RunStreamEvent.ThreadRunCreated:
                //     break;
                // case RunStreamEvent.ThreadRunCompleted:
                //     break;
                case ErrorEvent.Error:
                    console.log(`An error occurred. Data ${eventMessage.data}`);
                    break;
                // case DoneEvent.Done:
                //     break;
            }
        }

    } finally {
        if (thread) {
            await client.agents.deleteThread(thread.id);
        }

        if (agent) {
            await client.agents.deleteAgent(agent.id);
        }
    }
}

main().catch((err) => {
    console.error("The sample encountered an error:", err);
});
```

</details>

<details><summary>C#</summary>

```bash
dotnet add package Azure.AI.Projects
dotnet add package Azure.Identity
```

```csharp:Program.cs
using Azure;
using Azure.AI.Projects;
using Azure.Identity;

var connectionString = "<YOUR_PROJECT_CONNECTION_STRING>";

AgentsClient client = new AgentsClient(connectionString, new DefaultAzureCredential());
Agent? agent = null;
AgentThread? thread = null;
try
{
    Response<Agent> agentResponse = await client.CreateAgentAsync(
        model: "gpt-4o-0513",
        name: "My Agent",
        instructions: "You are a helpful agent.");
    agent = agentResponse.Value;

    Response<AgentThread?> threadResponse = await client.CreateThreadAsync();
    thread = threadResponse.Value;

    Response<ThreadMessage> messageResponse = await client.CreateMessageAsync(
        thread?.Id,
        MessageRole.User,
        "シアトルに行きます。おすすめの観光スポットは？");

    await foreach (StreamingUpdate streamingUpdate in client.CreateRunStreamingAsync(thread?.Id, agent.Id))
    {
        if (streamingUpdate is MessageContentUpdate contentUpdate)
            Console.Write(contentUpdate.Text);
        else
            /*Console.WriteLine(streamingUpdate)*/;
    }
}
finally
{
    await client.DeleteThreadAsync(thread?.Id);
    await client.DeleteAgentAsync(agent?.Id);
}
```
</details>

Azure AI Agent Serviceでは、ProjectClientを作成し、次にAgent、Thead、MessageをTheadに追加してから実行、という流れになります。Toolを使う場合であってもこの流れは基本的に同じです。

このサンプルコードのようにStreamで結果を受け取る場合、処理が終了するまで戻り値をイベント型で受信し続けます。そのためイベントタイプを判別して処理をする必要があります。

[Python: Create Run, Run_and_Process, or Stream](https://learn.microsoft.com/ja-jp/python/api/overview/azure/ai-projects-readme?view=azure-python-preview&context=%2Fazure%2Fai-services%2Fagents%2Fcontext%2Fcontext#create-run-run_and_process-or-stream)

[C#: Azure AI Projects client library for .NET - version 1.0.0-beta.6](https://learn.microsoft.com/ja-jp/dotnet/api/overview/azure/ai.projects-readme?view=azure-dotnet-preview&context=%2Fazure%2Fai-services%2Fagents%2Fcontext%2Fcontext#function-call)
（Function Callの項の一番最後のサンプルがStreamingの唯一のサンプルです）

また、ThreadとAgentは明示的に削除しないとAzure側に残り続けます。継続して使用しないのであれば削除するように実装することをお勧めします。

### 2. OpenAI SDK

Azure AI Agent Serviceは、Assistant APIの上位互換です。そのため、OpenAI のAssistant API用のSDKを使うこともできます。

サンプルコードはGPT-4o 2024-11-20で動作確認していますのでご注意ください。(2024-05-13では動きません)

<details><summary>Python</summary>

```text:requirements.txt
azure-ai-projects
azure-identity
openai
```

```bash
pip install -r requirements.txt
```

```python:main.py
from azure.ai.projects import AIProjectClient
from azure.identity import DefaultAzureCredential
from openai import AzureOpenAI

with AIProjectClient.from_connection_string(
    credential=DefaultAzureCredential(),
    conn_str="<YOUR_PROJECT_CONNECTION_STRING>",
) as project_client:

    client: AzureOpenAI = project_client.inference.get_azure_openai_client(
        api_version = "2025-03-01-preview",
    )

    with client:
        try:        
            agent = client.beta.assistants.create(
                model="gpt-4o", name="my-agent", instructions="You are a helpful agent"
            )
            print(f"Created agent, agent ID: {agent.id}")

            thread = client.beta.threads.create()
            print(f"Created thread, thread ID: {thread.id}")

            message = client.beta.threads.messages.create(
                thread_id=thread.id,
                role="user",
                content="シアトルに行きます。おすすめの観光スポットは？"
            )
            print(f"Created message, message ID: {message.id}")

            stream = client.beta.threads.runs.create(thread_id=thread.id, assistant_id=agent.id, stream=True)

            for streamEvent in stream:
                if streamEvent.event == "thread.message.delta":
                    print(streamEvent.data.delta.content[0].text.value, end="", flush=True)
                elif streamEvent.event == "error":
                    print(f"Error: {streamEvent}")
                    exit(1)

            print("")

        finally:
            client.beta.assistants.delete(agent.id)
            client.beta.threads.delete(thread.id)
```
</details>