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.Core
v1.52.0-alpha -
Microsoft.SemanticKernel.Process.LocalRuntime
v1.52.0-alpha -
Microsoft.SemanticKernel
v1.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