Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Die IChatClient-Schnittstelle definiert eine Client-Abstraktion, die für die Interaktion mit KI-Diensten verantwortlich ist, die Chatfunktionen bereitstellen. Es enthält Methoden zum Senden und Empfangen von Nachrichten mit multimodalem Inhalt (wie z. B. Text, Bilder und Audio), entweder als kompletter Satz oder inkrementell gestreamt. Darüber hinaus ermöglicht es das Abrufen stark typisierter Dienste, die vom Client oder seinen zugrunde liegenden Diensten bereitgestellt werden.
.NET-Bibliotheken, die Clients für Sprachmodelle und -dienste bereitstellen, können eine Implementierung der IChatClient Schnittstelle bereitstellen. Alle Verbraucher der Schnittstelle können dann nahtlos mit diesen Modellen und Diensten über die Abstraktionen zusammenarbeiten. Beispiele finden Sie im Abschnitt "Implementierungsbeispiele ".
Anfordern einer Chatantwort
Mit einer Instanz von IChatClientkönnen Sie die IChatClient.GetResponseAsync Methode aufrufen, um eine Anforderung zu senden und eine Antwort zu erhalten. Die Anforderung besteht aus einer oder mehreren Nachrichten, die jeweils aus einem oder mehreren Inhalten bestehen. Es gibt Accelerator-Methoden, die allgemeine Fälle vereinfachen, wie z. B. das Erstellen einer Anforderung für ein einzelnes Stück Textinhalt.
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?"));
Die Kernmethode IChatClient.GetResponseAsync akzeptiert eine Liste von Nachrichten. Diese Liste stellt den Verlauf aller Nachrichten dar, die Teil der Unterhaltung sind.
Console.WriteLine(await client.GetResponseAsync(
[
new(ChatRole.System, "You are a helpful AI assistant"),
new(ChatRole.User, "What is AI?"),
]));
Das zurückgegebene ChatResponse-Element von GetResponseAsync stellt eine Liste der ChatMessage-Instanzen bereit, die eine oder mehrere im Rahmen des Vorgangs generierte Nachrichten repräsentieren. In häufigen Fällen gibt es nur eine Antwortnachricht, aber in einigen Situationen kann es mehrere Nachrichten geben. Die Nachrichtenliste wird sortiert, sodass die letzte Nachricht in der Liste die endgültige Nachricht für die Anforderung darstellt. Um alle diese Antwortnachrichten in einer nachfolgenden Anforderung an den Dienst zurückzugeben, können Sie die Nachrichten aus der Antwort zurück zur Nachrichtenliste hinzufügen.
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);
}
Anfordern einer Streaming-Chatantwort
Die Eingaben für IChatClient.GetStreamingResponseAsync sind mit denen von GetResponseAsync identisch. Anstatt jedoch die vollständige Antwort als Element eines ChatResponse-Objekts zurückzugeben, gibt die Methode ein IAsyncEnumerable<T>-Objekt zurück, wobei TChatResponseUpdate ist und einen Strom von Aktualisierungen liefert, die zusammen die einzige Antwort bilden.
await foreach (ChatResponseUpdate update in client.GetStreamingResponseAsync("What is AI?"))
{
Console.Write(update);
}
Tipp
Streaming-APIs sind fast gleichbedeutend mit KI-Benutzererfahrungen. C# ermöglicht überzeugende Szenarien durch seine IAsyncEnumerable<T>-Unterstützung und bietet eine natürliche und effiziente Methode zum Streamen von Daten.
Wie bei GetResponseAsync können Sie die Updates aus IChatClient.GetStreamingResponseAsync zur Nachrichtenliste wieder hinzuzufügen. Da die Updates einzelne Teile einer Antwort sind, können Sie Hilfsmittel wie ToChatResponse(IEnumerable<ChatResponseUpdate>) verwenden, um ein oder mehrere Updates zu einer einzigen ChatResponse Instanz zusammenzufassen.
Hilfsprogramme wie AddMessages erstellen ein ChatResponse und extrahieren dann die komponierten Nachrichten aus der Antwort, um sie einer Liste hinzuzufügen.
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);
}
Toolaufruf
Einige Modelle und Dienste unterstützen Aufrufe von Tools. Um zusätzliche Informationen zu sammeln, können Sie ChatOptions so konfigurieren, dass es Informationen zu Tools enthält (in der Regel .NET-Methoden), die das Modell anfordern kann, damit der Client sie aufruft. Anstatt eine endgültige Antwort zu senden, fordert das Modell einen Funktionsaufruf mit bestimmten Argumenten an. Der Client ruft dann die Funktion auf und sendet die Ergebnisse zurück an das Modell mit dem Unterhaltungsverlauf. Die Microsoft.Extensions.AI.Abstractions-Bibliothek enthält Abstraktionen für verschiedene Nachrichteninhaltstypen, einschließlich Funktionsaufrufanforderungen und -ergebnisse.
IChatClient Während Verbraucher direkt mit diesen Inhalten interagieren können, stellen Sie Hilfsprogramme bereit, Microsoft.Extensions.AI die das automatische Aufrufen der Tools als Reaktion auf entsprechende Anforderungen aktivieren können. Die Microsoft.Extensions.AI.Abstractions und Microsoft.Extensions.AI Bibliotheken stellen die folgenden Typen bereit:
- AIFunction: Stellt eine Funktion dar, die in einem KI-Modell beschrieben und aufgerufen werden kann.
-
AIFunctionFactory: Stellt Factorymethoden zum Erstellen von
AIFunctionInstanzen bereit, die .NET-Methoden darstellen. -
FunctionInvokingChatClient: Verpackt ein
IChatClientals ein weiteresIChatClient, das automatische Funktionsaufruffähigkeiten hinzufügt.
Das folgende Beispiel zeigt einen zufälligen Funktionsaufruf (dieses Beispiel hängt vom OllamaSharp NuGet-Paket ab📦):
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);
}
Der vorherige Code:
- Definiert eine Funktion namens
GetCurrentWeather, die eine zufällige Wettervorhersage zurückgibt. - Instanziiert eine ChatClientBuilder mit einer
OllamaSharp.OllamaApiClientund konfiguriert sie für die Verwendung von Funktionsaufrufen. - Ruft
GetStreamingResponseAsyncauf dem Client auf und übergibt einen Prompt und eine Liste von Tools, die eine mit Create erstellte Funktion enthält. - Iteriert über die Antwort und gibt jede Aktualisierung auf der Konsole aus.
Weitere Informationen zum Erstellen von KI-Funktionen finden Sie unter Access-Daten in KI-Funktionen.
Sie können auch MCP-Tools (Modelkontextprotokoll) mit Ihren IChatClient verwenden. Weitere Informationen finden Sie unter Erstellen eines minimalen MCP-Clients.
Werkzeugreduzierung (experimentell)
Von Bedeutung
Dieses Feature ist experimentell und kann geändert werden.
Die Toolreduzierung hilft beim Verwalten großer Toolkataloge, indem sie basierend auf der Relevanz für den aktuellen Unterhaltungskontext gekürzt werden. Die IToolReductionStrategy Schnittstelle definiert Strategien zur Verringerung der Anzahl der tools, die an das Modell gesendet werden. Die Bibliothek stellt Implementierungen wie EmbeddingToolReductionStrategy bereit, die Tools nach Einbettungsähnlichkeit zu dem Gespräch einordnen. Verwenden Sie die UseToolReduction-Erweiterungsmethode, um Ihrer Chat-Client-Pipeline eine Werkzeugreduzierung hinzuzufügen.
Antworten aus dem Cache
Wenn Sie mit der Zwischenspeicherung in .NET vertraut sind, ist es gut zu wissen, dass Microsoft.Extensions.AI das Delegieren von Implementierungen für das Zwischenspeichern IChatClient bietet. Das DistributedCachingChatClient ist ein IChatClient, das das Zwischenspeichern über eine andere beliebige IChatClient- Instanz legt. Wenn ein neuer Chatverlauf an die DistributedCachingChatClient gesendet wird, leitet sie ihn an den zugrunde liegenden Client weiter und speichert die Antwort im Zwischenspeicher, bevor sie an den Verbraucher zurückgeschickt wird. Wenn derselbe Verlauf das nächste Mal übermittelt wird, sodass eine zwischengespeicherte Antwort im Cache gefunden werden kann, gibt die DistributedCachingChatClient zwischengespeicherte Antwort zurück, anstatt die Anforderung entlang der Pipeline weiterzuleiten.
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();
}
Dieses Beispiel hängt vom Microsoft.Extensions.Caching.Memory NuGet-Paket ab📦. Weitere Informationen finden Sie unter Zwischenspeichern in .NET.
Verwenden der Telemetrie
Ein weiteres Beispiel für einen delegierenden Chat-Client ist die OpenTelemetryChatClient. Diese Implementierung entspricht den OpenTelemetry-Semantikkonventionen für generative KI-Systeme. Ähnlich wie andere IChatClient-Delegatoren schichtet es Metriken und erstreckt sich um andere beliebige IChatClientImplementierungen.
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);
(Das vorangehende Beispiel hängt vom 📦 NuGet-Paket "OpenTelemetry.Exporter.Console ".)
Alternativ bietet die LoggingChatClient und die entsprechende UseLogging(ChatClientBuilder, ILoggerFactory, Action<LoggingChatClient>) Methode eine einfache Möglichkeit zum Schreiben von Protokolleinträgen in eine ILogger für jede Anforderung und Antwort.
Bieten Sie Optionen
Jeder Aufruf von GetResponseAsync oder GetStreamingResponseAsync kann optional eine ChatOptions-Instanz liefern, die zusätzliche Parameter für den Vorgang enthält. Die gängigsten Parameter bei KI-Modellen und Diensten werden als stark typisierte Eigenschaften des Typs angezeigt, wie z. B. ChatOptions.Temperature. Andere Parameter können anhand eines schwach typierten Namens, über das ChatOptions.AdditionalProperties Wörterbuch oder über eine Optionsinstanz bereitgestellt werden, die vom zugrunde liegenden Anbieter mithilfe der ChatOptions.RawRepresentationFactory Eigenschaft verstanden wird.
Sie können auch Optionen angeben, wenn Sie ein IChatClient mit der fluent ChatClientBuilder API erstellen oder einen Aufruf der ConfigureOptions(ChatClientBuilder, Action<ChatOptions>)-Erweiterungsmethode verketten. Dieser delegierende Client umhüllt einen anderen Client und ruft den bereitgestellten Delegaten auf, um bei jedem Aufruf eine ChatOptions-Instanz zu befüllen. Um beispielsweise sicherzustellen, dass die Eigenschaft ChatOptions.ModelId standardmäßig einen bestimmten Modellnamen enthält, können Sie den folgenden Code verwenden:
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" }));
Funktionalitäts-Pipelines
IChatClient Instanzen können überschichtet werden, um eine Pipeline von Komponenten zu erstellen, die jeweils zusätzliche Funktionen hinzufügen. Diese Komponenten können aus Microsoft.Extensions.AI, anderen NuGet-Paketen oder benutzerdefinierten Implementierungen stammen. Mit diesem Ansatz können Sie das Verhalten der IChatClient auf verschiedene Weise erweitern, um Ihre spezifischen Anforderungen zu erfüllen. Betrachten Sie den folgenden Codeausschnitt, der einen verteilten Cache, einen Funktionsaufruf und die OpenTelemetry-Ablaufverfolgung um einen Beispiel-Chatclient überlagert.
// 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();
Angepasste IChatClient-Middleware
Um zusätzliche Funktionen hinzuzufügen, können Sie IChatClient direkt implementieren oder die DelegatingChatClient Klasse verwenden. Diese Klasse dient als Basis zum Erstellen von Chatclients, die Vorgänge an eine andere IChatClient-Instanz delegieren. Es vereinfacht das Verketten mehrerer Clients, wodurch Aufrufe an einen zugrunde liegenden Client übergeben werden können.
Die DelegatingChatClient-Klasse stellt Standardimplementierungen für Methoden wie GetResponseAsync, GetStreamingResponseAsync und Dispose bereit, die Aufrufe an den inneren Client weiterleiten. Eine abgeleitete Klasse kann dann nur die Methoden außer Kraft setzen, die zum Erweitern des Verhaltens erforderlich sind, während andere Aufrufe an die Basisimplementierung delegiert werden. Dieser Ansatz ist nützlich, um flexible und modulare Chatclients zu erstellen, die einfach zu erweitern und zu verfassen sind.
Im Folgenden finden Sie eine Beispielklasse, die von DelegatingChatClient abgeleitet ist und die System.Threading.RateLimiting-Bibliothek nutzt, um rate-limiting-Funktionalität bereitzustellen.
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);
}
}
Wie bei anderen IChatClient Implementierungen kann folgendes RateLimitingChatClient erstellt werden:
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?"));
Um die Komposition solcher Komponenten mit anderen zu vereinfachen, sollten die Autoren der Komponente eine Use*-Erweiterungsmethode erstellen, um die Komponente in einer Pipeline zu registrieren. Betrachten Sie beispielsweise die folgende UseRateLimiting Erweiterungsmethode:
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)
);
}
Solche Erweiterungen können auch relevante Dienste aus dem DI-Container abfragen; das von der Pipeline verwendete IServiceProvider wird als optionaler Parameter übergeben:
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>())
);
}
Jetzt ist es für den Verbraucher einfach, dies in ihrer Pipeline zu verwenden, z. B.:
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));
Die vorherigen Erweiterungsmethoden veranschaulichen die Verwendung einer Use Methode für ChatClientBuilder. Sie ChatClientBuilder bietet auch Use-Überladungen, die es einfacher machen, solche delegierenden Handler zu schreiben. Im früheren Beispiel RateLimitingChatClient müssen die Überschreibungen von GetResponseAsync und GetStreamingResponseAsync nur vor und nach der Delegierung an den nächsten Client in der Pipeline ausgeführt werden. Um dasselbe zu erreichen, ohne eine angepasste Klasse schreiben zu müssen, können Sie eine Überladung von Use verwenden, die einen Delegaten akzeptiert, der sowohl für GetResponseAsync als auch für GetStreamingResponseAsync verwendet wird, wodurch sich die erforderliche Boilerplate reduziert:
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();
Für Szenarien, in denen Sie unterschiedliche Implementierungen für GetResponseAsync und GetStreamingResponseAsync benötigen, um deren jeweils einzigartige Rückgabetypen zu behandeln, können Sie die Use(Func<IEnumerable<ChatMessage>,ChatOptions,IChatClient,CancellationToken,
Task<ChatResponse>>, Func<IEnumerable<ChatMessage>,ChatOptions,
IChatClient,CancellationToken,IAsyncEnumerable<ChatResponseUpdate>>)-Überladung verwenden, die für jeden einen Delegaten akzeptiert.
Abhängigkeitsinjektion
IChatClient Implementierungen werden häufig über Abhängigkeitsinjektion (DI) für eine Anwendung bereitgestellt. Im folgenden Beispiel wird ein IDistributedCache in den DI-Container hinzugefügt, ebenso wie ein IChatClient. Bei der Registrierung für IChatClient wird ein Builder verwendet, der eine Pipeline erstellt, die einen Caching-Client (der dann einen IDistributedCache verwendet, der von DI abgerufen wird) und den Beispiel-Client enthält. Das injizierte IChatClient kann an anderer Stelle in der App abgerufen und verwendet werden.
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?"));
Welche Instanz und Konfiguration eingefügt wird, kann je nach den aktuellen Anforderungen der Anwendung unterschiedlich sein, und mehrere Pipelines können mit unterschiedlichen Schlüsseln eingefügt werden.
Zustandslose vs. zustandsbehaftete Clients
Zustandslose Dienste erfordern, dass der gesamte relevante Konversationsverlauf bei jeder Anfrage übermittelt wird. Im Gegensatz dazu verfolgen zustandsbehaftete Dienste den Verlauf und benötigen lediglich zusätzliche Nachrichten, die zusammen mit einer Anforderung gesendet werden. Die IChatClient Schnittstelle wurde entwickelt, um sowohl zustandslose als auch zustandsbehaftete KI-Dienste zu verarbeiten.
Beim Arbeiten mit einem zustandslosen Dienst verwalten Anrufer eine Liste aller Nachrichten. Sie fügen alle empfangenen Antwortnachrichten hinzu und stellen die Liste bei nachfolgenden Interaktionen wieder zur Verfügung.
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);
}
Für zustandsbehaftete Dienste kennen Sie möglicherweise bereits die Kennung, die für die relevante Konversation verwendet wird. Sie können diesen Bezeichner in ChatOptions.ConversationId einfügen. Die Verwendung folgt dann demselben Muster, es sei denn, es ist nicht erforderlich, einen Verlauf manuell zu verwalten.
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));
}
Einige Dienste unterstützen möglicherweise das automatische Erstellen einer Unterhaltungs-ID für eine Anforderung, die keine besitzt, oder das Erstellen einer neuen Unterhaltungs-ID, die den aktuellen Zustand der Unterhaltung nach der Integration der letzten Nachrichtenrunde repräsentiert. In solchen Fällen können Sie die ChatResponse.ConversationId für nachfolgende Anfragen in die ChatOptions.ConversationId übertragen. Beispiel:
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;
}
Wenn Sie vorab nicht wissen, ob der Dienst zustandslos oder zustandsbehaftet ist, können Sie die Antwort ConversationId überprüfen und auf Grundlage ihres Wertes handeln. Wenn sie festgelegt ist, wird dieser Wert an die Optionen weitergegeben, und der Verlauf wird gelöscht, damit derselbe Verlauf nicht erneut zurückgegeben wird. Wenn die Antwort ConversationId nicht festgelegt ist, wird die Antwortnachricht dem Verlauf hinzugefügt, sodass sie beim nächsten Schritt an den Dienst zurückgesendet wird.
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);
}
}
Implementierungsbeispiele
Im folgenden Beispiel wird IChatClient implementiert, um die allgemeine Struktur zu zeigen.
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() { }
}
Für realistischere, konkrete Umsetzungen von IChatClient, siehe:
Chatreduzierung (experimentell)
Von Bedeutung
Dieses Feature ist experimentell und kann geändert werden.
Durch die Reduzierung von Chats können Sie den Unterhaltungsverlauf verwalten, indem sie die Anzahl der Nachrichten einschränken oder ältere Nachrichten zusammenfassen, wenn die Unterhaltung eine bestimmte Länge überschreitet. Die Microsoft.Extensions.AI Bibliothek bietet Reducer wie MessageCountingChatReducer , die die Anzahl von Nicht-System-Nachrichten begrenzen, und SummarizingChatReducer , die ältere Nachrichten automatisch zusammenfassen und dabei den Kontext beibehalten.