.NET + AI | Semantic Kernel vs Microsoft.Extensions.AI

Microsoft.Extensions.AI 在 .NET AI 应用架构中的定位示意图:应用程序通过 Microsoft.Extensions.AI 调用下层各种 AI 服务(如 Semantic Kernel、OpenAI、Ollama 等),其核心由 Microsoft.Extensions.AI.Abstractions 提供统一接口。

Microsoft.Extensions.AI 简介

随着 .NET 9 的发布,微软引入了一套 统一的 AI 抽象层 来简化 AI 模型在 .NET 应用中的集成。Microsoft.Extensions.AI 正是这套 AI 支持库的核心组成部分,它提供标准的接口和管道 让开发者可以通过一致的方式调用各类 AI 服务(无论是 OpenAI、Azure OpenAI 还是本地部署的模型)。这一设计类似于 .NET 中日志、配置等扩展模型 ------ 通过定义统一接口和依赖注入,让不同厂商的实现可以无缝插入,从而在应用层屏蔽差异。

简单来说,Microsoft.Extensions.AI 提供了一组 跨供应商的通用接口。其中最重要的是几个核心抽象:

  • IChatClient 接口 :表示一个聊天/对话模型客户端,可与底层的大语言模型交互。无论使用 OpenAI、Azure OpenAI 或其他模型,只要提供了 IChatClient 的实现,应用就可以通过这一接口统一地发送对话请求并获取回复。
  • IEmbeddingGenerator<TInput, TEmbedding> 接口:表示一个向量嵌入生成器,用于将输入(如文本)转换为向量表示。不同向量数据库或模型的嵌入生成,也可通过这一接口统一调用。
  • ChatMessageChatResponse 等类型:用于表示聊天消息、模型回复等,通过强类型封装对话内容。

Microsoft.Extensions.AI 作为 .NET AI 支持的一部分,被深度嵌入到了 依赖注入(DI) 容器和 中间件管道 中。这意味着开发者可以像使用日志记录(ILogger)等其他 Microsoft.Extensions 组件一样,通过 DI 获取 AI 客户端服务,并利用熟悉的中间件模式为 AI 请求添加缓存、监控等功能。同时,它也能方便地结合 .NET 的配置系统,例如通过配置文件或用户密钥存储(User Secrets)提供 API 密钥、端点 URL 等,让应用在不同环境下灵活调整 AI 服务配置。

值得注意的是,Microsoft.Extensions.AI 本身不直接提供具体的 AI 功能 ,而是定义了标准接口和扩展点,实际的功能由不同提供商的实现来完成。比如,针对 OpenAI 有专门的 Microsoft.Extensions.AI.OpenAI 扩展包,实现了 IChatClient 接口以调用 OpenAI 或其兼容端点(如 Azure OpenAI);针对本地模型有 Microsoft.Extensions.AI.Ollama 扩展包等等。通过这种方式,Microsoft.Extensions.AI 扮演着 "AI 插件体系的粘合层" 角色,为 .NET 开发者提供统一、松耦合的调用方式。在开发阶段可以选用本地轻量模型调试,部署到生产时再切换到云端强大的模型,而无需改动应用代码。

总结来说,Microsoft.Extensions.AI 在 .NET 9 的 AI 体系中提供了标准化可插拔的 AI 接口层。开发者和架构师可以借助它,将 AI 能力嵌入应用的各个层面,同时保持对底层模型和服务的选择弹性。

集成方式:依赖注入与管道中间件

依赖注入(DI)集成Microsoft.Extensions.AI 的一大特点。通过扩展方法,开发者可以方便地将 AI 模型客户端注册到 DI 容器中,并在应用其他部分通过接口获取。例如,可以使用 AddChatClient 扩展方法注册一个 IChatClient 实现:

csharp 复制代码
var ollamaClient = new OllamaChatClient(new Uri("http://localhost:11434"), "llama3.1");
builder.Services.AddChatClient(ollamaClient)
.UseDistributedCache();

上面代码示例中,我们将本地 Ollama 模型的客户端加入了 DI,并通过 .UseDistributedCache() 为其构建了一个带有分布式缓存功能的管道。当应用运行时,可以通过 host.Services.GetRequiredService<IChatClient>() 获取到这个已配置好的客户端实例,并直接调用 AI 模型接口。

