普通と違う感じの Semantic Kernel 入門 001「関数」
はじめに
ここでは Semantic Kernel の自分的な入門を書いていこうと思います。
一般的な入門記事というよりは、私が Semantic Kernel を触ったりコードを読んだり、サンプルコードを読んだり、登壇して発表したりして得た知見をまとめていく感じです。「Semantic Kernel とは」とかについては公式ドキュメントの セマンティック カーネルの概要 を見ていただくか、先日書かれた AI エージェントフレームワーク Semantic Kernel 超入門!! (コード例は Python ですが) などに纏まっているので、そちらを参照してください。
Semanic Kernel の関数
Semantic Kernel で個人的に一番ベースとなる機能は何かと考えたときに浮かんでくるのは「関数」です。
Semantic Kernel の関数は AI を呼び出したり、C# のコードを実行したり、Web API を呼び出したり、MCP のツールを呼び出したりといった様々な処理を行う部分です。
この関数は KernelFunction
というクラスで表現されています。コンソールアプリを作成して Microsoft.SemanticKernel
パッケージをインストールして、以下のようなコードを書いてみましょう。
using Microsoft.SemanticKernel;
// デリゲートをラップした KernelFunction を作成
KernelFunction hello = KernelFunctionFactory.CreateFromMethod((string name) => $"Hello, {name}!");
// 関数に渡す引数を定義
// IDictionary<string, object?> を実装しているため普通の Dictionary のように使える
KernelArguments arguments = new()
{
["name"] = "Kazuki"
};
// 引数を渡して関数を呼び出す
object? result = await hello.InvokeAsync(arguments);
// Hello, Kazuki! が出力されるはず
Console.WriteLine(result);
このコードでは、KernelFunctionFactory.CreateFromMethod
メソッドを使って、デリゲートをラップした KernelFunction
を作成しています。ここでは string
型の引数を受け取り、string
型の結果を返す関数を定義しています。KernelArguments
を使って引数を渡し、InvokeAsync
メソッドで関数を呼び出しています。結果は Hello, Kazuki!
という文字列になります。
InvokeAsync
には Semantic Kernel のコアのサービスの Kernel
を渡すオーバーロードがあります。
どちらかというと Semantic Kernel では、こちらのオーバーロードを使うことが多いです。
以下のように書き換えてみましょう。
using Microsoft.SemanticKernel;
// デリゲートをラップした KernelFunction を作成
KernelFunction hello = KernelFunctionFactory.CreateFromMethod((string name) => $"Hello, {name}!");
// 関数に渡す引数を定義
// IDictionary<string, object?> を実装しているため普通の Dictionary のように使える
KernelArguments arguments = new()
{
["name"] = "Kazuki"
};
// Kernel を作成
var kernel = new Kernel();
// Kernel を渡す場合は FunctionResult が返ってくる
FunctionResult result1 = await hello.InvokeAsync(kernel, arguments);
// GetValue で戻り値を取得
// Hello, Kazuki! が出力される
Console.WriteLine(result1.GetValue<string>());
// Kernel から関数を呼び出すことも可能
FunctionResult result2 = await kernel.InvokeAsync(hello, arguments);
// GetValue で戻り値を取得
// Hello, Kazuki! が出力される
Console.WriteLine(result1.GetValue<string>());
KernelFunctino
の InvokeAsync
を呼ぶ方法と Kernel
の InvokeAsync
を呼ぶ方法の2つがありますが、どちらも同じ結果を得ることができます。Kernel
クラスの InvokeAsync
メソッドは KernelFunction
の InvokeAsync
メソッドを内部で呼び出しているだけです。
ちなみに複数の引数を受け取る関数にも対応しています。その場合は KernelArguments
で複数の引数を定義するだけです。
以下のように書いてみましょう。
using Microsoft.SemanticKernel;
// 複数引数の関数を定義
KernelFunction add = KernelFunctionFactory.CreateFromMethod((int x, int y) => x + y);
// 関数に渡す引数を定義
KernelArguments arguments = new()
{
["x"] = 5,
["y"] = 10
};
// Kernel を作成
var kernel = new Kernel();
// Kernel を渡す場合は FunctionResult が返ってくる
FunctionResult result1 = await add.InvokeAsync(kernel, arguments);
// GetValue で戻り値を取得
// 15 が出力される
Console.WriteLine(result1.GetValue<int>());
関数の引数に指定できるもの
Semantic Kernel の関数の引数に渡す値は KernelArguments
を使って定義します。しかし、その他にもいくつか特別な型を受け取ることが出来ます。
以下のようなコードを書いてみましょう。関数の第一引数に Kernel
を指定しています。
using Microsoft.SemanticKernel;
KernelFunction f = KernelFunctionFactory.CreateFromMethod(
// Kernel を引数に受け取れる
(Kernel kernel, string name) => $"Hello, {name} from {kernel}!");
Kernel kernel = new();
// KernelArguments では name 引数だけを渡す
var result = await f.InvokeAsync(kernel, new KernelArguments
{
["name"] = "Kazuki"
});
// 結果を表示
Console.WriteLine(result.GetValue<string>());
実行すると以下のような結果になります。ちゃんと Kernel
型の引数が渡されていることがわかります。
Hello, Kazuki from Microsoft.SemanticKernel.Kernel!
その他にも Kernel
に登録されているサービスを引数に受け取ることが出来ます。
その場合には Kernel
のサービスから引数を受け取ることを明示するために FromKernelServicesAttribute
を指定します。
Kernel
にサービスを登録するには、一般的なサンプルであるように KernelBuilder
を使ってサービスを登録して Kernel
を作成するか、Kernel
のコンストラクタに IServiceProvider
を渡して作成します。今回は後者の方法でやってみましょう。TimeProvider
をサービスに登録して、それを関数の引数で受け取るようにしたいと思います。
using Microsoft.Extensions.DependencyInjection;
using Microsoft.SemanticKernel;
KernelFunction f = KernelFunctionFactory.CreateFromMethod(
// Kernel と TimeProvider と string を引数に受け取る
(Kernel kernel, [FromKernelServices]TimeProvider timeProvider, string name) =>
$"Hello, {name} from {kernel} at {timeProvider.GetLocalNow()}!");
// TimeProvider を登録した IServiceProvider を作成
var services = new ServiceCollection()
.AddSingleton(TimeProvider.System)
.BuildServiceProvider();
// Kernel を作成するときに IServiceProvider を渡すことができる
var kernel = new Kernel(services);
// KernelArguments では name 引数だけを渡す
var r = await f.InvokeAsync(kernel, new KernelArguments
{
["name"] = "Kazuki"
});
// 結果を表示
Console.WriteLine(r.GetValue<string>());
実行すると以下のような結果になります。TimeProvider
が正しく引数に渡されていることがわかります。
Hello, Kazuki from Microsoft.SemanticKernel.Kernel at 2025/05/26 19:45:24 +09:00!
まとめ
ここでは Semantic Kernel の関数について、基本的な使い方を紹介しました。
ポイントとしてはデリゲートから作られた関数には、KernelArguments
で引数を渡す他に Kernel
を受け取ることが出来たり、Kernel
に登録されているサービスを引数に受け取ることが出来るという点です。この特性があるため、関数でかなり色々なことが出来ます。ここではやりませんでしたが、一般的には Kernel
には AI サービスが登録されています。そのため関数で AI を呼び出すためのサービスを引数として受け取り AI を呼び出すことが出来ます。
AI Agent を作るためのフレームワークの Semantic Kernel の入門の最初で AI を一度も呼び出していませんが次回は AI をよびだすための関数について書いていきたいと思います。
Discussion