共用方式為


如何使用語意核心將資料內嵌至向量存放區 (預覽)

警告

語意核心向量存放區功能處於預覽狀態,且需要重大變更的改善可能仍會在發行前有限的情況下發生。

本文將示範如何建立應用程式

  1. 從Microsoft Word 檔中的每個段落取得文字
  2. 為每個段落產生內嵌
  3. 將文字、內嵌和原始位置的參考向上插入 Redis 實例。

必要條件

針對此範例,您將需要

  1. 裝載在 Azure 或其他您選擇的提供者中的內嵌產生模型。
  2. Redis 或 Docker Desktop 的實例,讓您可以在本機執行 Redis。
  3. 要剖析和載入的 Word 檔。 以下是一個 zip,其中包含您可以下載及使用的範例 Word 檔: vector-store-data-ingestion-input.zip

設定 Redis

如果您已經有 Redis 實例,則可以使用該實例。 如果您想要在本機測試專案,您可以使用 Docker 輕鬆地啟動 Redis 容器。

docker run -d --name redis-stack -p 6379:6379 -p 8001:8001 redis/redis-stack:latest

若要確認它是否順利執行,請在瀏覽器中流覽 http://localhost:8001/redis-stack/browser

這些指示的其餘部分會假設您使用上述設定使用此容器。

建立專案

建立新的專案,並從 Semantic Kernel 新增 Redis 連接器的 nuget 套件參考、開啟的 xml 套件,以使用 和 OpenAI 連接器從 Semantic Kernel 讀取 Word 檔,以產生內嵌。

dotnet new console --framework net8.0 --name SKVectorIngest
cd SKVectorIngest
dotnet add package Microsoft.SemanticKernel.Connectors.AzureOpenAI
dotnet add package Microsoft.SemanticKernel.Connectors.Redis --prerelease
dotnet add package DocumentFormat.OpenXml

新增資料模型

若要上傳數據,我們必須先描述資料庫中數據應該具有的格式。 我們可以藉由使用描述每個屬性函數的屬性來建立數據模型來執行此動作。

將新檔案新增至名為 TextParagraph.cs 的專案,並將下列模型新增至該專案。

using Microsoft.Extensions.VectorData;

namespace SKVectorIngest;

internal class TextParagraph
{
    /// <summary>A unique key for the text paragraph.</summary>
    [VectorStoreRecordKey]
    public required string Key { get; init; }

    /// <summary>A uri that points at the original location of the document containing the text.</summary>
    [VectorStoreRecordData]
    public required string DocumentUri { get; init; }

    /// <summary>The id of the paragraph from the document containing the text.</summary>
    [VectorStoreRecordData]
    public required string ParagraphId { get; init; }

    /// <summary>The text of the paragraph.</summary>
    [VectorStoreRecordData]
    public required string Text { get; init; }

    /// <summary>The embedding generated from the Text.</summary>
    [VectorStoreRecordVector(1536)]
    public ReadOnlyMemory<float> TextEmbedding { get; set; }
}

請注意,我們會將 值 1536 傳遞至 VectorStoreRecordVectorAttribute。 這是向量的維度大小,必須符合您選擇的內嵌產生器所產生的向量大小。

提示

如需如何標註數據模型以及如何為每個屬性提供哪些其他選項的詳細資訊,請參閱 定義數據模型

讀取檔中的段落

我們需要一些程式代碼來讀取文字檔,並在其中尋找每個段落的文字。

將新檔案新增至名為 DocumentReader.cs 的專案,並新增下列類別,以從檔讀取段落。

using System.Text;
using System.Xml;
using DocumentFormat.OpenXml.Packaging;

namespace SKVectorIngest;

internal class DocumentReader
{
    public static IEnumerable<TextParagraph> ReadParagraphs(Stream documentContents, string documentUri)
    {
        // Open the document.
        using WordprocessingDocument wordDoc = WordprocessingDocument.Open(documentContents, false);
        if (wordDoc.MainDocumentPart == null)
        {
            yield break;
        }

        // Create an XmlDocument to hold the document contents and load the document contents into the XmlDocument.
        XmlDocument xmlDoc = new XmlDocument();
        XmlNamespaceManager nsManager = new XmlNamespaceManager(xmlDoc.NameTable);
        nsManager.AddNamespace("w", "http://schemas.openxmlformats.org/wordprocessingml/2006/main");
        nsManager.AddNamespace("w14", "http://schemas.microsoft.com/office/word/2010/wordml");

        xmlDoc.Load(wordDoc.MainDocumentPart.GetStream());

        // Select all paragraphs in the document and break if none found.
        XmlNodeList? paragraphs = xmlDoc.SelectNodes("//w:p", nsManager);
        if (paragraphs == null)
        {
            yield break;
        }

        // Iterate over each paragraph.
        foreach (XmlNode paragraph in paragraphs)
        {
            // Select all text nodes in the paragraph and continue if none found.
            XmlNodeList? texts = paragraph.SelectNodes(".//w:t", nsManager);
            if (texts == null)
            {
                continue;
            }

            // Combine all non-empty text nodes into a single string.
            var textBuilder = new StringBuilder();
            foreach (XmlNode text in texts)
            {
                if (!string.IsNullOrWhiteSpace(text.InnerText))
                {
                    textBuilder.Append(text.InnerText);
                }
            }

            // Yield a new TextParagraph if the combined text is not empty.
            var combinedText = textBuilder.ToString();
            if (!string.IsNullOrWhiteSpace(combinedText))
            {
                Console.WriteLine("Found paragraph:");
                Console.WriteLine(combinedText);
                Console.WriteLine();

                yield return new TextParagraph
                {
                    Key = Guid.NewGuid().ToString(),
                    DocumentUri = documentUri,
                    ParagraphId = paragraph.Attributes?["w14:paraId"]?.Value ?? string.Empty,
                    Text = combinedText
                };
            }
        }
    }
}

