Semantic Kernel の Process Framework 入門 その 1「Hello world」
今日から Semantic Kernel の Process Framework について入門していきたいと思います。
これは入門するにあたってやったことのメモであって、決して入門記事ではありませんが、読むことで私のやったこと思ったことを追体験出来ると思います。
あと、Process Framework は alpha 版で、これからどんどん変わっていくと思いますので、あくまで参考程度にしてください。
Process Framework とは
Semantic Kernel の Process Framework はワークフローをいい感じに定義して実行できるフレームワークみたいです。
ワークフローは、まだ理解できていませんが柔軟に定義できて、さらにローカルで実行したりクラウドで実行したりすることも簡単にできるみたいです。さらに、分散処理もできるみたいです。凄いですね。Durable Functions みたい。
まずは Hello world から
まずは、Hello world から始めてみます。
Semantic Kernel の Process Framework を使うには、まずは NuGet からインストールします。
-
Microsoft.SemanticKernel.Process.Corev1.52.0-alpha -
Microsoft.SemanticKernel.Process.LocalRuntimev1.52.0-alpha -
Microsoft.SemanticKernelv1.52.0
最初の 2 つが Process Framework のコアとローカルランタイムです。
Process Framework を使うために最低限定義しないといけないものは Step と Process です。
Step は処理の単位で、Process はその集合です。Process には Step 間のデータの流れなどを定義して色々な処理を組むことが出来るようになっています。
まずは Step がないと始まらないので、Step を定義します。今回は以下の 3 つの Step を定義します。
-
Step1:nameを受け取ってHello {name}を返す -
Step2: 文字列を受け取って!を末尾に追加して返す -
Step3: 文字列を受け取って標準出力に出力する
Step1 から Step3 までが実行されると、Hello {name}! が出力されるイメージです。では Step1 から Step3 を定義していきます。各 Step は KernelProcessStep を継承して定義します。そして、その中に KernelFunction を定義します。複数の関数を定義することもできるみたいですが、ややこしくなるみたいなので、今回は必要最低限の 1 つだけ定義します。
#pragma warning disable SKEXP0080
using Microsoft.SemanticKernel;
namespace ConsoleApp18;
class Step1 : KernelProcessStep
{
[KernelFunction]
public string Execute(string input) => $"Hello {input}";
}
class Step2 : KernelProcessStep
{
[KernelFunction]
public string Execute(string input) => $"{input}!";
}
class Step3 : KernelProcessStep
{
[KernelFunction]
public void Execute(string input) => Console.WriteLine(input);
}
これで Step が出来ました。次は Process を定義します。Process を定義するには ProcessBuilder を使います。ProcessBuilder クラスの AddStepFromType<T>() メソッドを使って Step を追加していきます。AddStepFromType<T>() の戻り値は ProcessStepBuilder という型で、これを使って Step 間のデータの流れを定義していきます。
まず、Step を追加する部分までコードを書きます。
#pragma warning disable SKEXP0080
using ConsoleApp18;
using Microsoft.SemanticKernel;
var processBuilder = new ProcessBuilder("Sample");
var step1 = processBuilder.AddStepFromType<Step1>();
var step2 = processBuilder.AddStepFromType<Step2>();
var step3 = processBuilder.AddStepFromType<Step3>();
ProcessBuilder のコンストラクタには Process の名前を指定します。AddStepFromType<T>() メソッドには Step の型を指定します。これで Process に 3 つの Step が追加されました。このままだと、まだ Step が 3 つあるだけで、どの順番で実行するのかといったことがさっぱりわかりません。この後で処理の流れを定義していきます。
まず最初に Process の最初に実行する Step を定義します。
これは ProcessBuilder の OnInputEvent() メソッドを使います。OnInputEvent() メソッドの引数には、イベントの名前を指定します。この名前は何でも良くて、後で Process を実行する時に指定するイベントの名前になります。そして、OnInputEvent に続けて SendEventTo() メソッドを使って次に実行する関数を指定します。
今回は Step 内に 1 つの関数しかないので単純に ProcessFunctionTargetBuilder に ProcessStepBuilder を渡して作成するという形になります。
ProcessStepBuilder にもイベントが起きた時に次に実行する Step を指定することが出来ます。今回は純粋に KernelFunction の呼び出しが完了したあとに次の Step を実行するようにします。その場合は OnFunctionResult() メソッドを使います。OnFunctionResult() に続いて SendEventTo() メソッドを使って次に実行する Step を指定します。
ということで Process に Start という名前のイベントが来たら Step1 から Step3 まで順番に呼ばれるプロセスは以下のような定義になります。
processBuilder
.OnInputEvent("Start")
.SendEventTo(new ProcessFunctionTargetBuilder(step1));
step1.OnFunctionResult()
.SendEventTo(new ProcessFunctionTargetBuilder(step2));
step2.OnFunctionResult()
.SendEventTo(new ProcessFunctionTargetBuilder(step3));
ここまで出来たらあとは Process を作成して実行するだけです。Process は ProcessBuilder の Build() メソッドを使って作成します。Process を実行するには Process の StartAsync() メソッドを使います。StartAsync() メソッドの引数には Kernel と KernelProcessEvent を指定します。Kernel は Semantic Kernel の Kernel です。KernelProcessEvent は Id にイベントの名前を指定して、Data に Step1 に渡すデータを指定します。
それでは Process を作成して Start イベントを指定して実行してみましょう。データは Kazuki Ota を渡してみます。
var process = processBuilder.Build();
await process.StartAsync(
// 今回は Kernel は特に使わないので空っぽの Kernel を渡す
new Kernel(),
new KernelProcessEvent { Id = "Start", Data = "Kazuki Ota" });
ここまでのコードを実行すると以下のような結果になります。
Hello Kazuki Ota!
ちゃんと Step1 から Step3 まで実行されて Hello Kazuki Ota! が出力されました。これで Hello world は完成です。
作成された Process の確認
Process には ToMermaid() というメソッドがあります。これを使うと定義した Process を Mermaid 形式で出力してくれます。これを使うと定義した Process がどのようなものかを確認することが出来ます。早速、先ほどのコードの最後に Mermaid を出力するコードを追加してみます。
Console.WriteLine(process.ToMermaid());
これを実行すると以下のような Mermaid 形式の文字列が出力されます。
flowchart LR
Start["Start"]
End["End"]
Step1["Step1"]
Step1["Step1"] --> Step2["Step2"]
Step2["Step2"]
Step2["Step2"] --> Step3["Step3"]
Step3["Step3"]
Start --> Step1["Step1"]
Step3["Step3"] --> End
図として表示すると以下のようになります。開始が Start で、Step1 から Step3 まで順番に実行されて終了する Process になっています。
まとめ
Semantic Kernel の Process Framework を使って Hello world を実行してみました。Step と Process を定義して、Process を実行するところまでやってみました。Process の定義はまだまだ色々なことが出来るみたいなので、次回はもう少し詳しく見ていきたいと思います。
最後に、今日のコードの全体を載せておきます。
#pragma warning disable SKEXP0080
using Microsoft.SemanticKernel;
namespace ConsoleApp18;
class Step1 : KernelProcessStep
{
[KernelFunction]
public string Execute(string input) => $"Hello {input}";
}
class Step2 : KernelProcessStep
{
[KernelFunction]
public string Execute(string input) => $"{input}!";
}
class Step3 : KernelProcessStep
{
[KernelFunction]
public void Execute(string input) => Console.WriteLine(input);
}
#pragma warning disable SKEXP0080
using ConsoleApp18;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Process.Tools;
var processBuilder = new ProcessBuilder("Sample");
var step1 = processBuilder.AddStepFromType<Step1>();
var step2 = processBuilder.AddStepFromType<Step2>();
var step3 = processBuilder.AddStepFromType<Step3>();
processBuilder
.OnInputEvent("Start")
.SendEventTo(new ProcessFunctionTargetBuilder(step1));
step1.OnFunctionResult()
.SendEventTo(new ProcessFunctionTargetBuilder(step2));
step2.OnFunctionResult()
.SendEventTo(new ProcessFunctionTargetBuilder(step3));
var process = processBuilder.Build();
await process.StartAsync(new Kernel(), new KernelProcessEvent { Id = "Start", Data = "Kazuki Ota" });
Console.WriteLine(process.ToMermaid());
Discussion