Freigeben über


Verwenden der IEmbeddingGenerator Schnittstelle

Die Schnittstelle IEmbeddingGenerator<TInput,TEmbedding> stellt einen generischen Generator für Einbettungen dar. Bei den generischen Typparametern TInput handelt es sich um den Typ der eingebetteten Eingabewerte und TEmbedding ist der Typ der generierten Einbettung, die von der Embedding Klasse erbt.

Die Klasse Embedding dient als Basisklasse für Einbettungen, die von einer IEmbeddingGenerator generiert werden. Es wurde entwickelt, um die Metadaten und Daten zu speichern und zu verwalten, die mit Einbettungen verknüpft sind. Abgeleitete Typen, wie Embedding<T>, stellen die konkreten Einbettungsvektordaten bereit. Beispielsweise stellt ein Embedding<float> eine ReadOnlyMemory<float> Vector { get; }-Eigenschaft bereit, um auf die eingebetteten Daten zuzugreifen.

Die Schnittstelle IEmbeddingGenerator definiert eine Methode zur asynchronen Generierung von Einbettungen für eine Sammlung von Eingabewerten, mit optionaler Unterstützung für Konfiguration und Abbruch. Außerdem werden Metadaten bereitgestellt, die den Generator beschreiben, und es ermöglicht den Abruf von stark typisierten Diensten, die entweder vom Generator selbst oder von seinen zugrunde liegenden Diensten bereitgestellt werden können.

Erstellen von Einbettungen

Der primäre Vorgang, der mit IEmbeddingGenerator<TInput,TEmbedding> ausgeführt wird, ist die Generierung von Einbettungen, die mit der Methode GenerateAsync durchgeführt wird.

using Microsoft.Extensions.AI;
using OllamaSharp;

IEmbeddingGenerator<string, Embedding<float>> generator =
    new OllamaApiClient(new Uri("http://localhost:11434/"), "phi3:mini");

foreach (Embedding<float> embedding in
    await generator.GenerateAsync(["What is AI?", "What is .NET?"]))
{
    Console.WriteLine(string.Join(", ", embedding.Vector.ToArray()));
}

Es gibt auch Methoden zur Beschleunigungserweiterung, um häufige Fälle zu vereinfachen, z. B. das Generieren eines Einbettungsvektors aus einer einzigen Eingabe.

ReadOnlyMemory<float> vector = await generator.GenerateVectorAsync("What is AI?");

Funktionspipelines

Wie bei IChatClientkönnen IEmbeddingGenerator-Implementierungen gestapelt werden. Microsoft.Extensions.AI bietet eine delegierende Implementierung für IEmbeddingGenerator, die zur Zwischenspeicherung und Telemetrie dient.

using Microsoft.Extensions.AI;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Options;
using OllamaSharp;
using OpenTelemetry.Trace;

// Configure OpenTelemetry exporter
string sourceName = Guid.NewGuid().ToString();
TracerProvider tracerProvider = OpenTelemetry.Sdk.CreateTracerProviderBuilder()
    .AddSource(sourceName)
    .AddConsoleExporter()
    .Build();

// Explore changing the order of the intermediate "Use" calls to see
// what impact that has on what gets cached and traced.
IEmbeddingGenerator<string, Embedding<float>> generator = new EmbeddingGeneratorBuilder<string, Embedding<float>>(
        new OllamaApiClient(new Uri("http://localhost:11434/"), "phi3:mini"))
    .UseDistributedCache(
        new MemoryDistributedCache(
            Options.Create(new MemoryDistributedCacheOptions())))
    .UseOpenTelemetry(sourceName: sourceName)
    .Build();

GeneratedEmbeddings<Embedding<float>> embeddings = await generator.GenerateAsync(
[
    "What is AI?",
    "What is .NET?",
    "What is AI?"
]);

foreach (Embedding<float> embedding in embeddings)
{
    Console.WriteLine(string.Join(", ", embedding.Vector.ToArray()));
}

Die IEmbeddingGenerator ermöglicht die Erstellung angepasster Middleware, die die Funktionalität einer IEmbeddingGenerator erweitert. Die DelegatingEmbeddingGenerator<TInput,TEmbedding>-Klasse ist eine Implementierung der IEmbeddingGenerator<TInput, TEmbedding>-Schnittstelle, die als Basisklasse zum Erstellen von Einbettungsgeneratoren dient, die ihre Vorgänge an eine andere IEmbeddingGenerator<TInput, TEmbedding>-Instanz delegieren. Sie ermöglicht das Verketten mehrerer Generatoren in beliebiger Reihenfolge, indem Aufrufe an einen zugrunde liegenden Generator übergeben werden. Die Klasse stellt Standardimplementierungen für Methoden wie GenerateAsync und Disposebereit, die die Aufrufe an die innere Generatorinstanz weiterleiten und dadurch eine flexible und modulare Erstellung von Einbettungen ermöglichen.

