共用方式為


.NET 中的人工智慧 (預覽)

隨著越來越多的人工智慧(AI)服務可供使用,開發人員需要一種方法,才能在其 .NET 應用程式中整合這些服務並與其互動。 Microsoft.Extensions.AI 函式庫提供統一的方法來表示生成式 AI 組件,實現與各種 AI 服務的順暢整合和互操作性。 本文介紹連結庫,並提供安裝指示和使用範例,以協助您開始使用。

安裝套件

若要安裝 📦 Microsoft.Extensions.AI NuGet 套件,請使用 .NET CLI 或直接將套件參考新增至 C# 專案檔:

dotnet add package Microsoft.Extensions.AI --prelease

如需詳細資訊,請參閱 dotnet add package管理 .NET 應用程式中的套件相依性

使用範例

IChatClient 介面會定義負責與提供聊天功能的 AI 服務互動的用戶端抽象概念。 它包含以多重模式內容傳送和接收訊息的方法(例如文字、影像和音訊),可以是完整集合或以累加方式串流處理。 此外,該系統提供有關客戶端的元數據資訊,並允許擷取強類型服務。

重要

如需更多使用範例和真實案例,請參閱適用於 .NET 開發人員的 AI

本節中

IChatClient 介面

下列範例會實作 IChatClient 以顯示一般結構。

using System.Runtime.CompilerServices;
using Microsoft.Extensions.AI;

public sealed class SampleChatClient(Uri endpoint, string modelId) : IChatClient
{
    public ChatClientMetadata Metadata { get; } = new(nameof(SampleChatClient), endpoint, modelId);

    public async Task<ChatCompletion> CompleteAsync(
        IList<ChatMessage> chatMessages,
        ChatOptions? options = null,
        CancellationToken cancellationToken = default)
    {
        // Simulate some operation.
        await Task.Delay(300, cancellationToken);

        // Return a sample chat completion response randomly.
        string[] responses =
        [
            "This is the first sample response.",
            "Here is another example of a response message.",
            "This is yet another response message."
        ];

        return new([new ChatMessage()
        {
            Role = ChatRole.Assistant,
            Text = responses[Random.Shared.Next(responses.Length)],
        }]);
    }

    public async IAsyncEnumerable<StreamingChatCompletionUpdate> CompleteStreamingAsync(
        IList<ChatMessage> chatMessages,
        ChatOptions? options = null,
        [EnumeratorCancellation] CancellationToken cancellationToken = default)
    {
        // Simulate streaming by yielding messages one by one.
        string[] words = ["This ", "is ", "the ", "response ", "for ", "the ", "request."];
        foreach (string word in words)
        {
            // Simulate some operation.
            await Task.Delay(100, cancellationToken);

            // Yield the next message in the response.
            yield return new StreamingChatCompletionUpdate
            {
                Role = ChatRole.Assistant,
                Text = word,
            };
        }
    }

    public object? GetService(Type serviceType, object? serviceKey) => this;

    public TService? GetService<TService>(object? key = null)
        where TService : class => this as TService;

    void IDisposable.Dispose() { }
}

您可以在下列 NuGet 套件中找到其他具體 IChatClient 實作:

請求完成對話

若要要求完成,請呼叫 IChatClient.CompleteAsync 方法。 要求是由一或多個訊息所組成,每個訊息是由一或多個內容片段所組成。 加速器方法存在以簡化常見案例,例如建構單一文字內容的請求。

using Microsoft.Extensions.AI;

IChatClient client = new SampleChatClient(
    new Uri("http://coolsite.ai"), "target-ai-model");

var response = await client.CompleteAsync("What is AI?");

Console.WriteLine(response.Message);

核心 IChatClient.CompleteAsync 方法會接受訊息清單。 此清單代表交談中所有訊息的歷程記錄。

using Microsoft.Extensions.AI;

IChatClient client = new SampleChatClient(
    new Uri("http://coolsite.ai"), "target-ai-model");

