しばやん雑記

Azure とメイドさんが大好きなフリーランスのプログラマーのブログ

Microsoft.Extensions.AI のプレビューが公開されたので試した

昨夜 C# 向けの新しい拡張ライブラリとして Microsoft Extensions.AI のプレビューが公開されました。突然出てきた感が凄いですが、個人的には Microsoft.Extensions 名前空間に用意されたライブラリは有用なものが多いので気に入っています。

今回は AI 向けなのでどのレベルをサポートしているのか気になる部分ですが、各種 AI サービスの SDK と Semantic Kernel のようなフレームワークの間を埋めるような抽象化されたライブラリでした。

サンプルでも書かれているように開発中は Ollama を使うけども、本番では Azure AI Inference を使うといった処理が簡単に実現できます。各種 SDK の作法を知らなくてもアプリケーションに組み込めるのは良いですね。必要に応じてキャッシュなども組み込みやすそうな気配です。

正直なところ公式ブログを読むだけで十分といった感じなのですが、OpenAI については Azure OpenAI 向けではなく公式向けのコードになっていて、Azure OpenAI でも使えるのかという疑問もありました。

そこで公式ブログでは書かれていなかった部分について確認したので、メモがてら簡単に残しておきます。

Azure OpenAI を利用する

結論から書いてしまうと、問題なく Azure OpenAI を使うことが可能です。理由としては新しい Azure.AI.OpenAIOpenAI を継承して実装されているため、`OpenAIClient` 向けの拡張メソッドがそのまま利用可能なためです。

NuGet Gallery で Azure.AI.OpenAI の依存関係を確認すると OpenAI が存在していることがわかります。

Azure OpenAI 向けに利用する場合は、Azure OpenAI の SDK と OpenAI 向けのライブラリをインストールするだけで準備ができます。非常に簡単ですね。

実際に Azure OpenAI を使うサンプルコードは以下のようになります。公式ブログで書かれている OpenAI 向けコードとほぼ同じですが、AzureOpenAIClient を使っている部分が大きな違いです。Azure OpenAI の SDK は v2 で大きく使い方が変わったので、その部分で少し戸惑うかもしれません。

using Azure.AI.OpenAI;

using Microsoft.Extensions.AI;

IChatClient client =
    new AzureOpenAIClient(new Uri("https://***.openai.azure.com/"), new System.ClientModel.ApiKeyCredential("AOAI_API_KEY"))
        .AsChatClient("gpt-4o");

var response = await client.CompleteAsync("What is AI?");

Console.WriteLine(response.Message);

このサンプルコードを実行すると、以下のように回答が表示されます。使っている CompleteAsync は推論結果が出るまで待つ API になるため、表示までの待ち時間は長くなります。

最近はよく使われているストリーミングで返す API も用意されていて、以下のように CompleteStreamingAsync を呼び出すと IAsyncEnumerable<T> が返ってくるので await foreach で読み取ればお馴染みのストリーミング出力が簡単に行えるようになっています。

using Azure.AI.OpenAI;

using Microsoft.Extensions.AI;

IChatClient client =
    new AzureOpenAIClient(new Uri("https://***.openai.azure.com/"), new System.ClientModel.ApiKeyCredential("AOAI_API_KEY"))
        .AsChatClient("gpt-4o");

await foreach (var update in client.CompleteStreamingAsync("Microsoft について簡単に説明してください"))
{
    Console.Write(update);
}

実行結果のスクリーンショットでは動作はわからないと思いますが、素早く推論結果の出力が開始されるのでユーザー体験としてはよくなります。このライブラリは .NET チームが作っているようなので、最新の C# 言語機能をちゃんと使っている点もポイントが高いです。

Embedding についても同じように使えるので省略しますが、各種 SDK を直接叩くよりも簡単に Generative AI の機能を利用できるため、アプリケーションへ組み込みやすくなりそうです。

AI Toolkit 経由で SLM を利用する

MEAI で OpenAI SDK が使えるのなら、以前書いた AI Toolkit が公開している OpenAI 互換の REST API を経由して SLM を利用できるのでは?と思ったので実際に試してみました。AI Toolkit については以下のエントリを参照してください。

AI Toolkit のドキュメントでは古い OpenAI SDK ベースでカスタマイズするコードが載っていますが、現行の v2 では書き方が若干変わっているので修正する必要があります。

具体的には OverrideRequestUriPolicy の実装を Azure.Core から System.ClientModel を使うように変更しました。System.ClientModel は Azure SDK の知見から新しく実装された REST API を呼び出す SDK を作りやすくするライブラリです。

実際に用意したサンプルコードは以下のようになります。ほぼ OpenAI を使うコードと同じですがリクエスト URI をオーバーライドするために、追加のオプションを渡している点のみ異なります。

using Microsoft.Extensions.AI;

using OpenAI;

using System.ClientModel.Primitives;

OpenAIClientOptions clientOptions = new();
clientOptions.AddPolicy(new OverrideRequestUriPolicy(new("http://localhost:5272/v1/chat/completions")), PipelinePosition.BeforeTransport);

IChatClient client =
    new OpenAIClient(new System.ClientModel.ApiKeyCredential("unused"), clientOptions)
        .AsChatClient("Phi-3-mini-4k-directml-int4-awq-block-128-onnx");

await foreach (var update in client.CompleteStreamingAsync("Microsoft について簡単に説明してください"))
{
    Console.Write(update);
}

internal class OverrideRequestUriPolicy(Uri overrideUri) : HttpClientPipelineTransport
{
    protected override void OnSendingRequest(PipelineMessage message, HttpRequestMessage httpRequest)
    {
        httpRequest.RequestUri = overrideUri;
    }
}

AI Toolkit を起動させておいて、このコードを実行すると REST API 経由で SLM を使った推論が実行されるので、以下のように Phi-3 を使ったローカルでの実行が簡単に行えます。

推論結果については SLM の性能に依存するのでどうしようもないのですが、ONNX Runtime を使わなくても簡単にローカルで検証できるのは AI Toolkit の OpenAI 互換 API を使うメリットですね。

そして MEAI を使うことで、LLM と SLM の両方を同じコードで実行できるというのもかなり熱いです。