📘

Semantic Kernel のスキルを好きにカスタマイズしよう (余談です)

2023/05/01に公開

今回は Semantic Kernel のコードを見ながら色々調べてる過程で見つけたことを書いていきます。普通に Semantic Kernel を使う人にはあまり関係ない内容です。

RegisterCustomFunction で好きな関数を登録

普通やることはないのですが IKernelRegisterCustomFunction を使うことで ISKFunction を登録することが出来ます。普通はネイティブ スキルやセマンティック スキルで十分なのですが、何か特殊なこと (使い方がパッとは思いつかない) をしたい場合に使うことが出来ます。

ISKFunction インターフェースは、普通の C# のインターフェースです。個々のプロパティやメソッドの実装は頑張って調べる必要がありますが、本当に必要最低限動けばいいという場合は以下のように実装することが出来ます。

class MyFunction : ISKFunction
{
    public string Name => "MyFunction";

    public string SkillName => "MySkill";

    public string Description => "To upper case.";

    public bool IsSemantic => false;

    public CompleteRequestSettings RequestSettings => new();

    public FunctionView Describe() => 
        new FunctionView(Name, SkillName, Description, new List<ParameterView>(), IsSemantic, false);

    public Task<SKContext> InvokeAsync(string input, SKContext? context = null, CompleteRequestSettings? settings = null, ILogger? log = null, CancellationToken? cancel = null)
    {
        // ここに好きな処理を書こう
        context ??= new SKContext(new(""), NullMemory.Instance, null, log ?? NullLogger.Instance, cancel ?? default);
        context.Variables.Update(input.ToUpperInvariant());
        return Task.FromResult(context);
    }

    public Task<SKContext> InvokeAsync(SKContext? context = null, CompleteRequestSettings? settings = null, ILogger? log = null, CancellationToken? cancel = null) => 
        InvokeAsync(context?.Variables?.Input ?? "", context, settings, log, cancel);

    public ISKFunction SetAIConfiguration(CompleteRequestSettings settings) => this;

    public ISKFunction SetAIService(Func<ITextCompletion> serviceFactory) => this;

    public ISKFunction SetDefaultSkillCollection(IReadOnlySkillCollection skills) => this;
}

これだけ長い処理を書いてやっていることは入力で渡された文字列を大文字にしているだけです。実際に IKernel に登録して使ってみましょう。

using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.AI.TextCompletion;
using Microsoft.SemanticKernel.Memory;
using Microsoft.SemanticKernel.Orchestration;
using Microsoft.SemanticKernel.SkillDefinition;

var kernel = Kernel.Builder
    .WithLogger(LoggerFactory.Create(b =>
    {
        b.AddFilter(_ => true);
        b.AddConsole();
    }).CreateLogger<Program>())
    .Build();

kernel.RegisterCustomFunction("MySkill", new MyFunction());

// hello world を大文字にする
var result = await kernel.RunAsync("hello world", kernel.Func("MySkill", "MyFunction"));
Console.WriteLine(result);

実行結果は、これだけ仰々しいコードを書いたにも拘らず以下のようになります…。

HELLO WORLD

因みにネイティブ スキルで同じものを実装すると以下のようになります。本当にこの処理であれば ISKFunction を実装する理由は 1 ミリもありません。

using Microsoft.Extensions.Logging;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Orchestration;
using Microsoft.SemanticKernel.SkillDefinition;

var kernel = Kernel.Builder
    .WithLogger(LoggerFactory.Create(b =>
    {
        b.AddFilter(_ => true);
        b.AddConsole();
    }).CreateLogger<Program>())
    .Build();

kernel.ImportSkill(new MySkill(), nameof(MySkill));

// hello world を大文字にする
var result = await kernel.RunAsync("hello world", kernel.Func("MySkill", "MyFunction"));
Console.WriteLine(result);

class MySkill
{
    [SKFunction("To upper.")]
    public string MyFunction(string input) => input.ToUpperInvariant();
}

さらに、文字列を大文字にするといったような、皆が使いたくなるようなスキルは Microsoft.SemanticKernel.CoreSkills 名前空間に TextSkill というクラスがあって、そこで定義されています。それを使うと、以下のようになります。

using Microsoft.Extensions.Logging;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.CoreSkills;

var kernel = Kernel.Builder
    .WithLogger(LoggerFactory.Create(b =>
    {
        b.AddFilter(_ => true);
        b.AddConsole();
    }).CreateLogger<Program>())
    .Build();

kernel.ImportSkill(new TextSkill(), nameof(TextSkill));

// hello world を大文字にする
var result = await kernel.RunAsync("hello world", kernel.Func(nameof(TextSkill), "Uppercase"));
Console.WriteLine(result);

まとめ

ということで、Semantic Kernel にどんな機能があるかコード見てたら見つけたので書いてみました。今回は IKernelRegisterCustomFunction で好きな関数を登録する方法を紹介しました。これを使うことで、好きな処理を好きなように実装することが出来ます。ただ、ネイティブ スキルやセマンティック スキルで十分な場合がほとんどなので、そちらを使うことをお勧めします。

もし、自分の開発しているサービスを Semantic Kernel に接続したい!!といったようなケースでは選択肢に入るかもしれませんが実は Microsoft.SemanticKernel.Skills.OpenAPI というパッケージがあって OpenAPI 形式の API の定義があれば Semantic Kernel のスキルとして登録できるような機能もあるので、それをベースに実装するのが良いと思います。

ということで、本当にユースケースのない機能紹介でした。

Microsoft (有志)

Discussion