Megosztás a következőn keresztül:


Mesterséges intelligencia a .NET-ben (előzetes verzió)

A mesterséges intelligencia (AI) egyre több szolgáltatása érhető el, így a fejlesztőknek módot kell biztosítaniuk arra, hogy integrálják és használják ezeket a szolgáltatásokat a .NET-alkalmazásaikban. A Microsoft.Extensions.AI kódtár egységes megközelítést biztosít a generatív AI-összetevők ábrázolására, amely zökkenőmentes integrációt és együttműködést tesz lehetővé a különböző AI-szolgáltatásokkal. Ez a cikk bemutatja a kódtárat, és telepítési utasításokat és használati példákat tartalmaz az első lépésekhez.

A csomag telepítése

A 📦 Microsoft.Extensions.AI NuGet-csomag telepítéséhez használja a .NET CLI-t, vagy adjon hozzá egy csomaghivatkozást közvetlenül a C#-projektfájlhoz:

dotnet add package Microsoft.Extensions.AI --prerelease

További információ: dotnet add package vagy Csomagfüggőségek kezelése .NET alkalmazásokban.

Használati példák

A IChatClient felület egy ügyfél absztrakciót határoz meg, amely a csevegési képességeket biztosító AI-szolgáltatásokkal való interakcióért felel. Ez magában foglalja a több modális tartalommal (például szöveggel, képpel és hanggal) rendelkező üzenetek küldésének és fogadásának módszereit, akár teljes készletként, akár növekményesen streamelve. Emellett metaadat-információkat is biztosít az ügyfélről, és lehetővé teszi az erősen gépelt szolgáltatások lekérését.

Fontos

További használati példák és valós forgatókönyvek: AI .NET-fejlesztőknek.

Ebben a szakaszban

A IChatClient felület

Az alábbi minta IChatClient valósít meg az általános struktúra megjelenítéséhez.

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

A IChatClient egyéb konkrét implementációit a következő NuGet-csomagokban találja:

Csevegés befejezésének kérése

A befejezés kéréséhez hívja meg a IChatClient.CompleteAsync metódust. A kérés egy vagy több üzenetből áll, amelyek mindegyike egy vagy több tartalomból áll. A gyorsító módszerek a gyakori esetek egyszerűsítése érdekében léteznek, például egyetlen szöveges tartalomra vonatkozó kérelem létrehozására.

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

Az alapvető IChatClient.CompleteAsync metódus elfogadja az üzenetek listáját. Ez a lista a beszélgetés részét képező összes üzenet előzményeit jelöli.

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

Az előzmények minden üzenetét egy ChatMessage objektum jelöli. A ChatMessage osztály egy ChatMessage.Role tulajdonságot biztosít, amely az üzenet szerepét jelzi. Alapértelmezés szerint a ChatRole.User van használatban. A következő szerepkörök érhetők el:

  • ChatRole.Assistant: Utasítja vagy beállítja az asszisztens viselkedését.
  • ChatRole.System: Választ ad a rendszer által utasított, felhasználó által megkérdezett bemenetre.
  • ChatRole.Tool: További információkat és hivatkozásokat biztosít a csevegés befejezéséhez.
  • ChatRole.User: Bemenetet biztosít a csevegés befejezéséhez.

A rendszer minden chatüzenetet példányosít azáltal, hogy új Contents-et rendel hozzá a TextContent tulajdonsághoz. Különböző tartalomtípusokat lehet ábrázolni, például egy egyszerű sztringet vagy egy összetettebb objektumot, amely szöveggel, képekkel és hanggal ellátott, több modális üzenetet jelöl:

Csevegés befejezésének kérése streameléssel

A IChatClient.CompleteStreamingAsync bemenetei megegyeznek a CompleteAsyncbemenetével. Ahelyett azonban, hogy a teljes választ egy ChatCompletion objektum részeként adja vissza, a metódus egy olyan IAsyncEnumerable<T> ad vissza, amelyben TStreamingChatCompletionUpdate, és egy olyan frissítési adatfolyamot biztosít, amely együttesen alkotja az egyetlen választ.

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

Borravaló

A streamelési API-k szinte szinonimák az AI felhasználói élményével. A C# IAsyncEnumerable<T> támogatásával lenyűgöző forgatókönyveket tesz lehetővé, így természetes és hatékony módon streamelheti az adatokat.

Eszközhívási funkció

