다음을 통해 공유


IChatClient 인터페이스 사용

IChatClient 인터페이스는 채팅 기능을 제공하는 AI 서비스와 상호 작용하는 클라이언트 추상화 작업을 정의합니다. 이 방법에는 텍스트, 이미지 및 오디오와 같은 다중 모달 콘텐츠를 사용하여 메시지를 보내고 받는 기능이 포함되며, 이는 전체 집합으로 또는 점진적 스트리밍으로 이루어질 수 있습니다. 또한 클라이언트 또는 해당 기본 서비스에서 제공하는 강력한 형식의 서비스를 검색할 수 있습니다.

언어 모델 및 서비스에 대한 클라이언트를 제공하는 .NET 라이브러리는 IChatClient 인터페이스의 구현을 제공할 수 있습니다. 그러면 인터페이스의 모든 소비자가 추상화로 이러한 모델 및 서비스와 원활하게 상호 운용할 수 있습니다. 구현 예제 섹션에서 예제를 찾을 수 있습니다.

채팅 응답 요청

인스턴스 IChatClient를 사용하여 메서드를 IChatClient.GetResponseAsync 호출하여 요청을 보내고 응답을 가져올 수 있습니다. 요청은 하나 이상의 메시지로 구성되며 각 메시지는 하나 이상의 콘텐츠로 구성됩니다. 액셀러레이터 메서드는 단일 텍스트 콘텐츠에 대한 요청 생성과 같은 일반적인 사례를 간소화하기 위해 존재합니다.

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는 작업의 일부로 생성된 하나 이상의 메시지를 나타내는 ChatMessage 인스턴스 목록을 포함합니다. 일반적으로 하나의 응답 메시지만 있지만 경우에 따라 여러 메시지가 있을 수 있습니다. 목록의 마지막 메시지가 요청에 대한 최종 메시지를 나타내도록 메시지 목록의 순서가 지정됩니다. 후속 요청에서 이러한 모든 응답 메시지를 서비스에 다시 제공하려면 응답의 메시지를 메시지 목록에 다시 추가할 수 있습니다.

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>TChatResponseUpdate을 반환하여, 이 업데이트 스트림이 모여서 단일 응답을 형성하도록 합니다.

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

팁 (조언)

스트리밍 API는 AI 사용자 환경과 거의 동의어입니다. C#을 사용하면 IAsyncEnumerable<T> 지원으로 매력적인 시나리오를 구현하여 자연스럽고 효율적인 방식으로 데이터를 스트리밍할 수 있습니다.

GetResponseAsync와 마찬가지로, 업데이트를 IChatClient.GetStreamingResponseAsync에서 메시지 목록에 다시 추가할 수 있습니다. 업데이트는 응답의 개별 부분이므로 도우미를 사용하여 ToChatResponse(IEnumerable<ChatResponseUpdate>) 하나 이상의 업데이트를 단일 ChatResponse 인스턴스로 다시 작성할 수 있습니다.

도우미AddMessagesChatResponse을 구성한 다음, 응답에서 구성된 메시지를 추출하여 목록에 추가합니다.

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

도구 호출

일부 모델 및 서비스는 도구 호출을 지원합니다. 추가 정보를 수집하려면 모델이 클라이언트에 ChatOptions 호출을 요청할 수 있는 도구(일반적으로 .NET 메서드)에 대한 정보를 사용하여 구성할 수 있습니다. 최종 응답을 보내는 대신 모델은 특정 인수를 사용하여 함수 호출을 요청합니다. 그런 다음, 클라이언트는 함수를 호출하고 대화 기록을 사용하여 결과를 모델로 다시 보냅니다. Microsoft.Extensions.AI.Abstractions 라이브러리에는 함수 호출 요청 및 결과를 포함하여 다양한 메시지 콘텐츠 형식에 대한 추상화가 포함됩니다. 소비자는 이 콘텐츠와 직접 상호 작용할 수 있지만 IChatClient 해당 Microsoft.Extensions.AI 요청에 대한 응답으로 도구를 자동으로 호출할 수 있는 도우미를 제공합니다. Microsoft.Extensions.AI.AbstractionsMicrosoft.Extensions.AI 라이브러리는 다음 형식을 제공합니다.

  • AIFunction: AI 모델에 설명하고 호출할 수 있는 함수를 나타냅니다.
  • AIFunctionFactory: .NET 메서드를 나타내는 인스턴스를 만들기 AIFunction 위한 팩터리 메서드를 제공합니다.
  • FunctionInvokingChatClient: IChatClientIChatClient을(를) 감싸 자동 함수 호출 기능을 추가합니다.

