.NET AI 生态关键拼图:全面解读 AI Extensions 和 Vector Extensions 如何重塑.NET开发生态

引言

关注.NET AI.NET Vector原生开发已有半年之久了,其核心组件在历经这半年预发布期的持续迭代后,终于于5月16日和5月20日逐步发布了。。在此之前,基于预发布版本撰写的文章和调试工作常常受限于功能的缺失,许多特性无法正常调用,只能通过下载源代码进行调试。

如今,随着正式版的发布,这些库为 .NET 原生 AI 开发提供了强大的基础,支持开发者构建可扩展、可维护且具备互作性的 AI 驱动型应用程序。

什么是 AI 和 Vector Data Extensions

AIVector Data Extensions 是一组专为 .NET 设计的库,旨在处理 AI 模型和矢量存储相关任务。它们通过提供共享的抽象和实用工具,帮助开发者在 .NET 生态系统中无缝集成 AI 功能。

  • 以下是这些库的核心组成部分:
库名称 功能描述
Microsoft.Extensions.AI.Abstractions 定义 AI 模型的常见类型和抽象
Microsoft.Extensions.AI 提供 AI 扩展的实用工具
Microsoft.Extensions.VectorData.Abstractions 为向量存储提供交换类型和抽象

很有意思的是,虽然本次发布的这三个库都是第一个正式版本,但是正式版的版本号是从 9.5.0 开始,大家一定要注意,如下图所示:
Microsoft.Extensions.AI Microsoft.Extensions.AI.Abstractions Microsoft.Extensions.VectorData.Abstractions

  • 这些库作为更高级别组件的基础构建块,致力于实现以下目标:

应用目标及说明

构建库 vs. 构建应用程序

  • 构建开发基础库 :保持对特定 AI 或矢量系统的不可知性至关重要。仅依赖共享抽象可以避免将用户绑定到某一特定提供商,同时确保库与其他库的互作性,从而提升生态系统的灵活性和兼容性。
  • 构建应用程序 :开发者可以更自由地选择具体实现,享受一致 API 带来的便利,轻松切换或组合不同提供商,而无需大幅调整代码。

AIVector Data Extensions 为开发者提供了关键的构建块,使其能够更轻松地在应用程序中实现高级 AI 功能,例如结构化输出、工具调用和可观察性等。这些库通过一致的抽象,助力开发者打造强大、可维护且生产就绪的解决方案,满足特定需求。

AI 和 Vector Data Extensions 的应用

依赖注入配置

现代 .NET 应用程序依赖于依赖注入(DI)来管理服务的配置和生命周期。AIVector Data 扩展库专为与 DI 模型保持一致而设计。

无论是本地开发还是生产环境,这些扩展都能无缝注册到现有的 DI 容器中,使 AI 组件与其他应用程序部分一样易于组合和配置,如下代码需要安装:

复制代码
dotnet add package Microsoft.SemanticKernel --version 1.54.0
dotnet add package Microsoft.SemanticKernel.Connectors.InMemory --version 1.54.0
dotnet add package Microsoft.SemanticKernel.Connectors.Ollama --version 1.54.0-preview
dotnet add package Microsoft.SemanticKernel.Connectors.Qdrant --version 1.54.0-preview
dotnet add package Microsoft.SemanticKernel.Plugins.OpenApi --version 1.54.0-preview
dotnet add package OllamaSharp --version 5.1.19

using Microsoft.Extensions.AI;
using OllamaSharp;

// 添加聊天客户端
builder.Services.AddChatClient(sp => new OpenAI.OpenAIClient("OpenAIKey").GetChatClient("ModelName").AsIChatClient())
    .UseLogging()
    .UseOpenTelemetry();

// 添加嵌入生成器
builder.Services.AddEmbeddingGenerator(sp => new OllamaApiClient("http://localhost:11434/", defaultModel: "text-embedding-3-small"))
    .UseLogging()
    .UseOpenTelemetry();