Egyes modellek és szolgáltatások támogatják az eszközök használatát, ahol a kérések olyan eszközöket is tartalmazhatnak, amelyekkel a modell funkciókat hívhat meg további információk megszerzéséhez. A végső válasz küldése helyett a modell függvényhívást kér adott argumentumokkal. Az ügyfél ezután meghívja a függvényt, és visszaküldi az eredményeket a modellnek a beszélgetési előzményekkel együtt. A Microsoft.Extensions.AI kódtár absztrakciókat tartalmaz a különböző üzenettartalmakhoz, beleértve a függvényhívási kérelmeket és az eredményeket. Bár a felhasználók közvetlenül is kezelhetik ezt a tartalmat, Microsoft.Extensions.AI automatizálják ezeket az interakciókat, és a következőkre szolgálnak:

  • AIFunction: Egy AI-szolgáltatásnak leírható és meghívható függvényt jelöl.
  • AIFunctionFactory: Gyári metódusokat biztosít a AIFunctiongyakran használt implementációinak létrehozásához.
  • FunctionInvokingChatClient: Egy IChatClient körbefuttatása automatikus függvényhívási képességek hozzáadásához.

Vegye figyelembe az alábbi példát, amely egy véletlenszerű függvényhívást mutat be:

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

A megelőző példa a 📦 Microsoft.Extensions.AI.Ollama NuGet-csomagtól függ.

Az előző kód:

  • Egy GetCurrentWeather nevű függvényt definiál, amely véletlenszerű időjárás-előrejelzést ad vissza.
    • Ezt a függvényt egy DescriptionAttributedíszíti, amely a függvény leírásának megadására szolgál az AI-szolgáltatás számára.
  • Létrehoz egy ChatClientBuilder-t egy OllamaChatClient-gyel, és konfigurálja a függvényhívás használatára.
  • Meghívja CompleteStreamingAsync az ügyfélen, és átad egy parancssort és egy olyan eszközlistát, amely tartalmazza a Createáltal létrehozott függvényt.
  • Átfut a válaszon, és az egyes frissítéseket a konzolra nyomtatja.

Gyorsítótár-válaszok

Ha ismeri a .NET-gyorsítótárazását, jó tudni, hogy a más hasonló delegálási implementációkat is biztosít. A DistributedCachingChatClient egy IChatClient, amely egy másik IChatClient tetszőleges példánya köré rétegezi a gyorsítótárazást. Ha egy egyedi csevegési előzményt küld a DistributedCachingChatClient-hoz, továbbítja azt a mögöttes ügyfélnek, majd gyorsítótárazza a választ, mielőtt visszaküldené a felhasználónak. Amikor legközelebb ugyanazt a kérést küldi el, úgy, hogy a gyorsítótárban megtalálható a tárolt válasz, a DistributedCachingChatClient a tárolt választ adja vissza, ahelyett hogy a kérést végig kellene küldenie a folyamaton.

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

Az előző példa a Microsoft.Extensions.Caching.Memory NuGet-csomag függ. További információért lásd: Gyorsítótárazás a .NET-ben.

Telemetria használata

Egy másik példa a delegáló csevegőalkalmazásra a OpenTelemetryChatClient. Ez a megvalósítás megfelel a generatív mesterséges intelligencia rendszerek OpenTelemetry szemantikai konvencióinak. A többi IChatClient delegálóhoz hasonlóan a metrikákat és spanokat rétegezi az alapul szolgáló IChatClient implementáció köré, ami fokozott megfigyelhetőséget biztosít.

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

Az előző példa az 📦 OpenTelemetry.Exporter.Console NuGet-csomagtól függ.

Beállítások megadása

A CompleteAsync vagy CompleteStreamingAsync hívásai opcionálisan megadhatnak egy ChatOptions példányt, amely további paramétereket tartalmaz a művelethez. Az AI-modellek és -szolgáltatások leggyakoribb paraméterei erősen beírt tulajdonságokként jelennek meg a típuson, például ChatOptions.Temperature. Más paramétereket a ChatOptions.AdditionalProperties szótáron keresztül, név alapján, gyengén gépelt módon lehet megadni.

Opciókat is megadhat a IChatClient létrehozásakor a fluent ChatClientBuilder API-val és amikor láncolja a hívást a ConfigureOptions bővítménymetódushoz. Ez a delegált ügyfél egy másik ügyfelet burkol, és meghívja a megadott meghatalmazottat, hogy minden híváshoz feltöltsön egy ChatOptions-példányt. Ha például azt szeretné, hogy a ChatOptions.ModelId tulajdonság alapértelmezés szerint egy adott modellnév legyen, az alábbihoz hasonló kódot használhat:

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

A megelőző példa a 📦 Microsoft.Extensions.AI.Ollama NuGet-csomagtól függ.

Funkcionalitási csővezetékek