Microsoft.Extensions.AI 采用了管道式的客户端构建模式AddChatClient 返回一个可链式调用的 ChatClientBuilder,开发者可以在其上附加各种中间件(Middleware)功能,然后再调用 .Build() 将最终管道生成客户端实例。常用的中间件扩展包括:

  • 缓存(Caching) :通过 .UseDistributedCache() 将对话结果缓存到分布式缓存(如内存、Redis),下次相同请求可直接取缓存。
  • 遥测(Telemetry) :通过 .UseOpenTelemetry() 将请求和响应打点到 OpenTelemetry,方便监控 AI 调用的性能和频率。
  • 限流(Rate Limiting) :可以利用 .UseRateLimiting() 或自定义中间件,在请求发送前后加入并发限制等控制。
  • 函数调用(Tool/Function Calling) :通过 .UseFunctionInvocation() 使模型具备调用预先注册的函数的能力,在对话中实现工具调用(类似于 OpenAI Functions)。

这些中间件的设计使得AI 客户端可以按需组合功能 ,类似 ASP.NET Core 的 HTTP 中间件管道或者 HttpClient 的委托处理器模式。通过 ChatClientBuilder.Use(...) 扩展点,开发者也可以插入自定义逻辑,封装在请求前后执行。例如,上述限流可以用自定义委托在每次请求前申请许可,再调用下一步。这种拦截管道机制让 AI 集成更灵活,且易于测试和维护。

配置集成方面,Microsoft.Extensions.AI 与 .NET 的配置系统天然兼容。常见模式包括:

  • Options 模式 :可通过 IOptions<ChatOptions> 等将默认参数(如模型 ID、温度等)注入。也可以使用 .ConfigureOptions(...) 在构建管道时统一设置默认选项。
  • 用户密钥/配置文件 :利用 Microsoft.Extensions.Configuration ,可以将 API Key、端点 URL 等敏感信息放入配置文件或用户秘密存储。例如,Azure OpenAI 的 endpoint模型部署名称 可以通过 dotnet user-secrets 注入,然后在代码中通过配置读取,再用于创建客户端。

通过以上机制,Microsoft.Extensions.AI 与依赖注入和配置体系融为一体。架构师 可以预先在应用的 Composition Root(例如 Startup/Program)中配置好所需的 AI 客户端及其管道,而开发者 在具体业务逻辑中仅需请求 IChatClient 接口并调用方法即可,极大降低了使用 AI 服务的门槛和耦合度。

使用案例:通过 Microsoft.Extensions.AI 集成 OpenAI 模型

下面我们通过一个具体案例,演示如何使用 Microsoft.Extensions.AI 来集成一个 AI 大语言模型(以 OpenAI 的 GPT-4 模型为例)。假设我们要构建一个简单的控制台聊天程序,用户输入问题,程序调用 OpenAI 模型获取回答并显示。

步骤1:添加 NuGet 包

首先,创建一个 .NET 9 控制台应用项目,并添加以下 NuGet 包(可使用 dotnet add package 命令):

  • Microsoft.Extensions.AI (预览版) -- 核心 AI 抽象和管道支持
  • Microsoft.Extensions.AI.OpenAI (预览版) -- 提供 OpenAI 接口的实现

确保包含预发布版本,因为截至 .NET 9 发布时这些扩展仍处于预览状态。

步骤2:配置依赖注入并注册 AI 客户端

