.NET의 인공 지능(미리 보기)
점점 더 다양한 AI(인공 지능) 서비스를 사용할 수 있으므로 개발자는 .NET 애플리케이션에서 이러한 서비스를 통합하고 상호 작용하는 방법이 필요합니다.
Microsoft.Extensions.AI
라이브러리는 다양한 AI 서비스와의 원활한 통합 및 상호 운용성을 가능하게 하는 생성 AI 구성 요소를 나타내는 통합된 접근 방식을 제공합니다. 이 문서에서는 라이브러리를 소개하고 시작하는 데 도움이 되는 설치 지침 및 사용 예제를 제공합니다.
패키지 설치
📦 Microsoft.Extensions.AI NuGet 패키지를 설치하려면 .NET CLI를 사용하거나 C# 프로젝트 파일에 직접 패키지 참조를 추가합니다.
자세한 내용은 dotnet 패키지 추가 또는 .NET 애플리케이션에서 패키지 종속성 관리 를 참조하세요.
사용 예제
IChatClient 인터페이스는 채팅 기능을 제공하는 AI 서비스와 상호 작용하는 클라이언트 추상화 작업을 정의합니다. 이 방법에는 텍스트, 이미지 및 오디오와 같은 다중 모달 콘텐츠를 사용하여 메시지를 보내고 받는 기능이 포함되며, 이는 전체 집합으로 또는 점진적 스트리밍으로 이루어질 수 있습니다. 또한 클라이언트에 대한 메타데이터 정보를 제공하고 강력한 형식의 서비스를 검색할 수 있습니다.
중요하다
더 많은 사용 예제 및 실제 시나리오는.NET 개발자를 위한
이 섹션
IChatClient
인터페이스
다음 샘플에서는 일반 구조를 표시하는 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<ChatCompletion> CompleteAsync(
IList<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()
{
Role = ChatRole.Assistant,
Text = responses[Random.Shared.Next(responses.Length)],
}]);
}
public async IAsyncEnumerable<StreamingChatCompletionUpdate> CompleteStreamingAsync(
IList<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 StreamingChatCompletionUpdate
{
Role = ChatRole.Assistant,
Text = 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() { }
}
다음 NuGet 패키지에서 IChatClient
다른 구체적인 구현을 찾을 수 있습니다.
- 📦 Microsoft.Extensions.AI.AzureAIInference: Azure AI 모델 유추 API통해 지원되는 구현입니다.
- 📦 Microsoft.Extensions.AI.Ollama: Ollama기반으로 한 구현입니다.
- 📦 Microsoft.Extensions.AI.OpenAI: OpenAI 또는 OpenAI 호환 엔드포인트(예: Azure OpenAI)에서 지원되는 구현입니다.
채팅 완료 요청
완료를 요청하려면 IChatClient.CompleteAsync 메서드를 호출합니다. 요청은 하나 이상의 메시지로 구성되며 각 메시지는 하나 이상의 콘텐츠로 구성됩니다. 액셀러레이터 메서드는 단일 텍스트 콘텐츠에 대한 요청 생성과 같은 일반적인 사례를 간소화하기 위해 존재합니다.
using Microsoft.Extensions.AI;
IChatClient client = new SampleChatClient(
new Uri("http://coolsite.ai"), "target-ai-model");
var response = await client.CompleteAsync("What is AI?");
Console.WriteLine(response.Message);
핵심 IChatClient.CompleteAsync
메서드는 메시지 목록을 허용합니다. 이 목록은 대화의 일부인 모든 메시지의 기록을 나타냅니다.
using Microsoft.Extensions.AI;
IChatClient client = new SampleChatClient(
new Uri("http://coolsite.ai"), "target-ai-model");
Console.WriteLine(await client.CompleteAsync(
[
new(ChatRole.System, "You are a helpful AI assistant"),
new(ChatRole.User, "What is AI?"),
]));
기록의 각 메시지는 ChatMessage 개체로 표시됩니다.
ChatMessage
클래스는 메시지의 역할을 나타내는 ChatMessage.Role 속성을 제공합니다. 기본적으로 ChatRole.User 사용됩니다. 사용할 수 있는 역할은 다음과 같습니다.
- ChatRole.Assistant: 도우미의 동작을 지시하거나 설정합니다.
- ChatRole.System: 시스템 지시, 사용자 프롬프트 입력에 대한 응답을 제공합니다.
- ChatRole.Tool: 채팅 완료를 위한 추가 정보 및 참조를 제공합니다.
- ChatRole.User: 채팅 완료를 위한 입력을 제공합니다.
각 채팅 메시지는 인스턴스화되어 Contents 속성에 새 TextContent이 할당됩니다. 간단한 문자열 또는 텍스트, 이미지 및 오디오가 있는 다중 모달 메시지를 나타내는 더 복잡한 개체와 같이 나타낼 수 있는 다양한 유형의 콘텐츠 있습니다.
- AudioContent
- DataContent
- FunctionCallContent
- FunctionResultContent
- ImageContent
- TextContent
- UsageContent
스트리밍을 사용하여 채팅 완료 요청
IChatClient.CompleteStreamingAsync 입력은 CompleteAsync
입력과 동일합니다. 메서드는 전체 응답을 ChatCompletion 개체의 일부로 반환하는 대신 IAsyncEnumerable<T>가 T
인 StreamingChatCompletionUpdate을 반환하여, 이 업데이트 스트림이 모여서 단일 응답을 형성하도록 합니다.
using Microsoft.Extensions.AI;
IChatClient client = new SampleChatClient(
new Uri("http://coolsite.ai"), "target-ai-model");
await foreach (var update in client.CompleteStreamingAsync("What is AI?"))
{
Console.Write(update);
}
팁
스트리밍 API는 AI 사용자 환경과 거의 동의어입니다. C#을 사용하면 IAsyncEnumerable<T>
지원으로 매력적인 시나리오를 구현하여 자연스럽고 효율적인 방식으로 데이터를 스트리밍할 수 있습니다.
도구 호출
일부 모델 및 서비스는호출하는 Microsoft.Extensions.AI
라이브러리에는 함수 호출 요청 및 결과를 포함하여 다양한 메시지 콘텐츠 형식에 대한 추상화가 포함됩니다. 소비자는 이 콘텐츠와 직접 상호 작용할 수 있지만, Microsoft.Extensions.AI
는 이러한 상호 작용을 자동화하여 다음을 제공합니다.
- AIFunction: AI 서비스에 설명하고 호출할 수 있는 함수를 나타냅니다.
-
AIFunctionFactory:
AIFunction
의 자주 사용되는 구현을 생성하기 위한 팩터리 메서드를 제공합니다. -
FunctionInvokingChatClient:
IChatClient
을(를) 감싸서 자동 함수 호출 기능을 추가합니다.
임의 함수 호출을 보여 주는 다음 예제를 살펴보겠습니다.
using System.ComponentModel;
using Microsoft.Extensions.AI;
[Description("Gets the current weather")]
string GetCurrentWeather() => Random.Shared.NextDouble() > 0.5
? "It's sunny"
: "It's raining";
IChatClient client = new ChatClientBuilder(
new OllamaChatClient(new Uri("http://localhost:11434"), "llama3.1"))
.UseFunctionInvocation()
.Build();
var response = client.CompleteStreamingAsync(
"Should I wear a rain coat?",
new() { Tools = [AIFunctionFactory.Create(GetCurrentWeather)] });
await foreach (var update in response)
{
Console.Write(update);
}
앞의 예제는 📦 Microsoft.Extensions.AI.Ollama NuGet 패키지에 따라 달라집니다.
앞의 코드는 다음과 같습니다.
- 임의 일기 예보를 반환하는
GetCurrentWeather
함수를 정의합니다.- 이 함수는 AI 서비스에 함수 설명을 제공하기 위해 사용되는 DescriptionAttribute로 데코레이트됩니다.
- ChatClientBuilder를 사용하여 OllamaChatClient을 인스턴스화하고 함수 호출을 사용하도록 구성합니다.
- 클라이언트에서
CompleteStreamingAsync
호출하여 프롬프트 및 Create사용하여 만든 함수를 포함하는 도구 목록을 전달합니다. - 응답을 반복하여 각 업데이트를 콘솔에 인쇄합니다.
캐시 응답
.NETIChatClient
인스턴스 주위에 캐싱을 레이어하는 IChatClient
입니다. 고유한 채팅 기록이 DistributedCachingChatClient
에 제출되면, 이는 기본 클라이언트로 전달되고 응답을 캐시한 후 다시 소비자에게 전송됩니다. 다음에 캐시된 응답을 캐시에서 찾을 수 있도록 동일한 프롬프트가 제출될 때 DistributedCachingChatClient
파이프라인을 따라 요청을 전달할 필요 없이 캐시된 응답을 반환합니다.
using Microsoft.Extensions.AI;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Options;
var sampleChatClient = new SampleChatClient(
new Uri("http://coolsite.ai"), "target-ai-model");
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.CompleteStreamingAsync(prompt))
{
Console.Write(update);
}
Console.WriteLine();
}
앞의 예제는 📦 Microsoft.Extensions.Caching.Memory NuGet 패키지에 따라 달라집니다. 자세한 내용은 .NET에서의 캐싱
원격 분석 사용
위임 채팅 클라이언트의 또 다른 예는 OpenTelemetryChatClient. 이 구현은생성 AI 시스템에 대한 IChatClient
위임자와 마찬가지로, 메트릭을 계층화하고 기본 IChatClient
구현을 중심으로 확장하여 향상된 관찰 가능성을 제공합니다.
using Microsoft.Extensions.AI;
using OpenTelemetry.Trace;
// Configure OpenTelemetry exporter
var sourceName = Guid.NewGuid().ToString();
var tracerProvider = OpenTelemetry.Sdk.CreateTracerProviderBuilder()
.AddSource(sourceName)
.AddConsoleExporter()
.Build();
var sampleChatClient = new SampleChatClient(
new Uri("http://coolsite.ai"), "target-ai-model");
IChatClient client = new ChatClientBuilder(sampleChatClient)
.UseOpenTelemetry(
sourceName: sourceName,
configure: static c => c.EnableSensitiveData = true)
.Build();
Console.WriteLine((await client.CompleteAsync("What is AI?")).Message);
앞의 예제는 📦 OpenTelemetry.Exporter.Console NuGet 패키지에 따라 달라집니다.
옵션 제공
CompleteAsync 또는 CompleteStreamingAsync 대한 모든 호출은 필요에 따라 작업에 대한 추가 매개 변수를 포함하는 ChatOptions 인스턴스를 제공할 수 있습니다. AI 모델 및 서비스에서 가장 일반적인 매개 변수는 형식에서 강력한 형 지정 속성으로(예: ChatOptions.Temperature) 표시됩니다. 다른 매개 변수는 ChatOptions.AdditionalProperties 사전을 통해 약하게 형식화된 방식으로 이름으로 제공할 수 있습니다.
옵션을 지정하여 IChatClient
를 빌드할 수 있으며, 이를 위해 fluent ChatClientBuilder API를 사용하고 ConfigureOptions
확장 메서드로의 호출을 연결할 수 있습니다. 이 위임 클라이언트는 다른 클라이언트를 감싸고, 제공된 대리자를 호출하여 각 호출에서 ChatOptions
인스턴스를 생성합니다. 예를 들어 ChatOptions.ModelId 속성이 기본값으로 특정 모델 이름으로 설정되도록 하려면 다음과 같은 코드를 사용할 수 있습니다.
using Microsoft.Extensions.AI;
IChatClient client = new ChatClientBuilder(
new OllamaChatClient(new Uri("http://localhost:11434")))
.ConfigureOptions(options => options.ModelId ??= "phi3")
.Build();
// will request "phi3"
Console.WriteLine(await client.CompleteAsync("What is AI?"));
// will request "llama3.1"
Console.WriteLine(await client.CompleteAsync(
"What is AI?", new() { ModelId = "llama3.1" }));
앞의 예제는 📦 Microsoft.Extensions.AI.Ollama NuGet 패키지에 따라 달라집니다.
기능 파이프라인
IChatClient
인스턴스를 계층화하여 특정 기능을 추가하는 구성 요소 파이프라인을 만들 수 있습니다. 이러한 구성 요소는 Microsoft.Extensions.AI
, 기타 NuGet 패키지 또는 사용자 지정 구현에서 제공됩니다. 이 방법을 사용하면 특정 요구 사항을 충족하기 위해 다양한 방법으로 IChatClient
동작을 보강할 수 있습니다. 다음 예제 코드는 샘플 채팅 클라이언트를 중심으로 분산 캐시, 함수 호출 및 OpenTelemetry 추적을 계층화합니다.
using Microsoft.Extensions.AI;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Options;
using OpenTelemetry.Trace;
// Configure OpenTelemetry exporter
var sourceName = Guid.NewGuid().ToString();
var tracerProvider = OpenTelemetry.Sdk.CreateTracerProviderBuilder()
.AddSource(sourceName)
.AddConsoleExporter()
.Build();
// Explore changing the order of the intermediate "Use" calls to see that impact
// that has on what gets cached, traced, etc.
IChatClient client = new ChatClientBuilder(
new OllamaChatClient(new Uri("http://localhost:11434"), "llama3.1"))
.UseDistributedCache(new MemoryDistributedCache(
Options.Create(new MemoryDistributedCacheOptions())))
.UseFunctionInvocation()
.UseOpenTelemetry(
sourceName: sourceName,
configure: static c => c.EnableSensitiveData = true)
.Build();
ChatOptions options = new()
{
Tools =
[
AIFunctionFactory.Create(
() => Random.Shared.NextDouble() > 0.5 ? "It's sunny" : "It's raining",
name: "GetCurrentWeather",
description: "Gets the current weather")
]
};
for (int i = 0; i < 3; ++i)
{
List<ChatMessage> history =
[
new ChatMessage(ChatRole.System, "You are a helpful AI assistant"),
new ChatMessage(ChatRole.User, "Do I need an umbrella?")
];
Console.WriteLine(await client.CompleteAsync(history, options));
}
앞의 예제는 다음 NuGet 패키지에 따라 달라집니다.
- 📦 Microsoft.Extensions.Caching.Memory
- 📦 Microsoft.Extensions.AI.Ollama
- 📦 OpenTelemetry.Exporter.Console
사용자 지정 IChatClient
미들웨어
추가 기능을 추가하려면 IChatClient
직접 구현하거나 DelegatingChatClient 클래스를 사용할 수 있습니다. 이 클래스는 작업을 다른 IChatClient
인스턴스에 위임하는 채팅 클라이언트를 만들기 위한 기반으로 사용됩니다. 여러 클라이언트를 연결하여 호출이 기본 클라이언트로 전달되도록 간소화합니다.
DelegatingChatClient
클래스는 내부 클라이언트에 호출을 전달하는 CompleteAsync
, CompleteStreamingAsync
및 Dispose
같은 메서드에 대한 기본 구현을 제공합니다. 필요한 메서드만 재정의하여 동작을 향상시키는 동시에 다른 호출은 기본 구현에 위임할 수 있도록 이 클래스를 상속받을 수 있습니다. 이 방법은 확장 및 작성하기 쉬운 유연하고 모듈식 채팅 클라이언트를 만드는 데 도움이 됩니다.
다음은 DelegatingChatClient
에서 파생되어 RateLimiter을 활용하여 속도 제한 기능을 제공하는 예제 클래스입니다.
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<ChatCompletion> CompleteAsync(
IList<ChatMessage> chatMessages,
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.CompleteAsync(chatMessages, options, cancellationToken)
.ConfigureAwait(false);
}
public override async IAsyncEnumerable<StreamingChatCompletionUpdate> CompleteStreamingAsync(
IList<ChatMessage> chatMessages,
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.CompleteStreamingAsync(chatMessages, options, cancellationToken)
.ConfigureAwait(false))
{
yield return update;
}
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
rateLimiter.Dispose();
}
base.Dispose(disposing);
}
}
앞의 예제는 📦 System.Threading.RateLimiting NuGet 패키지에 따라 달라집니다. 다른 클라이언트와의 RateLimitingChatClient
구성은 간단합니다.
using Microsoft.Extensions.AI;
using System.Threading.RateLimiting;
var client = new RateLimitingChatClient(
new SampleChatClient(new Uri("http://localhost"), "test"),
new ConcurrencyLimiter(new()
{
PermitLimit = 1,
QueueLimit = int.MaxValue
}));
await client.CompleteAsync("What color is the sky?");
이러한 구성 요소를 다른 구성 요소와 결합하기 쉽게 하기 위해, 구성 요소 작성자는 Use*
확장 메서드를 만들어 해당 구성 요소를 파이프라인에 등록해야 합니다. 예를 들어 다음 확장 메서드를 고려합니다.
namespace Example.One;
// <one>
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));
}
// </one>
이러한 확장은 DI 컨테이너에서 관련 서비스를 쿼리할 수도 있습니다. 파이프라인에서 사용하는 것으로 표시된 IServiceProvider은 선택적 매개 변수로 전달됩니다.
namespace Example.Two;
// <two>
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,
rateLimiter ?? services.GetRequiredService<RateLimiter>()));
}
// </two>
그러면 소비자는 파이프라인에서 이를 쉽게 사용할 수 있습니다. 예를 들면 다음과 같습니다.
using Microsoft.Extensions.AI;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
var builder = Host.CreateApplicationBuilder(args);
builder.Services.AddChatClient(services =>
new SampleChatClient(new Uri("http://localhost"), "test")
.AsBuilder()
.UseDistributedCache()
.UseRateLimiting()
.UseOpenTelemetry()
.Build(services));
using var app = builder.Build();
// Elsewhere in the app
var chatClient = app.Services.GetRequiredService<IChatClient>();
Console.WriteLine(await chatClient.CompleteAsync("What is AI?"));
app.Run();
이 예제는 소비자가 종속성 주입을 사용해 인스턴스를 제공받는 RateLimiter
을 보여줍니다. 앞서 언급한 확장 메서드는 Use
메서드를 ChatClientBuilder에서 사용하는 방법을 보여 줍니다. 또한 ChatClientBuilder
이러한 위임 처리기를 더 쉽게 작성할 수 있는 Use 오버로드를 제공합니다.
- Use(AnonymousDelegatingChatClient+CompleteSharedFunc)
- Use(Func<IChatClient,IChatClient>)
- Use(Func<IChatClient,IServiceProvider,IChatClient>)
- Use(Func<IList<ChatMessage>,ChatOptions,IChatClient,CancellationToken, Task<ChatCompletion>>, Func<IList<ChatMessage>,ChatOptions,IChatClient, CancellationToken,IAsyncEnumerable<StreamingChatCompletionUpdate>>)
예를 들어 이전 RateLimitingChatClient
예제에서 CompleteAsync
및 CompleteStreamingAsync
재정의는 파이프라인의 다음 클라이언트에 위임하기 전과 후에만 작업을 진행해야 합니다. 사용자 지정 클래스를 작성할 필요 없이 동일한 작업을 수행하려면 Use
및 CompleteAsync
모두에 사용될 대리자를 허용하는 CompleteStreamingAsync
의 오버로드 기능을 사용하여 반복적인 코드를 줄일 수 있습니다.
using Microsoft.Extensions.AI;
using System.Threading.RateLimiting;
RateLimiter rateLimiter = new ConcurrencyLimiter(new()
{
PermitLimit = 1,
QueueLimit = int.MaxValue
});
var client = new SampleChatClient(new Uri("http://localhost"), "test")
.AsBuilder()
.UseDistributedCache()
.Use(async (chatMessages, 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(chatMessages, options, cancellationToken);
})
.UseOpenTelemetry()
.Build();
// Use client
위의 오버로드는 내부적으로 AnonymousDelegatingChatClient
을 사용하며, 약간의 추가 코드만으로 더 복잡한 패턴을 가능하게 합니다. 예를 들어, DI에서 RateLimiter을 검색하여 동일한 결과를 얻으려면 다음을 수행합니다.
using System.Threading.RateLimiting;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.DependencyInjection;
var client = new SampleChatClient(new Uri("http://localhost"), "test")
.AsBuilder()
.UseDistributedCache()
.Use(static (innerClient, services) =>
{
var rateLimiter = services.GetRequiredService<RateLimiter>();
return new AnonymousDelegatingChatClient(
innerClient, async (chatMessages, 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(chatMessages, options, cancellationToken);
});
})
.UseOpenTelemetry()
.Build();
개발자가 CompleteAsync
및 CompleteStreamingAsync
인라인의 위임 구현을 지정하고 고유한 반환 형식을 특별히 처리하기 위해 각각에 대해 다른 구현을 작성할 수 있어야 하는 시나리오의 경우 각각에 대한 대리자를 수락하는 또 다른 Use
오버로드가 존재합니다.
종속성 주입
IChatClient 구현은 일반적으로 종속성 주입(DI)을 통해 애플리케이션에 제공될 것입니다. 이 예제에서는 IDistributedCache이 DI 컨테이너에 추가되고, IChatClient
도 추가됩니다.
IChatClient
등록은 캐싱 클라이언트(DI에서 검색된 IDistributedCache
사용)와 샘플 클라이언트를 포함하는 파이프라인을 만드는 작성기를 사용합니다. 삽입된 IChatClient
앱의 다른 위치에서 검색하고 사용할 수 있습니다.
using Microsoft.Extensions.AI;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
// App setup
var builder = Host.CreateApplicationBuilder();
builder.Services.AddDistributedMemoryCache();
builder.Services.AddChatClient(new SampleChatClient(
new Uri("http://coolsite.ai"), "target-ai-model"))
.UseDistributedCache();
using var app = builder.Build();
// Elsewhere in the app
var chatClient = app.Services.GetRequiredService<IChatClient>();
Console.WriteLine(await chatClient.CompleteAsync("What is AI?"));
app.Run();
앞의 예제는 다음 NuGet 패키지에 따라 달라집니다.
- Microsoft.Extensions.Hosting
- 📦 Microsoft.Extensions.Caching.Memory
삽입되는 인스턴스 및 구성은 애플리케이션의 현재 요구 사항에 따라 다를 수 있으며 여러 파이프라인을 다른 키로 삽입할 수 있습니다.
IEmbeddingGenerator
인터페이스
IEmbeddingGenerator<TInput,TEmbedding> 인터페이스는 임베딩의 일반적인 생성기를 나타냅니다. 여기서 TInput
포함되는 입력 값의 형식이며, TEmbedding
Embedding 클래스에서 상속되는 생성된 포함 형식입니다.
Embedding
클래스는 IEmbeddingGenerator
에 의해 생성된 포함의 기본 클래스 역할을 합니다. 포함과 관련된 메타데이터 및 데이터를 저장하고 관리하도록 설계되었습니다.
Embedding<T>
같은 파생 형식은 구체적인 포함 벡터 데이터를 제공합니다. 예를 들어, 임베딩은 임베딩 데이터를 액세스할 수 있도록 Embedding<T>.Vector 속성을 노출합니다.
IEmbeddingGenerator
인터페이스는 선택적 구성 및 취소 지원을 사용하여 입력 값 모음에 대해 임베딩을 비동기적으로 생성하는 메서드를 정의합니다. 또한 생성기를 설명하는 메타데이터를 제공하고 생성기 또는 해당 기본 서비스에서 제공할 수 있는 강력한 형식의 서비스를 검색할 수 있습니다.
샘플 구현
일반적인 구조를 보여주기 위해 무작위 임베딩 벡터를 생성하는 IEmbeddingGenerator
을(를) 보여주는 다음의 샘플 구현을 고려하십시오.
using Microsoft.Extensions.AI;
public sealed class SampleEmbeddingGenerator(
Uri endpoint, string modelId)
: IEmbeddingGenerator<string, Embedding<float>>
{
public EmbeddingGeneratorMetadata Metadata { get; } =
new(nameof(SampleEmbeddingGenerator), endpoint, modelId);
public async Task<GeneratedEmbeddings<Embedding<float>>> GenerateAsync(
IEnumerable<string> values,
EmbeddingGenerationOptions? options = null,
CancellationToken cancellationToken = default)
{
// Simulate some async operation
await Task.Delay(100, cancellationToken);
// Create random embeddings
return
[
.. from value in values
select new Embedding<float>(
Enumerable.Range(0, 384)
.Select(_ => Random.Shared.NextSingle())
.ToArray())
];
}
public object? GetService(Type serviceType, object? serviceKey) => this;
public TService? GetService<TService>(object? key = null)
where TService : class => this as TService;
void IDisposable.Dispose() { }
}
앞의 코드는 다음과 같습니다.
-
SampleEmbeddingGenerator
인터페이스를 구현하는IEmbeddingGenerator<string, Embedding<float>>
클래스를 정의합니다. - 생성기를 식별하는 데 사용되는 엔드포인트 및 모델 ID를 허용하는 기본 생성자가 있습니다.
- 생성기에 대한 메타데이터를 제공하는
Metadata
속성을 노출합니다. - 입력 값 컬렉션에 대한 임베딩을 생성하기 위해
GenerateAsync
메서드를 구현합니다.- 100밀리초 동안 지연하여 비동기 작업을 시뮬레이트합니다.
- 각 입력 값에 대한 임의의 임베딩을 반환합니다.
다음 패키지에서 실제 구체적인 구현을 찾을 수 있습니다.
임베딩 생성
IEmbeddingGenerator<TInput,TEmbedding>을 사용하여 수행되는 주요 작업은 GenerateAsync 메서드를 이용한 임베딩 생성입니다.
using Microsoft.Extensions.AI;
IEmbeddingGenerator<string, Embedding<float>> generator =
new SampleEmbeddingGenerator(
new Uri("http://coolsite.ai"), "target-ai-model");
foreach (var embedding in await generator.GenerateAsync(["What is AI?", "What is .NET?"]))
{
Console.WriteLine(string.Join(", ", embedding.Vector.ToArray()));
}
사용자 지정 IEmbeddingGenerator
미들웨어
IChatClient
마찬가지로 IEmbeddingGenerator
구현을 계층화할 수 있습니다.
Microsoft.Extensions.AI
이 캐싱 및 원격 분석을 위한 IChatClient
위임 구현을 제공하는 것처럼, IEmbeddingGenerator
에 대한 구현도 제공합니다.
using Microsoft.Extensions.AI;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Options;
using OpenTelemetry.Trace;
// Configure OpenTelemetry exporter
var sourceName = Guid.NewGuid().ToString();
var tracerProvider = OpenTelemetry.Sdk.CreateTracerProviderBuilder()
.AddSource(sourceName)
.AddConsoleExporter()
.Build();
// Explore changing the order of the intermediate "Use" calls to see that impact
// that has on what gets cached, traced, etc.
var generator = new EmbeddingGeneratorBuilder<string, Embedding<float>>(
new SampleEmbeddingGenerator(new Uri("http://coolsite.ai"), "target-ai-model"))
.UseDistributedCache(
new MemoryDistributedCache(
Options.Create(new MemoryDistributedCacheOptions())))
.UseOpenTelemetry(sourceName: sourceName)
.Build();
var embeddings = await generator.GenerateAsync(
[
"What is AI?",
"What is .NET?",
"What is AI?"
]);
foreach (var embedding in embeddings)
{
Console.WriteLine(string.Join(", ", embedding.Vector.ToArray()));
}
IEmbeddingGenerator
는 IEmbeddingGenerator
의 기능을 확장할 수 있는 사용자 지정 미들웨어를 구축할 수 있도록 합니다.
DelegatingEmbeddingGenerator<TInput,TEmbedding> 클래스는 다른 IEmbeddingGenerator<TInput, TEmbedding>
인스턴스에 작업을 위임하는 포함 생성기를 만들기 위한 기본 클래스 역할을 하는 IEmbeddingGenerator<TInput, TEmbedding>
인터페이스의 구현입니다. 여러 생성기를 순서대로 연결하여 기본 생성기에 호출을 전달할 수 있습니다. 이 클래스는 호출을 내부 생성기 인스턴스로 전달하는 GenerateAsync 및 Dispose
같은 메서드에 대한 기본 구현을 제공하여 유연하고 모듈식 포함 생성을 가능하게 합니다.
다음은 임베딩 생성 요청의 속도를 제한하는 위임 임베딩 생성기의 예제 구현입니다.
using Microsoft.Extensions.AI;
using System.Threading.RateLimiting;
public class RateLimitingEmbeddingGenerator(
IEmbeddingGenerator<string, Embedding<float>> innerGenerator, RateLimiter rateLimiter)
: DelegatingEmbeddingGenerator<string, Embedding<float>>(innerGenerator)
{
public override async Task<GeneratedEmbeddings<Embedding<float>>> GenerateAsync(
IEnumerable<string> values,
EmbeddingGenerationOptions? 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.GenerateAsync(values, options, cancellationToken);
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
rateLimiter.Dispose();
}
base.Dispose(disposing);
}
}
그런 다음 임의의 IEmbeddingGenerator<string, Embedding<float>>
주위에 계층화하여 수행되는 모든 포함 생성 작업의 속도를 제한할 수 있습니다.
using Microsoft.Extensions.AI;
using System.Threading.RateLimiting;
IEmbeddingGenerator<string, Embedding<float>> generator =
new RateLimitingEmbeddingGenerator(
new SampleEmbeddingGenerator(new Uri("http://coolsite.ai"), "target-ai-model"),
new ConcurrencyLimiter(new()
{
PermitLimit = 1,
QueueLimit = int.MaxValue
}));
foreach (var embedding in await generator.GenerateAsync(["What is AI?", "What is .NET?"]))
{
Console.WriteLine(string.Join(", ", embedding.Vector.ToArray()));
}
이러한 방식으로 RateLimitingEmbeddingGenerator
다른 IEmbeddingGenerator<string, Embedding<float>>
인스턴스와 함께 구성하여 속도 제한 기능을 제공할 수 있습니다.
참고하세요
- AI 기능을 사용하여 .NET 애플리케이션 개발
- Microsoft.Extensions.AI 사용하여 .NET용 통합 AI 구성 요소
- .NET 사용하여 AI 채팅 앱 빌드
- .NET 종속성 주입
- .NET에서 HTTP 처리기를 속도 제한하기
- .NET 제네릭 호스트
- .NET에서의 캐싱
.NET