Dela via


Så här matar du in data i ett vektorlager med semantisk kernel (förhandsversion)

Varning

Funktionen Semantic Kernel Vector Store är i förhandsversion, och förbättringar som kräver icke-bakåtkompatibla ändringar kan fortfarande ske under begränsade omständigheter före lanseringen.

Den här artikeln visar hur du skapar ett program för att

  1. Ta text från varje stycke i ett Microsoft Word-dokument
  2. Generera en inbäddning för varje stycke
  3. Utöka texten, inbäddningen och en referens till den ursprungliga platsen i en Redis-instans.

Förutsättningar

För det här exemplet behöver du

  1. En inbäddningsgenereringsmodell som finns i Azure eller någon annan leverantör som du väljer.
  2. En instans av Redis eller Docker Desktop så att du kan köra Redis lokalt.
  3. Ett Word-dokument som ska parsas och läsas in. Här är en zip som innehåller ett Word-exempeldokument som du kan ladda ned och använda: vector-store-data-ingestion-input.zip.

Konfigurera Redis

Om du redan har en Redis-instans kan du använda den. Om du föredrar att testa projektet lokalt kan du enkelt starta en Redis-container med docker.

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

Om du vill kontrollera att den körs på ett lyckat sätt går du till http://localhost:8001/redis-stack/browser webbläsaren.

Resten av dessa instruktioner förutsätter att du använder den här containern med hjälp av ovanstående inställningar.

Skapa projektet

Skapa ett nytt projekt och lägg till nuget-paketreferenser för Redis-anslutningsappen från Semantic Kernel, det öppna XML-paketet för att läsa ordet dokument med och OpenAI-anslutningsappen från semantisk kernel för att generera inbäddningar.

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

Lägga till en datamodell

För att ladda upp data måste vi först beskriva vilket format data ska ha i databasen. Vi kan göra detta genom att skapa en datamodell med attribut som beskriver funktionen för varje egenskap.

Lägg till en ny fil i projektet med namnet TextParagraph.cs och lägg till följande modell i den.

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

Observera att vi skickar värdet 1536 till VectorStoreRecordVectorAttribute. Det här är dimensionsstorleken för vektorn och måste matcha storleken på vektorn som den valda inbäddningsgeneratorn producerar.

Dricks

Mer information om hur du kommenterar din datamodell och vilka ytterligare alternativ som är tillgängliga för varje attribut finns i definiera din datamodell.

Läs styckena i dokumentet

Vi behöver lite kod för att läsa ordet dokument och hitta texten i varje stycke i det.

Lägg till en ny fil i projektet med namnet DocumentReader.cs och lägg till följande klass för att läsa styckena från ett dokument.

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

Generera inbäddningar och ladda upp data

Vi behöver lite kod för att generera inbäddningar och ladda upp styckena till Redis. Låt oss göra detta i en separat klass.

Lägg till en ny fil med namnet DataUploader.cs och lägg till följande klass i den.

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

Färdigställa allt

Slutligen måste vi sätta ihop de olika bitarna. I det här exemplet använder vi containern semantisk kernelberoendeinmatning, men det är också möjligt att använda valfri IServiceCollection baserad container.

Lägg till följande kod i Program.cs filen för att skapa containern, registrera Redis-vektorarkivet och registrera inbäddningstjänsten. Ersätt inställningarna för inbäddning av textgenerering med dina egna värden.

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

Som ett sista steg vill vi läsa styckena från vårt orddokument och anropa datauppladdaren för att generera inbäddningarna och ladda upp styckena.

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

Se dina data i Redis

Gå till Redis Stack-webbläsaren, t.ex. http://localhost:8001/redis-stack/browser där du nu ska kunna se dina uppladdade stycken. Här är ett exempel på vad du bör se för ett av de uppladdade styckena.

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

Kommer snart

Ytterligare instruktioner kommer snart

Kommer snart

Ytterligare instruktioner kommer snart