IChatClient példányok rétegezhetők az összetevők láncának létrehozásához, amelyek mindegyike adott funkciókat ad hozzá. Ezek az összetevők Microsoft.Extensions.AI, más NuGet-csomagokból vagy egyéni implementációkból származhatnak. Ez a megközelítés lehetővé teszi, hogy a IChatClient viselkedését különböző módokon bővítse az ön igényeinek megfelelően. Tekintse meg az alábbi példakódot, amely elosztott gyorsítótárat, függvényhívást és OpenTelemetry-nyomkövetést rétegez egy minta csevegőügyfél köré:

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

Az előző példa a következő NuGet-csomagoktól függ:

Egyéni IChatClient közbenső szoftver

További funkciók hozzáadásához közvetlenül implementálhatja IChatClient, vagy használhatja a DelegatingChatClient osztályt. Ez az osztály olyan csevegési ügyfelek létrehozására szolgál, amelyek műveleteket delegálnak egy másik IChatClient-példányba. Leegyszerűsíti a több ügyfél láncolását, így a hívások továbbítva lesznek egy mögöttes ügyfélnek.

A DelegatingChatClient osztály alapértelmezett implementációkat biztosít olyan metódusokhoz, mint a CompleteAsync, a CompleteStreamingAsyncés a Dispose, amelyek a belső ügyfélnek továbbítják a hívásokat. Ebből az osztályból származhat, és csak azokat a metódusokat bírálhatja felül, amelyekre szüksége van a viselkedés javításához, miközben más hívásokat delegál az alap implementációba. Ez a megközelítés segít rugalmas és moduláris csevegési ügyfeleket létrehozni, amelyek könnyen bővíthetőek és összeállíthatók.

Az alábbi példaosztály a DelegatingChatClient-ból van származtatva, hogy terheléskorlátozó funkciókat biztosítson, a RateLimiterfelhasználásával.

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

Az előző példa a 📦 System.Threading.RateLimiting NuGet-csomagon alapszik. A RateLimitingChatClient kombinációja egy másik ügyféllel egyszerű:

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

Az ilyen összetevők másokkal való összetételének egyszerűsítése érdekében az összetevő-szerzőknek létre kell hozniuk egy Use* bővítménymetódust az összetevő folyamatba való regisztrálásához. Vegyük például a következő bővítménymetódust:

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>

Az ilyen bővítmények a DI-tárolóból is lekérdezhetik a releváns szolgáltatásokat; a csővezeték által használt IServiceProvider opcionális paraméterként kerül átadásra.

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>

A fogyasztó ezután egyszerűen használhatja ezt a csővezetékben, például:

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

Ez a példa bemutatja a fogadott forgatókönyv, ahol a fogyasztó a függőségi injekcióra támaszkodik a RateLimiter példány biztosítása érdekében. Az előző bővítménymetódusok bemutatják egy Use metódus használatát ChatClientBuilder-n. A ChatClientBuilderUse túlterheléseket is biztosít, amelyek megkönnyítik az ilyen delegáló kezelők írását.

A korábbi RateLimitingChatClient példában például a CompleteAsync és CompleteStreamingAsync felülbírálásainak csak a folyamat következő ügyfélére való delegálás előtt és után kell működnie. Ha ugyanazt szeretné elérni anélkül, hogy egyéni osztályt írna, használhatja a Use túlterhelését, amely elfogad egy delegáltat, amely mind a CompleteAsync, mind a CompleteStreamingAsyncszámára használatos, a szükséges sablonkódot csökkentve.

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

** Az előző túlterhelés belsőleg használ egy AnonymousDelegatingChatClient-t, amely lehetővé teszi bonyolultabb minták használatát, csupán kevés plusz kóddal. Ha például ugyanazt az eredményt szeretné elérni, de a DI-ből visszakeresett 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();

Azokban az esetekben, amikor a fejlesztő közvetlenül szeretné megadni a CompleteAsync és CompleteStreamingAsync delegált implementációit, és fontos, hogy mindegyikhez eltérő implementációt lehessen írni a különleges visszatérési típusok kezeléséhez, létezik a Use egy másik túlterhelése, amely mindegyikhez fogad egy delegáltat.

Függőséginjektálás

IChatClient implementációkat általában függőséginjektálás (DI)keresztül biztosítják az alkalmazásoknak. Ebben a példában egy IDistributedCache kerül hozzáadásra a DI-konténerbe, csakúgy mint egy IChatClient. A IChatClient regisztrációja egy olyan építőt alkalmaz, amely létrehoz egy folyamatot, amely egy gyorsítótárazási ügyfelet tartalmaz (amely ezután a DI-ből lekért IDistributedCache-et használja) és a mintaügyfelet. Az IChatClient injektált elem lekérhető és felhasználható az alkalmazás más részein is.

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

