BEACHSIDE BLOG

Azure と GitHub と C# が好きなエンジニアの個人メモ ( ・ㅂ・)و ̑̑

Azure OpenAI で C# のSDK を使うとき最初に知っておきたい入門知識

今回は C# で OpenAI の SDK で ChatCompletion API を使ってチャットのサンプルコードを書きつつ、SDK を使うとき最初に知っておきたいいくつかの Tips を書きます。今回はざっくり以下を環境で話を進めます。

  • Azure OpenAI でモデルは GPT-3.5 以降のモデル
  • ChatCompletion API
  • C# のコンソールアプリ (C#11)
  • Azure SDK ファミリーの NuGet パッケージ: Azure.AI.OpenAI


🚧 2023年6月時点の内容になります。基本的なとこはそんなに変わらんと思いますが SDK のアップデートは早いのでご注意ください 🚧


ほんとは OpenAI のプロンプトエンジニアリングの基本的な原則を C# のコードとともに書くつもりが、そこにたどり着くまでの文が長くなったのでこのタイトルに替えました (汗) 。原則シリーズは次回に書きます。

Azure SDK を利用するメリット

Azure の SDK を使うメリットであり Tips のひとつの紹介です。OpenAI の SDK だけでなく Blob や Cosmos DB など他の Azure のリソースを操作する際の SDK を含め、インスタンスの初期化や認証、リトライの仕様が統一されているので、他のリソースを使ったことがあるとわかりやすいのが良いです。

具体的な例を1つあげると、リトライ処理は Azure SDK の Core 部分で仕様が統一されておりデフォルトで組み込まれています。リトライが組み込まれてるなんて当たり前感しかないですが、初学者が SDK を初めて利用するときには気づかないですよね。

ちなみにリトライのデフォルト値は以下になっています。

  • MaxRetries: 3
  • Delay: 0.8秒
  • MaxDelaye: 1分
  • Mode: Exponential (RetryMode.Exponential)
  • NetworkTimeout: 100秒

より詳しい情報やカスタマイズ方法については以下を参照するとよいです。

ということで話を戻し Azure OpenAI の準備に進みます。

Azure OpenAI の準備

OpenAIClient を初期化するには、Azure OpenAI のエンドポイントと API key を使います。

認証については今回はわかりやすい API Key でやります。Azure では Managed Identity を使うことで .... つまり雑に説明すると Azure AD の機能で認証することで API Key の管理が不要になりキー漏洩のリスクがなくなるって話しなので Managed Identity 使うのがおすすめです。

が、ここでそれ書くと脱線しつつやり方書くのが面倒なので、サンプルコードで試すには気楽に API Key でやっていきます。

Azure OpenAI のリソース作成とモデルのデプロイ

Azure OepnAI のリソースが無い場合は、Azure ポータルで作ります。以下のドキュメントを参考に Azure ポータルからポチポチするだけです。後でモデルも使うので Azure OpenAI Studio でモデルのデプロイもしておきましょう。

今回は ChatCompletion API でチャットをするので、gpt-35-turbo のモデルを選んでおくとよいです。(ちなみに GPT-4 は使うには申請必要)

方法 - Azure OpenAI Service を使用してリソースを作成し、モデルをデプロイする - Azure OpenAI | Microsoft Learn

エンドポイントと API Key、デプロイしたモデルのデプロイ名の取得

Azure ポータルで OpenAI のリソースを開く > "Key and Endpoint" を開くとエンドポイントと API Key が取得できますのでメモしておきます。

モデル名は、Azure OpenAI Studio を開き、"デプロイ" からデプロイしたモデルのデプロイ名 (DeploymentName) をメモしておきます。

コンソールアプリでチャットを試す

コンソールアプリの作成

プロンプトを構成する際に C# の生文字リテラルの機能を使いたいので、C#11 を使います。ということで .NET7 SDK は必要です。ちなみに Console App や Azure Fuction App 自体のバージョンは .NET6 でも LangVersion11 にすれば大丈夫です。

ということでまずはコンソールアプリを作っていきます。VS 2022 を起動して "新しいプロジェクトの作成" をクリックします。

コンソールアプリを選び "次へ" をクリックします。

プロジェクト名とか場所とか適当に入れて次へ進みます。

フレームワークは .NET6 でも .NET7でも OK ですが、.NET6 を選んだときに C#11を使う方法を書くので私は .NET6で行きます。

プロジェクトができたら生文字リテラル書いてみましょうか。Program.cs でコードをこんな感じにしてみました。

var text = """
    Hello
    "Raw string literal text"
    Foooo!
    """;

Console.WriteLine(text);

コンソールアプリさんが怒ります。

C#11にあげましょう。プロジェクト名をクリックしてプロジェクトファイルを開きます。 以下図のように <LangVersion>11</LangVersion> を追加して保存します。

これで生文字リテラルが利用できるようになったので、正常に実行できるようになります。ゆーてサンプルコード書くくらいなら生文字リテラル使わないことの方が多そうなんですがね...。

NuGet パッケージのインストール

Visual Studio 2022 で Ctrl + Q キーをおして検索にカーソルをあわせ「nuget」と入力してNuGet パッケージマネージャーの管理をクリックします (ソリューションでもプロジェクトのでもどっちでもいいです) 。