// 添加 SQLite 集合,假设 Product 类已定义
builder.Services.AddQdrantCollection<int, Product>("Products", "localhost");

多模型和向量存储多样化

无论是在本地开发与生产环境中使用不同模型提供商,还是构建依赖多种模型的代理,AIVector Data Extensions 都能提供一致的 API

随着官方和社区支持的软件包生态系统不断扩展,集成不同模型和向量数据库变得更加简单高效,这进一步提高了多模型之间和向量存储提供商之间的可迁移性,确保了开发过程的灵活性与应用的广泛适应性。

如下的演示中,我通过配置Ollama环境,结合 phi3:latest 模型来完成,有条件的同学可以试试Azure AI。

复制代码
using Microsoft.Extensions.AI;
using Microsoft.Extensions.VectorData;
using Microsoft.SemanticKernel.Connectors.Qdrant;
using OllamaSharp;
using Qdrant.Client;

IChatClient chatClient = new OllamaApiClient("http://localhost:11434/", "phi3:latest");

// 有条件的同学可以试试Azure AI
// IChatClient chatClient =  : new AzureOpenAIClient("YOUR-AZURE-OPENAI-ENDPOINT", new DefaultAzureCredential()).GetChatClient("gpt-4.1").AsIChatClient();

await foreach (ChatResponseUpdate message in chatClient.GetStreamingResponseAsync("What is AI?"))
{
    Console.Write($"{message.Text}");
}

IEmbeddingGenerator<string, Embedding<float>> embeddingGenerator = new OllamaApiClient("http://localhost:11434/", "phi3:latest");

//IEmbeddingGenerator<string, Embedding<float>> embeddingGenerator = new AzureOpenAIClient("YOUR-AZURE-OPENAI-ENDPOINT", new DefaultAzureCredential()).GetEmbeddingClient("text-embedding-3-small").AsIEmbeddingGenerator();

Embedding<float> embedding = await embeddingGenerator.GenerateAsync("What is AI?");

// 大家也可以使用Sqlite,此处不做演示
// VectorStoreCollection<ulong, Product> collection = new SqliteCollection<int, Product>("Data Source=products.db", "products", new SqliteCollectionOptions { EmbeddingGenerator = embeddingGenerator})

VectorStoreCollection<ulong, Product> collection = new QdrantCollection<ulong, Product>(
    new QdrantClient("localhost"),
    "products",
    true,
    new QdrantCollectionOptions { EmbeddingGenerator = embeddingGenerator });

await collection.EnsureCollectionExistsAsync();

await collection.UpsertAsync(new Product
{
    Id = 1,
    Name = "Test",
    TenantId = 5,
    Embedding = embedding
});

Console.Write("向量写入成功");

Console.ReadKey();

record Product
{
    [VectorStoreKey]
    public ulong Id { get; set; }

    [VectorStoreData]
    public required string Name { get; set; }

    [VectorStoreData]
    public int TenantId { get; set; }

    [VectorStoreVector(Dimensions: 3072)]
    public Embedding<float>? Embedding { get; set; }
}

运行结果

请注意 Product 实体的声明

  • VectorStoreKey:用于标记数据模型中唯一键的属性。

    record Product
    {
    [VectorStoreKey]
    public int Key { get; set; }
    }

  • VectorStoreData:属性用于标记数据字段,可以指定是否支持索引或全文搜索。

    [VectorStoreData(IsIndexed = true)]
    public string HotelName { get; set; }

  • VectorStoreVector:属性用于标记向量字段,指定向量的维度、距离函数(如余弦相似度)等。

    [VectorStoreVector(Dimensions = 1536, DistanceFunction = DistanceFunction.CosineSimilarity)]
    public ReadOnlyMemory<float> DescriptionEmbedding { get; set; }

注意向量存储尚未完整发布

如下图所示,Qdrant、Pipecone、Mongodb、Weaviate、SQL Server等,都处于preview版本,我相信不用太久,这些原生支持的正式版都会发布出来。

