🐙

Semantic Kernel の 0.17.230626.1-preview のネイティブ関数の引数に関する新機能

2023/06/29に公開

今朝出たみたいです。
ここ何個かのバージョンアップはスルーしてきたのですが、個人的に今回のバージョンアップで改善された、ネイティブ関数で複数のパラメーターが受け取れるようになった改善が気になったので取り上げてみます。

ネイティブ関数に複数の引数を渡せるようになった

今まで複数のパラメーターを受けたい場合は SKContextVariablesSet する必要がありました。
しかも文字列型である必要があります。

今回のアップデートでは、ネイティブ関数は以下のように普通の関数を定義して属性を足すだけで大丈夫になりました。
これは便利!

class MyPlugin
{
    [SKFunction]
    [Description("入力文字列を times 回繰り返した文字列にして返す。")]
    public string Join(
        [Description("元になる入力文字列")]string input, 
        [Description("繰り返し回数")]int times = 1,
        [Description("区切り文字列")]string separator = ",")
    {
        return string.Join(separator, Enumerable.Range(0, times).Select(_ => input));
    }
}

しかも、ソースを確認すると TypeConverter を使って文字列と相互変換可能な型であればなんでもいけそうでした。なので、例えば以下のようにすることで引数をオブジェクト型にすることが出来ます。

class MyPlugin
{
    [SKFunction]
    [Description("入力文字列を times 回繰り返した文字列にして返す。")]
    public string Join(
        [Description("元になる入力文字列")] string input,
        [Description("プロパティに区切り文字を表す Separator と繰り返し回数を表す Times を持った JSON 形式の文字列")]Param param)
    {
        return string.Join(param.Separator, Enumerable.Range(0, param.Times).Select(_ => input));
    }
}

[TypeConverter(typeof(ParamTypeConverter))]
class Param
{
    public int Times { get; set; } = 1;
    public string Separator { get; set; } = ",";
}

class ParamTypeConverter : TypeConverter
{
    public override bool CanConvertTo(ITypeDescriptorContext? context, [NotNullWhen(true)] Type? destinationType)
    {
        return destinationType == typeof(string);
    }

    public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType)
    {
        if (destinationType == typeof(string))
        {
            var param = (Param)value!;
            return JsonSerializer.Serialize(param);
        }

        return base.ConvertTo(context, culture, value, destinationType);
    }

    public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType)
    {
        return sourceType == typeof(string);
    }

    public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value)
    {
        if (value is string str)
        {
            return JsonSerializer.Deserialize<Param>(str);
        }

        return base.ConvertFrom(context, culture, value);
    }
}

パラメーターを渡すときは TypeConverter が期待する形の文字列(今回の例では JSON) を渡す感じになります。例えば以下のようになります。

using Azure.Identity;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.SkillDefinition;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Text.Json;

var kernel = Kernel.Builder
    .WithAzureChatCompletionService("gpt-35-turboのデプロイ名",
    "https://リソース名.openai.azure.com/",
    new AzureCliCredential())
    .Build();

kernel.ImportSkill(new MyPlugin(), nameof(MyPlugin));
var context = kernel.CreateNewContext();
context.Variables.Update("井上");
context.Variables.Set("param", """
    {
        "Times": 3,
        "Separator": ","
    }
    """);
var result = await kernel.Func("MyPlugin", "Join").InvokeAsync(context);
Console.WriteLine(result.Result); // 井上,井上,井上

まとめ

こういう細かい改善系大好きなので紹介してみました。
他にも気になる機能があるので触ったタイミングで記事にしていきたいと思います。

Microsoft (有志)

Discussion