.NET 開發人員需要在其應用程式中整合和互動越來越多的人工智慧 (AI) 服務。
Microsoft.Extensions.AI
庫提供了一種統一的方法來表示生成式 AI 元件,並實現與各種 AI 服務的無縫整合與互操作性。 本文介紹連結庫,並提供深入的使用範例,以協助您開始使用。
套件
📦 Microsoft.Extensions.AI.Abstractions 套件提供核心交換類型,包括 IChatClient 和 IEmbeddingGenerator<TInput,TEmbedding>。 任何提供 LLM 用戶端的 .NET 程式庫都可以實作 IChatClient
介面,以便與取用程式碼輕鬆整合。
Microsoft.Extensions.AI 套件對 📦 套件有隱含的依賴性。 此套件可讓您使用熟悉的相依性插入和中間件模式,輕鬆地將自動函式工具調用、遙測和快取等元件整合到您的應用程式中。 例如,它提供了 UseOpenTelemetry(ChatClientBuilder, ILoggerFactory, String, Action<OpenTelemetryChatClient>) 擴充方法,該方法為聊天客戶端管道添加了 OpenTelemetry 支援。
要參考的套件
提供抽象實作的函式庫通常只會參考Microsoft.Extensions.AI.Abstractions
。
若要存取用於處理生成式 AI 元件的高階工具,請參考Microsoft.Extensions.AI
套件(此套件本身亦參考Microsoft.Extensions.AI.Abstractions
)。 大部分的取用應用程式和服務都應該參考 Microsoft.Extensions.AI
套件,以及一或多個程式庫,以提供抽象概念的具體實作。
安裝套件
如需如何安裝 NuGet 套件的詳細資訊,請參閱 .NET 應用程式中 的 dotnet 套件新增 或 管理套件相依性。
API 使用範例
下列小節顯示特定的 IChatClient 使用範例:
下列各節顯示特定的 IEmbeddingGenerator 使用範例:
IChatClient
介面
IChatClient 介面會定義負責與提供聊天功能的 AI 服務互動的用戶端抽象概念。 它包含以多重模式內容傳送和接收訊息的方法(例如文字、影像和音訊),可以是完整集合或以累加方式串流處理。 此外,它允許擷取用戶端或其基礎服務所提供的強型別服務。
.NET 程式庫能夠提供語言模型和服務的客戶端,並提供介面 IChatClient
的實作。 接著,介面的任何取用者都可以透過抽象概念順暢地與這些模型和服務互通。 您可以在 IChatClient 和 IEmbeddingGenerator 的範例實作中看到簡單的實作。
要求聊天回應
使用 的 IChatClient實例,您可以呼叫 IChatClient.GetResponseAsync 方法來傳送要求並取得回應。 要求是由一或多個訊息所組成,每個訊息是由一或多個內容片段所組成。 加速器方法存在以簡化常見案例,例如建構單一文字內容的請求。
using Microsoft.Extensions.AI;
using OllamaSharp;
IChatClient client = new OllamaApiClient(
new Uri("http://localhost:11434/"), "phi3:mini");
Console.WriteLine(await client.GetResponseAsync("What is AI?"));
核心 IChatClient.GetResponseAsync
方法會接受訊息清單。 此清單代表交談中所有訊息的歷程記錄。
Console.WriteLine(await client.GetResponseAsync(
[
new(ChatRole.System, "You are a helpful AI assistant"),
new(ChatRole.User, "What is AI?"),
]));
ChatResponse 從 GetResponseAsync
傳回後,會公開ChatMessage實例的清單,代表在作業中產生的一或多個訊息。 在常見情況下,只有一個回應消息,但在某些情況下,可能會有多個訊息。 訊息清單會排序,讓清單中的最後一則訊息代表要求的最後一則訊息。 若要在後續要求中將這些回應消息全部提供給服務,您可以將回應中的訊息新增回訊息清單。
List<ChatMessage> history = [];
while (true)
{
Console.Write("Q: ");
history.Add(new(ChatRole.User, Console.ReadLine()));
ChatResponse response = await client.GetResponseAsync(history);
Console.WriteLine(response);
history.AddMessages(response);
}
請求直播聊天回應
IChatClient.GetStreamingResponseAsync 的輸入與 GetResponseAsync
的輸入相同。 不過,與其傳回作為 ChatResponse 物件的完整回應,該方法會傳回 IAsyncEnumerable<T>,其中 T
是 ChatResponseUpdate,提供一個共同構成單一回應的更新數據流。
await foreach (ChatResponseUpdate update in client.GetStreamingResponseAsync("What is AI?"))
{
Console.Write(update);
}
小提示
串流 API 幾乎與 AI 用戶體驗同義。 C# 可透過其 IAsyncEnumerable<T>
功能來啟用引人注目的應用場景,以自然且高效的方式串流數據。
如同 GetResponseAsync
一樣,您可以將 IChatClient.GetStreamingResponseAsync 中的更新新增回訊息清單。 由於更新是回應的個別部分,因此您可以使用輔助工具,將一或多個更新整合到單個 ToChatResponse(IEnumerable<ChatResponseUpdate>) 實例中。
協助程式(例如AddMessages)會撰寫ChatResponse,然後從回應中擷取撰寫的訊息,並將其新增至清單。
List<ChatMessage> chatHistory = [];
while (true)
{
Console.Write("Q: ");
chatHistory.Add(new(ChatRole.User, Console.ReadLine()));
List<ChatResponseUpdate> updates = [];
await foreach (ChatResponseUpdate update in
client.GetStreamingResponseAsync(history))
{
Console.Write(update);
updates.Add(update);
}
Console.WriteLine();
chatHistory.AddMessages(updates);
}
工具調用
某些模型和服務支援 呼叫工具。 若要收集其他資訊,您可以將 ChatOptions 設定為包含工具相關資訊,通常是 .NET 方法,這樣模型可以請求用戶端來執行這些方法。 模型不需要傳送最終回應,而是要求具有特定自變數的函式調用。 客戶端接著會叫用 函式,並將結果傳回具有交談歷程記錄的模型。
Microsoft.Extensions.AI.Abstractions
資料庫包含各種訊息內容類型的抽象概念,包括函數調用要求和結果。 雖然 IChatClient
消費者可以直接與此內容互動,Microsoft.Extensions.AI
提供了可以自動響應相應請求而調用工具的輔助工具。
Microsoft.Extensions.AI.Abstractions
和Microsoft.Extensions.AI
庫提供下列型態:
- AIFunction:表示可描述至 AI 模型並叫用的函式。
-
AIFunctionFactory:提供 Factory 方法來建立
AIFunction
代表 .NET 方法的實例。 -
FunctionInvokingChatClient:將
IChatClient
包裝為另一個IChatClient
,可以自動調用函式。
下列範例示範隨機函式調用(此範例取決於 📦 OllamaSharp NuGet 套件):
using Microsoft.Extensions.AI;
using OllamaSharp;
string GetCurrentWeather() => Random.Shared.NextDouble() > 0.5 ? "It's sunny" : "It's raining";
IChatClient client = new OllamaApiClient(new Uri("http://localhost:11434"), "llama3.1");
client = ChatClientBuilderChatClientExtensions
.AsBuilder(client)
.UseFunctionInvocation()
.Build();
ChatOptions options = new() { Tools = [AIFunctionFactory.Create(GetCurrentWeather)] };
var response = client.GetStreamingResponseAsync("Should I wear a rain coat?", options);
await foreach (var update in response)
{
Console.Write(update);
}
上述 程式碼:
- 定義名為
GetCurrentWeather
的函式,以傳回隨機天氣預報。 - 使用 ChatClientBuilder 實例化
OllamaSharp.OllamaApiClient
,並將其配置為使用函數調用。 - 在用戶端上呼叫
GetStreamingResponseAsync
,傳遞一個提示和一個工具清單,清單中包含一個以 Create建立的函式。 - 逐一查看回應,將每個更新列印至主控台。
緩存回應
如果您熟悉 .NET中的 IChatClient
,將快取層疊於另一個任意 IChatClient
實例之上。 當新的聊天記錄提交至 DistributedCachingChatClient
時,它會將它轉送至基礎客戶端,然後在將回應傳回給取用者之前快取回應。 下次提交相同的歷程記錄時,如果快取中有對應的回應,DistributedCachingChatClient
會返回該快取回應,而不是將要求沿著管線繼續傳送。
using Microsoft.Extensions.AI;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Options;
using OllamaSharp;
var sampleChatClient = new OllamaApiClient(new Uri("http://localhost:11434"), "llama3.1");
IChatClient client = new ChatClientBuilder(sampleChatClient)
.UseDistributedCache(new MemoryDistributedCache(
Options.Create(new MemoryDistributedCacheOptions())))
.Build();
string[] prompts = ["What is AI?", "What is .NET?", "What is AI?"];
foreach (var prompt in prompts)
{
await foreach (var update in client.GetStreamingResponseAsync(prompt))
{
Console.Write(update);
}
Console.WriteLine();
}
此範例取決於 📦 Microsoft.Extensions.Caching.Memory NuGet 套件。 如需詳細資訊,請參閱在 .NET中
使用遙測
委派聊天客戶端的另一個例子是 OpenTelemetryChatClient。 此實作遵循 OpenTelemetry 語意慣例,用於生成式 AI 系統。 類似於其他 IChatClient
委派者,它會分層計量,並跨越其他任意 IChatClient
實作。
using Microsoft.Extensions.AI;
using OllamaSharp;
using OpenTelemetry.Trace;
// Configure OpenTelemetry exporter.
string sourceName = Guid.NewGuid().ToString();
TracerProvider tracerProvider = OpenTelemetry.Sdk.CreateTracerProviderBuilder()
.AddSource(sourceName)
.AddConsoleExporter()
.Build();
IChatClient ollamaClient = new OllamaApiClient(
new Uri("http://localhost:11434/"), "phi3:mini");
IChatClient client = new ChatClientBuilder(ollamaClient)
.UseOpenTelemetry(
sourceName: sourceName,
configure: c => c.EnableSensitiveData = true)
.Build();
Console.WriteLine((await client.GetResponseAsync("What is AI?")).Text);
(上述範例取決於 📦 OpenTelemetry.Exporter.Console NuGet 套件。
或者, LoggingChatClient 和對應的 UseLogging(ChatClientBuilder, ILoggerFactory, Action<LoggingChatClient>) 方法提供一個簡單的方法,將記錄專案 ILogger 寫入每個要求和回應的 。
提供選項
每個對 GetResponseAsync 或 GetStreamingResponseAsync 的呼叫都可以選擇性地提供包含作業其他參數的 ChatOptions 實例。 在 AI 模型和服務中,最常見的參數會作為類型上的強類型屬性出現,例如 ChatOptions.Temperature。 其他參數可以透過名稱以弱型別方式、透過 ChatOptions.AdditionalProperties 字典,或透過基礎提供者可以理解的選項實例並使用 ChatOptions.RawRepresentationFactory 屬性來提供。
您也可以透過 Fluent IChatClient
API 來建構ChatClientBuilder,並在鏈結調用ConfigureOptions(ChatClientBuilder, Action<ChatOptions>)擴充方法時指定選項。 這個委派客戶端包裝了另一個客戶端,並調用提供的委派以在每次呼叫時填充一個ChatOptions
實例。 例如,若要確保 ChatOptions.ModelId 屬性預設為特定模型名稱,您可以使用如下所示的程式代碼:
using Microsoft.Extensions.AI;
using OllamaSharp;
IChatClient client = new OllamaApiClient(new Uri("http://localhost:11434"));
client = ChatClientBuilderChatClientExtensions.AsBuilder(client)
.ConfigureOptions(options => options.ModelId ??= "phi3")
.Build();
// Will request "phi3".
Console.WriteLine(await client.GetResponseAsync("What is AI?"));
// Will request "llama3.1".
Console.WriteLine(await client.GetResponseAsync("What is AI?", new() { ModelId = "llama3.1" }));
功能管線
IChatClient
實例可以分層,以建立元件管線,每個元件都會新增額外的功能。 這些元件可以來自 Microsoft.Extensions.AI
、其他 NuGet 套件或自定義實作。 此方法可讓您以各種方式增強 IChatClient
的行為,以符合您的特定需求。 請考慮以下代碼段,它對範例聊天客戶端進行分層,包括分散式快取、函式調用和 OpenTelemetry 追蹤。
// Explore changing the order of the intermediate "Use" calls.
IChatClient client = new ChatClientBuilder(new OllamaApiClient(new Uri("http://localhost:11434"), "llama3.1"))
.UseDistributedCache(new MemoryDistributedCache(Options.Create(new MemoryDistributedCacheOptions())))
.UseFunctionInvocation()
.UseOpenTelemetry(sourceName: sourceName, configure: c => c.EnableSensitiveData = true)
.Build();
自訂IChatClient
中間件
若要新增其他功能,您可以直接實作 IChatClient
或使用 DelegatingChatClient 類別。 這個類別可作為建立聊天客戶端的基礎,以將作業委派給另一個 IChatClient
實例。 它簡化了鏈結多個客戶端,允許呼叫傳遞至基礎用戶端。
DelegatingChatClient
類別會針對 GetResponseAsync
、GetStreamingResponseAsync
和 Dispose
等方法提供預設實作,以將呼叫轉送至內部用戶端。 因此,衍生類別可以只覆寫需要增強其行為的方法,同時委派其他呼叫給基類的實作。 這種方法很適合用來建立易於擴充和撰寫的彈性和模組化聊天用戶端。
以下是衍生自 DelegatingChatClient
的範例類別,其使用 System.Threading.RateLimiting 連結庫來提供速率限制功能。
using Microsoft.Extensions.AI;
using System.Runtime.CompilerServices;
using System.Threading.RateLimiting;
public sealed class RateLimitingChatClient(
IChatClient innerClient, RateLimiter rateLimiter)
: DelegatingChatClient(innerClient)
{
public override async Task<ChatResponse> GetResponseAsync(
IEnumerable<ChatMessage> messages,
ChatOptions? options = null,
CancellationToken cancellationToken = default)
{
using var lease = await rateLimiter.AcquireAsync(permitCount: 1, cancellationToken)
.ConfigureAwait(false);
if (!lease.IsAcquired)
throw new InvalidOperationException("Unable to acquire lease.");
return await base.GetResponseAsync(messages, options, cancellationToken)
.ConfigureAwait(false);
}
public override async IAsyncEnumerable<ChatResponseUpdate> GetStreamingResponseAsync(
IEnumerable<ChatMessage> messages,
ChatOptions? options = null,
[EnumeratorCancellation] CancellationToken cancellationToken = default)
{
using var lease = await rateLimiter.AcquireAsync(permitCount: 1, cancellationToken)
.ConfigureAwait(false);
if (!lease.IsAcquired)
throw new InvalidOperationException("Unable to acquire lease.");
await foreach (var update in base.GetStreamingResponseAsync(messages, options, cancellationToken)
.ConfigureAwait(false))
{
yield return update;
}
}
protected override void Dispose(bool disposing)
{
if (disposing)
rateLimiter.Dispose();
base.Dispose(disposing);
}
}
如同其他 IChatClient
實作, RateLimitingChatClient
可以組成:
using Microsoft.Extensions.AI;
using OllamaSharp;
using System.Threading.RateLimiting;
var client = new RateLimitingChatClient(
new OllamaApiClient(new Uri("http://localhost:11434"), "llama3.1"),
new ConcurrencyLimiter(new() { PermitLimit = 1, QueueLimit = int.MaxValue }));
Console.WriteLine(await client.GetResponseAsync("What color is the sky?"));
為了簡化將這類元件與其他元件進行組合,元件作者應該建立一個 Use*
擴充方法,以便將元件註冊到流水線中。 例如,請考慮下列 UseRatingLimiting
擴充方法:
using Microsoft.Extensions.AI;
using System.Threading.RateLimiting;
public static class RateLimitingChatClientExtensions
{
public static ChatClientBuilder UseRateLimiting(
this ChatClientBuilder builder,
RateLimiter rateLimiter) =>
builder.Use(innerClient =>
new RateLimitingChatClient(innerClient, rateLimiter)
);
}
這類擴充功能也可以從 DI 容器查詢相關服務;管線所使用的 IServiceProvider 會以選擇性參數的形式傳入:
using Microsoft.Extensions.AI;
using Microsoft.Extensions.DependencyInjection;
using System.Threading.RateLimiting;
public static class RateLimitingChatClientExtensions
{
public static ChatClientBuilder UseRateLimiting(
this ChatClientBuilder builder,
RateLimiter? rateLimiter = null) =>
builder.Use((innerClient, services) =>
new RateLimitingChatClient(
innerClient,
services.GetRequiredService<RateLimiter>())
);
}
現在,消費者可以很容易地在管線中使用這個功能,例如:
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
IChatClient client = new OllamaApiClient(
new Uri("http://localhost:11434/"),
"phi3:mini");
builder.Services.AddChatClient(services =>
client
.AsBuilder()
.UseDistributedCache()
.UseRateLimiting()
.UseOpenTelemetry()
.Build(services));
先前的擴充方法示範如何在 Use
上使用 ChatClientBuilder方法。
ChatClientBuilder
也提供 Use 多載,可讓您更輕鬆地撰寫這類委派處理程式。 例如,在前面的 RateLimitingChatClient
範例中,GetResponseAsync
和 GetStreamingResponseAsync
的覆寫只需要在委派給下一個管線中的客戶端之前和之後進行工作。 若要在不撰寫自訂類別的情況下達到相同的目標,您可以使用接受委派以便用於 Use
和 GetResponseAsync
的 GetStreamingResponseAsync
重載,以減少所需的樣板程式碼:
using Microsoft.Extensions.AI;
using OllamaSharp;
using System.Threading.RateLimiting;
RateLimiter rateLimiter = new ConcurrencyLimiter(new()
{
PermitLimit = 1,
QueueLimit = int.MaxValue
});
IChatClient client = new OllamaApiClient(new Uri("http://localhost:11434"), "llama3.1");
client = ChatClientBuilderChatClientExtensions
.AsBuilder(client)
.UseDistributedCache()
.Use(async (messages, options, nextAsync, cancellationToken) =>
{
using var lease = await rateLimiter.AcquireAsync(permitCount: 1, cancellationToken).ConfigureAwait(false);
if (!lease.IsAcquired)
throw new InvalidOperationException("Unable to acquire lease.");
await nextAsync(messages, options, cancellationToken);
})
.UseOpenTelemetry()
.Build();
在需要針對GetResponseAsync
和GetStreamingResponseAsync
使用不同的實作以處理各自獨特的傳回型別的情境中,您可以使用Use(Func<IEnumerable<ChatMessage>,ChatOptions,IChatClient,CancellationToken,
Task<ChatResponse>>, Func<IEnumerable<ChatMessage>,ChatOptions,
IChatClient,CancellationToken,IAsyncEnumerable<ChatResponseUpdate>>)多載來為每一個使用委派。
依賴注入
IChatClient 實作通常會透過 相依性插入 (DI) 提供給應用程式。 在此範例中,會將 IDistributedCache 新增至 DI 容器,就像加入 IChatClient
一樣。 註冊 IChatClient
使用一個建構器來建立管線,其中包含一個快取用戶端(接著使用從 DI 擷取的 IDistributedCache
)以及範例用戶端。 注入的 IChatClient
可以被取回並在應用程式的其他地方使用。
using Microsoft.Extensions.AI;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using OllamaSharp;
// App setup.
var builder = Host.CreateApplicationBuilder();
builder.Services.AddDistributedMemoryCache();
builder.Services.AddChatClient(new OllamaApiClient(new Uri("http://localhost:11434"), "llama3.1"))
.UseDistributedCache();
var host = builder.Build();
// Elsewhere in the app.
var chatClient = host.Services.GetRequiredService<IChatClient>();
Console.WriteLine(await chatClient.GetResponseAsync("What is AI?"));
插入的實例和組態可能會根據應用程式目前的需求而有所不同,而且可以使用不同的索引鍵插入多個管線。
無狀態與具狀態用戶端
無狀態服務需要在每個要求中傳回所有相關的對話歷程。 相反地, 具狀態 服務會追蹤歷程記錄,而且只需要使用要求傳送其他訊息。 介面 IChatClient 的設計目的是要處理無狀態和具狀態 AI 服務。
使用無狀態服務時,呼叫端會維護所有訊息的清單。 他們會新增所有已接收的回應消息,並在後續互動時提供清單。
List<ChatMessage> history = [];
while (true)
{
Console.Write("Q: ");
history.Add(new(ChatRole.User, Console.ReadLine()));
var response = await client.GetResponseAsync(history);
Console.WriteLine(response);
history.AddMessages(response);
}
對於具狀態的服務,您可能已經知道用於相關對話的識別碼。 您可以將該識別碼放入 ChatOptions.ConversationId。 使用量接著會遵循相同的模式,但不需要手動維護歷程記錄。
ChatOptions statefulOptions = new() { ConversationId = "my-conversation-id" };
while (true)
{
Console.Write("Q: ");
ChatMessage message = new(ChatRole.User, Console.ReadLine());
Console.WriteLine(await client.GetResponseAsync(message, statefulOptions));
}
某些服務可能支援針對沒有對話 ID 的請求自動創建對話 ID,或創建新的對話 ID,以表示納入最後一輪訊息後對話的當前狀態。 在這種情況下,您可以將 ChatResponse.ConversationId 移轉到 ChatOptions.ConversationId
,以處理後續的請求。 例如:
ChatOptions options = new();
while (true)
{
Console.Write("Q: ");
ChatMessage message = new(ChatRole.User, Console.ReadLine());
ChatResponse response = await client.GetResponseAsync(message, options);
Console.WriteLine(response);
options.ConversationId = response.ConversationId;
}
如果您事先不知道服務是否為無狀態或具狀態,您可以檢查回應 ConversationId 並根據其值採取行動。 如果已設定,則會將該值傳播至選項,並清除歷程記錄,以免再次重新傳送相同的歷程記錄。 如果未設定回應 ConversationId
,則會將回應訊息新增至歷程記錄,以便在下一回合傳回服務。
List<ChatMessage> chatHistory = [];
ChatOptions chatOptions = new();
while (true)
{
Console.Write("Q: ");
chatHistory.Add(new(ChatRole.User, Console.ReadLine()));
ChatResponse response = await client.GetResponseAsync(chatHistory);
Console.WriteLine(response);
chatOptions.ConversationId = response.ConversationId;
if (response.ConversationId is not null)
{
chatHistory.Clear();
}
else
{
chatHistory.AddMessages(response);
}
}
IEmbeddingGenerator
介面
IEmbeddingGenerator<TInput,TEmbedding> 介面代表嵌入向量的通用生成器。 針對泛型型別參數, TInput
是要內嵌的輸入值類型,而 TEmbedding
是產生的內嵌類型,其繼承自 Embedding 類別。
Embedding
類別作為由 IEmbeddingGenerator
所生成嵌入向量的基礎類別。 其設計目的是儲存和管理與內嵌相關聯的元數據和數據。 衍生型別,例如 Embedding<T>,提供具象內嵌向量數據。 例如, Embedding<float>
會公開一個ReadOnlyMemory<float> Vector { get; }
屬性以存取其內嵌數據。
IEmbeddingGenerator
介面定義了一種方法,以非同步的方式為一組輸入值生成嵌入,並支援可選的組態和取消功能。 它還提供描述產生器的元數據,並允許提取可由產生器或其底層服務提供的強型別服務。
大部分的使用者不需要實作 IEmbeddingGenerator
介面。 不過,如果您是連結庫作者,您可以在 IChatClient 和 IEmbeddingGenerator 的範例實作中看到簡單的實作。
建立內嵌
使用 IEmbeddingGenerator<TInput,TEmbedding> 執行的主要作業是嵌入生成,這是通過其 GenerateAsync 方法實現的。
using Microsoft.Extensions.AI;
using OllamaSharp;
IEmbeddingGenerator<string, Embedding<float>> generator =
new OllamaApiClient(new Uri("http://localhost:11434/"), "phi3:mini");
foreach (Embedding<float> embedding in
await generator.GenerateAsync(["What is AI?", "What is .NET?"]))
{
Console.WriteLine(string.Join(", ", embedding.Vector.ToArray()));
}
加速器擴充方法也存在以簡化常見案例,例如從單一輸入產生內嵌向量。
ReadOnlyMemory<float> vector = await generator.GenerateVectorAsync("What is AI?");
功能的管線
如同 IChatClient
,IEmbeddingGenerator
實作可以分層。
Microsoft.Extensions.AI
提供用於快取和遙測的委派實 IEmbeddingGenerator
作。
using Microsoft.Extensions.AI;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Options;
using OllamaSharp;
using OpenTelemetry.Trace;
// Configure OpenTelemetry exporter
string sourceName = Guid.NewGuid().ToString();
TracerProvider tracerProvider = OpenTelemetry.Sdk.CreateTracerProviderBuilder()
.AddSource(sourceName)
.AddConsoleExporter()
.Build();
// Explore changing the order of the intermediate "Use" calls to see
// what impact that has on what gets cached and traced.
IEmbeddingGenerator<string, Embedding<float>> generator = new EmbeddingGeneratorBuilder<string, Embedding<float>>(
new OllamaApiClient(new Uri("http://localhost:11434/"), "phi3:mini"))
.UseDistributedCache(
new MemoryDistributedCache(
Options.Create(new MemoryDistributedCacheOptions())))
.UseOpenTelemetry(sourceName: sourceName)
.Build();
GeneratedEmbeddings<Embedding<float>> embeddings = await generator.GenerateAsync(
[
"What is AI?",
"What is .NET?",
"What is AI?"
]);
foreach (Embedding<float> 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 OllamaSharp;
using System.Threading.RateLimiting;
IEmbeddingGenerator<string, Embedding<float>> generator =
new RateLimitingEmbeddingGenerator(
new OllamaApiClient(new Uri("http://localhost:11434/"), "phi3:mini"),
new ConcurrencyLimiter(new()
{
PermitLimit = 1,
QueueLimit = int.MaxValue
}));
foreach (Embedding<float> embedding in
await generator.GenerateAsync(["What is AI?", "What is .NET?"]))
{
Console.WriteLine(string.Join(", ", embedding.Vector.ToArray()));
}
如此一來, RateLimitingEmbeddingGenerator
即可與其他 IEmbeddingGenerator<string, Embedding<float>>
實例組成,以提供速率限制功能。
使用 Microsoft.Extensions.AI 建置
您可以以下列方式開始使用 Microsoft.Extensions.AI
:
- 連結庫開發人員:如果您擁有提供 AI 服務用戶端的連結庫,請考慮在連結庫中實作介面。 這可讓使用者透過抽象概念輕鬆地整合 NuGet 套件。 如需範例實作,請參閱 IChatClient 和 IEmbeddingGenerator 的範例實作。
- 服務使用者:如果您要開發取用 AI 服務的程式庫,請使用抽象層,而不是將硬式編碼至特定的 AI 服務。 這種方法可讓您的取用者彈性選擇慣用的提供者。
- 應用程式開發人員:使用抽象概念簡化應用程式整合。 這可讓您跨模型和服務進行可移植性、促進測試和模擬、利用生態系統提供的中間件,並在整個應用程式中維護一致的API,即使您在應用程式的不同部分使用不同的服務也一樣。
- 生態系統參與者:如果您有興趣參與生態系統,請考慮撰寫自定義中間件元件。
如需更多範例,請參閱 dotnet/ai-samples GitHub 存放庫。 如需端對端範例,請參閱 eShopSupport。