Console.WriteLine(await client.CompleteAsync(
[
    new(ChatRole.System, "You are a helpful AI assistant"),
    new(ChatRole.User, "What is AI?"),
]));

歷程記錄中的每個訊息都會以 ChatMessage 物件表示。 ChatMessage 類別提供 ChatMessage.Role 屬性,指出訊息的角色。 根據預設,會使用 ChatRole.User。 以下角色可供使用:

每個聊天訊息都會初始化,並將其 Contents 屬性指定為新的 TextContent。 有各種 類型的內容 可以表示,例如簡單的字串或更複雜的物件,代表含有文字、影像和音訊的多模式訊息:

使用串流技術請求聊天完成

IChatClient.CompleteStreamingAsync 的輸入與 CompleteAsync的輸入相同。 不過,與其傳回作為 ChatCompletion 物件的完整回應,該方法會傳回 IAsyncEnumerable<T>,其中 TStreamingChatCompletionUpdate,提供一個共同構成單一回應的更新數據流。

using Microsoft.Extensions.AI;

IChatClient client = new SampleChatClient(
    new Uri("http://coolsite.ai"), "target-ai-model");

await foreach (var update in client.CompleteStreamingAsync("What is AI?"))
{
    Console.Write(update);
}

提示

串流 API 幾乎與 AI 用戶體驗同義。 C# 可透過其 IAsyncEnumerable<T> 功能來啟用引人注目的應用場景,以自然且高效的方式串流數據。

工具呼叫

某些模型和服務支援 呼叫的工具,其中要求可以包含工具,讓模型叫用函式以收集其他資訊。 模型不需要傳送最終回應,而是要求具有特定自變數的函式調用。 用戶端接著會叫用 函式,並將結果連同交談歷程記錄一起傳回模型。 Microsoft.Extensions.AI 資料庫包含各種訊息內容類型的抽象概念,包括函數調用要求和結果。 雖然消費者可以直接與此內容互動,但 Microsoft.Extensions.AI 將這些互動自動化並提供:

請考慮下列示範隨機函式調用的範例:

using System.ComponentModel;
using Microsoft.Extensions.AI;

[Description("Gets the current weather")]
string GetCurrentWeather() => Random.Shared.NextDouble() > 0.5
    ? "It's sunny"
    : "It's raining";

IChatClient client = new ChatClientBuilder(
        new OllamaChatClient(new Uri("http://localhost:11434"), "llama3.1"))
    .UseFunctionInvocation()
    .Build();

var response = client.CompleteStreamingAsync(
    "Should I wear a rain coat?",
    new() { Tools = [AIFunctionFactory.Create(GetCurrentWeather)] });

await foreach (var update in response)
{
    Console.Write(update);
}

上述範例取決於 📦 Microsoft.Extensions.AI.Ollama NuGet 套件。

上述程式代碼:

  • 定義名為 GetCurrentWeather 的函式,以傳回隨機天氣預報。
  • 使用 OllamaChatClient 實例化 ChatClientBuilder,並將其配置為使用函數調用。
  • 在用戶端上呼叫 CompleteStreamingAsync,傳遞一個提示和一個工具清單,清單中包含一個以 Create建立的函式。
  • 逐一查看回應,將每個更新列印至主控台。

緩存回應

如果您熟悉 .NET中的 快取,您應該知道 提供其他類似的委派式 實作。 DistributedCachingChatClient 是一種 IChatClient,將快取層疊於另一個任意 IChatClient 實例之上。 當獨特的聊天記錄提交至 DistributedCachingChatClient時,它會轉送至基礎客戶端,然後在將回應傳回給使用者之前快取回應。 下次提交相同的提示時,如果在快取中找到相應的回應,DistributedCachingChatClient 會回傳該快取的回應,而無需沿著管線轉發請求。

using Microsoft.Extensions.AI;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Options;

var sampleChatClient = new SampleChatClient(
    new Uri("http://coolsite.ai"), "target-ai-model");

IChatClient client = new ChatClientBuilder(sampleChatClient)
    .UseDistributedCache(new MemoryDistributedCache(
        Options.Create(new MemoryDistributedCacheOptions())))
    .Build();

string[] prompts = ["What is AI?", "What is .NET?", "What is AI?"];

foreach (var prompt in prompts)
{
    await foreach (var update in client.CompleteStreamingAsync(prompt))
    {
        Console.Write(update);
    }

    Console.WriteLine();
}

上述範例取決於 📦 Microsoft.Extensions.Caching.Memory NuGet 套件。 如需詳細資訊,請參閱在 .NET中 快取。

使用遙測

委派聊天客戶端的另一個例子是 OpenTelemetryChatClient。 此實作遵循 OpenTelemetry 語意慣例,用於生成式 AI 系統。 與其他 IChatClient 委派器類似,它會分層計量,並跨越任何基礎 IChatClient 實作,以提供增強的可檢視性。

using Microsoft.Extensions.AI;
using OpenTelemetry.Trace;

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

var sampleChatClient = new SampleChatClient(
    new Uri("http://coolsite.ai"), "target-ai-model");

IChatClient client = new ChatClientBuilder(sampleChatClient)
    .UseOpenTelemetry(
        sourceName: sourceName,
        configure: static c => c.EnableSensitiveData = true)
    .Build();

Console.WriteLine((await client.CompleteAsync("What is AI?")).Message);

上述範例取決於 📦 OpenTelemetry.Exporter.Console NuGet 套件。

提供選項

每個對 CompleteAsyncCompleteStreamingAsync 的呼叫都可以選擇性地提供包含作業其他參數的 ChatOptions 實例。 AI 模型和服務中最常見的參數會顯示為類型的強型別屬性,例如 ChatOptions.Temperature。 其他參數可以透過 ChatOptions.AdditionalProperties 字典以名稱形式提供並使用弱型別。

您也可以在使用 Fluent ChatClientBuilder API 建置 IChatClient 並鏈結呼叫 ConfigureOptions 擴充方法時,指定選項。 此委派用戶端會包裝另一個用戶端,並叫用提供的委派,為每個呼叫填入 ChatOptions 實例。 例如,若要確保 ChatOptions.ModelId 屬性預設為特定模型名稱,您可以使用如下所示的程式代碼:

using Microsoft.Extensions.AI;

IChatClient client = new ChatClientBuilder(
        new OllamaChatClient(new Uri("http://localhost:11434")))
    .ConfigureOptions(options => options.ModelId ??= "phi3")
    .Build();

// will request "phi3"
Console.WriteLine(await client.CompleteAsync("What is AI?"));

// will request "llama3.1"
Console.WriteLine(await client.CompleteAsync(
    "What is AI?", new() { ModelId = "llama3.1" }));

上述範例取決於 📦 Microsoft.Extensions.AI.Ollama NuGet 套件。

功能管線

IChatClient 實例可以分層來建立元件的管線,每個實例都會新增特定的功能。 這些元件可以來自 Microsoft.Extensions.AI、其他 NuGet 套件或自定義實作。 此方法可讓您以各種方式增強 IChatClient 的行為,以符合您的特定需求。 請考慮下列範例程式代碼,以分層分散式快取、函式調用和 OpenTelemetry 追蹤範例聊天用戶端:

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

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

// Explore changing the order of the intermediate "Use" calls to see that impact
// that has on what gets cached, traced, etc.
IChatClient client = new ChatClientBuilder(
        new OllamaChatClient(new Uri("http://localhost:11434"), "llama3.1"))
    .UseDistributedCache(new MemoryDistributedCache(
        Options.Create(new MemoryDistributedCacheOptions())))
    .UseFunctionInvocation()
    .UseOpenTelemetry(
        sourceName: sourceName,
        configure: static c => c.EnableSensitiveData = true)
    .Build();

ChatOptions options = new()
{
    Tools =
    [
        AIFunctionFactory.Create(
            () => Random.Shared.NextDouble() > 0.5 ? "It's sunny" : "It's raining",
            name: "GetCurrentWeather",
            description: "Gets the current weather")
    ]
};

for (int i = 0; i < 3; ++i)
{
    List<ChatMessage> history =
    [
        new ChatMessage(ChatRole.System, "You are a helpful AI assistant"),
        new ChatMessage(ChatRole.User, "Do I need an umbrella?")
    ];

    Console.WriteLine(await client.CompleteAsync(history, options));
}

上述範例取決於下列 NuGet 套件:

自訂IChatClient中間件

若要新增其他功能,您可以直接實作 IChatClient 或使用 DelegatingChatClient 類別。 這個類別可作為建立聊天客戶端的基礎,以將作業委派給另一個 IChatClient 實例。 它簡化了鏈結多個客戶端,允許呼叫傳遞至基礎用戶端。

DelegatingChatClient 類別會針對 CompleteAsyncCompleteStreamingAsyncDispose等方法提供預設實作,以將呼叫轉送至內部用戶端。 您可以從這個類別衍生,並只覆寫需要增強行為的方法,同時委派其餘呼叫給基底實作。 此方法可協助建立易於擴充和撰寫的彈性模組化聊天用戶端。

以下是從 DelegatingChatClient 衍生而來的範例類別,用於提供速率限制功能,並透過 RateLimiter來實現:

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

public sealed class RateLimitingChatClient(
    IChatClient innerClient, RateLimiter rateLimiter)
        : DelegatingChatClient(innerClient)
{
    public override async Task<ChatCompletion> CompleteAsync(
        IList<ChatMessage> chatMessages,
        ChatOptions? 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.CompleteAsync(chatMessages, options, cancellationToken)
            .ConfigureAwait(false);
    }

    public override async IAsyncEnumerable<StreamingChatCompletionUpdate> CompleteStreamingAsync(
        IList<ChatMessage> chatMessages,
        ChatOptions? options = null,
        [EnumeratorCancellation] CancellationToken cancellationToken = default)
    {
        using var lease = await rateLimiter.AcquireAsync(permitCount: 1, cancellationToken)
            .ConfigureAwait(false);

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

        await foreach (var update in base.CompleteStreamingAsync(chatMessages, options, cancellationToken)
            .ConfigureAwait(false))
        {
            yield return update;
        }
    }

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

        base.Dispose(disposing);
    }
}

上述範例取決於 📦 System.Threading.RateLimiting NuGet 套件。 使用另一個用戶端的 RateLimitingChatClient 組合很簡單:

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

var client = new RateLimitingChatClient(
    new SampleChatClient(new Uri("http://localhost"), "test"),
    new ConcurrencyLimiter(new()
    {
        PermitLimit = 1,
        QueueLimit = int.MaxValue
    }));

await client.CompleteAsync("What color is the sky?");

為了簡化將這類元件與其他元件進行組合,元件作者應該建立一個 Use* 擴充方法,以便將元件註冊到流水線中。 例如,請考慮下列擴充方法:

namespace Example.One;

// <one>
using Microsoft.Extensions.AI;
using System.Threading.RateLimiting;

public static class RateLimitingChatClientExtensions
{
    public static ChatClientBuilder UseRateLimiting(
        this ChatClientBuilder builder, RateLimiter rateLimiter) =>
        builder.Use(innerClient => new RateLimitingChatClient(innerClient, rateLimiter));
}
// </one>

這類擴充功能也可以從 DI 容器查詢相關服務;管線所使用的 IServiceProvider 會以選擇性參數的形式傳入:

namespace Example.Two;

// <two>
using Microsoft.Extensions.AI;
using Microsoft.Extensions.DependencyInjection;
using System.Threading.RateLimiting;

public static class RateLimitingChatClientExtensions
{
    public static ChatClientBuilder UseRateLimiting(
        this ChatClientBuilder builder, RateLimiter? rateLimiter = null) =>
        builder.Use((innerClient, services) =>
            new RateLimitingChatClient(
                innerClient,
                rateLimiter ?? services.GetRequiredService<RateLimiter>()));
}
// </two>

消費者接著可以在流程中輕鬆使用此內容,例如:

using Microsoft.Extensions.AI;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

var builder = Host.CreateApplicationBuilder(args);

builder.Services.AddChatClient(services =>
    new SampleChatClient(new Uri("http://localhost"), "test")
        .AsBuilder()
        .UseDistributedCache()
        .UseRateLimiting()
        .UseOpenTelemetry()
        .Build(services));

using var app = builder.Build();

// Elsewhere in the app
var chatClient = app.Services.GetRequiredService<IChatClient>();

Console.WriteLine(await chatClient.CompleteAsync("What is AI?"));

app.Run();

此範例示範 裝載的案例,其中消費者依賴 依賴注入 來提供 RateLimiter 實例。 上述擴充方法示範如何在 ChatClientBuilder上使用 Use 方法。 ChatClientBuilder 也提供 Use 重載,使您更容易撰寫這類委派處理程式。

例如,在先前的 RateLimitingChatClient 範例中,CompleteAsyncCompleteStreamingAsync 的覆寫只需要在委派至管線中的下一個用戶端之前和之後執行工作。 若要在不撰寫自訂類別的情況下達到相同的目標,您可以使用接受委派以便用於 CompleteAsyncCompleteStreamingAsyncUse 重載,以減少所需的樣板程式碼:

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

RateLimiter rateLimiter = new ConcurrencyLimiter(new()
{
    PermitLimit = 1,
    QueueLimit = int.MaxValue
});

var client = new SampleChatClient(new Uri("http://localhost"), "test")
    .AsBuilder()
    .UseDistributedCache()
    .Use(async (chatMessages, options, nextAsync, cancellationToken) =>
    {
        using var lease = await rateLimiter.AcquireAsync(permitCount: 1, cancellationToken)
            .ConfigureAwait(false);

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

        await nextAsync(chatMessages, options, cancellationToken);
    })
    .UseOpenTelemetry()
    .Build();

// Use client

前述的多載在內部使用了一個 AnonymousDelegatingChatClient,這使得只需少量額外的程式代碼即可實現更複雜的模式。 例如,若要達到相同的結果,但使用從 DI 擷取 RateLimiter

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

var client = new SampleChatClient(new Uri("http://localhost"), "test")
    .AsBuilder()
    .UseDistributedCache()
    .Use(static (innerClient, services) =>
    {
        var rateLimiter = services.GetRequiredService<RateLimiter>();

        return new AnonymousDelegatingChatClient(
            innerClient, async (chatMessages, options, nextAsync, cancellationToken) =>
            {
                using var lease = await rateLimiter.AcquireAsync(permitCount: 1, cancellationToken)
                    .ConfigureAwait(false);

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

                await nextAsync(chatMessages, options, cancellationToken);
            });
    })
    .UseOpenTelemetry()
    .Build();

針對開發人員希望內聯指定 CompleteAsyncCompleteStreamingAsync 的委派實作,以及需要為每個實作撰寫不同的版本以特別處理其獨特的傳回型別的情境,還有一個 Use 多載可以接收對應每個委派的委派函式。

依賴注入

IChatClient 實作通常會透過 相依注入(DI)提供給應用程式。 在此範例中,會將 IDistributedCache 新增至 DI 容器,就像加入 IChatClient一樣。 IChatClient 的註冊會採用建立器來建立包含快取用戶端的管線(接著會使用從 DI 擷取的 IDistributedCache),以及範例用戶端。 插入的 IChatClient 可以在應用程式中的其他地方擷取及使用。

::code language=“csharp” source=“snippets/ai/ConsoleAI.DependencyInjection/Program.cs”::

上述範例取決於下列 NuGet 套件:

插入的實例和組態可能會根據應用程式目前的需求而有所不同,而且可以使用不同的索引鍵插入多個管線。

IEmbeddingGenerator 介面

IEmbeddingGenerator<TInput,TEmbedding> 介面代表嵌入向量的通用生成器。 在這裡,TInput 是內嵌的輸入值類型,TEmbedding 是產生的內嵌類型,其繼承自 Embedding 類別。

Embedding 類別作為由 IEmbeddingGenerator所生成嵌入向量的基礎類別。 其設計目的是儲存和管理與內嵌相關聯的元數據和數據。 Embedding<T> 之類的衍生型別提供具體內嵌向量數據。 例如,內嵌會公開 Embedding<T>.Vector 屬性來存取其內嵌數據。

IEmbeddingGenerator 介面定義了一種方法,以非同步的方式為一組輸入值生成嵌入,並支援可選的組態和取消功能。 它還提供描述產生器的元數據,並允許提取可由產生器或其底層服務提供的強型別服務。

範例實作

請考慮以下範例實作 IEmbeddingGenerator,以展示一般結構,這僅會生成隨機嵌入向量。

using Microsoft.Extensions.AI;

public sealed class SampleEmbeddingGenerator(
    Uri endpoint, string modelId)
        : IEmbeddingGenerator<string, Embedding<float>>
{
    public EmbeddingGeneratorMetadata Metadata { get; } =
        new(nameof(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) => this;

    public TService? GetService<TService>(object? key = null)
        where TService : class => this as TService;

    void IDisposable.Dispose() { }
}

上述程式代碼:

  • 定義名為 SampleEmbeddingGenerator 的類別,以實作 IEmbeddingGenerator<string, Embedding<float>> 介面。
  • 具有接受端點和模型標識碼的主要建構函式,用來識別產生器。
  • 公開 Metadata 屬性,提供有關產生器的元數據。
  • 實現 GenerateAsync 方法來為一組輸入值生成嵌入表示:
    • 藉由延遲 100 毫秒來模擬異步操作。
    • 傳回每個輸入值的隨機內嵌。

您可以在下列套件中找到實際的具體實作:

建立內嵌

使用 IEmbeddingGenerator<TInput,TEmbedding> 執行的主要作業是嵌入生成,這是通過其 GenerateAsync 方法實現的。

::code language=“csharp” source=“snippets/ai/ConsoleAI.CreateEmbeddings/Program.cs”::

自訂 IEmbeddingGenerator 中間件

如同 IChatClientIEmbeddingGenerator 實作可以分層。 就像 Microsoft.Extensions.AI 提供快取和遙測的 IChatClient 委派實作一樣,它也為 IEmbeddingGenerator 提供實作。

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

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

// Explore changing the order of the intermediate "Use" calls to see that impact
// that has on what gets cached, traced, etc.
var generator = new EmbeddingGeneratorBuilder<string, Embedding<float>>(
        new SampleEmbeddingGenerator(new Uri("http://coolsite.ai"), "target-ai-model"))
    .UseDistributedCache(
        new MemoryDistributedCache(
            Options.Create(new MemoryDistributedCacheOptions())))
    .UseOpenTelemetry(sourceName: sourceName)
    .Build();

var embeddings = await generator.GenerateAsync(
[
    "What is AI?",
    "What is .NET?",
    "What is AI?"
]);

foreach (var 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 System.Threading.RateLimiting;

IEmbeddingGenerator<string, Embedding<float>> generator =
    new RateLimitingEmbeddingGenerator(
        new SampleEmbeddingGenerator(new Uri("http://coolsite.ai"), "target-ai-model"),
        new ConcurrencyLimiter(new()
        {
            PermitLimit = 1,
            QueueLimit = int.MaxValue
        }));

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

如此一來,RateLimitingEmbeddingGenerator 可以與其他 IEmbeddingGenerator<string, Embedding<float>> 實例組成,以提供速率限制功能。

另請參閱