Partager via


Utiliser l’interface IChatClient

L’interface IChatClient définit une abstraction cliente chargée d’interagir avec les services IA qui fournissent des fonctionnalités de conversation. Elle inclut des méthodes d’envoi et de réception de messages avec du contenu multimodal (par exemple, du texte, des images et de l’audio), en tant qu’ensemble complet ou diffusé de manière incrémentielle. En outre, il permet de récupérer des services fortement typés fournis par le client ou ses services sous-jacents.

Les bibliothèques .NET qui fournissent des clients pour les modèles de langage et les services peuvent fournir une implémentation de l’interface IChatClient . Tous les consommateurs de l’interface sont alors en mesure d’interagir en toute transparence avec ces modèles et services via les abstractions. Vous trouverez des exemples dans la section Exemples d’implémentation .

Demander une réponse de conversation

Avec une instance de IChatClient, vous pouvez appeler la IChatClient.GetResponseAsync méthode pour envoyer une demande et obtenir une réponse. La requête est composée d’un ou plusieurs messages, chacun composé d’une ou plusieurs parties de contenu. Des méthodes d’accélérateur existent pour simplifier les cas courants, tels que la construction d’une requête pour un seul élément de contenu texte.

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?"));

La méthode principale IChatClient.GetResponseAsync accepte une liste de messages. Cette liste représente l’historique de tous les messages qui font partie de la conversation.

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

L'objet ChatResponse retourné depuis GetResponseAsync fournit une liste d'instances de ChatMessage qui représentent un ou plusieurs messages générés. Dans les cas courants, il n’existe qu’un seul message de réponse, mais dans certaines situations, il peut y avoir plusieurs messages. La liste des messages est triée, de sorte que le dernier message de la liste représente le message final à la demande. Pour fournir tous ces messages de réponse au service dans une demande ultérieure, vous pouvez ajouter les messages de la réponse dans la liste des messages.

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

Demander une réponse de conversation en streaming

Les entrées dans IChatClient.GetStreamingResponseAsync sont identiques à celles de GetResponseAsync. Toutefois, au lieu de renvoyer la réponse complète dans le cadre d’un objet ChatResponse, la méthode retourne un IAsyncEnumerable<T>T est ChatResponseUpdate, fournissant ainsi un flux de mises à jour qui forment collectivement la réponse unique.

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

Conseil / Astuce

Les API de streaming sont presque synonymes d’expériences utilisateur IA. C# permet des scénarios convaincants avec sa prise en charge de IAsyncEnumerable<T>, offrant un moyen naturel et efficace de diffuser des données de flux.

Comme avec GetResponseAsync, vous pouvez ajouter à nouveau les mises à jour de IChatClient.GetStreamingResponseAsync dans la liste des messages. Étant donné que les mises à jour sont des éléments individuels d’une réponse, vous pouvez utiliser des helpers comme ToChatResponse(IEnumerable<ChatResponseUpdate>) pour composer une ou plusieurs mises à jour dans une seule ChatResponse instance.

Les assistants comme AddMessages composent un ChatResponse, puis extraient les messages composés de la réponse et les ajoutent à une liste.

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

Appel de l’outil

Certains modèles et services prennent en charge l’appel d’outils. Pour collecter des informations supplémentaires, vous pouvez configurer le ChatOptions avec des informations sur les outils (généralement des méthodes .NET) que le modèle peut demander au client d'invoquer. Au lieu d’envoyer une réponse finale, le modèle demande un appel de fonction avec des arguments spécifiques. Le client appelle ensuite la fonction et renvoie les résultats au modèle avec l’historique des conversations. La bibliothèque Microsoft.Extensions.AI.Abstractions inclut des abstractions pour différents types de contenu de message, notamment les demandes d’appel de fonction et les résultats. Bien que IChatClient les consommateurs puissent interagir directement avec ce contenu, Microsoft.Extensions.AI fournit des assistances qui peuvent activer l’appel automatique des outils en réponse aux demandes correspondantes. Les bibliothèques Microsoft.Extensions.AI.Abstractions et Microsoft.Extensions.AI offrent les types suivants :

  • AIFunction: représente une fonction qui peut être décrite dans un modèle IA et appelée.
  • AIFunctionFactory: fournit des méthodes de fabrique pour la création AIFunction d’instances qui représentent des méthodes .NET.
  • FunctionInvokingChatClient : enveloppe un IChatClient en tant que IChatClient supplémentaire qui ajoute des capacités d'invocation automatique de fonction.

L’exemple suivant illustre un appel de fonction aléatoire (cet exemple dépend du 📦 package 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);
}

Code précédent :

  • Définit une fonction nommée GetCurrentWeather qui retourne une prévision météorologique aléatoire.
  • Instancie un ChatClientBuilder avec un OllamaSharp.OllamaApiClient et le configure pour utiliser l'invocation de fonction.
  • Appelle GetStreamingResponseAsync sur le client, en transmettant une invite et une liste d’outils qui incluent une fonction créée avec Create.
  • Effectue une itération sur la réponse, en imprimant chaque mise à jour sur la console.