多模态处理

生成式 AI 模型不仅限于处理文本,还能应对图像、音频等多种数据类型。虽然模型输出通常是非结构化的,与应用程序的集成较为复杂,但是现在许多模型已支持结构化输出,可以根据预定义的架构(如 JSON)格式化响应,从而提升输出的可靠性和可预测性。

为此,AI 扩展库提供了灵活的基础,用于表示不同格式的数据。AI 扩展库与结构化输出无缝协作,让模型响应能够直接映射到 C# 类型。

以下是一个处理收据图像并提取数据的示例:

复制代码
using Microsoft.Extensions.AI;
using OllamaSharp;

Uri imageUri = new Uri("https://images.cnblogs.com/cnblogs_com/blogs/272929/galleries/2447197/o_250526033729_qrcode_for_gh_5d49c4cbffe5_258.jpgg");

List<AIContent> content = [
    new TextContent("Process this receipt"),
    new UriContent(imageUri, mediaType: "image/jpeg")
];

ChatMessage message = new ChatMessage(ChatRole.User, content);
IChatClient chatClient = new OllamaApiClient("http://localhost:11434/", "phi3:latest");
ChatResponse<Receipt> response = await chatClient.GetResponseAsync<Receipt>(message);

response.TryGetResult(out Receipt? receiptData);

Console.WriteLine($"Merchant: {receiptData.Merchant} | Total: {receiptData.Total} | Category: {receiptData.Category}");

Console.ReadKey();

record Item(string Name, float Price);
enum Category { Food, Electronics, Clothing, Services };
record Receipt(string Merchant, List<Item> Items, float Total, Category Category);

运行结果

辅助功能集成

为了确保系统的可靠性和性能,还需要通过日志记录、缓存和可观测性等功能进行增强,可以通过插入自己的 ILoggerIDistributedCache 以及 OpenTelemetry 兼容的工具,无需从头构建。

以下是一个简单的启用示例:

复制代码
using Microsoft.Extensions.AI;
using OllamaSharp;
using System.ComponentModel;
using System.Text.Json;
using ChatMessage = Microsoft.Extensions.AI.ChatMessage;

IChatClient chatClient = new OllamaApiClient("http://localhost:11434/", "phi3:latest");

IChatClient chatClient1 = new ChatClientBuilder(chatClient)
    .UseLogging()
    .UseDistributedCache()
    .UseOpenTelemetry()
    .Build();

自定义扩展

例如,您可以通过以下方式实现调用频率限制:

复制代码
using Microsoft.Extensions.AI;
using OllamaSharp;
using System.ComponentModel;
using System.Text.Json;
using System.Threading.RateLimiting;
using ChatMessage = Microsoft.Extensions.AI.ChatMessage;

IChatClient chatClient = new OllamaApiClient("http://localhost:11434/", "phi3:latest");

RateLimiter rateLimiter = new ConcurrencyLimiter(new()
{
    PermitLimit = 1,
    QueueLimit = int.MaxValue
});