Nachfolgend sehen Sie eine Beispielimplementierung eines delegierenden Einbettungsgenerators, der die Häufigkeit von Einbettungsgenerierungsanforderungen begrenzt.

using Microsoft.Extensions.AI;
using System.Threading.RateLimiting;

public class RateLimitingEmbeddingGenerator(
    IEmbeddingGenerator<string, Embedding<float>> innerGenerator, RateLimiter rateLimiter)
        : DelegatingEmbeddingGenerator<string, Embedding<float>>(innerGenerator)
{
    public override async Task<GeneratedEmbeddings<Embedding<float>>> GenerateAsync(
        IEnumerable<string> values,
        EmbeddingGenerationOptions? options = null,
        CancellationToken cancellationToken = default)
    {
        using var lease = await rateLimiter.AcquireAsync(permitCount: 1, cancellationToken)
            .ConfigureAwait(false);

        if (!lease.IsAcquired)
        {
            throw new InvalidOperationException("Unable to acquire lease.");
        }

        return await base.GenerateAsync(values, options, cancellationToken);
    }

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            rateLimiter.Dispose();
        }

        base.Dispose(disposing);
    }
}

Dieser kann dann um einen beliebigen IEmbeddingGenerator<string, Embedding<float>> herum geschichtet werden, um die Rate aller Einbettungsgenerierungsvorgänge zu begrenzen.

using Microsoft.Extensions.AI;
using OllamaSharp;
using System.Threading.RateLimiting;

IEmbeddingGenerator<string, Embedding<float>> generator =
    new RateLimitingEmbeddingGenerator(
        new OllamaApiClient(new Uri("http://localhost:11434/"), "phi3:mini"),
        new ConcurrencyLimiter(new()
        {
            PermitLimit = 1,
            QueueLimit = int.MaxValue
        }));

foreach (Embedding<float> embedding in
    await generator.GenerateAsync(["What is AI?", "What is .NET?"]))
{
    Console.WriteLine(string.Join(", ", embedding.Vector.ToArray()));
}

Auf diese Weise kann die RateLimitingEmbeddingGenerator-Instanz mit anderen IEmbeddingGenerator<string, Embedding<float>>-Instanzen kombiniert werden, um Bewertungsbegrenzungsfunktionen bereitzustellen.

Implementierungsbeispiele

Die meisten Benutzer müssen die IEmbeddingGenerator Schnittstelle nicht implementieren. Wenn Sie jedoch ein Bibliotheksautor sind, ist es möglicherweise hilfreich, sich diese Implementierungsbeispiele anzusehen.

Der folgende Code zeigt, wie die SampleEmbeddingGenerator Klasse die IEmbeddingGenerator<TInput,TEmbedding> Schnittstelle implementiert. Er verfügt über einen primären Konstruktor, der einen Endpunkt und eine Modell-ID akzeptiert, die zum Identifizieren des Generators verwendet werden. Außerdem wird die GenerateAsync(IEnumerable<TInput>, EmbeddingGenerationOptions, CancellationToken) Methode implementiert, um Einbettungen für eine Sammlung von Eingabewerten zu generieren.

using Microsoft.Extensions.AI;

public sealed class SampleEmbeddingGenerator(
    Uri endpoint, string modelId)
        : IEmbeddingGenerator<string, Embedding<float>>
{
    private readonly EmbeddingGeneratorMetadata _metadata =
        new("SampleEmbeddingGenerator", endpoint, modelId);

    public async Task<GeneratedEmbeddings<Embedding<float>>> GenerateAsync(
        IEnumerable<string> values,
        EmbeddingGenerationOptions? options = null,
        CancellationToken cancellationToken = default)
    {
        // Simulate some async operation.
        await Task.Delay(100, cancellationToken);

        // Create random embeddings.
        return [.. from value in values
            select new Embedding<float>(
                Enumerable.Range(0, 384)
                .Select(_ => Random.Shared.NextSingle()).ToArray())];
    }

    public object? GetService(Type serviceType, object? serviceKey) =>
        serviceKey is not null
        ? null
        : serviceType == typeof(EmbeddingGeneratorMetadata)
            ? _metadata
            : serviceType?.IsInstanceOfType(this) is true
                ? this
                : null;

    void IDisposable.Dispose() { }
}

Diese Beispielimplementierung generiert nur zufällige Einbettungsvektoren. Eine realistischere, konkretere Umsetzung finden Sie unter OpenTelemetryEmbeddingGenerator.cs.