Umělá inteligence v .NET (Preview)
Díky rostoucí škále dostupných služeb umělé inteligence (AI) potřebují vývojáři způsob integrace a interakce s těmito službami ve svých aplikacích .NET. Knihovna Microsoft.Extensions.AI
poskytuje jednotný přístup pro reprezentaci generovaných komponent AI, což umožňuje bezproblémovou integraci a interoperabilitu s různými službami AI. Tento článek představuje knihovnu a obsahuje pokyny k instalaci a příklady použití, které vám pomůžou začít.
Instalace balíčku
Pokud chcete nainstalovat balíček NuGet 📦 Microsoft.Extensions.AI, použijte .NET CLI nebo přidejte odkaz na balíček přímo do souboru projektu C#:
dotnet add package Microsoft.Extensions.AI --prelease
Další informace najdete v tématu dotnet add package nebo Správa závislostí balíčku v aplikacích .NET.
Příklady použití
Rozhraní IChatClient definuje abstrakci klienta zodpovědnou za interakci se službami AI, které poskytují možnosti chatu. Obsahuje metody pro odesílání a přijímání zpráv s vícemodálním obsahem (například textem, obrázky a zvukem), a to buď jako kompletní sada, nebo streamovaná přírůstkově. Kromě toho poskytuje metadata o klientovi a umožňuje načítání služeb silného typu.
Důležitý
Další příklady použití a reálné scénáře najdete v tématu AI pro vývojáře .NET.
V této části
-
IChatClient
rozhraní- Žádost o dokončení chatu
- dokončení chatu žádostmi o streamování
- Volání nástroje
- odpovědi mezipaměti
- Použít telemetrii
- Zadání možností
- kanály funkcí
-
Vlastního middleware
IChatClient
- injektáž závislostí
-
IEmbeddingGenerator
rozhraní
Rozhraní IChatClient
Následující ukázka implementuje IChatClient
, aby zobrazila obecnou strukturu.
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() { }
}
Další konkrétní implementace IChatClient
najdete v následujících balíčcích NuGet:
- 📦 Microsoft.Extensions.AI.AzureAIInference: Implementace podporovaná rozhraním API pro odvozování modelů Azure AI.
- 📦 Microsoft.Extensions.AI.Ollama: Implementace s podporou Ollama.
- 📦 Microsoft.Extensions.AI.OpenAI: Implementace podporovaná koncovými body OpenAI nebo koncovými body kompatibilními s OpenAI (například Azure OpenAI).
Žádost o dokončení chatu
Pokud chcete požádat o dokončení, zavolejte metodu IChatClient.CompleteAsync. Požadavek se skládá z jedné nebo více zpráv, z nichž každá se skládá z jednoho nebo více částí obsahu. Existují metody akcelerátoru, které zjednodušují běžné případy, například vytvoření požadavku na jeden textový obsah.
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);
Základní IChatClient.CompleteAsync
metoda přijímá seznam zpráv. Tento seznam představuje historii všech zpráv, které jsou součástí konverzace.
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?"),
]));
Každá zpráva v historii je reprezentována objektem ChatMessage. Třída ChatMessage
poskytuje ChatMessage.Role vlastnost, která označuje roli zprávy. Ve výchozím nastavení se používá ChatRole.User. K dispozici jsou následující role:
- ChatRole.Assistant: Dává pokyn nebo nastavuje chování asistenta.
- ChatRole.System: Poskytuje odpovědi na vstupy vyvolané systémem a vyžádané uživatelem.
- ChatRole.Tool: Poskytuje další informace a odkazy na dokončení chatu.
- ChatRole.User: Poskytuje vstup pro dokončení chatu.
Každá chatová zpráva je vytvořena jako instance a je přiřazena ke své vlastnosti Contents nová TextContent. Existují různé typy obsahu, které lze reprezentovat, například jednoduchý řetězec nebo složitější objekt, který představuje vícemodální zprávu s textem, obrázky a zvukem:
- AudioContent
- DataContent
- FunctionCallContent
- FunctionResultContent
- ImageContent
- TextContent
- UsageContent
Vyžádání dokončení chatu se streamováním
Vstupy IChatClient.CompleteStreamingAsync jsou stejné jako vstupy CompleteAsync
. Místo vrácení úplné odpovědi jako součásti objektu ChatCompletion však metoda vrátí IAsyncEnumerable<T>, kde T
je StreamingChatCompletionUpdate, poskytuje datový proud aktualizací, které společně tvoří jedinou odpověď.
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);
}
Spropitné
Rozhraní API pro streamování jsou téměř synonymem uživatelského prostředí umělé inteligence. Jazyk C# umožňuje přesvědčivé scénáře s podporou IAsyncEnumerable<T>
, což umožňuje přirozený a efektivní způsob streamování dat.
Volání nástrojů
Některé modely a služby podporují volání nástrojů, kde žádosti mohou obsahovat nástroje, které umožňují modelu spouštět funkce pro získání dalších informací. Místo odeslání konečné odpovědi model požaduje vyvolání funkce s konkrétními argumenty. Klient pak vyvolá funkci a odešle výsledky zpět do modelu spolu s historií konverzací. Knihovna Microsoft.Extensions.AI
obsahuje abstrakce pro různé typy obsahu zpráv, včetně žádostí o volání funkcí a výsledků. I když uživatelé můžou s tímto obsahem pracovat přímo, Microsoft.Extensions.AI
tyto interakce automatizuje a poskytuje:
- AIFunction: Představuje funkci, kterou lze popsat službě AI a vyvolat ji.
-
AIFunctionFactory: Poskytuje tovární metody pro vytváření běžně používaných implementací
AIFunction
. -
FunctionInvokingChatClient: Zabalí
IChatClient
, aby se přidaly možnosti automatického vyvolání funkce.
Podívejte se na následující příklad, který ukazuje vyvolání náhodné funkce:
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);
}
Předchozí příklad závisí na
Předchozí kód:
- Definuje funkci s názvem
GetCurrentWeather
, která vrací náhodnou předpověď počasí.- Tato funkce je zdobena DescriptionAttribute, která slouží k zadání popisu funkce službě AI.
- Vytvoří instanci ChatClientBuilder s OllamaChatClient a nakonfiguruje ji tak, aby používala volání funkce.
- Volá na
CompleteStreamingAsync
na straně klienta, předá výzvu a seznam nástrojů, které zahrnují funkci vytvořenou pomocí Create. - Iteruje přes odpověď a vytiskne každou aktualizaci do konzoly.
Odpovědi mezipaměti
Pokud znáte ukládání do mezipaměti v rozhraní .NET, je dobré vědět, že Microsoft.Extensions.AI poskytuje další takové delegování IChatClient
implementací.
DistributedCachingChatClient je IChatClient
, který vrství uložení do mezipaměti kolem jiné libovolné instance IChatClient
. Když se do DistributedCachingChatClient
odešle jedinečná historie chatu, předá ji podkladovému klientovi a před odesláním zpět příjemci ji uloží do mezipaměti. Příště, když bude odeslána stejná výzva a odpověď bude nalezena v mezipaměti, DistributedCachingChatClient
vrátí tuto uloženou odpověď, aniž by bylo nutné požadavek předávat přes kanál.
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();
}
Předchozí příklad závisí na 📦 balíčku NuGet microsoft.Extensions.Caching.Memory. Další informace naleznete v tématu Ukládání do mezipaměti v rozhraní .NET
Použití telemetrie
Dalším příkladem delegujícího chatovacího klienta je OpenTelemetryChatClient. Tato implementace dodržuje sémantické konvence OpenTelemetry pro systémy generující AI. Podobně jako u jiných IChatClient
delegátorů vrství metriky a rozsahy kolem jakékoli podkladové IChatClient
implementace, což poskytuje lepší pozorovatelnost.
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);
Předchozí příklad závisí na 📦 OpenTelemetry.Exporter.Console balíčku NuGet.
Poskytnout možnosti
Každé volání CompleteAsync nebo CompleteStreamingAsync může volitelně zadat instanci ChatOptions obsahující další parametry operace. Nejběžnější parametry mezi modely AI a službami se zobrazují jako silně typované vlastnosti, například ChatOptions.Temperature. Jiné parametry lze zadat slabě typovaným způsobem pomocí slovníku ChatOptions.AdditionalProperties.
Při vytváření IChatClient
pomocí fluentního rozhraní API ChatClientBuilder můžete také určit možnosti a řetězit volání metody rozšíření ConfigureOptions
. Tento delegující klient obaluje jiného klienta a vyvolává předaného delegáta, aby naplnil instanci ChatOptions
pro každé volání. Pokud chcete například zajistit, aby vlastnost ChatOptions.ModelId ve výchozím nastavení používala konkrétní název modelu, můžete použít kód podobný tomuto:
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" }));
Předchozí příklad závisí na balíčku NuGet 📦 Microsoft.Extensions.AI.Ollama.
Kanály funkcí
IChatClient
instance mohou být vrstvené tak, aby vytvořily kanál komponent, z nichž každá přidává konkrétní funkce. Tyto komponenty mohou pocházet z Microsoft.Extensions.AI
, jiných balíčků NuGet nebo vlastních implementací. Tento přístup umožňuje rozšířit chování IChatClient
různými způsoby, aby vyhovovaly vašim konkrétním potřebám. Představte si následující příklad kódu, který vrství distribuovanou mezipaměť, vyvolání funkcí a trasování OpenTelemetry kolem ukázkového chatovacího klienta:
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));
}
Předchozí příklad závisí na následujících balíčcích NuGet:
- 📦 microsoft.Extensions.Caching.Memory
- 📦 Microsoft.Extensions.AI.Ollama
- 📦 OpenTelemetry.Exporter.Console
Vlastní middleware IChatClient
Pokud chcete přidat další funkce, můžete implementovat IChatClient
přímo nebo použít třídu DelegatingChatClient. Tato třída slouží jako základ pro vytváření chatovacích klientů, který deleguje operace na jinou instanci IChatClient
. Zjednodušuje řetězení více klientů, což umožňuje volání předávat podkladovému klientovi.
Třída DelegatingChatClient
poskytuje výchozí implementace pro metody, jako jsou CompleteAsync
, CompleteStreamingAsync
a Dispose
, které přesměrovává volání do vnitřního klienta. Můžete tuto třídu rozšířit a přepsat pouze metody, které potřebujete k vylepšení chování, a přitom můžete delegovat ostatní volání na základní implementaci. Tento přístup pomáhá vytvářet flexibilní a modulární chatovací klienty, které se dají snadno rozšířit a vytvářet.
Následuje příklad třídy odvozené od DelegatingChatClient
, která poskytuje funkce omezování rychlosti s využitím 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);
}
}
Předchozí příklad závisí na 📦 System.Threading.RateLimiting balíčku NuGet. Složení RateLimitingChatClient
s jiným klientem je jednoduché:
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?");
Aby se zjednodušilo složení těchto komponent s ostatními, autoři komponent by měli vytvořit metodu rozšíření Use*
pro registraci komponenty do kanálu. Představte si například následující metodu rozšíření:
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>
Tato rozšíření se také mohou dotazovat na relevantní služby z kontejneru DI; IServiceProvider, který používá kanál, se předává jako volitelný parametr:
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>
Spotřebitel to pak může snadno použít ve svém procesu, například:
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();
Tento příklad ukazuje hostovaný scénář, kde spotřebitel spoléhá na injektáž závislostí k poskytnutí instance RateLimiter
. Předchozí metody rozšíření ukazují použití metody Use
na ChatClientBuilder.
ChatClientBuilder
také poskytuje Use přetížení, která usnadňují zápis takových delegujících obslužných rutin.
- Use(IChatClient)
- Use(Func<IChatClient,IChatClient>)
- Use(Func<IServiceProvider,IChatClient,IChatClient>)
Například v předchozím příkladu RateLimitingChatClient
je při přetížení metod CompleteAsync
a CompleteStreamingAsync
třeba vykonat operace před a po delegování na dalšího klienta v řetězci. Chcete-li dosáhnout stejného výsledku bez psaní vlastní třídy, můžete použít přetížení Use
, které přijímá delegáta používaného pro CompleteAsync
i CompleteStreamingAsync
, čímž snížíte množství potřebného šablonového kódu.
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
Předchozí přetížení interně využívá AnonymousDelegatingChatClient
, což umožňuje komplikovanější vzory s minimálním dodatečným kódem. Pokud například chcete dosáhnout stejného výsledku, ale s RateLimiter načteným z 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();
Ve scénářích, kde by vývojář chtěl specifikovat delegující implementace CompleteAsync
a CompleteStreamingAsync
přímo v kódu, a kde je důležité napsat pro každou z nich jinou implementaci, aby bylo možné speciálně zpracovat jejich jedinečné návratové typy, existuje další přetížení Use
, které přijímá delegáta pro každou z nich.
Injektáž závislostí
IChatClient implementace se obvykle poskytují aplikaci prostřednictvím injektáže závislostí (DI). V tomto příkladu se do kontejneru DI přidá IDistributedCache, stejně jako IChatClient
. Registrace pro IChatClient
využívá builder, který vytvoří potrubí obsahující klienta pro ukládání do mezipaměti (který pak použije IDistributedCache
získané z DI) a ukázkového klienta. Vložený IChatClient
lze načíst a použít jinde v aplikaci.
::code language="csharp" source="snippets/ai/ConsoleAI.DependencyInjection/Program.cs":::
Předchozí příklad závisí na následujících balíčcích NuGet:
Jaká instance a konfigurace se vkládají, se může lišit v závislosti na aktuálních potřebách aplikace a několik kanálů se dá vkládat s různými klíči.
Rozhraní IEmbeddingGenerator
Rozhraní IEmbeddingGenerator<TInput,TEmbedding> představuje obecný generátor vnoření. Tady TInput
je typ vstupních hodnot, které se vkládají, a TEmbedding
je typ vygenerovaného vkládání, který dědí z Embedding třídy.
Třída Embedding
slouží jako základní třída pro vkládání vytvořená IEmbeddingGenerator
. Je navržený tak, aby ukládal a spravoval metadata a data spojená s embedováním. Odvozené typy, jako Embedding<T>
poskytují konkrétní data vektoru vkládání. Vložení například zpřístupňuje vlastnost Embedding<T>.Vector pro přístup k vloženým datům.
Rozhraní IEmbeddingGenerator
definuje metodu pro asynchronní generování vkládání pro kolekci vstupních hodnot s volitelnou konfigurací a podporou zrušení. Poskytuje také metadata popisující generátor a umožňuje načtení pevně typovaných služeb, které může generátor nebo jeho základní služby poskytovat.
Ukázková implementace
Podívejte se na následující ukázkovou implementaci IEmbeddingGenerator
, která zobrazuje obecnou strukturu, ale právě generuje náhodné vložené vektory.
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() { }
}
Předchozí kód:
- Definuje třídu s názvem
SampleEmbeddingGenerator
, která implementuje rozhraníIEmbeddingGenerator<string, Embedding<float>>
. - Má primární konstruktor, který přijímá ID koncového bodu a modelu, které slouží k identifikaci generátoru.
- Zveřejňuje vlastnost
Metadata
, která poskytuje metadata o generátoru. - Implementuje metodu
GenerateAsync
pro generování vkládání pro kolekci vstupních hodnot:- Simuluje asynchronní operaci zpožděním 100 milisekund.
- Vrátí náhodné vkládání pro každou vstupní hodnotu.
Skutečné konkrétní implementace najdete v následujících balíčcích:
Vytváření vložených objektů
Primární operace prováděná s IEmbeddingGenerator<TInput,TEmbedding> je vytváření vnoření, které se provádí pomocí metody GenerateAsync.
::code language="csharp" source="snippets/ai/ConsoleAI.CreateEmbeddings/Program.cs":::
Vlastní middleware IEmbeddingGenerator
Stejně jako u IChatClient
mohou být implementace IEmbeddingGenerator
vrstvené. Stejně jako Microsoft.Extensions.AI
poskytuje delegování implementací IChatClient
pro ukládání do mezipaměti a telemetrii, poskytuje také implementaci 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
umožňuje vytvářet vlastní middleware, který rozšiřuje funkce IEmbeddingGenerator
. Třída DelegatingEmbeddingGenerator<TInput,TEmbedding> je implementace IEmbeddingGenerator<TInput, TEmbedding>
rozhraní, které slouží jako základní třída pro vytváření generátorů pro vkládání, které delegují své operace na jinou instanci IEmbeddingGenerator<TInput, TEmbedding>
. Umožňuje řetězit více generátorů v libovolném pořadí a předávat volání do podkladového generátoru. Třída poskytuje výchozí implementace pro metody, jako jsou GenerateAsync a Dispose
, které přesměrují volání do instance vnitřního generátoru, což umožňuje flexibilní a modulární generování vkládání.
Následuje příklad implementace takového delegujícího generátoru vkládání, který omezuje četnost požadavků na generování vkládání:
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);
}
}
To se pak dá vrstvit kolem libovolného IEmbeddingGenerator<string, Embedding<float>>
, aby se omezily všechny operace generování embedování provedené.
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()));
}
Tímto způsobem lze RateLimitingEmbeddingGenerator
skládat s jinými instancemi IEmbeddingGenerator<string, Embedding<float>>
, aby poskytovaly funkci omezování rychlosti.