IChatClient client = new ChatClientBuilder(chatClient)
    .UseDistributedCache()
    // 功能新增
    .Use(async (messages, options, nextAsync, cancellationToken) =>
    {
        using RateLimitLease 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();

这种方式允许可以不改变核心逻辑的情况下,灵活添加自定义行为。

Function Call

AI 模型虽能处理数据并理解自然语言,但无法独立执行行动。为了实现有意义的交互,它们需要访问外部工具和系统。Function Call 功能应运而生,许多现代生成式 AI 模型已支持此功能,允许模型根据用户意图自动调用函数。

AI 扩展库让这一功能在应用程序中变得简单易用。以下示例展示了如何将 CalculateTax 方法注册为 AI 可调用函数,模型会根据用户请求自动触发:

复制代码
using Microsoft.Extensions.AI;
using OllamaSharp;
using System.ComponentModel;
using System.Text.Json;
using ChatMessage = Microsoft.Extensions.AI.ChatMessage;

[Description("Calculate tax given a receipt and tax rate")]
float CalculateTax(Receipt receipt, float taxRate)
{
    return receipt.Total * (1 + taxRate);
}

IChatClient chatClient = new OllamaApiClient("http://localhost:11434/", "llama3.2:latest");

IChatClient functionChatClient =
    chatClient
        .AsBuilder()
        .UseFunctionInvocation()
        .Build();

Receipt receiptData = new Receipt(
    "Test Merchant",
    new List<Item>
    {
        new Item("Item 1", 10.00f),
        new Item("Item 2", 20.00f),
        new Item("Item 3", 30.00f)
    },
    60.00f,
    Category.Food
);

ChatMessage message = new ChatMessage(ChatRole.User, [
    new TextContent("Here is information from a recent purchase"),
    new TextContent($"{JsonSerializer.Serialize(receiptData)}"),
    new TextContent("What is the total price after tax given a tax rate of 10%?")
]);

ChatResponse<ReceiptTotal> response = await functionChatClient.GetResponseAsync<ReceiptTotal>(message, new ChatOptions { Tools = [AIFunctionFactory.Create(CalculateTax)] });

response.TryGetResult(out ReceiptTotal? receiptTotal);
Console.WriteLine($"SubTotal: {receiptTotal.SubTotal} | TaxAmount: {receiptTotal.TaxAmount} | TaxRate: {receiptTotal.TaxRate} | Total: {receiptTotal.Total}");
Console.ReadKey();

record ReceiptTotal(float SubTotal, float TaxAmount, float TaxRate, float Total);
record Item(string Name, float Price);
enum Category { Food, Electronics, Clothing, Services };
record Receipt(string Merchant, List<Item> Items, float Total, Category Category);

运行结果

此处需要特别注意:要换一个模型,我使用的是 llama3.2:latest ,因为这个模型支持工具调用。如果继续使用 phi3,则会报如下错误:

搜索功能

根据具体的业务场景和数据模型,需要更高级的搜索能力。Vector Data Extension 提供了丰富的搜索功能,包括多种相似性指标、向量搜索、混合搜索以及筛选支持,查询过程被极大简化:

  • 传入纯文本
  • 抽象层自动处理嵌入生成、应用相似性指标
  • 筛选
  • 返回最相关结果

以下示例展示了如何搜索与自然语言查询匹配的产品,并按租户过滤:

复制代码
using Microsoft.Extensions.AI;
using Microsoft.Extensions.VectorData;
using OllamaSharp;
using Microsoft.SemanticKernel.Connectors.Qdrant;
using Navyblue.BaseLibrary;
using Qdrant.Client;

IEmbeddingGenerator<string, Embedding<float>> embeddingGenerator = new OllamaApiClient("http://localhost:11434/", "phi3:latest");
VectorStoreCollection<ulong, Product> collection =new QdrantCollection<ulong, Product>(
    new QdrantClient("localhost"),
    "products",
    true,
    new QdrantCollectionOptions { EmbeddingGenerator = embeddingGenerator });

string query = "Test";

await foreach (VectorSearchResult<Product> result in collection.SearchAsync(query, top: 5, new() { Filter = r => r.TenantId == 5 }))
{
    Console.WriteLine(result.Record.ToJson());
}

record Product
{
    [VectorStoreKey]
    public ulong Id { get; set; }

    [VectorStoreData]
    public required string Name { get; set; }

    [VectorStoreData]
    public int TenantId { get; set; }

    [VectorStoreVector(Dimensions: 3072)]
    public Embedding<float>? Embedding { get; set; }
}

运行结果

生态系统

AIVector Data 扩展库的采用率持续攀升,仅在短短几个月内,下载量已超过 300 万次,近 100 个公共 NuGet 包依赖于它们。以下是一些官方和社区项目的应用示例:

应用示例 描述
模型上下文协议(MCP)、AI 评估、Pieces
代理框架 Semantic Kernel、AutoGen
SDK Azure OpenAI、OllamaSharp、Anthropic、Google、HuggingFace、Sqlite、Qdrant、CosmosDB、AzureSQL
UI 组件 DevExpress、Syncfusion、Progress Telerik

模型上下文协议 (MCP) C# SDK

MCP 是一种开放标准,充当 AI 模型的通用适配器,使模型能够通过一致的标准化接口与外部数据源、工具和 API 交互,从而简化集成过程。

我们与 Anthropic 合作提供了官方 MCP C# SDK。该 SDK 构建在 AIContentAIFunctionIChatClient 等共享 AI 抽象之上,便于 MCP 客户端和服务器定义和调用工具:

复制代码
var mcpClient = await McpClientFactory.CreateAsync(clientTransport, mcpClientOptions, loggerFactory);
var tools = await mcpClient.ListToolsAsync();
var response = await _chatClient.GetResponseAsync<List<TripOption>>(messages, new ChatOptions { Tools = [.. tools ] });

评估

Microsoft.Extensions.AI.Evaluation 库旨在简化 AI 评估流程与应用程序的集成。它提供了一个强大的框架,用于评估 AI 应用程序并自动评估其性能。评估功能它确保了系统的安全性、可靠性以及与预期行为的符合性,这些工具能够无缝集成到开发工作流程中,支持对 AI 系统的持续监控和改进。在构建可信赖的 AI 应用程序中至关重要。

基于 Microsoft.Extensions.AI 抽象构建的评估库由以下 NuGet 包组成:

  • Microsoft.Extensions.AI.Evaluation: 用于支持评估的核心抽象和类型。
  • Microsoft.Extensions.AI.Evaluation.Quality: 包含根据相关性和完整性等指标,评估应用程序中 LLM 响应质量的评估器。这些评估员直接使用 LLM 来执行评估。
  • Microsoft.Extensions.AI.Evaluation.Safety: 包含使用 Azure AI Foundry 评估服务执行评估的评估器,例如ProtectedMaterialEvaluatorContentHarmEvaluator
  • Microsoft.Extensions.AI.Evaluation.Reporting: 包含对缓存LLM响应、存储评估结果以及从该数据生成报告的支持。
  • Microsoft.Extensions.AI.Evaluation.Reporting.Azure: 报告库支持,用于缓存 LLM 响应并将评估结果存储在 Azure 存储容器中。
  • Microsoft.Extensions.AI.Evaluation.Console: 用于生成报告和管理评估数据的命令行工具。

按照如下步骤生成报告

复制代码
dotnet tool install --local Microsoft.Extensions.AI.Evaluation.Console
dotnet tool run aieval report --path <path\to\your\cache\storage> --output report.html

打开report.html,如下所示:
开发者可以通过评估,根据真实场景和质量标准,系统地测试和验证 AI 模型,

Semantic Kernel

Semantic Kernel 提供高级组件,简化了 AI 在应用程序中的集成。随着 AI Agent 时代的到来,Agent 需要访问模型、数据和工具以高效执行任务。

Semantic Kernel 允许开发者利用熟悉的 AI 扩展(如 IChatClient)构建代理。

以下示例展示了如何在 Semantic KernelAgent Framework 中使用 IChatClient 作为代理基础:

复制代码
using System.ComponentModel;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.VectorData;
using OllamaSharp;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Agents;


[Description("Calculate tax given a receipt and tax rate")]
float CalculateTax(Receipt receipt, float taxRate)
{
    return receipt.Total * (1 + taxRate);
}

IKernelBuilder builder = Kernel.CreateBuilder();

// Add your IChatClient
builder.Services.AddChatClient(new OllamaApiClient("http://localhost:11434/", "llama3.2:latest"))
    .UseFunctionInvocation()
    .Build();

#pragma warning disable SKEXP0001 // 类型仅用于评估,在将来的更新中可能会被更改或删除。取消此诊断以继续。
builder.Plugins.AddFromFunctions(
    nameof(CalculateTax),
    [AIFunctionFactory.Create(CalculateTax).AsKernelFunction()]);
#pragma warning restore SKEXP0001 // 类型仅用于评估,在将来的更新中可能会被更改或删除。取消此诊断以继续。

Kernel kernel = builder.Build();

ChatCompletionAgent agent = new ChatCompletionAgent
{
    Name = "TravelAgent",
    Description = "A travel agent that helps users with travel plans",
    Instructions = "Help users come up with a travel itinerary",
    Kernel = kernel,
    Arguments = new KernelArguments(
        new PromptExecutionSettings
        {
            FunctionChoiceBehavior = FunctionChoiceBehavior.Auto()
        })
};

ChatMessageContent[] result = await agent.InvokeAsync([]).ToArrayAsync();

Console.WriteLine(result.LongLength);
Console.ReadKey();

record ReceiptTotal(float SubTotal, float TaxAmount, float TaxRate, float Total);
record Item(string Name, float Price);
enum Category { Food, Electronics, Clothing, Services };
record Receipt(string Merchant, List<Item> Items, float Total, Category Category);

运行结果:

Semantic Kernel 还为向量数据库提供了一组统一的连接器,这些连接器基于 Vector Data 扩展构建,通过一致的编程模型简化了集成。

AI 开发库

AI Dev Gallery 专为 Windows 开发人员设计,作为 .NET AI 开发的综合游乐场,有助于将 AI 功能集成到应用和项目中。它提供了一个完全离线的环境,让开发者能够探索、试验和实现 AI 功能,无需依赖云服务。

它的功能包括:

  • 探索由本地 AI 模型提供支持的超过 25 个交互式示例
  • 从 Hugging Face 和 GitHub 轻松浏览、下载和运行模型
  • 查看 C# 源代码,只需单击一下即可导出独立的 Visual Studio 项目

AI Dev Gallery 目前提供公共预览版

主页面

主页面

案例页面

案例页面

这个页面在浏览的时候会被阻止,大家可以集思广益来解决这个问题。

通过模型界面可以下载相应的模型:
模型界面

AI Dev Gallery 建立在 AIVector Data 扩展之上,为模型和数据集成奠定了坚实基础,同时利用以下组件:

组件 描述
Microsoft.ML.Tokenizers 用于高效的文本预处理和分词
System.Numerics.Tensors 用于模型输出的高性能处理

这些组件共同使 AI Dev Gallery 成为本地端到端 AI 实验和开发的强大工具。

参考链接

相关推荐
阑梦清川2 小时前
C#建立与数据库连接(版本问题的解决方案)踩坑总结
开发语言·数据库·c#
code_li2 小时前
C#实现语音预处理:降噪/静音检测/自动增益
开发语言·c#
军训猫猫头2 小时前
100.Complex[]同时储存实数和虚数两组double的数组 C#例子
算法·c#·信号处理
Takoony7 小时前
HuggingFace镜像配置失效问题深度解析:Python模块导入机制的陷阱
python·ai
o0向阳而生0o7 小时前
71、C# Parallel.ForEach 详解
c#
code bean8 小时前
【设计模式】用观察者模式对比事件订阅(相机举例)
观察者模式·设计模式·c#
上位机付工8 小时前
不会PLC,怎么学上位机?
c#·上位机·modbus·三菱·西门子·欧姆龙plc
lgbisha9 小时前
华为云Flexus+DeepSeek征文|体验华为云ModelArts快速搭建Dify-LLM应用开发平台并搭建查询数据库的大模型工作流
人工智能·ai·自然语言处理·华为云
Kookoos9 小时前
ABP VNext + MongoDB 数据存储:多模型支持与 NoSQL 扩展
后端·mongodb·c#·.net·abp vnext
甄天10 小时前
WPF数据绑定
c#·wpf