Az előző példa a következő NuGet-csomagoktól függ:

A beszúrt példány és konfiguráció az alkalmazás aktuális igényeitől függően eltérő lehet, és több folyamat is injektálható különböző kulcsokkal.

A IEmbeddingGenerator felület

A IEmbeddingGenerator<TInput,TEmbedding> felület a beágyazások általános generátorát jelöli. Itt TInput a beágyazott bemeneti értékek típusa, TEmbedding pedig a generált beágyazás típusa, amely a Embedding osztálytól öröklődik.

A Embedding osztály egy IEmbeddingGeneratoráltal létrehozott beágyazások alaposztályaként szolgál. A beágyazásokhoz társított metaadatok és adatok tárolására és kezelésére szolgál. Az olyan származtatott típusok, mint a Embedding<T>, konkrét beágyazási vektoradatokat szolgáltatnak. A beágyazás például egy Embedding<T>.Vector tulajdonságot tesz elérhetővé a beágyazási adatok eléréséhez.

A IEmbeddingGenerator felület meghatároz egy metódust, amellyel aszinkron módon generálhat beágyazást a bemeneti értékek gyűjteményéhez, opcionális konfigurációs és lemondási támogatással. Metaadatokat biztosít, amelyek leírják a generátort, és lehetővé teszi az erősen típusos szolgáltatások lekérését, amelyeket a generátor vagy annak alapjául szolgáló szolgáltatások nyújthatnak.

Minta implementáció

Fontolja meg egy IEmbeddingGenerator alábbi mintaalkalmazását az általános struktúra megjelenítéséhez, de ez csak véletlenszerű beágyazási vektorokat hoz létre.

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

Az előző kód:

  • Definiál egy SampleEmbeddingGenerator nevű osztályt, amely implementálja a IEmbeddingGenerator<string, Embedding<float>> felületet.
  • Rendelkezik egy elsődleges konstruktorral, amely elfogadja a végpontot és a modellazonosítót, amely a generátor azonosítására szolgál.
  • Egy Metadata tulajdonságot tesz elérhetővé, amely metaadatokat biztosít a generátorról.
  • Implementálja a GenerateAsync metódust a bemeneti értékek gyűjteményéhez tartozó beágyazások létrehozásához:
    • Aszinkron műveletet szimulál 100 ezredmásodperc késleltetésével.
    • Véletlenszerű beágyazásokat ad vissza az egyes bemeneti értékekhez.

A tényleges konkrét megvalósításokat a következő csomagokban találja:

Beágyazások létrehozása

Az IEmbeddingGenerator<TInput,TEmbedding> által végrehajtott elsődleges művelet a beágyazás, amely a GenerateAsync metódusával történik.

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

Egyéni IEmbeddingGenerator közbenső szoftver

A IChatClient-hez hasonlóan IEmbeddingGenerator implementációk rétegezhetők. Ahogyan a Microsoft.Extensions.AI biztosít delegáló implementációkat a IChatClient számára gyorsítótárazáshoz és telemetriához, úgy nyújt implementációt a IEmbeddingGenerator számára is.

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

A IEmbeddingGenerator lehetővé teszi az egyéni köztes szoftver készítését, amely kibővíti a IEmbeddingGeneratorfunkcióit. A DelegatingEmbeddingGenerator<TInput,TEmbedding> osztály a IEmbeddingGenerator<TInput, TEmbedding> felület implementálása, amely alaposztályként szolgál olyan beágyazási generátorok létrehozásához, amelyek a műveleteket egy másik IEmbeddingGenerator<TInput, TEmbedding>-példányba delegálják. Lehetővé teszi több generátor bármilyen sorrendben történő láncolását, és a hívásokat átengedi egy mögöttes generátornak. Az osztály alapértelmezett implementációkat biztosít az olyan metódusokhoz, mint a GenerateAsync és a Dispose, amelyek továbbítják a hívásokat a belső generátorpéldánynak, lehetővé téve a rugalmas és moduláris beágyazási generációt.

Az alábbi példa egy ilyen delegáló beágyazási generátor implementációja, amely korlátozza a beágyazási generálási kérések gyakoriságát.

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

Ez ezután egy tetszőleges IEmbeddingGenerator<string, Embedding<float>> körül rétegzhető, amely korlátozza az összes végrehajtott beágyazási létrehozási műveletet.

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

Ilyen módon a RateLimitingEmbeddingGenerator más IEmbeddingGenerator<string, Embedding<float>> példányokkal is összeállítható a sebességkorlátozó funkció biztosítása érdekében.

Lásd még: