Semantic Kernelを使用してAzure Cognitive Searchを活用できるプラグインを作成してみた

MicrosoftからSemantic Kernelが提供されています。

これまでに作成した下記記事の内容について、Semantic Kernelを使用してAzure Cognitive Searchに接続し、同様のことが実現できるプラグインを作成できるのか試してみました。

Azure OpenAIのChatGPT APIとGPT-4を利用してFAQチャットボットのようなシステムを作れないか再検証してみた - JBS Tech Blog

具体的には、下記の画像のようなアウトプットを取得できるのか試してみました。

Semantic Kernelを使用すると、GPTを便利にアプリ開発に活用することができます。

Semantic Kernelの詳細については、下記の記事を参考にしてください。

作業概要

作業は下記の手順で行いました。

  • コンソールアプリを作成
  • ネイティブプラグインを作成
  • セマンティックプラグインを作成
  • セマンティックプラグインとネイティブプラグインをディレクトリに配置
  • コンソールアプリを実行

ネイティブプラグインとセマンティックプラグインの作成方法は下記のサイトを参考にしました。

How to write native functions in Semantic Kernel | Microsoft Learn

これまでは、ネイティブスキル、セマンティックスキルという名称で呼ばれていました。(その為、ソースコードのコメントやファイル名にスキルと一部記載があります。)

今後はプラグインという名称に変更されるので、本記事でもネイティブプラグイン、セマンティックプラグインと記載します。

※本記事で掲載しているネイティブプラグイン、セマンティックプラグインは私が独自に考えたプラグインであり、Microsoftなどが公開しているわけではありません。

作業手順

コンソールアプリを作成

Visual Studioを使用してコンソールアプリを作成しました。

NuGetパッケージマネージャーで下記2つのライブラリをインストールします。

ネイティブプラグインを作成

作成したネイティブプラグインは下記です。

Azure Cognitive Searchに接続し、キーワード検索で上位5件のデータを取得するSearchプラグインを作成しました。

※Azure Cognitive Searchをセマンティックメモリとして使用する下記のサンプルがあるので、もしかしたらAzure.Search.Documentsを使用しなくても接続できるのかもしれません。

semantic-kernel/Example14_SemanticMemory.cs at main · microsoft/semantic-kernel · GitHub

using System.Text.Json.Serialization;

using Azure;
using Azure.Search.Documents;
using Azure.Search.Documents.Models;

using Microsoft.SemanticKernel.SkillDefinition;

namespace MySkillsDirectory;

public class MyCSharpSkill
{
	public string keyword = "";

	public static string AZURE_COGNITIVE_SEARCH_KEY = "**************************************";
	public static string AZURE_COGNITIVE_SEARCH_NAME = "*****************";
	public static string AZURE_COGNITIVE_SEARCH_INDEX = "***************";

    [SKFunction("ユーザーの質問内容をAzure Cognitive Searchで検索するスキル")]
    public string Search(string input)
    {
        SearchClient SRCHCLIENT = CreateSearchClientForQueries(AZURE_COGNITIVE_SEARCH_INDEX);
        SearchOptions options = new SearchOptions()
        {
            IncludeTotalCount = true,
            OrderBy = { "" },
            Size = 5
        };

        SearchResults<Getazuresearch> response = SRCHCLIENT.Search<Getazuresearch>(input, options);

        var lists = response.GetResults().ToList();

        List<string> result = new List<string>();

        foreach (var i in lists)
        {
            var SampleAnswer = i.Document.SampleAnswer;

            result.Add("SampleAnswer:" + SampleAnswer + "\n");
        }

        result.Add("question:" + input + "\n");

        return string.Join(",", result);
    }

    private static SearchClient CreateSearchClientForQueries(string indexName)
    {
        string searchServiceEndPoint = $"https://{AZURE_COGNITIVE_SEARCH_NAME}.search.windows.net/";
        string queryApiKey = AZURE_COGNITIVE_SEARCH_KEY;

        SearchClient searchClient = new SearchClient(new Uri(searchServiceEndPoint), indexName, new AzureKeyCredential(queryApiKey));
        return searchClient;
    }

    public class Getazuresearch
    {

        [JsonPropertyName("SampleID")]
        public string SampleID { get; set; }

        [JsonPropertyName("SampleQuestion")]
        public string SampleQuestion { get; set; }

        [JsonPropertyName("SampleAnswer")]
        public string SampleAnswer { get; set; }

    }
}

セマンティックプラグインを作成

skprompt.txtは下記の内容で作成しました。

※そろそろ下記のプロンプトは修正した方が良いと思っているのですが、、これまでの流れから今回も採用しました。

あなたはFAQチャットボットです。
次の文章を読んで、質問に答えて下さい。
文章が質問への回答として正解であればY、正解でなければNをanswerに出力して下さい。
文章が質問への回答として適切な内容であればOK、適切な内容でなければNGをcheckに出力して下さい。
質問にはOK、NG、Y、Nしか回答できません。
回答と信頼性スコアはフォーマットに記載された形式で出力して下さい。
信頼性スコアは整数のみ出力して下さい。
文章が複数であった場合は、それぞれの文章について上記を考慮して回答して下さい。
段階的に、論理的に考えて下さい。