Pour plus d’informations sur la création de fonctions IA, consultez Accéder aux données dans les fonctions IA.

Vous pouvez également utiliser des outils MCP (Model Context Protocol) avec votre IChatClient. Pour plus d’informations, consultez Générer un client MCP minimal.

Réduction des outils (expérimentale)

Important

Cette fonctionnalité est expérimentale et susceptible de changer.

La réduction des outils permet de gérer de grands catalogues d’outils en les supprimant en fonction de la pertinence du contexte de conversation actuel. L’interface IToolReductionStrategy définit des stratégies pour réduire le nombre d’outils envoyés au modèle. La bibliothèque fournit des implémentations comme EmbeddingToolReductionStrategy celles qui classent les outils en incorporant la similarité à la conversation. Utilisez la méthode d’extension UseToolReduction pour ajouter la réduction des outils à votre pipeline de client de chat.

Mettre en cache des réponses

Si vous connaissez la mise en cache dans .NET, il est judicieux de savoir qui Microsoft.Extensions.AI fournit des implémentations de délégation IChatClient pour la mise en cache. Le DistributedCachingChatClient est un IChatClient qui applique une mise en cache par couches autour d'une autre instance IChatClient arbitraire. Lorsqu'un nouvel historique de conversation est soumis au DistributedCachingChatClient, il le transfère au client sous-jacent, puis met en cache la réponse avant de la renvoyer au consommateur. La prochaine fois que le même historique est envoyé, de sorte qu’une réponse mise en cache se trouve dans le cache, elle DistributedCachingChatClient renvoie la réponse mise en cache plutôt que de transférer la requête le long du pipeline.

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

Cet exemple dépend du 📦 package NuGet Microsoft.Extensions.Caching.Memory . Pour plus d’informations, consultez Mise en cache dans .NET.

Utiliser la télémétrie

Un autre exemple d’un client de conversation délégué est le OpenTelemetryChatClient. Cette implémentation respecte les conventions sémantiques openTelemetry pour les systèmes d’IA générative. Comme pour d'autres délégateurs IChatClient, il superpose les mesures et englobe d'autres implémentations arbitraires IChatClient.

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

(L’exemple précédent dépend du 📦 package NuGet OpenTelemetry.Exporter.Console .)

Alternativement, la méthode LoggingChatClient et la méthode correspondante UseLogging(ChatClientBuilder, ILoggerFactory, Action<LoggingChatClient>) offrent un moyen simple d'écrire des entrées de journal dans un ILogger pour chaque requête et réponse.

Proposer des options

Chaque appel à GetResponseAsync ou GetStreamingResponseAsync peut éventuellement fournir une instance de ChatOptions contenant des paramètres supplémentaires pour l’opération. Les paramètres les plus courants parmi les modèles et services d’IA s’affichent comme des propriétés fortement typées sur le type, telles que ChatOptions.Temperature. D’autres paramètres peuvent être fournis par nom de manière faiblement typée, via le ChatOptions.AdditionalProperties dictionnaire ou via une instance d’options que le fournisseur sous-jacent comprend, à l’aide de la ChatOptions.RawRepresentationFactory propriété.

Vous pouvez également spécifier des options lors de la construction d'une IChatClient avec l'API fluente ChatClientBuilder en chaînant un appel à la méthode d'extension ConfigureOptions(ChatClientBuilder, Action<ChatOptions>). Ce client délégateur enveloppe un autre client et appelle le délégué fourni pour remplir une instance ChatOptions pour chaque appel. Par exemple, pour vous assurer que la propriété ChatOptions.ModelId est par défaut un nom de modèle particulier, vous pouvez utiliser du code comme suit :

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

Pipelines de fonctionnalités

IChatClient les instances peuvent être superposées pour créer un pipeline de composants qui ajoutent chacune des fonctionnalités supplémentaires. Ces composants peuvent provenir de Microsoft.Extensions.AI, d’autres packages NuGet ou d’implémentations personnalisées. Cette approche vous permet d’augmenter le comportement du IChatClient de différentes façons pour répondre à vos besoins spécifiques. Considérez l’extrait de code suivant qui couche un cache distribué, un appel de fonction et un suivi OpenTelemetry autour d’un exemple de client de conversation :

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

Middleware IChatClient personnalisé

Pour ajouter des fonctionnalités supplémentaires, vous pouvez implémenter IChatClient directement ou utiliser la classe DelegatingChatClient. Cette classe sert de base pour créer des clients de conversation qui délèguent des opérations à une autre instance IChatClient. Il simplifie le chaînage de plusieurs clients, ce qui permet aux appels de passer à un client sous-jacent.

La classe DelegatingChatClient fournit des implémentations par défaut pour les méthodes telles que GetResponseAsync, GetStreamingResponseAsync et Dispose, qui transfèrent les appels au client interne. Une classe dérivée peut ensuite remplacer uniquement les méthodes dont elle a besoin pour augmenter le comportement, tout en déléguant d’autres appels à l’implémentation de base. Cette approche est utile pour créer des clients de conversation flexibles et modulaires faciles à étendre et à composer.

Voici un exemple de classe dérivée de DelegatingChatClient laquelle utilise la bibliothèque System.Threading.RateLimiting pour fournir des fonctionnalités de limitation de débit.

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

Comme avec d'autres IChatClient implémentations, RateLimitingChatClient peut être composé :

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?"));

Pour simplifier l’association de ces composants avec d’autres, les auteurs de composants doivent créer une méthode d’extension Use* pour inscrire le composant dans un pipeline. Par exemple, considérez la méthode d’extension suivante UseRateLimiting :

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

Ces extensions peuvent également demander les services pertinents au conteneur DI ; le IServiceProvider utilisé par le pipeline est transmis en tant que paramètre facultatif :

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

Maintenant, il est facile pour le consommateur d’utiliser ceci dans son pipeline, par exemple :

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

Les méthodes d’extension précédentes illustrent l’utilisation d’une Use méthode sur ChatClientBuilder. ChatClientBuilder fournit également des surcharges Use qui facilitent l’écriture de tels gestionnaires de délégation. Par exemple, dans l’exemple précédent de RateLimitingChatClient, les remplacements de GetResponseAsync et GetStreamingResponseAsync doivent uniquement effectuer des tâches avant et après la délégation au client suivant dans le pipeline. Pour obtenir le même résultat sans écrire une classe personnalisée, vous pouvez utiliser une surcharge de Use qui accepte un délégué utilisé à la fois pour GetResponseAsync et GetStreamingResponseAsync, ce qui réduit les lignes de code nécessaires :

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

Pour les scénarios où vous avez besoin d’une implémentation différente pour GetResponseAsync et GetStreamingResponseAsync pour gérer leurs types de retour uniques, vous pouvez utiliser la Use(Func<IEnumerable<ChatMessage>,ChatOptions,IChatClient,CancellationToken, Task<ChatResponse>>, Func<IEnumerable<ChatMessage>,ChatOptions, IChatClient,CancellationToken,IAsyncEnumerable<ChatResponseUpdate>>) surcharge qui accepte un délégué pour chacun d’eux.

Injection de dépendances

IChatClientLes implémentations sont souvent fournies à une application via l’injection de dépendances (DI). Dans l’exemple suivant, un IDistributedCache conteneur d’adresses de domaine est ajouté, tel qu’un IChatClient. L’inscription du IChatClient utilise un générateur qui crée un pipeline contenant un client de mise en cache (qui utilise ensuite un IDistributedCache récupéré à partir de la DI) et le client d'exemple. Le IChatClient injecté peut être récupéré et utilisé ailleurs dans l’application.

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?"));

L'instance et la configuration injectées peuvent différer en fonction des besoins actuels de l'application, et plusieurs pipelines peuvent être injectés avec des clés différentes.

Clients sans état et avec état

Les services sans état nécessitent que tous les historiques de conversation pertinents soient envoyés à nouveau à chaque requête. En revanche, les services avec état effectuent le suivi de l’historique et requièrent uniquement que des messages supplémentaires soient envoyés avec une demande. L'interface IChatClient est conçue pour gérer les services d'IA, qu'ils soient sans état ou avec état.

Lors de l’utilisation d’un service sans état, les appelants gèrent une liste de tous les messages. Ils ajoutent tous les messages de réponse reçus et fournissent la liste à nouveau sur les interactions suivantes.

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

Pour les services avec état, vous pouvez déjà connaître l’identificateur utilisé pour la conversation concernée. Vous pouvez placer cet identificateur dans ChatOptions.ConversationId. L’utilisation suit ensuite le même modèle, sauf qu’il n’est pas nécessaire de conserver manuellement un historique.

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

Certains services peuvent prendre en charge la création automatique d’un ID de conversation pour une demande qui n’en a pas, ou la création d’un ID de conversation qui représente l’état actuel de la conversation après avoir incorporé le dernier cycle de messages. Dans ce cas, vous pouvez transférer le ChatResponse.ConversationId au ChatOptions.ConversationId pour les demandes suivantes. Par exemple:

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

Si vous ne savez pas à l’avance si le service est sans état ou avec état, vous pouvez vérifier la réponse ConversationId et agir en fonction de sa valeur. Si elle est définie, cette valeur est propagée aux options et l’historique est effacé afin de ne pas renvoyer à nouveau le même historique. Si la réponse ConversationId n’est pas définie, le message de réponse est ajouté à l’historique afin qu’il soit renvoyé au service au tour suivant.

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

Exemples d’implémentation

L’exemple suivant implémente IChatClient pour afficher la structure générale.

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

Pour obtenir des implémentations plus réalistes et concrètes de IChatClient, consultez :

Réduction des conversations (expérimentale)

Important

Cette fonctionnalité est expérimentale et susceptible de changer.

La réduction des conversations permet de gérer l’historique des conversations en limitant le nombre de messages ou en récapitunant les anciens messages lorsque la conversation dépasse une longueur spécifiée. La Microsoft.Extensions.AI bibliothèque fournit des réducteurs tels MessageCountingChatReducer que cela limite le nombre de messages non système et SummarizingChatReducer résume automatiquement les anciens messages tout en préservant le contexte.