普通と違う感じの Semantic Kernel 入門 009「Microsoft.Extensions.AI との統合」
これまでの記事
- 普通と違う感じの Semantic Kernel 入門 001「関数」
- 普通と違う感じの Semantic Kernel 入門 002「テンプレートエンジン」
- 普通と違う感じの Semantic Kernel 入門 003「AI を呼ぶ関数」
- 普通と違う感じの Semantic Kernel 入門 004「プラグイン」
- 普通と違う感じの Semantic Kernel 入門 005「Chat Completions API を使おう」
- 普通と違う感じの Semantic Kernel 入門 006「Kernel」
- 普通と違う感じの Semantic Kernel 入門 007「フィルター」
- 普通と違う感じの Semantic Kernel 入門 008「DI コンテナとの統合」
はじめに
ここでは、Microsoft.Extensions.AI
と Semantic Kernel の統合について解説します。
Microsoft.Extensions.AI は .NET のチームの方で開発している AI (Chat Completion API や Embedding API) を使うための API の抽象化レイヤーを提供するものです。この他に Microsoft.Extesnsions.VectorData
というベクトルストアを使うための抽象化レイヤーや Microsoft.Extensions.AI.Evaluation
という評価のためのライブラリもあります。簡単に言いうと .NET で AI を使うための準公式ライブラリです。
各々のライブラリの特徴を箇条書きにすると以下のようになります。
-
Microsoft.Extensions.AI
(.NET チームにより開発)- Chat Completions API や Embedding API を使うための抽象化レイヤー
- OpenAI や Azure OpenAI Service などのプロバイダーをサポート
- DI コンテナとの統合が容易
-
Microsoft.Extensions.VectorData
(Semantic Kernel チームにより開発)- ベクトルストアを使うための抽象化レイヤー
- 様々なベクトルストアをサポート
- DI コンテナとの統合が容易
-
Microsoft.Extensions.AI.Evaluation
(.NET チームにより開発)- AI の応答を評価するためのライブラリ
オーナーとなるチームが VectorData
だけ Semantic Kernel チームで、他の 2 つは .NET チームが開発しています。もしかしたら将来的には全部 .NET チームに移管されるかもしれませんが、現在リポジトリの所在的には VectorData
だけが Semantic Kernel チームの管轄となっています。
この記事では、この中の Microsoft.Extensions.AI
のみに焦点を当てて、Semantic Kernel との統合について解説します。
Microsoft.Extensions.AI
で提供されている機能
Microsoft.Extensions.AI
は、AI を使うための抽象化レイヤーを提供します。
もう少し細かく説明を行うと Microsoft.Extensions.AI.Abstractions
パッケージに抽象化レイヤーが実装されています。
具体的には、以下のような機能を提供しています。
-
IChatClient
インターフェース- Chat Completions API を使うためのインターフェース
-
IEmbeddingClient
インターフェース- Embedding API を使うためのインターフェース
-
ISpeechToTextClient
インターフェース- 音声をテキストに変換するためのインターフェース
- 現時点では実験的な機能
この他に AI が呼び出すためのツールの抽象化の AITool
も提供しています。
そして、その抽象化レイヤーを使った共通的な機能が Microsoft.Extensions.AI
パッケージに実装されています。
具体的には、以下のような機能を提供しています。
- キャッシュ機能
- ログ機能
- OpenTelemetry
- 関数の自動呼出し
これらの機能はミドルウェアとして実装されており、任意の IChatClient
や IEmbeddingClient
の実装に対して適用することができます。ミドルウェア以外の機能としては、各クライアントにこれらのミドルウェアを追加するためのビルダークラスや、Structured Output を使った際に JSON のパースまでしてくれる機能などがあります。
Microsoft.Extensions.AI の実装
ここまでは、基本的に特定の LLM プロバイダーに依存しない部分の話になります。
各 LLM プロバイダーは .NET 向けの SDK を提供する際に Microsoft.Extensions.Abstractions
の抽象化レイヤーに対して実装を提供する形になります。
Microsoft.Extensions.AI を中心としてエコシステム
Microsoft.Extensions.AI
は .NET チームが提供する抽象化レイヤーのため .NET のエコシステムの中で広く使われることを想定しています。
現時点でも OpenAI
用の実装や Ollama
用の実装や Azure AI Foundry
用の実装などが提供されています。
開発者は IChatCLient
などの抽象化レイヤーを使って開発をしている限り、基本的には LLM のプロバイダーに依存しないコードを書くことができます。
また、AI を使うための各種ライブラリも Microsoft.Extensions.AI
で提供されている抽象化レイヤーに対して機能を実装することで Microsoft.Extensions.AI
のエコシステムの一部として機能します。1 つ実装するだけで全てのエコシステム内のライブラリや LLM プロバイダーと連携できるようになります。
例えば、Microsoft.Extensions.AI.Evaluation
は Microsoft.Extensions.AI.Abstractions
を使って AI の応答を評価するためのライブラリです。これにより、AI の応答を評価するためのコードは LLM プロバイダーに依存しない形で書くことができます。さらに Model Context Protocol (MCP) も Microsoft.Extensions.AI
の抽象化レイヤーを使って実装されているため、個別の LLM のプロバイダーが MCP をサポートする必要はありません。
そして、Semantic Kernel も Microsoft.Extensions.AI
を使って実装されたマルチエージェント システムを作るためのライブラリです。Semantic Kernel も .NET の Microsoft.Extensions.AI
のエコシステムの一部として機能します。
図で表すと以下のようなイメージです。
Semantic Kernel 内での Microsoft.Extensions.AI
Semantic Kernel は Microsoft.Extensions.AI
の登場以前から AI を使うための抽象化レイヤーを提供していました。
Chat Completions API のようなタイプの API のための抽象化レイヤーとして IChatCompletionService
インターフェースがありました。この IChatCompletionService
を実装して AzureOpenAIChatCompletionService
などの各種 LLM 向けの実装があるという形でした。そして Semantic Kernel の各種機能は IChatCompletionService
を使って実装することで LLM に依存しないかたちを実現していました。
この IChatCompletionService
は Microsoft.Extensions.AI
の IChatClient
に相当するものです。現在、この IChatCompletionService
を使える箇所で IChatClient
も使えるように変更が進められています。主要な機能は、大体 IChatClient
を使うように変更されているので、Semantic Kernel の機能を使う際には IChatClient
を使うことができます。
例えば KernelFunction
は、Microsoft.Extensions.AI
対応の過程で以下のように AITool
を継承する形に変更されました。以下のような継承関係になっています。internals
の部分は Semantic Kernel の内部で使われるクラスです。
AIFunction
から継承しているのに AIFunction
をラップする AIFunctionKernelFunction
があるのが個人的には面白いです。AIFunction
を KernelFunction
として扱うときに必要とは言え、初見では少し混乱するような構造になっているなと思いました。
このような構造になっているため、AITool
を受け取る箇所には KernelFunction
を渡すことが出来ます。一点注意点として、KernelFunction
を実行するには多くのケースで Kernel
が必要になります。そのため KernelFunction
を、そのまま渡すと実行時にエラーになることがあります。
例えば以下のような Kernel
がまったく必要ない処理の場合は問題なく動作します。
using Microsoft.Extensions.AI;
using Microsoft.SemanticKernel;
// Kernel がなくても動く関数
var simpleFunction = KernelFunctionFactory.CreateFromMethod(TimeProvider.System.GetLocalNow, "GetLocalNow");
await InvokeAIFunctionAndWriteOutputAsync(simpleFunction);
async Task InvokeAIFunctionAndWriteOutputAsync(AIFunction function)
{
var result = await function.InvokeAsync();
Console.WriteLine(result);
}
TimeProvider.System.GetLocalNow
を呼び出すだけの関数なので、Kernel
は必要ありません。そのまま AIFunction
を受け取る処理に渡して実行することができます。
上記のコードを実行すると、以下のように現在のローカル時間が出力されます。
2025-06-03T09:57:17.864371+09:00
ただし、以下のように Kernel
が必要な処理を行う場合は、Kernel
を渡す必要があります。
using Azure.Identity;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.Configuration;
using Microsoft.SemanticKernel;
// User Secrets から設定を読み込む
var configuration = new ConfigurationBuilder()
.AddUserSecrets<Program>()
.Build();
// AOAI にデプロイしているモデル名
var modelDeploymentName = configuration["AOAI:ModelDeploymentName"]
?? throw new ArgumentNullException("AOAI:ModelDeploymentName is not set in the configuration.");
// AOAI のエンドポイント
var endpoint = configuration["AOAI:Endpoint"]
?? throw new ArgumentNullException("AOAI:Endpoint is not set in the configuration.");
// Builder を作成
var builder = Kernel.CreateBuilder();
// AOAI 用の Chat Client を登録
builder.AddAzureOpenAIChatClient(
modelDeploymentName,
endpoint,
new AzureCliCredential());
// AI サービスが登録された Kernel を作成
var kernel = builder.Build();
// プロンプトから関数を作成 (実行するには AI サービスの登録された Kernel が必要)
var greetingFunction = KernelFunctionFactory.CreateFromPrompt("こんにちは");
// そのまま実行するとエラーになる
await InvokeAIFunctionAndWriteOutputAsync(greetingFunction);
async Task InvokeAIFunctionAndWriteOutputAsync(AIFunction function)
{
var result = await function.InvokeAsync();
Console.WriteLine(result);
}
このコードを実行すると、以下のようなメッセージの KernelException
が発生します。
No service was found for any of the supported types: Microsoft.SemanticKernel.ChatCompletion.IChatCompletionService, Microsoft.SemanticKernel.TextGeneration.ITextGenerationService, Microsoft.Extensions.AI.IChatClient.
これは Kernel
を指定せずに KernelFunction
を実行したときにはデフォルトで空の Kernel
が使われるためです。今回のような AI サービスを使う関数を実行するには、ちゃんとセットアップされた Kernel
を指定して実行する必要があります。Kernel
を渡すための WithKernel
という拡張メソッドが定義されています。これを使うと関数のクローンを作成して、その関数に Kernel
を設定することができます。
このメソッドを使うと上記コードは以下のように書き換えることができます。(Kernel
を作成する手前のコードは同じため省略しています)
// AI サービスが登録された Kernel を作成
var kernel = builder.Build();
// プロンプトから関数を作成 (実行するには AI サービスの登録された Kernel が必要)
var greetingFunction = KernelFunctionFactory.CreateFromPrompt("こんにちは");
// WithKernel メソッドを使うと Kernel を持った関数の Clone を作成できる
#pragma warning disable SKEXP0001
await InvokeAIFunctionAndWriteOutputAsync(greetingFunction.WithKernel(kernel));
#pragma warning restore SKEXP0001
async Task InvokeAIFunctionAndWriteOutputAsync(AIFunction function)
{
var result = await function.InvokeAsync();
Console.WriteLine(result);
}
実行すると以下のような結果になります。AIFunction
として KernelFunction
を実行すると実行結果の値が System.Text.Json.JsonElement
に変換されて帰ってきます。そのため上記のメソッドの実行結果は以下のようになります。
{
"messages": [
{
"role": "assistant",
"contents": [
{
"$type": "text",
"text": "こんにちは!\uD83D\uDE0A 何かお手伝いできることがありますか?"
}
],
"messageId": "chatcmpl-BeAXMBxRf4cw8M7Z8lIg7DVAVXDjM"
}
],
"responseId": "chatcmpl-BeAXMBxRf4cw8M7Z8lIg7DVAVXDjM",
"modelId": "gpt-4.1-2025-04-14",
"createdAt": "2025-06-03T01:15:24+00:00",
"finishReason": "stop",
"usage": {
"inputTokenCount": 8,
"outputTokenCount": 17,
"totalTokenCount": 25,
"additionalCounts": {
"InputTokenDetails.AudioTokenCount": 0,
"InputTokenDetails.CachedTokenCount": 0,
"OutputTokenDetails.ReasoningTokenCount": 0,
"OutputTokenDetails.AudioTokenCount": 0,
"OutputTokenDetails.AcceptedPredictionTokenCount": 0,
"OutputTokenDetails.RejectedPredictionTokenCount": 0
}
}
}
今回は、プロンプトの実行が IChatClient
を使って実行されるため生の戻り値は Microsoft.Extensions.AI.ChatResponse
になります。それが JsonElement
に変換されているため上記のような結果になっています。ここから応答のメッセージだけ抜き出したい場合は一度 ChatResponse
にデシリアライズするか、めんどくさいですが FunctionResult
に変換してから GetValue
メソッドを使ってメッセージを取得することができます。
例えば以下のように InvokeAIFunctionAndWriteOutputAsync
メソッドを変更すると、応答のメッセージだけを出力することができます。
async Task InvokeAIFunctionAndWriteOutputAsync(AIFunction function)
{
var result = await function.InvokeAsync();
// 結果は JSON 形式で返されるので、デシリアライズする
// デシリアライズの時のオプションは Microsoft.Extensions.AI でデフォルトで使われるオプションが
// 内部でも使われているので、それを指定する
var chatResponse = JsonSerializer.Deserialize<ChatResponse>(
(JsonElement) result!,
AIJsonUtilities.DefaultOptions);
#pragma warning disable SKEXP0001 // AsKernelFunction はプレビュー機能なので警告の抑止が必要
// KernelFunction と戻り値を使って FunctionResult を作成
var functionResult = new FunctionResult(function.AsKernelFunction(), chatResponse);
#pragma warning restore SKEXP0001
// FunctionResult から値を取得
var answer = functionResult.GetValue<string>();
// 結果を出力
Console.WriteLine(answer);
}
AIFunction
を KernelFunction
として使う場合は、AsKernelFunction
拡張メソッドを使うことで KernelFunction
に変換することができます。これによってプラグインとして登録するなどの Semantic Kernel の機能を使うことができます。以下に AIFunction
を KernelFunction
として使う例を示します。
using System.Text.Json;
using Microsoft.Extensions.AI;
using Microsoft.SemanticKernel;
// AIFunction を作成
var aiFunction = AIFunctionFactory.Create((string format) =>
{
return TimeProvider.System.GetLocalNow().ToString(format);
});
#pragma warning disable SKEXP0001
// AIFunction から KernelFunction に変換
// プレビュー機能なので警告の抑制が必要
var kernelFunction = aiFunction.AsKernelFunction();
#pragma warning restore SKEXP0001
// KernelFunction の API を使って関数を呼び出す
FunctionResult result = await kernelFunction.InvokeAsync(
new Kernel(),
new KernelArguments
{
["format"] = "yyyy-MM-dd HH:mm:ss zzz",
});
// デフォルトで戻り値は JsonElement になる点に注意
Console.WriteLine(result.GetValue<JsonElement>());
実行すると、以下のように現在のローカル時間が出力されます。
2025-06-03 15:58:10 +09:00
コメントにもありますが AIFunction
の戻り値はデフォルトで JsonElement
になるので、FuncionResult
の GetValue
の型引数には JsonElement
を指定する必要がある点に注意が必要です。JsonElement
以外にしたい場合は Semantic Kernel ではなく Microsoft.Extensions.AI
の世界の話なのでここでは割愛します。
ここまで KernelFunction
にフォーカスして Microsoft.Extensions.AI
との統合機能について解説してきましたが、ちょっとここで Semantic Kernel のプロジェクト構造から Microsoft.Extensions.AI
の統合機能について見ていこうと思います。一般的に Semantic Kernel を使う場合は、Microsoft.SemanticKernel
パッケージを使うことが多いのですが、これはおそらくメタパッケージ(このパッケージ自体には DLL などは含まれていなくて関連するパッケージをとりまとめるようなもの)になっているように見えます。
Microsoft.SemanticKernel
パッケージを参照すると Microsoft.SemanticKernel.Core
と Microsoft.SemanticKernel.Connectors.AzureOpenAI
の 2 つのパッケージが参照されます。それぞれの依存関係から今回関係のあるものを抜き出すと以下のようになります。
-
Microsoft.SemanticKernel.Core
-
Microsoft.SemanticKernel.Abstractions
Microsoft.Extensions.AI
Microsoft.Extensions.VectorData
-
Microsoft.SemanticKernel.Connectors.AzureOpenAI
これを見ると Microsoft.SemanticKernel.Abstractions
から Microsoft.Extensions.AI
と Microsoft.Extensions.VectorData
が参照されていることがわかります。Microsoft.SemanticKernel.Abstractions
は Semantic Kernel の抽象化レイヤーで、Semantic Kernel の機能を使うためのインターフェースやクラスが定義されています。この部分から相互運用が可能な形になっています。
私が見つけられた範囲の相互運用のための型変換は以下のようなものがありました。
-
PromptExecutionSettings
クラスとChatOptions
の相互変換-
PromptExecutionSettings
のToChatOptions
拡張メソッドでChatOptions
に変換 -
ChatOptions
のToPromptExecutionSettings
拡張メソッドでPromptExecutionSettings
に変換
-
-
KernelFunction
とAIFunction
の相互変換-
AIFunction
のAsKernelFunction
拡張メソッドでKernelFunction
に変換 -
KernelFunction
はAIFunction
を継承しているため、そのままAIFunction
として使うことができる。Kernel
が必要な場合はWithKernel
メソッドを使う。
-
-
IChatClient
のGetResponseAsync
メソッドにPromptExecutionSettings
とKernel
を受け取るオーバーロードがある -
IChatClient
と従来の抽象化レイヤーのIChatCompletionService
の相互変換-
IChatCompletionService
のAsChatClient
拡張メソッドでIChatClient
に変換 (プレビュー機能) -
IChatClient
のAsChatCompletionService
拡張メソッドでIChatCompletionService
に変換 (プレビュー機能)
-
さらに、IChatClient
のミドルウェアとして Semantic Kernel の KernelFunction
の関数を自動で呼び出すようにするための KernelFunctionInvokingChatClient
というものが実装されています。これは UseKernelFFunctionInvocation
拡張メソッドを IChatClient
のビルダーに対して呼び出すことで追加することが出来ます。ただ、通常は Semantic Kernel の AddAzureOpenAIChatClient
メソッドを使うと、内部で KernelFunctionInvokingChatClient
が登録されるため、Semantic Kernel の機能を使う場合は特に意識する必要はありません。
Semantic Kernel の AddAzureOpenAIChatClient
メソッドは以下のように実装されています。
var loggerFactory = serviceProvider.GetService<ILoggerFactory>();
var client = azureOpenAIClient ?? serviceProvider.GetRequiredService<AzureOpenAIClient>();
var builder = client.GetChatClient(deploymentName)
.AsIChatClient()
.AsBuilder()
.UseKernelFunctionInvocation(loggerFactory)
.UseOpenTelemetry(loggerFactory, openTelemetrySourceName, openTelemetryConfig);
if (loggerFactory is not null)
{
builder.UseLogging(loggerFactory);
}
return builder.Build();
KernelFunction
の自動呼出しと OpenTelemetry のミドルウェアが追加されていて、ILoggerFactory
がある場合はロギングのミドルウェアも追加されます。Semantic Kernel で登録された IChatClient
を使う場合は、このようなミドルウェアが追加されているということを頭に入れておくと良いでしょう。
Microsoft.Extensions.AI との統合のメリット
Microsoft.Extensions.AI
と Semantic Kernel の統合によって、以下のようなメリットがあります。
- 一貫したインターフェース:
IChatClient
を通じて、Microsoft.Extensions.AI の機能を簡単に利用できるようになります。 - 拡張性: Semantic Kernel の機能を活用しながら、Microsoft.Extensions.AI のエコシステムに統合することができます。
- 相互運用性: 両者の抽象化レイヤーが相互に変換可能なため、柔軟なアーキテクチャを構築できます。
この中の相互運用性が個人的には一番のメリットだと感じています。
例えば最初の方でも書いた内容になりますが Model Context Protocol (MCP) は MCP のツールを AITool
として返すような API になっています。
つまり、これはシームレスに Semantic Kernel の KernelFunction
として使うことが出来るということなので、Semantic Kernel のプラグインとして扱えるということになります。
Semantic Kernel 自体は特に MCP をサポートするための機能は提供していないにも関わらず Microsoft.Extensions.AI
のエコシステムの一部として提供されているため、Semantic Kernel の機能を使うことができます。
このように Microsoft.Extensions.AI
を中心として様々な機能が統合されているため、Semantic Kernel もその一部として機能することができます。これにより、Semantic Kernel の機能を使いながら、Microsoft.Extensions.AI のエコシステムの恩恵を受けることができるようになっています。
まとめ
Semantic Kernel は AI を使うための抽象化レイヤーとして独自の Microsoft.SemanticKernel.Abstractions
を提供していましたが、現在は Microsoft.Extensions.AI
にも対応を行い、IChatClient
を使う方向に進んでいます。一度 GA させてしまった IChatCompletionService
は Semantic Kernel の安定した API を提供するというポリシーがあるので、消えることはそうそうないと思いますが Semantic Kernel v2 のようなメジャーバージョンアップのタイミングで IChatCompletionService
を廃止して IChatClient
に統一される可能性はあるかもしれません。
現時点では Semantic Kernel の抽象化レイヤーと Microsoft.Extensions.AI の抽象化レイヤーが両方使えて、相互に変換可能な形になっています。これによって Microsoft.Extensions.AI のエコシステムの一部として Semantic Kernel が機能することが可能になっています。今後の広がりが楽しみですね。
次回は「Agent Framework」について書こうと思います。
Discussion