次の方法で共有


IChatClient インターフェイスを使用する

IChatClient インターフェイスは、チャット機能を提供する AI サービスとの対話を担当するクライアントの抽象化を定義します。 これには、完全なセットとして、または段階的にストリーミングされたマルチモーダル コンテンツ (テキスト、画像、オーディオなど) を含むメッセージを送受信するためのメソッドが含まれています。 さらに、クライアントまたはその基になるサービスによって提供される厳密に型指定されたサービスを取得できます。

言語モデルとサービスのクライアントを提供する .NET ライブラリは、 IChatClient インターフェイスの実装を提供できます。 その後、インターフェイスのすべてのコンシューマーは、抽象化を介してこれらのモデルやサービスとシームレスに相互運用できます。 例は、「実装例」セクション にあります

チャット応答を要求する

IChatClientのインスタンスを使用すると、IChatClient.GetResponseAsync メソッドを呼び出して要求を送信し、応答を取得できます。 要求は 1 つ以上のメッセージで構成され、それぞれが 1 つ以上のコンテンツで構成されます。 アクセラレータ メソッドは、テキスト コンテンツの 1 つの部分に対する要求の構築など、一般的なケースを簡略化するために存在します。

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

コア IChatClient.GetResponseAsync メソッドは、メッセージの一覧を受け入れます。 このリストは、会話の一部であるすべてのメッセージの履歴を表します。

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

ChatResponseから返されるGetResponseAsyncは、操作の一部として生成された 1 つ以上のメッセージを表すChatMessageインスタンスの一覧を公開します。 一般的なケースでは、応答メッセージは 1 つだけですが、状況によっては複数のメッセージが存在する場合があります。 メッセージ 一覧は、リスト内の最後のメッセージが要求の最後のメッセージを表すように並べ替えされます。 後続の要求でこれらのすべての応答メッセージをサービスに返すには、応答からのメッセージをメッセージ一覧に追加し直します。

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

ストリーミング チャットの応答を要求する

IChatClient.GetStreamingResponseAsync への入力は、GetResponseAsyncの入力と同じです。 ただし、完全な応答をChatResponseオブジェクトの一部として返すのではなく、このメソッドはIAsyncEnumerable<T>TとするChatResponseUpdateを返し、それによって単一の応答を形成する更新のストリームを提供します。

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

ヒント

ストリーミング API は、AI ユーザー エクスペリエンスとほぼ同義です。 C# を使用すると、 IAsyncEnumerable<T> サポートを使用して説得力のあるシナリオを実現し、自然で効率的な方法でデータをストリーミングできます。

GetResponseAsyncと同様に、IChatClient.GetStreamingResponseAsyncからの更新をメッセージ一覧に追加できます。 更新は応答の個々の部分であるため、 ToChatResponse(IEnumerable<ChatResponseUpdate>) などのヘルパーを使用して、1 つ以上の更新を 1 つの ChatResponse インスタンスに作成し直すことができます。

AddMessagesのようなヘルパーは、ChatResponseを作成し、応答から構成されたメッセージを抽出して一覧に追加します。

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

ツールの呼び出し

一部のモデルとサービスでは 、ツールの呼び出しがサポートされています。 追加情報を収集するために、モデルがクライアントに呼び出しを要求できるツール (通常は .NET メソッド) に関する情報を使用して ChatOptions を構成できます。 モデルは、最終的な応答を送信する代わりに、特定の引数を使用して関数呼び出しを要求します。 その後、クライアントは関数を呼び出し、会話履歴を使用して結果をモデルに送り返します。 Microsoft.Extensions.AI.Abstractions ライブラリには、関数呼び出し要求や結果など、さまざまなメッセージ コンテンツ タイプの抽象化が含まれています。 コンシューマー IChatClient 直接このコンテンツを操作できますが、 Microsoft.Extensions.AI は、対応する要求に応答してツールを自動的に呼び出すことができるヘルパーを提供します。 Microsoft.Extensions.AI.AbstractionsライブラリとMicrosoft.Extensions.AI ライブラリには、次の種類があります。

  • AIFunction: AI モデルに記述して呼び出すことができる関数を表します。
  • AIFunctionFactory: .NET メソッドを表す AIFunction インスタンスを作成するためのファクトリ メソッドを提供します。
  • FunctionInvokingChatClient: 自動関数呼び出し機能を追加する別のIChatClientとしてIChatClientをラップします。

次の例は、ランダムな関数呼び出しを示しています (この例は、📦 OllamaSharp NuGet パッケージによって異なります)。

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

前述のコード:

  • ランダムな天気予報を返す GetCurrentWeather という名前の関数を定義します。
  • ChatClientBuilder を使用して OllamaSharp.OllamaApiClient をインスタンス化し、関数呼び出しを使用するように構成します。
  • クライアントで GetStreamingResponseAsync を呼び出し、プロンプトと、 Createで作成された関数を含むツールの一覧を渡します。
  • 応答を反復処理し、各更新をコンソールに出力します。

