Použijte IChatClient rozhraní

Rozhraní IChatClient definuje abstrakci klienta zodpovědnou za interakci se službami AI, které poskytují možnosti chatu. Obsahuje metody pro odesílání a přijímání zpráv s vícemodálním obsahem (například textem, obrázky a zvukem), a to buď jako kompletní sada, nebo streamovaná přírůstkově. Kromě toho umožňuje načítat silně typované služby, které poskytuje klient nebo jeho podpůrné služby.

Knihovny .NET, které poskytují klientům jazykové modely a služby, můžou poskytovat implementaci IChatClient rozhraní. Všichni uživatelé rozhraní pak mohou bezproblémově spolupracovat s těmito modely a službami prostřednictvím abstrakcí. Příklady najdete v části Příklady implementace .

Žádost o odpověď na chat

Pomocí instance IChatClientmůžete volat metodu IChatClient.GetResponseAsync pro odeslání požadavku a získání odpovědi. Požadavek se skládá z jedné nebo více zpráv, z nichž každá se skládá z jednoho nebo více částí obsahu. Existují metody akcelerátoru, které zjednodušují běžné případy, například vytvoření požadavku na jeden textový obsah.

using Microsoft.Extensions.AI;
using OllamaSharp;

IChatClient client = new OllamaApiClient(
    new Uri("http://localhost:11434/"), "phi3:mini");

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

Základní IChatClient.GetResponseAsync metoda přijímá seznam zpráv. Tento seznam představuje historii všech zpráv, které jsou součástí konverzace.

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

Vrácený ChatResponse poskytuje seznam instancí GetResponseAsync, které reprezentují jednu nebo více zpráv vygenerovaných v rámci operace. V běžných případech existuje pouze jedna zpráva odpovědi, ale v některých situacích může existovat více zpráv. Seznam zpráv je seřazen tak, aby poslední zpráva v seznamu představovala konečnou zprávu požadavku. Aby bylo možné poskytnout všechny tyto zprávy jako odpověď službě v následné žádosti, můžete zprávy z odpovědi přidat zpět do seznamu zpráv.

List<ChatMessage> history = [];
while (true)
{
    Console.Write("Q: ");
    history.Add(new(ChatRole.User, Console.ReadLine()));

    ChatResponse response = await client.GetResponseAsync(history);
    Console.WriteLine(response);

    history.AddMessages(response);
}

Žádost o odpověď streamovaného chatu

Vstupy IChatClient.GetStreamingResponseAsync jsou stejné jako vstupy GetResponseAsync. Místo vrácení úplné odpovědi jako součásti objektu ChatResponse však metoda vrátí IAsyncEnumerable<T>, kde T je ChatResponseUpdate, poskytuje datový proud aktualizací, které společně tvoří jedinou odpověď.

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

Návod

Rozhraní API pro streamování jsou téměř synonymem uživatelského prostředí umělé inteligence. Jazyk C# umožňuje přesvědčivé scénáře s podporou IAsyncEnumerable<T>, což umožňuje přirozený a efektivní způsob streamování dat.

Stejně jako v případě GetResponseAsync, můžete aktualizace IChatClient.GetStreamingResponseAsync přidat zpět do seznamu zpráv. Vzhledem k tomu, že aktualizace jsou jednotlivé části odpovědi, můžete použít pomocné nástroje jako ToChatResponse(IEnumerable<ChatResponseUpdate>) ke složení jedné nebo více aktualizací zpět do jedné instance ChatResponse.

Pomocníci, jako je AddMessages, složí ChatResponse a poté vyextrahují složené zprávy z odpovědi a přidají je do seznamu.

List<ChatMessage> chatHistory = [];
while (true)
{
    Console.Write("Q: ");
    chatHistory.Add(new(ChatRole.User, Console.ReadLine()));

    List<ChatResponseUpdate> updates = [];
    await foreach (ChatResponseUpdate update in
        client.GetStreamingResponseAsync(chatHistory))
    {
        Console.Write(update);
        updates.Add(update);
    }
    Console.WriteLine();

    chatHistory.AddMessages(updates);
}

Zavolání nástrojů

Některé modely a služby podporují volání nástroje. Pro shromáždění dalších informací můžete konfigurovat ChatOptions s informacemi o nástrojích (obvykle metodách .NET), které model může požádat klienta, aby vyvolal. Místo odeslání konečné odpovědi model požaduje vyvolání funkce s konkrétními argumenty. Klient pak vyvolá funkci a odešle výsledky zpět do modelu s historií konverzací. Knihovna Microsoft.Extensions.AI.Abstractions obsahuje abstrakce pro různé typy obsahu zpráv, včetně žádostí o volání funkcí a výsledků. I když IChatClient uživatelé mohou s tímto obsahem přímo interagovat, Microsoft.Extensions.AI poskytuje pomocníky, kteří nástroje mohou automaticky vyvolávat v reakci na odpovídající požadavky. Knihovny Microsoft.Extensions.AI.Abstractions a Microsoft.Extensions.AI poskytují následující typy:

  • AIFunction: Představuje funkci, která může být popsána v modelu AI a vyvolána.
  • AIFunctionFactory: Poskytuje tovární metody pro vytváření AIFunction instancí, které představují metody .NET.
  • FunctionInvokingChatClient: Zabalí IChatClient jako další IChatClient, která přidává schopnosti automatického vyvolání.

Následující příklad ukazuje vyvolání náhodné funkce (tento příklad závisí na 📦 balíčku NuGet OllamaSharp ):

using Microsoft.Extensions.AI;
using OllamaSharp;

string GetCurrentWeather() => Random.Shared.NextDouble() > 0.5 ? "It's sunny" : "It's raining";

IChatClient client = new OllamaApiClient(new Uri("http://localhost:11434"), "llama3.1");

client = ChatClientBuilderChatClientExtensions
    .AsBuilder(client)
    .UseFunctionInvocation()
    .Build();

ChatOptions options = new() { Tools = [AIFunctionFactory.Create(GetCurrentWeather)] };

var response = client.GetStreamingResponseAsync("Should I wear a rain coat?", options);
await foreach (var update in response)
{
    Console.Write(update);
}

Předchozí kód:

  • Definuje funkci s názvem GetCurrentWeather, která vrací náhodnou předpověď počasí.
  • Vytvoří instanci ChatClientBuilder s OllamaSharp.OllamaApiClient a nakonfiguruje ji tak, aby používala volání funkce.
  • Volá na GetStreamingResponseAsync na straně klienta, předá výzvu a seznam nástrojů, které zahrnují funkci vytvořenou pomocí Create.
  • Iteruje přes odpověď a vytiskne každou aktualizaci do konzoly.

Další informace o vytváření funkcí AI najdete v tématu Access data ve funkcích AI.

Můžete také použít nástroje MCP (Model Context Protocol) s vaším IChatClient. Další informace najdete v tématu Sestavení minimálního klienta MCP.

Reakce z mezipaměti

Pokud znáte ukládání do mezipaměti v .NET, je dobré vědět, že Microsoft.Extensions.AI poskytuje delegování IChatClient implementací pro ukládání do mezipaměti. DistributedCachingChatClient je IChatClient, který vrství uložení do mezipaměti kolem jiné libovolné instance IChatClient. Když je nová historie chatu odeslána do DistributedCachingChatClient, předá ji podkladovému klientovi a před odesláním zpět uživateli ji uloží do mezipaměti. Při dalším odeslání stejného dotazu, aby se našla odpověď z mezipaměti, vrátí odpověď z mezipaměti místo toho, aby DistributedCachingChatClient předával požadavek potrubím.

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

var sampleChatClient = new OllamaApiClient(new Uri("http://localhost:11434"), "llama3.1");

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.GetStreamingResponseAsync(prompt))
    {
        Console.Write(update);
    }
    Console.WriteLine();
}

Tento příklad závisí na 📦 balíčku NuGet Microsoft.Extensions.Caching.Memory . Další informace naleznete v tématu Ukládání do mezipaměti v rozhraní .NET.

Použití telemetrie

Dalším příkladem delegujícího chatovacího klienta je OpenTelemetryChatClient. Tato implementace dodržuje sémantické konvence OpenTelemetry pro systémy generující AI. Podobně jako u jiných IChatClient delegátorů vrství metriky a zahrnuje i další libovolné IChatClient implementace.

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

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

IChatClient ollamaClient = new OllamaApiClient(
    new Uri("http://localhost:11434/"), "phi3:mini");

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

Console.WriteLine((await client.GetResponseAsync("What is AI?")).Text);

(Předchozí příklad závisí na balíčku NuGet OpenTelemetry.Exporter.Console.)

Alternativně LoggingChatClient a odpovídající UseLogging(ChatClientBuilder, ILoggerFactory, Action<LoggingChatClient>) metoda poskytují jednoduchý způsob zápisu položek protokolu do ILogger každého požadavku a odpovědi.

Poskytnout možnosti

Každé volání GetResponseAsync nebo GetStreamingResponseAsync může volitelně zadat instanci ChatOptions obsahující další parametry operace. Nejběžnější parametry mezi modely AI a službami se zobrazují jako silně typované vlastnosti, například ChatOptions.Temperature. Jiné parametry lze zadat slabě zadaným způsobem, prostřednictvím ChatOptions.AdditionalProperties slovníku nebo prostřednictvím instance možností, kterou podkladový zprostředkovatel rozumí, pomocí ChatOptions.RawRepresentationFactory vlastnosti.

Při vytváření IChatClient rozhraní API s fluentem ChatClientBuilder můžete také určit možnosti zřetězením volání ConfigureOptions(ChatClientBuilder, Action<ChatOptions>) metody rozšíření. Tento delegující klient obaluje jiného klienta a vyvolává předaného delegáta, aby naplnil instanci ChatOptions pro každé volání. Pokud chcete například zajistit, aby vlastnost ChatOptions.ModelId ve výchozím nastavení používala konkrétní název modelu, můžete použít kód podobný tomuto:

using Microsoft.Extensions.AI;
using OllamaSharp;

IChatClient client = new OllamaApiClient(new Uri("http://localhost:11434"));

client = ChatClientBuilderChatClientExtensions.AsBuilder(client)
    .ConfigureOptions(options => options.ModelId ??= "phi3")
    .Build();

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

Kanály funkcí

IChatClient Instance mohou být vrstveny k vytvoření potrubí komponent, které přidávají další funkcionalitu. Tyto komponenty mohou pocházet z Microsoft.Extensions.AI, jiných balíčků NuGet nebo vlastních implementací. Tento přístup umožňuje rozšířit chování IChatClient různými způsoby, aby vyhovovaly vašim konkrétním potřebám. Představte si následující fragment kódu, který vrství distribuovanou mezipaměť, vyvolání funkcí a trasování OpenTelemetry kolem ukázkového chatovacího klienta:

// Explore changing the order of the intermediate "Use" calls.
IChatClient client = new ChatClientBuilder(new OllamaApiClient(new Uri("http://localhost:11434"), "llama3.1"))
    .UseDistributedCache(new MemoryDistributedCache(Options.Create(new MemoryDistributedCacheOptions())))
    .UseFunctionInvocation()
    .UseOpenTelemetry(sourceName: sourceName, configure: c => c.EnableSensitiveData = true)
    .Build();

Vlastní middleware IChatClient

Pokud chcete přidat další funkce, můžete implementovat IChatClient přímo nebo použít třídu DelegatingChatClient. Tato třída slouží jako základ pro vytváření chatovacích klientů, který deleguje operace na jinou instanci IChatClient. Zjednodušuje řetězení více klientů, což umožňuje volání předávat podkladovému klientovi.

Třída DelegatingChatClient poskytuje výchozí implementace pro metody, jako jsou GetResponseAsync, GetStreamingResponseAsynca Dispose, které přesměrovává volání do vnitřního klienta. Odvozená třída pak může přepsat pouze metody, které potřebuje k rozšíření chování, zatímco ostatní volání předává základní implementaci. Tento přístup je užitečný pro vytváření flexibilních a modulárních chatovacích klientů, kteří se snadno rozšiřují a vytvářejí.

Následuje příklad třídy odvozené z DelegatingChatClient , která používá System.Threading.RateLimiting knihovny k poskytování funkcí omezování rychlosti.

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<ChatResponse> GetResponseAsync(
        IEnumerable<ChatMessage> messages,
        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.GetResponseAsync(messages, options, cancellationToken)
            .ConfigureAwait(false);
    }

    public override async IAsyncEnumerable<ChatResponseUpdate> GetStreamingResponseAsync(
        IEnumerable<ChatMessage> messages,
        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.GetStreamingResponseAsync(messages, options, cancellationToken)
            .ConfigureAwait(false))
        {
            yield return update;
        }
    }

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

        base.Dispose(disposing);
    }
}

Stejně jako u jiných IChatClient implementací se dá skládat RateLimitingChatClient :

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

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

Console.WriteLine(await client.GetResponseAsync("What color is the sky?"));

Aby se zjednodušilo složení těchto komponent s ostatními, autoři komponent by měli vytvořit metodu rozšíření Use* pro registraci komponenty do potrubí. Představte si například následující UseRateLimiting metodu rozšíření:

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

Tato rozšíření se také mohou dotazovat na relevantní služby z kontejneru DI; IServiceProvider, který používá kanál, se předává jako volitelný parametr:

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,
                services.GetRequiredService<RateLimiter>())
        );
}

Teď je pro spotřebitele snadné použít to ve svém procesu, například:

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

IChatClient client = new OllamaApiClient(
    new Uri("http://localhost:11434/"),
    "phi3:mini");

builder.Services.AddChatClient(services =>
        client
        .AsBuilder()
        .UseDistributedCache()
        .UseRateLimiting()
        .UseOpenTelemetry()
        .Build(services));

Předchozí metody rozšíření ukazují použití Use metody v ChatClientBuilder. ChatClientBuilder také poskytuje Use přetížení, která usnadňují psaní takových delegujících obslužných rutin. Například v předchozím příkladu RateLimitingChatClient je při přetížení metod GetResponseAsync a GetStreamingResponseAsync třeba vykonat operace před a po delegování na dalšího klienta v řetězci. Chcete-li dosáhnout stejného výsledku bez psaní vlastní třídy, můžete použít přetížení Use, které přijímá delegáta používaného pro GetResponseAsync i GetStreamingResponseAsync, čímž snížíte množství potřebného šablonového kódu.

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

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

IChatClient client = new OllamaApiClient(new Uri("http://localhost:11434"), "llama3.1");

client = ChatClientBuilderChatClientExtensions
    .AsBuilder(client)
    .UseDistributedCache()
    .Use(async (messages, 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(messages, options, cancellationToken);
    })
    .UseOpenTelemetry()
    .Build();

Ve scénářích, kde potřebujete různé implementace pro GetResponseAsync a GetStreamingResponseAsync pro jejich jedinečné návratové typy, můžete použít přetížení Use(Func<IEnumerable<ChatMessage>,ChatOptions,IChatClient,CancellationToken, Task<ChatResponse>>, Func<IEnumerable<ChatMessage>,ChatOptions, IChatClient,CancellationToken,IAsyncEnumerable<ChatResponseUpdate>>), které přijímá delegáta pro každou z nich.

Injekce závislostí

IChatClientimplementace se často poskytují aplikaci prostřednictvím dependency injection (DI). V následujícím příkladu se do kontejneru DI přidává IDistributedCache a rovněž IChatClient. Registrace pro IChatClient používá generátor, který vytvoří potrubí obsahující klienta pro ukládání do mezipaměti (který pak použije IDistributedCache načtený z DI) a vzorového klienta. Vložený IChatClient lze načíst a použít jinde v aplikaci.

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

// App setup.
var builder = Host.CreateApplicationBuilder();
builder.Services.AddDistributedMemoryCache();
builder.Services.AddChatClient(new OllamaApiClient(new Uri("http://localhost:11434"), "llama3.1"))
    .UseDistributedCache();
var host = builder.Build();

// Elsewhere in the app.
var chatClient = host.Services.GetRequiredService<IChatClient>();
Console.WriteLine(await chatClient.GetResponseAsync("What is AI?"));

Jaká instance a konfigurace se vkládají, se může lišit v závislosti na aktuálních potřebách aplikace a několik kanálů se dá vkládat s různými klíči.

Bezstavoví vs. stavoví klienti

Bezstavové služby vyžadují, aby se při každé žádosti odesílala zpět veškerá relevantní historie komunikace. Stavové služby naproti tomu sledují historii a vyžadují odeslání pouze dalších zpráv s požadavkem. Rozhraní IChatClient je navržené tak, aby zvládlo bezstavové i stavové služby AI.

Při práci s bezstavovou službou volající musí udržovat seznam všech zpráv. Přidají do všech přijatých zpráv odpovědi a poskytnou seznam zpět při následných interakcích.

List<ChatMessage> history = [];
while (true)
{
    Console.Write("Q: ");
    history.Add(new(ChatRole.User, Console.ReadLine()));

    var response = await client.GetResponseAsync(history);
    Console.WriteLine(response);

    history.AddMessages(response);
}

U stavových služeb už možná znáte identifikátor použitý pro příslušnou konverzaci. Tento identifikátor můžete vložit do ChatOptions.ConversationIdsouboru . Použití se pak řídí stejným vzorem, s výjimkou toho, že není nutné udržovat historii ručně.

ChatOptions statefulOptions = new() { ConversationId = "my-conversation-id" };
while (true)
{
    Console.Write("Q: ");
    ChatMessage message = new(ChatRole.User, Console.ReadLine());

    Console.WriteLine(await client.GetResponseAsync(message, statefulOptions));
}

Některé služby můžou podporovat automatické vytvoření ID konverzace pro požadavek, který ho nemá, nebo vytvoření nového ID konverzace, které představuje aktuální stav konverzace po začlenění posledního kola zpráv. V takových případech můžete přenést ChatResponse.ConversationId na ChatOptions.ConversationId pro následné požadavky. Například:

ChatOptions options = new();
while (true)
{
    Console.Write("Q: ");
    ChatMessage message = new(ChatRole.User, Console.ReadLine());

    ChatResponse response = await client.GetResponseAsync(message, options);
    Console.WriteLine(response);

    options.ConversationId = response.ConversationId;
}

Pokud předem nevíte, zda je služba bezstavová nebo stavová, můžete zkontrolovat odpověď ConversationId a reagovat na základě její hodnoty. Pokud je nastavena, tato hodnota se přenese do možností a historie se vymaže, aby se stejná historie neposílala znovu. ConversationId Pokud odpověď není nastavená, zpráva odpovědi se přidá do historie, aby se odeslala zpět do služby na dalším turnu.

List<ChatMessage> chatHistory = [];
ChatOptions chatOptions = new();
while (true)
{
    Console.Write("Q: ");
    chatHistory.Add(new(ChatRole.User, Console.ReadLine()));

    ChatResponse response = await client.GetResponseAsync(chatHistory);
    Console.WriteLine(response);

    chatOptions.ConversationId = response.ConversationId;
    if (response.ConversationId is not null)
    {
        chatHistory.Clear();
    }
    else
    {
        chatHistory.AddMessages(response);
    }
}

Příklad implementace

Následující ukázka implementuje IChatClient , aby se zobrazila obecná struktura.

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<ChatResponse> GetResponseAsync(
        IEnumerable<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(
            ChatRole.Assistant,
            responses[Random.Shared.Next(responses.Length)]
            ));
    }

    public async IAsyncEnumerable<ChatResponseUpdate> GetStreamingResponseAsync(
        IEnumerable<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 ChatResponseUpdate(ChatRole.Assistant, 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() { }
}

Realističtější, konkrétní implementace IChatClient, viz:

Snížení počtu chatů (experimentální)

Důležité

Tato funkce je experimentální a může se změnit.

Snížení počtu konverzací pomáhá spravovat historii konverzací omezením počtu zpráv nebo shrnutím starších zpráv, když konverzace překročí zadanou délku. Knihovna Microsoft.Extensions.AI poskytuje redukce jako MessageCountingChatReducer, které omezují počet nesystémových zpráv, a také SummarizingChatReducer, které automaticky shrnují starší zprávy při zachování kontextu.