"参照" タブを開き「プレスリリースを含める」にチェックをいれて「Azure.AI.OpenAI」と入力してインストールします。このブログ書いた時点だと 1.0.0-beta.5 が最新でした。

あとは、以下の2つも今回使うので入れておきます。バージョンは、.NET6 を使ってるならv6の最新版、.NET7を使ってるなら v7の最新版を入れておきます。

  • Microsoft.Extensions.Configuration.Binder
  • Microsoft.Extensions.Configuration.UserSecrets

エンドポイントと API キーの管理

サンプルコードだし適当でいいとは思ってるんですが、GitHub にあげることを考えて UserSecrets の機能を使うことにしておきます。

ということで、ソリューションエクスプローラーでプロジェクト名を右クリック > "ユーザーシークレットの管理" をクリックします。

secret.json のファイルが開くので以下の感じにします。

  • endpoint, apiKey, deploymentName は、先述の "エンドポイントと API Key、デプロイしたモデルのデプロイ名の取得" セクションでメモした値を入力します。
  • 補足として json をキャメルケースで書いても C# でコード変えなくても普通に読み込んでくれるし Linux 環境でも大丈夫なはずですが、万が一のトラブルに備えてパスカルケースにしてます。
{
  "OpenAIOptions": {
    "Endpoint": "https://xxxxx.openai.azure.com/",
    "ApiKey": "xxxxxxxx",
    "DeploymentName": "xxxx"
  }
}

あとはこれを管理するコードを雑に書いておきます。ここでは OpenAIOptions ってクラスを追加して、コードは以下のようにしました。

using Microsoft.Extensions.Configuration;

namespace OpenAIDemo;

public class OpenAIOptions
{
    public string Endpoint { get; set; }
    public string ApiKey { get; set; }
    public string DeploymentName { get; set; }

    public static OpenAIOptions ReadFromUserSecrets()
    {
        var builder = new ConfigurationBuilder()
            .AddUserSecrets<Program>();
        return builder.Build().GetSection(nameof(OpenAIOptions)).Get<OpenAIOptions>();
    }
}

これで、Program.cs でこんなコードを書いてデバッグ実行すると情報が取得できては確認しましょう。

using OpenAIDemo;

var options = OpenAIOptions.ReadFromUserSecrets();

Console.WriteLine();

OpenAIClient の初期化

ようやく OepnAIClient に手をかけます。初期化は先述でも書いた通り Managed Identity は使わず今回はシンプルに API Key で初期化します。コードは簡単です。

using Azure.AI.OpenAI;
using Azure;
using OpenAIDemo;

var options = OpenAIOptions.ReadFromUserSecrets();

var client = new OpenAIClient(new Uri(options.Endpoint), new AzureKeyCredential(options.ApiKey));

チャットをしてみる

ChatCompletion API を使ってのチャットはこんな感じです。今回は、ユーザーメッセージをなげてアシスタントのメッセージを保持していってるので、私の名前を言ったら覚えてくれている感じになります。

using Azure.AI.OpenAI;
using Azure;
using OpenAIDemo;

var options = OpenAIOptions.ReadFromUserSecrets();
// OpenAIClient の初期化
var client = new OpenAIClient(new Uri(options.Endpoint), new AzureKeyCredential(options.ApiKey));

// システムメッセージをエンドポイントになげる
var chatCompletionsOptions = new ChatCompletionsOptions
{
    MaxTokens = 300,
    Messages =
    {
        new ChatMessage(ChatRole.System, """
                                あなたは Azure の専門家です。
                                250文字以内で初心者にやさしい感じで回答を返します。
                                """)
    }
};

await client.GetChatCompletionsAsync(options.DeploymentName, chatCompletionsOptions);

//ユーザーメッセージの送信
var userMessages = new[]
{
    "こんにちは、私はBEACHSIDEです。",
    "Azure Open AI とはなんですか。",
    "私の名前を覚えていますか。"
};

foreach (var userMessage in userMessages)
{
    // 送信するユーザーメッセージを chatCompletionsOptions に追加
    chatCompletionsOptions.Messages.Add(new ChatMessage(ChatRole.User, userMessage));
    Console.WriteLine($"{ChatRole.User}: {userMessage}");

    // ユーザーメッセージを送信し、アシスタントからのレスポンスを受け取る
    var response = await client.GetChatCompletionsAsync(options.DeploymentName, chatCompletionsOptions);

    foreach (var choice in response.Value.Choices)
    {
        Console.WriteLine($"{choice.Message.Role}: {choice.Message.Content}");
        // アシスタントからのメッセージを chatCompletionsOptions に追加
        chatCompletionsOptions.Messages.Add(new ChatMessage(choice.Message.Role, choice.Message.Content));
    }
}

結果はこんな感じになります。

Azure SDK を使う際に絶対忘れてはあかん1つのこと

Azure SDK 利用時に最初にしっておきたいことのひとつは以下のドキュメントに書いてあります。雑にまとめると Client を使う際は基本的にシングルトンで扱い、(サンプルのお試しではなく) 真面目にコード書くときは DI でライフサイクルの管理しましょうくらいな話ですが、興味があったら一読しましょう。

まとめ

今回は、C# で Azure の SDK を使って ChatCompletion API でチャットする初めの一歩の話をしました。

次回以降でプロンプトエンジニアリングの原則的なのを C# のコードをまじえてやっていこうと思います。