在 Program 主函数中,设置主机和依赖注入容器,将 OpenAI 客户端添加到服务中。这里我们演示两种方式之一:

  • 方式A:使用 OpenAI API 密钥直连 OpenAI 云服务。 这种方式使用 API Key 验证,不涉及 Azure AD。假设我们有一个 OpenAI API 密钥。

    csharp 复制代码
    using Microsoft.Extensions.DependencyInjection;
    using Microsoft.Extensions.AI;
    var chatClient = new OpenAI.Chat.ChatClient("gpt-4o-mini", "OPENAI_API_KEY").AsIChatClient();
    var services = new ServiceCollection();
    services.AddChatClient(chatClient);
    var provider = services.BuildServiceProvider();

    上述代码通过 UseOpenAI(apiKey) 扩展方法,将 OpenAI 接入配置到 ChatClientBuilderAddChatClient 会自动在 DI 容器中注册一个 IChatClient 实例。我们在构建服务提供器后,就能够解析出 IChatClient 来使用。

  • 方式B:使用 DeepSeek 服务。 由于DeepSeek兼容OpenAI API格式,可直接利用 Microsoft.Extensions.AI.OpenAI 进行对接。

    csharp 复制代码
    using OpenAI;
    using System.ClientModel;
    using Microsoft.Extensions.AI;
    
    var deepseekApiKey = "<YOUR_API_KEY>";
    var deepseekModel = "deepseek-chat";
    var deepseekUri = "https://api.deepseek.com";
    
    OpenAIClientOptions clientOptions = new OpenAIClientOptions();
    clientOptions.Endpoint = new Uri(deepseekUri);
    
    // 创建自定义的OpenAI客户端
    OpenAIClient client = new(new ApiKeyCredential(deepseekApiKey), clientOptions);
    
    var chatClient = client.GetChatClient(deepseekModel).AsIChatClient();
    
    var builder = Host.CreateApplicationBuilder();
    builder.Services.AddChatClient(chatClient);
    using var app = builder.Build();
    var client = app.Services.GetRequiredService<IChatClient>();

    在这段代码中,我们使用 OpenAI 提供的 OpenAIClient 连接 DeepSeek 服务,然后调用 .GetChatClient(<部署名>).AsIChatClient() 将其转换为 Microsoft.Extensions.AI 的通用 IChatClient 实现并注册。这样获取到的 client 对象同样实现了 IChatClient 接口,可以像方式A那样统一使用。

步骤3:发送对话请求并获取回复

无论使用上述哪种方式获取到 IChatClient,都可以通过其方法与模型交互,例如获取聊天模型的回答。基本的调用方式有两种:

  • 单次完整请求 :使用 GetResponseAsync(或等价的 CompleteAsync 方法)发送一个提示,获取模型完整回答。
  • 流式请求 :使用 GetStreamingResponseAsync 以流方式接收模型的逐步输出,适合用于实时显示逐字输出或函数调用场景。

以下示例演示简单的单次请求:

csharp 复制代码
// 从 DI 获取已注册的 IChatClient(以方式A为例)
var chatClient = provider.GetRequiredService<IChatClient>();

// 准备用户消息并发送请求获取 AI 回复
string userQuestion = "能简单解释一下什么是 AI 吗?";
var response = await chatClient.GetResponseAsync(userQuestion);
Console.WriteLine("AI 答复: " + response);

我们调用了 GetResponseAsync,直接传入用户的问题字符串。由于这是一个简短问题,大多数 IChatClient 实现会将其作为单轮对话处理,返回一个 ChatResponse 对象,其中包含模型的回答。上例中直接将 response 输出,其中会调用其 .ToString().Message 得到文本。实际使用时也可以进一步获取结构化信息,例如 ChatResponse 下的消息列表、使用的线程 ID(用于有状态对话)等。

运行效果: 如果一切配置正确,程序将把用户输入的问题发送给后端的 OpenAI 模型,并打印出模型的回答内容(例如对 "什么是 AI?" 给出通俗的解释)。这个过程中,Microsoft.Extensions.AI 帮我们完成了请求的组装和发送,而应用代码只需关注问题和答案本身。

步骤4: 集成 Function Calling

Microsoft.Extensions.AI 也支持Function Calling,以下代码为简单示例:

csharp 复制代码
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.AI;

var chatClient = new OpenAI.Chat.ChatClient("gpt-4o-mini", "OPENAI_API_KEY").AsIChatClient();

IChatClient client =
    new ChatClientBuilder(chatClient)
    .UseFunctionInvocation()
    .Build();

var chatOptions = new ChatOptions
{
    Tools = [AIFunctionFactory.Create((string location, string unit) =>
    {
        // 在这里您可以调用天气 API
        // 获取该位置的天气信息。
        return "阵雨或毛毛雨,15摄氏度";    
    },
    "get_current_weather",
    "Get the current weather in a given location")]
};