產生內嵌並上傳數據

我們需要一些程式代碼來產生內嵌,並將段落上傳至 Redis。 讓我們在個別類別中執行這項操作。

新增名為 DataUploader.cs 的新檔案,並將下列類別新增至其中。

#pragma warning disable SKEXP0001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.

using Microsoft.Extensions.VectorData;
using Microsoft.SemanticKernel.Embeddings;

namespace SKVectorIngest;

internal class DataUploader(IVectorStore vectorStore, ITextEmbeddingGenerationService textEmbeddingGenerationService)
{
    /// <summary>
    /// Generate an embedding for each text paragraph and upload it to the specified collection.
    /// </summary>
    /// <param name="collectionName">The name of the collection to upload the text paragraphs to.</param>
    /// <param name="textParagraphs">The text paragraphs to upload.</param>
    /// <returns>An async task.</returns>
    public async Task GenerateEmbeddingsAndUpload(string collectionName, IEnumerable<TextParagraph> textParagraphs)
    {
        var collection = vectorStore.GetCollection<string, TextParagraph>(collectionName);
        await collection.CreateCollectionIfNotExistsAsync();

        foreach (var paragraph in textParagraphs)
        {
            // Generate the text embedding.
            Console.WriteLine($"Generating embedding for paragraph: {paragraph.ParagraphId}");
            paragraph.TextEmbedding = await textEmbeddingGenerationService.GenerateEmbeddingAsync(paragraph.Text);

            // Upload the text paragraph.
            Console.WriteLine($"Upserting paragraph: {paragraph.ParagraphId}");
            await collection.UpsertAsync(paragraph);

            Console.WriteLine();
        }
    }
}

組合在一起

最後,我們需要把不同的部分放在一起。 在此範例中,我們將使用語意核心相依性插入容器,但也可以使用任何 IServiceCollection 型容器。

將下列程式代碼新增至檔案 Program.cs 以建立容器、註冊 Redis 向量存放區並註冊內嵌服務。 請務必將文字內嵌產生設定取代為您自己的值。

#pragma warning disable SKEXP0010 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
#pragma warning disable SKEXP0020 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.

using Microsoft.Extensions.DependencyInjection;
using Microsoft.SemanticKernel;
using SKVectorIngest;

// Replace with your values.
var deploymentName = "text-embedding-ada-002";
var endpoint = "https://sksample.openai.azure.com/";
var apiKey = "your-api-key";

// Register Azure Open AI text embedding generation service and Redis vector store.
var builder = Kernel.CreateBuilder()
    .AddAzureOpenAITextEmbeddingGeneration(deploymentName, endpoint, apiKey)
    .AddRedisVectorStore("localhost:6379");

// Register the data uploader.
builder.Services.AddSingleton<DataUploader>();

// Build the kernel and get the data uploader.
var kernel = builder.Build();
var dataUploader = kernel.Services.GetRequiredService<DataUploader>();

最後一個步驟是,我們想要從文字檔讀取段落,並呼叫數據上傳器來產生內嵌並上傳段落。

// Load the data.
var textParagraphs = DocumentReader.ReadParagraphs(
    new FileStream(
        "vector-store-data-ingestion-input.docx",
        FileMode.Open),
    "file:///c:/vector-store-data-ingestion-input.docx");

await dataUploader.GenerateEmbeddingsAndUpload(
    "sk-documentation",
    textParagraphs);

在 Redis 中查看您的數據

流覽至 Redis 堆疊瀏覽器,例如 http://localhost:8001/redis-stack/browser 您現在應該可以看到已上傳段落的位置。 以下是其中一個上傳段落應該看到的範例。

{
    "DocumentUri" : "file:///c:/vector-store-data-ingestion-input.docx",
    "ParagraphId" : "14CA7304",
    "Text" : "Version 1.0+ support across C#, Python, and Java means it’s reliable, committed to non breaking changes. Any existing chat-based APIs are easily expanded to support additional modalities like voice and video.",
    "TextEmbedding" : [...]
}

即將推出

即將推出的進一步指示

即將推出

即將推出的進一步指示