使用 IEmbeddingGenerator 接口

IEmbeddingGenerator<TInput,TEmbedding> 接口表示嵌入的泛型生成器。 对于泛型类型参数, TInput 是嵌入的输入值的类型,是 TEmbedding 生成的嵌入类型,它继承自 Embedding 类。

Embedding 类充当由 IEmbeddingGenerator 生成的嵌入的基类。 它旨在存储和管理与嵌入相关的元数据和数据。 派生类型(例如 Embedding<T>)提供具体的嵌入向量数据。 例如,Embedding<float> 公开一个 ReadOnlyMemory<float> Vector { get; } 属性以便访问其嵌入的数据。

IEmbeddingGenerator 接口定义一种方法,以异步方式为输入值的集合生成嵌入,并提供可选配置和取消支持。 它还提供描述生成器的元数据,并允许检索可由生成器或其基础服务提供的强类型服务。

创建嵌入

使用 IEmbeddingGenerator<TInput,TEmbedding> 执行的主要操作是嵌入生成,这是通过其 GenerateAsync 方法完成的。

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

加速器扩展方法还存在以简化常见情况,例如从单个输入生成嵌入向量。

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

功能管道

IChatClient一样,IEmbeddingGenerator 实现可以分层。 Microsoft.Extensions.AI 提供用于 IEmbeddingGenerator 缓存和遥测的委派实现。

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

IEmbeddingGenerator 支持生成扩展 IEmbeddingGenerator 的功能的自定义中间件。 DelegatingEmbeddingGenerator<TInput,TEmbedding> 类是 IEmbeddingGenerator<TInput, TEmbedding> 接口的实现,它作为创建嵌入生成器的基类,将其操作委托给另一个 IEmbeddingGenerator<TInput, TEmbedding> 实例。 它允许以任何顺序链接多个生成器,将调用传递给基础生成器。 该类为方法(如 GenerateAsyncDispose)提供默认实现,这些方法将调用转发到内部生成器实例,从而实现灵活的模块化嵌入生成。

下面是此类委派嵌入生成器的示例实现,该生成器对嵌入生成请求进行速率限制:

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

然后,可以在任意 IEmbeddingGenerator<string, Embedding<float>> 周围进行分层,以限制所有嵌入生成操作的速率。

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

这样,RateLimitingEmbeddingGenerator 可以与其他 IEmbeddingGenerator<string, Embedding<float>> 实例组合,以提供速率限制功能。

实现示例

大多数用户不需要实现 IEmbeddingGenerator 接口。 但是,如果你是库作者,那么查看这些实现示例可能很有帮助。

以下代码演示如何类 SampleEmbeddingGenerator 实现 IEmbeddingGenerator<TInput,TEmbedding> 接口。 它有一个接受终结点和模型 ID 的主构造函数,用于标识生成器。 它还实现 GenerateAsync(IEnumerable<TInput>, EmbeddingGenerationOptions, CancellationToken) 为输入值集合生成嵌入的方法。

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() { }
}

此示例实现只生成随机嵌入向量。 有关更现实的具体实现,请参阅 OpenTelemetryEmbeddingGenerator.cs