AI 関数の作成の詳細については、「AI 関数 のデータにアクセスする」を参照してください。

IChatClientでモデル コンテキスト プロトコル (MCP) ツールを使用することもできます。 詳細については、「 最小 MCP クライアントの構築」を参照してください。

ツールの削減 (試験段階)

Important

この機能は試験段階であり、変更される可能性があります。

ツールの削減は、現在の会話コンテキストとの関連性に基づいて大きなツール カタログをトリミングすることで、大きなツール カタログを管理するのに役立ちます。 IToolReductionStrategy インターフェイスは、モデルに送信されるツールの数を減らすための戦略を定義します。 このライブラリには、会話と類似点を埋め込んでツールをランク付けする EmbeddingToolReductionStrategy などの実装が用意されています。 UseToolReduction拡張メソッドを使用して、チャット クライアント パイプラインにツールの削減を追加します。

応答のキャッシュ

.NET でのキャッシュに慣れている場合は、Microsoft.Extensions.AIがキャッシュの委任IChatClient実装を提供することを理解することをお勧めします。 DistributedCachingChatClient は、別の任意の IChatClient インスタンスの周りにキャッシュを階層化する IChatClient です。 新しいチャット履歴が DistributedCachingChatClientに送信されると、それを基になるクライアントに転送し、応答をキャッシュしてからコンシューマーに送り返します。 次に同じ履歴が送信されるときに、キャッシュされた応答がキャッシュに見つかると、 DistributedCachingChatClient はパイプラインに沿って要求を転送するのではなく、キャッシュされた応答を返します。

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

この例は📦 NuGet パッケージによって異なります。 詳細については、「.NETでのキャッシュ」を参照してください。

テレメトリを使用する

委任チャット クライアントのもう 1 つの例として、 OpenTelemetryChatClientがあります。 この実装は、 生成 AI システムの OpenTelemetry セマンティック規則に準拠しています。 他のIChatClient委任者と同様に、メトリックやスパンは他の任意の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);

(上記の例は、📦 OpenTelemetry.Exporter.Console NuGet パッケージによって異なります)。

また、 LoggingChatClient とそれに対応する UseLogging(ChatClientBuilder, ILoggerFactory, Action<LoggingChatClient>) メソッドを使用すると、要求と応答ごとにログ エントリを ILogger に簡単に書き込む方法が提供されます。

プロバイダー オプション

GetResponseAsync または GetStreamingResponseAsync を呼び出すたびに、必要に応じて、操作の追加パラメーターを含む ChatOptions インスタンスを指定できます。 AI モデルとサービスの中で最も一般的なパラメーターは、 ChatOptions.Temperatureなど、型に厳密に型指定されたプロパティとして表示されます。 その他のパラメーターは、厳密に型指定されていない方法で、 ChatOptions.AdditionalProperties ディクショナリを使用するか、基になるプロバイダーが理解するオプション インスタンスを使用して、 ChatOptions.RawRepresentationFactory プロパティを使用して、名前で指定できます。

IChatClient拡張メソッドの呼び出しをチェーンすることで、fluent ChatClientBuilder API を使用してConfigureOptions(ChatClientBuilder, Action<ChatOptions>)をビルドするときにオプションを指定することもできます。 この委任クライアントは、別のクライアントをラップし、指定されたデリゲートを呼び出して、呼び出しごとに ChatOptions インスタンスを設定します。 たとえば、 ChatOptions.ModelId プロパティの既定値が特定のモデル名であることを確認するには、次のようなコードを使用できます。

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

機能パイプライン

IChatClient インスタンスを階層化して、それぞれが追加機能を追加するコンポーネントのパイプラインを作成できます。 これらのコンポーネントは、 Microsoft.Extensions.AI、他の NuGet パッケージ、またはカスタム実装から取得できます。 このアプローチを使用すると、特定のニーズを満たすためにさまざまな方法で IChatClient の動作を拡張できます。 サンプル チャット クライアントを中心に分散キャッシュ、関数呼び出し、OpenTelemetry トレースを階層化する次のコード スニペットを考えてみましょう。

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

カスタム IChatClient ミドルウェア

追加の機能を追加するには、 IChatClient を直接実装するか、 DelegatingChatClient クラスを使用します。 このクラスは、操作を別の IChatClient インスタンスに委任するチャット クライアントを作成するためのベースとして機能します。 複数のクライアントを連鎖させることが容易になり、呼び出しが基盤となるクライアントに伝達されることを可能にします。

DelegatingChatClient クラスは、内部クライアントに呼び出しを転送する GetResponseAsyncGetStreamingResponseAsyncDisposeなどのメソッドの既定の実装を提供します。 派生クラスは、基本実装への他の呼び出しを委任しながら、動作を拡張するために必要なメソッドのみをオーバーライドできます。 このアプローチは、拡張と作成が容易な柔軟でモジュール式のチャット クライアントを作成する場合に便利です。