// System prompt to provide context.
List<ChatMessage> chatHistory = [new(ChatRole.System, """
    您是一位热爱徒步旅行的爱好者,帮助人们发现他们所在地区有趣的徒步路线。您充满热情且友好。
    """)];

// Weather conversation relevant to the registered function.
chatHistory.Add(new ChatMessage(ChatRole.User,
    "我住在深圳,我正在寻找一条中等强度的徒步路线。目前的天气怎么样?"));
Console.WriteLine($"{chatHistory.Last().Role} >>> {chatHistory.Last()}");

ChatResponse response = await client.GetResponseAsync(chatHistory, chatOptions);
Console.WriteLine($"Assistant >>> {response.Text}");

值得一提的是,通过 Microsoft.Extensions.AI 的抽象,更换底层模型或服务非常容易 。例如,我们可以在开发时将 UseOpenAI 换成 .UseOllama("http://localhost:11434") 来调用本地 Ollama 提供的模型;部署不同环境时也能通过配置选择 Azure 或其他服务。这种灵活性对于需要同时面对多种环境的架构师来说非常有价值。

Microsoft.Extensions.AI 与 Semantic Kernel 的对比分析

Microsoft.Extensions.AI 常被拿来与另一项技术 Semantic Kernel 相提并论。实际上,这两者在定位和功能上有显著差异,各自擅长不同的场景。下面我们从设计理念、可扩展性、开发模型、集成便利性等方面对比它们,并分析各自适用的场景。

设计理念差异

  • Microsoft.Extensions.AI :强调轻量级通用性 。它提供的只是最低限度的抽象接口(如 IChatClientIEmbeddingGenerator)和调用约定,没有附加高级的 AI 流程控制功能。可以将其看作是一个"LLM 调用底盘",重点在于以统一方式调用模型、获取结果,不对上层应用的业务逻辑做过多假设。
  • Semantic Kernel :定位于全功能的 AI 编排框架 。它在底层调用接口之上,提供了丰富的高级功能 ,如提示模板(Prompt Templating),功能编排(Functions 链式调用),长期记忆存储,规划和步骤控制等。Semantic Kernel 更像一个"AI 大脑",帮助开发者组织复杂的多步对话流程、工具调用和上下文管理。

换句话说,Microsoft.Extensions.AI 不包含"提示工程"、"记忆"等概念,而 Semantic Kernel 内置了这些强大的概念和工具。这使得 Microsoft.Extensions.AI 更中立纯粹,而 Semantic Kernel 则提供了开箱即用的高级AI能力。

可扩展性

  • Microsoft.Extensions.AI :采用开放接口设计,易于扩展不同的 AI 提供商。任何第三方或社区提供的 AI 服务,只要提供了 IChatClient 等接口的实现,就能直接插入整个生态,与现有代码兼容。例如,目前已经有适配 OpenAI、Azure OpenAI、Ollama 等模型的实现包,未来还可以有针对其他 LLM(包括开源模型、本地模型)的实现,只需引用相应包即可。通过这种标准化Microsoft.Extensions.AI 实际上鼓励 AI ISV 厂商快速提供 .NET 接口,从而丰富 .NET AI 生态。另外,中间件管道本身也支持扩展,开发者可以编写自定义的 UseXXX 方法包装自己的逻辑,这在复杂业务场景下非常实用。
  • Semantic Kernel :同样具有一定的扩展能力,例如支持插件式地添加新的 Memory 存储后端、新的模型连接器等。不过,相比 Microsoft.Extensions.AI 的接口级对接,Semantic Kernel 的扩展通常意味着深入其体系(如实现自定义 SKFunction ,或自定义 Planner 等)。Semantic Kernel 更多地扮演一个运行时协调者角色,可扩展性主要体现为可插入自定义技能模块 。但在支持新的底层模型方面,Semantic Kernel 通常通过其 Connectors(连接器)或直接利用 Microsoft.Extensions.AI 抽象来实现适配,因此在底层适配层上两者目标是一致的:Semantic Kernel 未来也计划以 Microsoft.Extensions.AI 作为其底层抽象,以标准方式调用各种模型。