다음 예제에서는 임의 함수 호출을 보여 줍니다(이 예제는 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 함수의 Access 데이터를 참조하세요.

MCP(모델 컨텍스트 프로토콜) 도구를 IChatClient와 함께 사용할 수도 있습니다. 자세한 내용은 최소 MCP 클라이언트 빌드를 참조하세요.

도구 감소(실험적)

중요합니다

이 기능은 실험적이며 변경될 수 있습니다.

도구 축소는 현재 대화 컨텍스트의 관련성에 따라 트리밍하여 큰 도구 카탈로그를 관리하는 데 도움이 됩니다. 인터페이스는 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();
}

이 예제는 Microsoft.Extensions.Caching.Memory NuGet 패키지에📦 따라 달라집니다. 자세한 내용은 .NET에서의 캐싱을 참조하세요.

원격 분석 사용

위임 채팅 클라이언트의 또 다른 예는 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 패키지에 따라 달라집니다.

LoggingChatClientUseLogging(ChatClientBuilder, ILoggerFactory, Action<LoggingChatClient>) 메서드는 모든 요청 및 응답에 대한 로그 항목을 ILogger에 기록하는 간단한 방법을 제공합니다.

옵션 제공

GetResponseAsync 또는 GetStreamingResponseAsync 대한 모든 호출은 필요에 따라 작업에 대한 추가 매개 변수를 포함하는 ChatOptions 인스턴스를 제공할 수 있습니다. AI 모델 및 서비스에서 가장 일반적인 매개 변수는 형식에서 강력한 형 지정 속성으로(예: ChatOptions.Temperature) 표시됩니다. 속성을 사용하여 ChatOptions.AdditionalProperties 방식으로, ChatOptions.RawRepresentationFactory 사전을 통해 약한 형식으로 다른 매개변수를 이름으로 제공하거나 기본 공급자가 이해하는 옵션 인스턴스를 통해 제공할 수 있습니다.

흐름 API인 IChatClient를 사용하여 ChatClientBuilder을(를) 빌드할 때 확장 메서드 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 클래스는 내부 클라이언트에 호출을 전달하는 GetResponseAsync, GetStreamingResponseAsyncDispose같은 메서드에 대한 기본 구현을 제공합니다. 파생 클래스는 기본 구현에 대한 다른 호출을 위임하는 동시에 동작을 보강하는 데 필요한 메서드만 재정의할 수 있습니다. 이 방법은 확장 및 작성하기 쉬운 유연하고 모듈식 채팅 클라이언트를 만드는 데 유용합니다.

다음은 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));

이전 확장 메서드는 Use 메서드를 ChatClientBuilder에 사용하는 방법을 보여 줍니다. 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(종속성 주입)를 통해 애플리케이션에 제공됩니다. DI 컨테이너에 IDistributedCacheIChatClient가 추가되는 다음 예제를 살펴보십시오. 등록 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 서비스를 모두 처리하도록 설계되었습니다.

상태 비저장 서비스를 사용하는 경우 호출자는 모든 메시지 목록을 유지 관리합니다. 수신된 모든 응답 메시지를 추가하고 후속 상호 작용에 대한 목록을 다시 제공합니다.

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

일부 서비스에서는 요청이 없는 요청에 대한 대화 ID를 자동으로 만들거나 마지막 메시지 라운드를 통합한 후 대화의 현재 상태를 나타내는 새 대화 ID를 만들 수 있습니다. 이러한 경우, ChatResponse.ConversationId을(를) ChatOptions.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를 참조하십시오.

채팅 감소(실험적)

중요합니다

이 기능은 실험적이며 변경될 수 있습니다.

채팅 감소는 메시지 수를 제한하거나 대화가 지정된 길이를 초과할 때 이전 메시지를 요약하여 대화 기록을 관리하는 데 도움이 됩니다. 라이브러리는 Microsoft.Extensions.AI와 같이 비시스템 메시지의 수를 제한하는 리듀서와, MessageCountingChatReducer처럼 컨텍스트를 유지하면서 이전 메시지를 자동으로 요약하는 리듀서를 제공합니다.