Udostępnij za pośrednictwem


Jak pozyskiwać dane do magazynu wektorów przy użyciu jądra semantycznego (wersja zapoznawcza)

Ostrzeżenie

Funkcja semantycznego magazynu wektorów jądra jest dostępna w wersji zapoznawczej, a ulepszenia wymagające zmian powodujących niezgodność mogą nadal występować w ograniczonych okolicznościach przed wydaniem.

W tym artykule pokazano, jak utworzyć aplikację

  1. Wykonywanie tekstu z każdego akapitu w dokumencie programu Microsoft Word
  2. Generowanie osadzania dla każdego akapitu
  3. Upsert tekst, osadzanie i odwołanie do oryginalnej lokalizacji w wystąpieniu usługi Redis.

Wymagania wstępne

W przypadku tego przykładu będziesz potrzebować

  1. Model generacji osadzania hostowany na platformie Azure lub innego wybranego dostawcy.
  2. Wystąpienie usługi Redis lub docker Desktop, dzięki czemu można uruchomić usługę Redis lokalnie.
  3. Dokument programu Word do analizowania i ładowania. Oto plik zip zawierający przykładowy dokument programu Word, który można pobrać i użyć: vector-store-data-ingestion-input.zip.

Konfigurowanie usługi Redis

Jeśli masz już wystąpienie usługi Redis, możesz go użyć. Jeśli wolisz przetestować projekt lokalnie, możesz łatwo uruchomić kontener Redis przy użyciu platformy Docker.

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

Aby sprawdzić, czy działa pomyślnie, odwiedź przeglądarkę http://localhost:8001/redis-stack/browser .

W pozostałych instrukcjach przyjęto założenie, że używasz tego kontenera przy użyciu powyższych ustawień.

Tworzenie projektu

Utwórz nowy projekt i dodaj odwołania do pakietu nuget dla łącznika Redis z jądra semantycznego, otwartego pakietu XML w celu odczytania dokumentu word za pomocą polecenia i łącznika OpenAI z jądra semantycznego na potrzeby generowania osadzonych elementów.

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

Dodawanie modelu danych

Aby przekazać dane, należy najpierw opisać format danych, które powinny znajdować się w bazie danych. Możemy to zrobić, tworząc model danych z atrybutami, które opisują funkcję każdej właściwości.

Dodaj nowy plik do projektu o nazwie TextParagraph.cs i dodaj do niego następujący model.

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; }
}

Należy pamiętać, że przekazujemy wartość do .1536 VectorStoreRecordVectorAttribute Jest to rozmiar wymiaru wektora i musi być zgodny z rozmiarem wektora, który generuje wybrany generator osadzania.

Napiwek

Aby uzyskać więcej informacji na temat dodawania adnotacji do modelu danych i dostępnych dodatkowych opcji dla każdego atrybutu, zapoznaj się z opisem modelu danych.

Odczytywanie akapitów w dokumencie

Potrzebujemy kodu, aby odczytać dokument wyrazu i znaleźć w nim tekst każdego akapitu.

Dodaj nowy plik do projektu o nazwie DocumentReader.cs i dodaj następującą klasę, aby odczytać akapity z dokumentu.

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
                };
            }
        }
    }
}

Generowanie osadzania i przekazywanie danych

Będziemy potrzebować kodu, aby wygenerować osadzanie i przekazać akapity do usługi Redis. Zróbmy to w oddzielnej klasie.

Dodaj do niego nowy plik o nazwie DataUploader.cs i dodaj do niego następującą klasę.

#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();
        }
    }
}

Zebranie wszystkich elementów

Wreszcie musimy zebrać różne kawałki. W tym przykładzie użyjemy kontenera wstrzykiwania zależności jądra semantycznego, ale można również użyć dowolnego IServiceCollection kontenera opartego na bazie.

Dodaj następujący kod do Program.cs pliku, aby utworzyć kontener, zarejestrować magazyn wektorów Redis i zarejestrować usługę osadzania. Pamiętaj, aby zastąpić ustawienia generowania osadzania tekstu własnymi wartościami.

#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>();

W ostatnim kroku chcemy odczytać akapity z naszego dokumentu słów i wywołać moduł przekazujący dane, aby wygenerować osadzanie i przekazać akapity.

// 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);

Wyświetlanie danych w usłudze Redis

Przejdź do przeglądarki stosu Usługi Redis, np. http://localhost:8001/redis-stack/browser gdzie powinny być teraz widoczne przekazane akapity. Oto przykład tego, co powinno zostać wyświetlone dla jednego z przekazanych akapitów.

{
    "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" : [...]
}

Wkrótce

Dalsze instrukcje będą wkrótce

Wkrótce

Dalsze instrukcje będą wkrótce