开发模型对比

  • Microsoft.Extensions.AI :开发者使用它的方式非常直接------通过 DI 获取 IChatClient,然后调用诸如 GetResponseAsync / CompleteAsync 之类的方法,与调用HTTP服务或数据库很相似。这种命令式调用模型 让普通 .NET 开发者感觉熟悉,几乎没有额外学习成本。同时,由于没有强制的上层框架,开发者可以自由决定如何构造提示、处理回复。例如,可以在代码中自行组织多轮对话历史,再每次调用 IChatClient 时传入完整历史,以支持无状态的聊天模型。
  • Semantic Kernel :其开发模型则带有声明式和框架式 色彩。开发者需要创建一个 Kernel 实例,通过 Builder 配置模型和内存,然后定义插件(Plugin)和函数 ,甚至使用模板语言编写 Prompt。开发过程更像是在编排一系列步骤:比如先调用翻译技能再调用摘要技能。Semantic Kernel 提供了丰富的 API 来组合这些步骤(如 kernel.CreatePipeline() 等),并管理上下文(通过 ContextVariables 传递信息)。这种模式功能强大,但相应地学习曲线更陡峭。开发者需要理解 Kernel、Plugin、Context 等一系列概念,初次接触时复杂度要高于 Microsoft.Extensions.AI 的直接调用。

简单来说,如果把调用 AI 比作开车:使用 Microsoft.Extensions.AI 就像手动驾驶 一辆熟悉的轿车,直接掌握方向盘;而使用 Semantic Kernel 则更像设置自动驾驶程序,让车辆按照预定策略行驶------前者直观灵活,后者智能强大但需要预先编排路线。

系统集成便利程度

  • Microsoft.Extensions.AI :由于其诞生于 Microsoft.Extensions 家族,天然易于融入现有 .NET 应用架构。开发者可以在 Startup/Program 中通过 AddChatClient 将 AI 客户端注入服务容器,然后在任意需要的地方通过构造函数注入 IChatClient 使用。这种方式与使用数据库上下文(DbContext)或 HttpClient 工厂非常类似,对架构影响极小。此外,它还能和现有基础设施结合,如利用 ASP.NET Core 中间件或消息管道,在请求入口就调用 AI 服务等。对于希望在现有微服务或Web应用中加入 AI 功能的团队来说,Microsoft.Extensions.AI 几乎不会破坏原有结构。而且它提供的 Telemetry、Caching 等支持也让运维监控更容易。
  • Semantic Kernel :作为一个框架,它的集成需要一些架构上的考量。通常你需要在应用中创建一个 Kernel 单例,并管理它的生命周期(可以将其注册为 Singleton 服务)。调用 Semantic Kernel 的代码可能集中在少数几个服务或模块中,因为 Kernel 会维护会话状态、内存等。对于简单场景,这可能略显繁重。不过,Semantic Kernel 也提供了与 ASP.NET 等集成的示例模板,方便快速启动智能应用 原型。总体而言,如果是从零打造一个以 AI 为核心的应用,Semantic Kernel 提供了很多便利;但在已有系统里嵌入,它需要你按照它的方式组织部分代码,集成成本相对更高。

适用场景对比

基于上述差异,可以总结出两者各自更适合的场景:

  • Microsoft.Extensions.AI 更适合:

    • 只需基础的 AI 调用场景,例如将一段文本发送给模型获得回答、获取补全或 embeddings 等。不涉及复杂的多轮对话管理或工具调用逻辑。
    • 希望保持低依赖和中立性的项目。例如开发一个库或中间件,需要调用 AI 服务但不想强耦合到某个具体实现或沉重框架。Microsoft.Extensions.AI 提供最小化的抽象,不会给使用者带来过多负担。
    • 已有应用需要嵌入 AI 功能,且团队更熟悉传统 .NET 编程模型。通过 Microsoft.Extensions.AI 可以很快地将 AI 调用融入现有代码,而无需学习新框架。
    • 需要在不同 AI 提供商之间灵活切换的情况。例如本地调试用开源模型、生产使用付费API。使用统一接口后切换只需改配置而非代码。
  • Semantic Kernel 更适合:

    • 复杂的 AI 工作流或智能体 场景。比如应用需要模型先执行一步分析,再根据结果查询数据库或调用另一个模型,最终形成答案。这种多步骤链式推理可以利用 Semantic Kernel 的管道和 Planner 功能来编排。
    • 长期对话或记忆需求。Semantic Kernel 内置 Memory 存储和检索机制,可以让应用具备上下文记忆,在对话轮次增多时保持一致的语境。
    • 希望利用本地代码与 AI 功能结合的强大能力。例如将 C# 方法封装为 SK 的本地函数(Native Function),让 AI 能请求调用这些函数。这个特性在需要 AI 与系统执行动作(如查资料、调用服务)结合时非常有用。
    • 健壮性要求高的场景。Semantic Kernel 提供了一些内建的策略如重试、超时、错误处理等。对于生产环境复杂AI调用,它的这些特性可以减少自己实现底层重试逻辑的工作。

