Kecerdasan buatan di .NET (Pratinjau)
Dengan berbagai layanan kecerdasan buatan (AI) yang tersedia, pengembang membutuhkan cara untuk mengintegrasikan dan berinteraksi dengan layanan ini dalam aplikasi .NET mereka. Pustaka Microsoft.Extensions.AI
menyediakan pendekatan terpadu untuk mewakili komponen AI generatif, yang memungkinkan integrasi dan interoperabilitas yang mulus dengan berbagai layanan AI. Artikel ini memperkenalkan pustaka dan menyediakan instruksi penginstalan dan contoh penggunaan untuk membantu Anda memulai.
Menginstal paket
Untuk menginstal paket nuGet 📦 Microsoft.Extensions.AI, gunakan .NET CLI atau tambahkan referensi paket langsung ke file proyek C#Anda:
dotnet add package Microsoft.Extensions.AI --prerelease
Untuk informasi selengkapnya, lihat dotnet menambahkan paket atau Mengelola dependensi paket dalam aplikasi .NET.
Contoh penggunaan
Antarmuka IChatClient mendefinisikan abstraksi klien yang bertanggung jawab untuk berinteraksi dengan layanan AI yang menyediakan kemampuan obrolan. Ini termasuk metode untuk mengirim dan menerima pesan dengan konten multi-modal (seperti teks, gambar, dan audio), baik sebagai set lengkap atau di-streaming secara bertahap. Selain itu, ini menyediakan informasi metadata tentang klien dan memungkinkan pengambilan layanan bertipe kuat.
Penting
Untuk contoh penggunaan dan skenario dunia nyata lainnya, lihat AI untuk pengembang .NET.
Di bagian ini
Antarmuka IChatClient
Sampel berikut mengimplementasikan IChatClient
untuk menunjukkan struktur umum.
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() { }
}
Anda dapat menemukan implementasi konkret lain dari IChatClient
dalam paket NuGet berikut:
- 📦 Microsoft.Extensions.AI.AzureAIInference : Implementasi yang didukung oleh AZURE AI Model Inference API.
- 📦 Microsoft.Extensions.AI.Ollama : Implementasi yang didukung oleh Ollama.
- 📦 Microsoft.Extensions.AI.OpenAI : Implementasi yang didukung oleh titik akhir yang kompatibel dengan OpenAI atau OpenAI (seperti Azure OpenAI).
Meminta hasil akhir obrolan
Untuk meminta penyelesaian, panggil metode IChatClient.CompleteAsync. Permintaan terdiri dari satu atau beberapa pesan, yang masing-masing terdiri dari satu atau beberapa bagian konten. Metode akselerator ada untuk menyederhanakan kasus umum, seperti membuat permintaan untuk satu konten teks.
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);
Metode IChatClient.CompleteAsync
inti menerima daftar pesan. Daftar ini mewakili riwayat semua pesan yang merupakan bagian dari percakapan.
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?"),
]));
Setiap pesan dalam riwayat diwakili oleh objek ChatMessage. Kelas ChatMessage
menyediakan properti ChatMessage.Role yang menunjukkan peran pesan. Secara default, ChatRole.User digunakan. Peran berikut tersedia:
- ChatRole.Assistant: Menginstruksikan atau mengatur perilaku asisten.
- ChatRole.System: Memberikan respons terhadap input yang diinstruksikan sistem dan diminta pengguna.
- ChatRole.Tool: Menyediakan informasi dan referensi tambahan untuk penyelesaian obrolan.
- ChatRole.User: Menyediakan input untuk melengkapi percakapan.
Setiap pesan obrolan dibuat, menetapkan Contentsbaru ke properti TextContent-nya. Ada berbagai jenis konten yang dapat diwakili, seperti string sederhana atau objek yang lebih kompleks yang mewakili pesan multi-modal dengan teks, gambar, dan audio:
- AudioContent
- DataContent
- FunctionCallContent
- FunctionResultContent
- ImageContent
- TextContent
- UsageContent
Meminta penyelesaian obrolan dengan streaming
Input ke IChatClient.CompleteStreamingAsync identik dengan CompleteAsync
. Namun, daripada mengembalikan respons lengkap sebagai bagian dari objek ChatCompletion, metode mengembalikan IAsyncEnumerable<T> di mana T
StreamingChatCompletionUpdate, menyediakan aliran pembaruan yang secara kolektif membentuk respons tunggal.
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);
}
Tips
API Streaming hampir identik dengan pengalaman pengguna AI. C# memungkinkan skenario yang menarik dengan dukungan IAsyncEnumerable<T>
, memungkinkan cara alami dan efisien untuk mengalirkan data.
Panggilan alat
Beberapa model dan layanan mendukung panggilan alat, di mana permintaan dapat menyertakan alat bagi model untuk memanggil fungsi untuk mengumpulkan informasi tambahan. Alih-alih mengirim respons akhir, model meminta pemanggilan fungsi dengan argumen tertentu. Klien kemudian memanggil fungsi dan mengirim hasilnya kembali ke model bersama dengan riwayat percakapan. Pustaka Microsoft.Extensions.AI
mencakup abstraksi untuk berbagai jenis konten pesan, termasuk permintaan dan hasil panggilan fungsi. Meskipun konsumen dapat berinteraksi dengan konten ini secara langsung, Microsoft.Extensions.AI
mengotomatiskan interaksi ini dan menyediakan:
- AIFunction: Mewakili fungsi yang dapat dijelaskan ke layanan AI dan dipanggil.
-
AIFunctionFactory: Menyediakan metode pabrik untuk membuat implementasi
AIFunction
yang umum digunakan. -
FunctionInvokingChatClient: Membungkus
IChatClient
untuk menambahkan kemampuan pemanggilan fungsi otomatis.
Pertimbangkan contoh berikut yang menunjukkan pemanggilan fungsi acak:
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);
}
Contoh sebelumnya tergantung pada paket 📦 Microsoft.Extensions.AI.Ollama NuGet.
Kode sebelumnya:
- Menentukan fungsi bernama
GetCurrentWeather
yang mengembalikan prakiraan cuaca acak.- Fungsi ini dihiasi dengan DescriptionAttribute, yang digunakan untuk memberikan deskripsi fungsi ke layanan AI.
- Membuat instans ChatClientBuilder dengan OllamaChatClient dan mengonfigurasinya untuk melakukan pemanggilan fungsi.
- Memanggil
CompleteStreamingAsync
pada klien, meneruskan perintah dan daftar alat yang menyertakan fungsi yang dibuat dengan Create. - Melakukan iterasi atas respons, mencetak setiap pembaruan ke konsol.
Respons cache
Jika Anda terbiasa dengan Caching di .NET, ada baiknya untuk mengetahui bahwa Microsoft.Extensions.AI menyediakan implementasi IChatClient
pendelegasian lainnya.
DistributedCachingChatClient adalah IChatClient
yang menambahkan penyimpanan sementara pada instans IChatClient
lain yang sewenang-wenang. Ketika riwayat obrolan unik dikirimkan ke DistributedCachingChatClient
, sistem tersebut meneruskannya ke klien yang mendasarinya, lalu mencache respons tersebut sebelum mengirimkannya kembali ke konsumen. Lain kali permintaan yang sama dikirimkan, sehingga respons yang di-cache dapat ditemukan di cache, DistributedCachingChatClient
mengembalikan respons yang di-cache daripada perlu meneruskan permintaan di sepanjang alur.
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();
}
Contoh sebelumnya bergantung pada paket NuGet 📦 Microsoft.Extensions.Caching.Memory. Untuk informasi selengkapnya, lihat Caching di .NET.
Menggunakan telemetri
Contoh lain dari klien obrolan yang mendelegasikan adalah OpenTelemetryChatClient. Implementasi ini mematuhi Konvensi Semantik OpenTelemetry untuk sistem Kecerdasan Buatan (AI) Generatif. Mirip dengan delegator IChatClient
lainnya, ia menyusun lapisan metrik dan rentang di sekitar implementasi IChatClient
yang mendasar, memberikan peningkatan pengamatan.
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);
Contoh sebelumnya tergantung pada 📦 paket OpenTelemetry.Exporter.Console NuGet.
Sediakan opsi
Setiap panggilan ke CompleteAsync atau CompleteStreamingAsync dapat secara opsional menyediakan instans ChatOptions yang berisi parameter tambahan untuk operasi. Parameter yang paling umum di antara model dan layanan AI muncul sebagai properti yang bertiped kuat pada tipenya, seperti ChatOptions.Temperature. Parameter lain dapat disediakan berdasarkan nama dengan cara yang ditik dengan lemah melalui kamus ChatOptions.AdditionalProperties.
Anda juga dapat menentukan opsi saat membangun IChatClient
dengan API ChatClientBuilder yang fasih dan menautkan panggilan ke metode ekstensi ConfigureOptions
. Klien yang mendelegasikan ini membungkus klien lain dan memanggil delegasi yang disediakan untuk mengisi instans ChatOptions
untuk setiap panggilan. Misalnya, untuk memastikan bahwa properti ChatOptions.ModelId default ke nama model tertentu, Anda dapat menggunakan kode seperti berikut:
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" }));
Contoh sebelumnya tergantung pada paket 📦 Microsoft.Extensions.AI.Ollama NuGet.
Rangkaian fungsionalitas
Instans IChatClient
dapat diberi lapisan untuk membuat pipa komponen, di mana masing-masing menambahkan fungsionalitas tertentu. Komponen-komponen ini dapat berasal dari Microsoft.Extensions.AI
, paket NuGet lainnya, atau implementasi kustom. Pendekatan ini memungkinkan Anda untuk menambah perilaku IChatClient
dengan berbagai cara untuk memenuhi kebutuhan spesifik Anda. Pertimbangkan contoh kode berikut yang melapisi cache terdistribusi, pemanggilan fungsi, dan pelacakan OpenTelemetry di sekitar klien obrolan sampel:
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));
}
Contoh sebelumnya tergantung pada paket NuGet berikut:
- 📦 Microsoft.Extensions.Caching.Memory
- 📦 Microsoft.Extensions.AI.Ollama
- 📦 OpenTelemetry.Exporter.Console
Middleware IChatClient
kustom
Untuk menambahkan fungsionalitas tambahan, Anda dapat menerapkan IChatClient
secara langsung atau menggunakan kelas DelegatingChatClient. Kelas ini berfungsi sebagai dasar untuk membuat klien obrolan yang mendelegasikan operasi ke instans IChatClient
lain. Ini menyederhanakan menghubungkan beberapa klien, memungkinkan panggilan untuk diteruskan ke klien dasar.
Kelas DelegatingChatClient
menyediakan implementasi default untuk metode seperti CompleteAsync
, CompleteStreamingAsync
, dan Dispose
, yang meneruskan panggilan ke klien dalam. Anda dapat memperoleh dari kelas ini dan hanya mengambil alih metode yang Anda butuhkan untuk meningkatkan perilaku, sambil mendelegasikan panggilan lain ke implementasi dasar. Pendekatan ini membantu menciptakan klien obrolan fleksibel dan modular yang mudah diperluas dan dibuat.
Berikut ini adalah contoh kelas yang berasal dari DelegatingChatClient
untuk menyediakan fungsionalitas pembatasan tarif, menggunakan 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);
}
}
Contoh sebelumnya tergantung pada paket 📦 System.Threading.RateLimiting NuGet. Penyusunan RateLimitingChatClient
dengan klien lain cukup sederhana.
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?");
Untuk menyederhanakan komposisi komponen tersebut dengan yang lain, penulis komponen harus membuat metode ekstensi Use*
untuk mendaftarkan komponen ke dalam alur. Misalnya, pertimbangkan metode ekstensi berikut:
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>
Ekstensi tersebut juga dapat meminta layanan yang relevan dari kontainer DI; IServiceProvider yang digunakan oleh pipeline diteruskan sebagai parameter opsional:
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>
Konsumen kemudian dapat dengan mudah menggunakan ini dalam alur mereka, misalnya:
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();
Contoh ini menunjukkan skenario yang dihosting Use
pada ChatClientBuilder.
ChatClientBuilder
juga menyediakan kelebihan beban Use yang memudahkan penulisan penangan pendelegasian tersebut.
- 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>>)
Misalnya, dalam contoh RateLimitingChatClient
sebelumnya, penggantian CompleteAsync
dan CompleteStreamingAsync
hanya perlu melakukan pekerjaan sebelum dan sesudah mendelegasikan ke klien berikutnya dalam pipeline. Untuk mencapai hal yang sama tanpa menulis kelas kustom, Anda dapat menggunakan overload dari Use
yang menerima delegate, yang digunakan untuk CompleteAsync
dan CompleteStreamingAsync
, sehingga mengurangi kode boilerplate yang diperlukan.
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
Kelebihan beban sebelumnya secara internal menggunakan AnonymousDelegatingChatClient
, yang memungkinkan pola yang lebih rumit hanya dengan sedikit kode tambahan. Misalnya, untuk mencapai hasil yang sama tetapi dengan RateLimiter diambil dari DI:
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();
Untuk skenario di mana pengembang ingin menentukan implementasi pendelegasian CompleteAsync
dan CompleteStreamingAsync
secara sebaris, dan penting untuk dapat menulis implementasi yang berbeda untuk masing-masing guna menangani jenis pengembalian unik mereka dengan cara yang khusus, terdapat kelebihan lain dari Use
, yang menerima delegasi untuk masing-masing.
Injeksi Ketergantungan
IChatClient implementasi biasanya akan diberikan kepada aplikasi melalui dependency injection (DI). Dalam contoh ini, IDistributedCache ditambahkan ke dalam kontainer DI, seperti halnya IChatClient
. Pendaftaran untuk IChatClient
menggunakan pembangun yang membuat rangkaian berisi klien caching (yang kemudian akan menggunakan IDistributedCache
yang diambil dari DI) dan klien contoh.
IChatClient
yang disuntikkan dapat diambil dan digunakan di bagian lain dari aplikasi.
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();
Contoh sebelumnya tergantung pada paket NuGet berikut:
Instans dan konfigurasi apa yang disuntikkan dapat berbeda berdasarkan kebutuhan aplikasi saat ini, dan beberapa alur dapat disuntikkan dengan kunci yang berbeda.
Antarmuka IEmbeddingGenerator
Antarmuka IEmbeddingGenerator<TInput,TEmbedding> mewakili generator generik penyematan. Di sini, TInput
adalah jenis nilai input yang disematkan, dan TEmbedding
adalah jenis penyematan yang dihasilkan, yang diwarisi dari kelas Embedding.
Kelas Embedding
berfungsi sebagai kelas dasar untuk penyematan yang dihasilkan oleh IEmbeddingGenerator
. Ini dirancang untuk menyimpan dan mengelola metadata dan data yang terkait dengan penyematan. Jenis turunan seperti Embedding<T>
menyediakan data vektor embedding yang nyata. Misalnya, penyematan mengekspos properti Embedding<T>.Vector untuk mengakses data penyematannya.
Antarmuka IEmbeddingGenerator
mendefinisikan metode untuk secara asinkron menghasilkan penyematan untuk kumpulan nilai input, dengan konfigurasi opsional dan dukungan pembatalan. Ini juga menyediakan metadata yang menjelaskan generator dan memungkinkan pengambilan layanan bertipe kuat yang dapat disediakan oleh generator atau layanan yang mendasari.
Contoh implementasi
Pertimbangkan contoh implementasi IEmbeddingGenerator
berikut untuk menunjukkan struktur umum tetapi itu hanya menghasilkan vektor penyematan acak.
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() { }
}
Kode sebelumnya:
- Menentukan kelas bernama
SampleEmbeddingGenerator
yang mengimplementasikan antarmukaIEmbeddingGenerator<string, Embedding<float>>
. - Memiliki konstruktor utama yang menerima titik akhir dan ID model, yang digunakan untuk mengidentifikasi generator.
- Mengekspos properti
Metadata
yang menyediakan metadata tentang generator. - Menerapkan metode
GenerateAsync
untuk menghasilkan penyematan untuk kumpulan nilai input:- Mensimulasikan operasi asinkron dengan menunda selama 100 milidetik.
- Mengembalikan penyematan acak untuk setiap nilai input.
Anda dapat menemukan implementasi konkret aktual dalam paket berikut:
- 📦 Microsoft.Extensions.AI.OpenAI
- 📦 Microsoft.Extensions.AI.Ollama
Membuat embedding
Operasi utama yang dilakukan dengan IEmbeddingGenerator<TInput,TEmbedding> adalah membuat penyematan, yang dilakukan melalui metode 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()));
}
Middleware IEmbeddingGenerator
kustom
Seperti halnya implementasi IChatClient
, IEmbeddingGenerator
dapat dilapisi. Sama seperti Microsoft.Extensions.AI
menyediakan implementasi pendelegasian IChatClient
untuk penembolokan dan telemetri, Microsoft.Extensions.AI
juga menyediakan implementasi untuk .
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
memungkinkan pembuatan middleware kustom yang memperluas fungsionalitas IEmbeddingGenerator
. Kelas DelegatingEmbeddingGenerator<TInput,TEmbedding> adalah implementasi antarmuka IEmbeddingGenerator<TInput, TEmbedding>
yang berfungsi sebagai kelas dasar untuk membuat generator penyematan yang mendelegasikan operasi mereka ke instans IEmbeddingGenerator<TInput, TEmbedding>
lain. Ini memungkinkan untuk menautkan beberapa generator dalam urutan apa pun, dengan meneruskan panggilan ke generator yang mendasarinya. Kelas ini menyediakan implementasi default untuk metode seperti GenerateAsync dan Dispose
, yang meneruskan panggilan ke instans generator dalam, memungkinkan pembuatan penyematan yang fleksibel dan modular.
Berikut ini adalah contoh implementasi generator pendelegasian penyematan yang membatasi laju permintaan pembuatan penyematan:
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);
}
}
Ini kemudian dapat dilapisi di sekitar IEmbeddingGenerator<string, Embedding<float>>
arbitrer untuk membatasi laju semua operasi pembangkitan penyematan yang dilakukan.
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()));
}
Dengan cara ini, RateLimitingEmbeddingGenerator
dapat digabungkan dengan instance IEmbeddingGenerator<string, Embedding<float>>
lainnya untuk menyediakan fungsionalitas pembatasan laju.