Desired format: """
answer:Y or N
check:OK or NG
reason:answer
score:reliability score
"""

input={{$input}}

config.jsonは下記を設定しました。

{
  "schema": 1,
  "type": "completion",
  "description": "Azure Cognitive Searchの検索結果を分析するスキル",
  "completion": {
    "max_tokens": 4000,
    "temperature": 0.5,
    "top_p": 0.95,
    "presence_penalty": 0.0,
    "frequency_penalty": 0.0
  },
  "input": {
    "parameters": [
      {
        "name": "input",
        "description": "Azure Cognitive Searchの検索結果",
        "defaultValue": ""
      }
    ]
  }
}

セマンティックプラグインとネイティブプラグインをディレクトリに配置

コンソールアプリのディレクトリに配置します。

今回は下記のように配置しましたが、ディレクトリの名称等は任意に設定できます。

Program.csに下記を記載しました。

using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Orchestration;
using Microsoft.SemanticKernel.Planning;

using MySkillsDirectory;

Console.OutputEncoding = System.Text.Encoding.UTF8;  //コンソールアプリの出力を文字化けさせない為に設定

string AZURE_OPENAI_MODEL = "****************";
string AZURE_OPENAI_URL = "https://**************.openai.azure.com/";
string AZURE_OPENAI_KEY = "***********************";

var kernel = new KernelBuilder().Build();

kernel.Config.AddAzureChatCompletionService(AZURE_OPENAI_MODEL, AZURE_OPENAI_URL, AZURE_OPENAI_KEY);

var planner = new SequentialPlanner(kernel);

// 実行ファイルのパスを取得
string exePath = AppDomain.CurrentDomain.BaseDirectory;

// プロジェクトの親ディレクトリを取得
string parentDirectory = Directory.GetParent(exePath).Parent.Parent.Parent.FullName;

// プロジェクトの親ディレクトリからの相対パスを作成
string skillsDirectory = Path.Combine(parentDirectory, "MySkillsDirectory");  

var mySemSkill = kernel.ImportSemanticSkillFromDirectory(skillsDirectory, "MySemanticSkill");
kernel.ImportSkill(new MyCSharpSkill(), "MyCSharpSkill");


Plan plan = await planner.CreatePlanAsync("母子手帳を受け取る方法について検索して分析してください。");

SKContext myOutput = await kernel.RunAsync(plan);

Console.WriteLine(myOutput);

コンソールアプリを実行

実行結果です。

概ね期待通りの結果が出力されました。

SequentialPlannerを使用して実行しているのですが、質問の内容を理解してプラグインを順番通りに実行できてます。

"母子手帳を受け取る方法について検索して分析してください。"という質問に対して、最初にAzure Cognitive Searchで検索するプラグインが実行されてます。

2番目にAzure Cognitive Searchの検索結果を分析するプラグインが実行されてます。

ちなみに、”母子手帳を受け取りたいのですが手続きを教えて下さい。”という質問ではプラグインは実行されませんでした。

検索や分析という用語を含めて質問すると、Plannerが理解して必要なプラグインを実行してくれるようです。

まとめ

Semantic Kernelを使用して、Azure Cognitive Searchを活用するプラグインを作成できることが確認できました。

今回はセマンティックプラグインは自然な文章をGPTに判断させて動く仕組みにしましたが、コンテキストを使用すれば具体的な変数名などを設定してより明確に処理を記述することもできます。

ネイティブプラグインともコンテキストを使用して変数などをやりとりできるのですが、あまりやりすぎると特定のプラグインからしか利用できない汎用性のないプラグインになってしまうので、この辺はプラグインの目的を考慮して対応していく必要がありますね。

※コンテキストで変数を使用するネイティブプラグインのコードとセマンティックプラグインのサンプル。

userやhistoryをコンテキストに設定すれば任意の文章に変数を設定できる。

var context = kernel.CreateNewContext();

var history = "";
context["history"] = history;
ChatBot can have a conversation with you about any topic.
It can give explicit instructions or say 'I don't know' if it does not have an answer.

{{$history}}
User: {{$userInput}}
ChatBot:

Azure Cognitive Searchにはベクトル検索機能も追加されるので、今後ますます便利に活用することができそうです。

Microsoft Build 2023でも発表されましたが、プラグインはChatGPTだけでなくCopilotやBingなどでも活用されていくようです。

Semantic Kernelでも簡単に活用できるようで、GitHubにはそれらしいサンプルが既に掲載されています。

※ChatGPT Pluginsで利用されているKlarnaプラグインを使用するサンプルです。

semantic-kernel/Example21_ChatGPTPlugins.cs at main · microsoft/semantic-kernel · GitHub

ChatGPTやCopilotで使用するプラグインと、Semantic Kernelで作成したネイティブプラグイン、セマンティックプラグインをどのように連携するのかは、社内ナレッジなどを活用していく上でも今後の課題になると思います。

執筆担当者プロフィール
株木 誠

株木 誠

先端技術部の株木です。 Azure OpenAI Service を活用するアプリ開発を担当しています。

担当記事一覧