normalian blog

Let's talk about Microsoft Azure, ASP.NET and Java!

Semantic Kernel/C# で Azure Cognitive Search にベクトル化データを保存する(成功編

今回は掲題のテーマを試してみたいと思います。実は以下の記事にて試した結果、上手く動きませんでした。
normalian.hatenablog.com

以前試した際は Semantic Kernel 側が Azure Cognitive Search のコネクタ対し未実装だったので結果が返ってこないという悲しい自体でした。しかし、以下の記事の様に無事実装がされたようなのでさっそく試してみました。
devblogs.microsoft.com

事前準備

以下のリソースを事前に作成してください。

  • Azure OpenAI リソース、text-davinci-003、text-embedding-ada-002 モデルのデプロイメント
  • Azure Cognitive Search(今回は Japan East を利用

次に Visual StudioC# のプロジェクトを作成作成します。作成したプロジェクトに NuGet でのライブラリインストールします。Visual Studio で NuGet の画面を開き、以下の Microsoft.SemanticKernel と Microsoft.SemanticKernel.Connectors.Memory.AzureCognitiveSearch の二つをインストールします。この際に "Include prerelease" にチェックを入れるのを忘れないようにしてください。

ソースコードの作成

C#ソースコードを記載します。私が今回利用したものは以下となります。

using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Connectors.Memory.AzureCognitiveSearch;
using Microsoft.SemanticKernel.Memory;
using Microsoft.SemanticKernel.Text;
using System.Reflection;
using System.Security.Cryptography;

namespace MyOpenAITest
{
    class Program
    {
        public static async Task Main(string[] args)
        {
            // Azure OpenAI parameters
            const string deploymentName_chat = "text-davinci-003";
            const string endpoint = "https://your-azureopenai-endpoint.openai.azure.com/";
            const string openAiKey = "your-azureopenai-key";
            const string deploymentName_embedding = "text-embedding-ada-002";

            const string azuresearchEndpoint = "https://your-azuresearch-endpoint.search.windows.net";
            const string azuresearchKey = "your-azuresearch-key";
            const string azuresearchIndexname = "SKGitHub";

            Console.WriteLine("== Start Applicaiton ==");

            var builder = new KernelBuilder();
            builder.WithAzureTextCompletionService(
                     deploymentName_chat,
                     endpoint,
                     openAiKey);
            builder.WithAzureTextEmbeddingGenerationService(
                deploymentName_embedding,
                endpoint,
                openAiKey);
            builder.WithMemoryStorage(new AzureCognitiveSearchMemoryStore(azuresearchEndpoint, azuresearchKey));

            var githubFiles = new Dictionary<string, string>()
            {
                ["https://github.com/microsoft/semantic-kernel/blob/main/README.md"]
                    = "README: Installation, getting started, and how to contribute",
                ["https://github.com/microsoft/semantic-kernel/blob/main/samples/notebooks/dotnet/02-running-prompts-from-file.ipynb"]
                    = "Jupyter notebook describing how to pass prompts from a file to a semantic skill or function",
                ["https://github.com/microsoft/semantic-kernel/blob/main/samples/notebooks/dotnet/00-getting-started.ipynb"]
                    = "Jupyter notebook describing how to get started with the Semantic Kernel",
                ["https://github.com/microsoft/semantic-kernel/tree/main/samples/skills/ChatSkill/ChatGPT"]
                    = "Sample demonstrating how to create a chat skill interfacing with ChatGPT",
                ["https://github.com/microsoft/semantic-kernel/blob/main/dotnet/src/SemanticKernel/Memory/Volatile/VolatileMemoryStore.cs"]
                    = "C# class that defines a volatile embedding store",
                ["https://github.com/microsoft/semantic-kernel/tree/main/samples/dotnet/KernelHttpServer/README.md"]
                    = "README: How to set up a Semantic Kernel Service API using Azure Function Runtime v4",
                ["https://github.com/microsoft/semantic-kernel/tree/main/samples/apps/chat-summary-webapp-react/README.md"]
                    = "README: README associated with a sample starter react-based chat summary webapp",
            };

            var kernel = builder.Build();
            Console.WriteLine("Adding some GitHub file URLs and their descriptions to a volatile Semantic Memory.");
            var i = 0;
            foreach (var entry in githubFiles)
            {
                await kernel.Memory.SaveReferenceAsync(
                    collection: azuresearchIndexname,
                    description: entry.Value,
                    text: entry.Value,
                    externalId: entry.Key,
                    externalSourceName: "GitHub"
                );
                Console.WriteLine($"  URL {++i} saved");
            }

            string ask = "I love Jupyter notebooks, how should I get started?";
            Console.WriteLine("===========================\n" +
                                "Query: " + ask + "\n");

            //From here, search with vector data store
            var memories = kernel.Memory.SearchAsync(azuresearchIndexname, ask, limit: 5, minRelevanceScore: 0.77);

            i = 0;
            await foreach (MemoryQueryResult memory in memories)
            {
                Console.WriteLine($"Result {++i}:");
                Console.WriteLine("  URL:     : " + memory.Metadata.Id);
                Console.WriteLine("  Title    : " + memory.Metadata.Description);
                Console.WriteLine("  Relevance: " + memory.Relevance);
                Console.WriteLine();
            }
        }
    }
}

アプリケーションの実行

実行すると以下のメッセージが返ってきました。 "Result 1:" が返ってきて、ちゃんと結果も記載されています。

Azure ポータル側でインデックスを確認すると、以下の様にドキュメントデータが保存されていることが分かります。

どの様なフィールドが作成されるかについては、以下のソースコードを参照すると理解が早い認識です。
semantic-kernel/dotnet/src/Connectors/Connectors.Memory.AzureCognitiveSearch/AzureCognitiveSearchMemoryRecord.cs at main · microsoft/semantic-kernel · GitHub

何とか無事に検索できることは分かりましたが、以下のソースコードを眺めている限り Azure Cognitive Search で利用可能なはずの Semantic Search を有効化するポイントがなく、どうやら実装を見送ったことが分かります。2023年7月時点でようやく Semantic Kernel で Azure Cognitive Search ベクトルデータストアとして活用することができるようになりましたが、急激に発展を遂げるこの分野、まだまだ目が離せないようです。
github.com