需要强调的是,这两者并非对立竞争关系。实际上,Semantic Kernel 已经在与 Extensions.AI 团队合作,将 Microsoft.Extensions.AI 作为其底层的一部分。可以将 Microsoft.Extensions.AI 看作基础设施 ,而 Semantic Kernel 构建在其上以提供更高级别的功能。在实际项目中,也完全可以同时使用 两者:例如通过 Semantic Kernel 来组织复杂对话流程,但底层连接模型的部分由 Microsoft.Extensions.AI 提供的 IChatClient 实现完成。

总结性建议

综合来看,Microsoft.Extensions.AISemantic Kernel 各有优势,选择取决于项目需求和团队技术偏好:

  • 如果你的项目主要需要直接、高效地调用 AI 接口 ,且偏好与现有 .NET 架构深度融合,那么 Microsoft.Extensions.AI 是首选。它轻量稳定,提供统一抽象,方便在不同模型服务之间切换。对于大部分需要将 AI 简单集成到业务功能的应用来说,Extensions.AI 提供了足够的能力和极佳的开发体验。

  • 如果你的项目希望构建复杂的 AI 驱动功能 ,需要模型具有一定"智能自主"行为(比如多步推理、工具调用、上下文记忆),那么 Semantic Kernel 更适合。这种情况下,Semantic Kernel 丰富的框架特性将大大简化开发难度,提供远超直接调用的能力,让 AI 逻辑与业务逻辑解耦,易于维护和演进。

最后建议,在评估使用哪种技术时,先明确你的应用场景复杂度。对于一般的问答、内容生成等,Microsoft.Extensions.AI 完全可以胜任且实现最快;而对于一个拟人助手、需要长程对话和多任务处理的系统,引入 Semantic Kernel 会更为有效。在实际应用中,两者并非水火不容:你可以先用 Extensions.AI 搭建基础 AI 调用,在碰到更复杂需求时逐步引入 Semantic Kernel 的相关组件。通过合理搭配,你的 .NET 9 应用将既能保持架构简洁,又拥有先进的智能能力。

相关推荐
yibuapi_com7 小时前
开源智能体MetaGPT记忆模块解读
python·ai·语言模型·chatgpt·架构·langchain·claude
eve杭8 小时前
游戏代码C
c语言·人工智能·python·游戏·ai
挽安学长9 小时前
ChatGPT对话导出工具-轻松提取聊天记录导出至本地[特殊字符]安装指南
人工智能·ai·科研
Hello server9 小时前
AI Agent 入门指南:从 LLM 到智能体
人工智能·ai·chatgpt·agent
AWS官方合作商10 小时前
从 AWS Marketplace 开始使用 AssemblyAI 的语音转文本模型构建语音智能
ai·云计算·aws
Johny_Zhao14 小时前
阿里云Ansible自动化运维平台部署
linux·人工智能·ai·信息安全·云计算·ansible·shell·yum源·系统运维·itsm
struggle202515 小时前
Ray开源程序 是用于扩展 AI 和 Python 应用程序的统一框架。Ray 由一个核心分布式运行时和一组用于简化 ML 计算的 AI 库组成
人工智能·python·ai
CoderJia程序员甲17 小时前
RAG_Techniques:探索GitHub热门RAG技术开源项目
ai·llm·github·ai教程·rag技术
在未来等你17 小时前
互联网大厂Java求职面试:云原生与AI融合下的系统设计挑战-2
java·微服务·ai·云原生·面试题·架构设计·系统设计