🧙‍♂️

Semantic Kernelを使ってC#でAI (4)

に公開

複数のモデルやサービスを切り替えて使う

今回、実は表題の通りです。
複数のサービスを使い分けるという話。

普段使いをAzure OpenAIのGPT-4o-miniにしておいて、いざというときにminiじゃないものを使う、とか。
4.1使うとか。
はたまた普段はローカルのOllama使って近々のデータで精度上げたいときにGPT使うとか。
そういうののやり方の話。

注意

この記事は Semantic Kernel が 1.48.0 の際に書いたものです。
Semantic Kernel は更新速度が非常に早く、記載の表記から変更されていることが非常によくあります。
実際にコーディングする際には確認の上、作成してください。

DIで複数サービス登録

いずれもChatで使うことを考えると、DIで出てくるものは IChatCompletion で受け取るわけです。
とすると、1つしか登録できない、という具合になるわけなのですが。
それを避ける方法は.Net8から既にあります。
Keyed Services です。

要するに名前付きで登録しておいて、引っ張ってくるときにも名前で引っ張ってくるわけです。

複数サービス登録するプログラム

サンプルは こちら

DI登録している部分は Sample04Extension.cs になります。

Sample04Extension.cs
using Microsoft.SemanticKernel;

namespace Sample04
{
    public static class Sample04Extension
    {
        public const string SERVICEKEY_OPENAI = "ChatGPT";
        public const string SERVICEKEY_GEMMA = "Gemma";
        public const string SERVICEKEY_PHI = "Phi";

        public static IServiceCollection AddAI(this IServiceCollection services)
        {
            var ollamaUri = new Uri("http://localhost:11434");

#pragma warning disable SKEXP0070 // 種類は、評価の目的でのみ提供されています。将来の更新で変更または削除されることがあります。続行するには、この診断を非表示にします。
            services
                .AddKernel()
                .AddAzureOpenAIChatCompletion(
                    Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT") ?? string.Empty,
                    Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? string.Empty,
                    Environment.GetEnvironmentVariable("AZURE_OPENAI_API_KEY") ?? string.Empty,
                    SERVICEKEY_OPENAI,
                    apiVersion: Environment.GetEnvironmentVariable("AZURE_OPENAI_API_VERSION") ?? string.Empty)
                .AddOllamaChatCompletion(
                    Environment.GetEnvironmentVariable("OLLAMA_MODEL_GEMMA") ?? string.Empty,
                    ollamaUri,
                    SERVICEKEY_GEMMA)
                .AddOllamaChatCompletion(
                    Environment.GetEnvironmentVariable("OLLAMA_MODEL_PHI4") ?? string.Empty,
                    ollamaUri,
                    SERVICEKEY_PHI)
                ;
#pragma warning restore SKEXP0070 // 種類は、評価の目的でのみ提供されています。将来の更新で変更または削除されることがあります。続行するには、この診断を非表示にします。

            return services;
        }
    }
}

Add~ChatCompletion の引数で、 serviceId の引数がDIでのキーになります。
上記ソースでは、 SERVICEKEY_OPENAI SERVICEKEY_GEMMA SERVICEKEY_PHI の部分ですね。
そして、引っ張ってくる時には

OpenAI.cshtml.cs
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.SemanticKernel.ChatCompletion;

namespace Sample04.Pages
{
    public class OpenAIModel : AIResultPageModel
    {
        public OpenAIModel(
            [FromKeyedServices(Sample04Extension.SERVICEKEY_OPENAI)] IChatCompletionService chatCompletion,
            IMemoryCache cache
            )
            : base(chatCompletion, cache, Sample04Extension.SERVICEKEY_OPENAI)
        {
        }
    }
}

のように、 [FromKeyedServices()] をつけてキーを指定します。

まとめ

Semantic KernelのDIへの Add~ 系の関数には serviceId の指定がありますので、複数切り替えで使用する際にはこちらのやり方で指定することが可能です。

かといって、TextEmbeddingを切り替えることはないとは思いますが…。

Discussion