DelegatingChatClient ライブラリを使用してレート制限機能を提供するから派生したクラスの例を次に示します。

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

他の IChatClient 実装と同様に、 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?"));

このようなコンポーネントを他のコンポーネントと簡単に構成するには、コンポーネント作成者は、コンポーネントをパイプラインに登録するための Use* 拡張メソッドを作成する必要があります。 たとえば、次の 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)
        );
}

このような拡張機能は、DI コンテナーから関連するサービスを照会することもできます。パイプラインで使用される IServiceProvider は、省略可能なパラメーターとして渡されます。

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

これで、コンシューマーがパイプラインでこれを簡単に使用できるようになりました。次に例を示します。

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

前の拡張メソッドは、UseChatClientBuilderメソッドを使用する方法を示しています。 ChatClientBuilder また、このような委任ハンドラーの記述を容易にする Use オーバーロードも提供します。 たとえば、前の RateLimitingChatClient の例では、 GetResponseAsyncGetStreamingResponseAsync のオーバーライドは、パイプライン内の次のクライアントに委任する前と後にのみ必要です。 カスタム クラスを記述せずに同じことを実現するには、 UseGetResponseAsyncの両方に使用されるデリゲートを受け入れる GetStreamingResponseAsync のオーバーロードを使用して、必要な定型句を減らすことができます。

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

一意の戻り値の型を処理するために GetResponseAsyncGetStreamingResponseAsync に別の実装が必要なシナリオでは、それぞれにデリゲートを受け入れる Use(Func<IEnumerable<ChatMessage>,ChatOptions,IChatClient,CancellationToken, Task<ChatResponse>>, Func<IEnumerable<ChatMessage>,ChatOptions, IChatClient,CancellationToken,IAsyncEnumerable<ChatResponseUpdate>>) オーバーロードを使用できます。

依存関係の挿入

IChatClient 実装は、多くの場合、 依存関係の挿入 (DI) を介してアプリケーションに提供されます。 次の例では、IDistributedCacheと同様に、IChatClientが DI コンテナーに追加されます。 IChatClientの登録では、キャッシュ クライアント (次に DI から取得したIDistributedCacheを使用) とサンプル クライアントを含むパイプラインを作成するビルダーが使用されます。 挿入された IChatClient は、アプリ内の別の場所で取得して使用できます。

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

挿入されるインスタンスと構成は、アプリケーションの現在のニーズに応じて異なる場合があり、複数のパイプラインを異なるキーで挿入できます。

ステートレス クライアントとステートフル クライアント

ステートレス サービスでは、すべての要求で関連するすべての会話履歴を返送する必要があります。 これに対し、 ステートフル サービスは履歴を追跡し、追加のメッセージのみを要求と共に送信する必要があります。 IChatClient インターフェイスは、ステートレス AI サービスとステートフル AI サービスの両方を処理するように設計されています。

ステートレス サービスを使用する場合、呼び出し元はすべてのメッセージの一覧を保持します。 受信したすべての応答メッセージを追加し、後続の操作に関するリストを返します。

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

ステートフル サービスの場合、関連する会話に使用される識別子が既にわかっている可能性があります。 その識別子を ChatOptions.ConversationIdに配置できます。 その後、履歴を手動で保持する必要がない点を除き、使用状況は同じパターンに従います。

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

一部のサービスでは、1 つも持たない要求の会話 ID の自動作成や、メッセージの最後のラウンドを組み込んだ後の会話の現在の状態を表す新しい会話 ID の作成がサポートされる場合があります。 このような場合は、後続の要求のために ChatResponse.ConversationIdChatOptions.ConversationId に転送できます。 例えば次が挙げられます。

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

サービスがステートレスかステートフルかを事前に把握していない場合は、応答 ConversationId を確認し、その値に基づいてアクションを実行できます。 設定されている場合、その値はオプションに反映され、同じ履歴が再び再送信されないように履歴がクリアされます。 応答 ConversationId が設定されていない場合は、応答メッセージが履歴に追加され、次のターンでサービスに送り返されます。

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

実装の例

次の例では、一般的な構造を示す 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<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() { }
}

IChatClientのより現実的で具体的な実装については、次を参照してください。

チャットの縮小 (試験段階)

Important

この機能は試験段階であり、変更される可能性があります。

チャットの削減は、メッセージの数を制限したり、会話が指定された長さを超えたときに古いメッセージを要約したりして、会話履歴を管理するのに役立ちます。 Microsoft.Extensions.AI ライブラリには、システム以外のメッセージの数を制限するMessageCountingChatReducerや、コンテキストを維持しながら古いメッセージを自動的に集計するSummarizingChatReducerなどのレジューサーが用意されています。