大家好,我是Edison。

微软在2024年11月就发布了新的AI核心库Microsoft.Extensions.AI,虽然目前还是一个预览版,但其可以大大简化我们的AI集成和开发工作。

Microsoft.Extensions.AI介绍

Microsoft.Extensions.AI 是一组核心 .NET 库,是在与整个 .NET 生态系统(包括语义内核)的开发人员协作中创建的。 这些库提供统一的 C# 抽象层,用于与 AI 服务交互,例如小型和大型语言模型(SLA 和 LLM)、嵌入和中间件。

Microsoft.Extensions.AI 提供可由各种服务实现的抽象,所有这些概念都遵循相同的核心概念。 此库不旨在提供针对任何特定提供商服务定制的 API。

Microsoft.Extensions.AI 目标是在 .NET 生态系统中充当一个统一层,使开发人员能够选择他们的首选框架和库,同时确保整个生态系统之间的无缝集成和协作

画外音>开发者可以节省时间下来专注自己的应用程序的业务逻辑实现,从而不必花过多时间去做AI服务的集成调试,点个大大的赞!

我能使用哪些服务实现?

Microsoft.Extensions.AI 通过 NuGet 包提供了以下服务的实现:

  • OpenAI
  • Azure OpenAI
  • Azure AI Inference
  • Ollama

将来,这些抽象的服务实现都将会是客户端库的一部分。

基本使用

安装NuGet包:

Microsoft.Extensions.AI (9.1.0-preview)
Microsoft.Extensions.AI.OpenAI (9.1.0-preivew)

这里我们使用SiliconCloud提供的 DeepSeek-R1-Distill-Llama-8B 模型,这是一个使用DeepSeek-R1开发的基于Llama-3.1-8B的蒸馏模型,免费好用。

注册SiliconCloud:https://cloud.siliconflow.cn/i/DomqCefW

简单对话:

var openAIClientOptions = new OpenAIClientOptions();
openAIClientOptions.Endpoint = new Uri("https://api.siliconflow.cn/v1"); var client = new OpenAIClient(new ApiKeyCredential("sk-xxxxxxxxxx"), openAIClientOptions);
var chatClient = client.AsChatClient("deepseek-ai/DeepSeek-R1-Distill-Llama-8B");
var response = await chatClient.CompleteAsync("Who are you?");
Console.WriteLine(response.Message);

封装的IChatClient对象可以十分方便地屏蔽差异,用起来十分方便。

函数调用

要想实现函数调用(Function Calling),则需要调整一下:

var openAIClientOptions = new OpenAIClientOptions();
openAIClientOptions.Endpoint = new Uri("https://api.siliconflow.cn/v1"); [Description("Get the current time")]
string GetCurrentTime() => DateTime.Now.ToLocalTime().ToString(); var client = new ChatClientBuilder()
.UseFunctionInvocation()
.Use(new OpenAIClient(new ApiKeyCredential("sk-xxxxxxxxx"), openAIClientOptions)
.AsChatClient("deepseek-ai/DeepSeek-R1-Distill-Llama-8B"));
var response = await client.CompleteAsync(
"What's the time now?",
new() { Tools = [AIFunctionFactory.Create(GetCurrentTime)] });
Console.Write(response);

可以看到,需要主动使用 UseFunctionInvocation 方法 及 提供 Tools 注册列表,就能使用我们封装的 Tools 了。

多模型使用

很多时候,我们希望Chat入口用一个模型,业务处理则用另一个模型,我们完全可以对其进行独立配置。

例如,这里参考mingupupu大佬的PaperAssistant,我也实现了一个。

在配置文件中,配置多AI模型:

{
// For Paper Smmary
"PaperSummaryModel": {
"ModelId": "deepseek-ai/DeepSeek-R1-Distill-Llama-8B",
"ApiKey": "sk-xxxxxxxxxx",
"EndPoint": "https://api.siliconflow.cn"
},
// For Main Chat
"MainChatModel": {
"ModelId": "Qwen/Qwen2.5-7B-Instruct",
"ApiKey": "sk-xxxxxxxxxx",
"EndPoint": "https://api.siliconflow.cn"
}
}

对于某个业务处理,将其封装为Plugin,并使用 DeepSeek-R1-Distill-Llama-8B 模型:

public sealed class PaperAssistantPlugins
{
public PaperAssistantPlugins(IConfiguration config)
{
var apiKeyCredential = new ApiKeyCredential(config["PaperSummaryModel:ApiKey"]);
var aiClientOptions = new OpenAIClientOptions();
aiClientOptions.Endpoint = new Uri(config["PaperSummaryModel:EndPoint"]);
var aiClient = new OpenAIClient(apiKeyCredential, aiClientOptions)
.AsChatClient(config["PaperSummaryModel:ModelId"]);
ChatClient = new ChatClientBuilder(aiClient)
.UseFunctionInvocation()
.Build();
} public IChatClient ChatClient { get; } [Description("Read the PDF content from the file path")]
[return: Description("PDF content")]
public string ExtractPdfContent(string filePath)
{
Console.WriteLine($"[Tool] Now executing {nameof(ExtractPdfContent)}, params: {filePath}");
var pdfContentBuilder = new StringBuilder();
using (var document = PdfDocument.Open(filePath))
{
foreach (var page in document.GetPages())
pdfContentBuilder.Append(page.Text);
}
return pdfContentBuilder.ToString();
} [Description("Create a markdown note file by file path")]
public void SaveMarkDownFile([Description("The file path to save")] string filePath, [Description("The content of markdown note")] string content)
{
Console.WriteLine($"[Tool] Now executing {nameof(SaveMarkDownFile)}, params: {filePath}, {content}");
try
{
if (!File.Exists(filePath))
File.WriteAllText(filePath, content);
else
File.WriteAllText(filePath, content);
}
catch (Exception ex)
{
Console.WriteLine($"[Error] An error occurred: {ex.Message}");
}
} [Description("Generate one summary of one paper and save the summary to a local file by file path")]
public async Task GeneratePaperSummary(string sourceFilePath, string destFilePath)
{
var pdfContent = this.ExtractPdfContent(sourceFilePath);
var prompt = """
You're one smart agent for reading the content of a PDF paper and summarizing it into a markdown note.
User will provide the path of the paper and the path to create the note.
Please make sure the file path is in the following format:
"D:\Documents\xxx.pdf"
"D:\Documents\xxx.md"
Please summarize the abstract, introduction, literature review, main points, research methods, results, and conclusion of the paper.
The tile should be 《[Title]》, Authour should be [Author] and published in [Year].
Please make sure the summary should include the following:
(1) Abstrat
(2) Introduction
(3) Literature Review
(4) Main Research Questions and Background
(5) Research Methods and Techniques Used
(6) Main Results and Findings
(7) Conclusion and Future Research Directions
""";
var history = new List<ChatMessage>
{
new ChatMessage(ChatRole.System, prompt),
new ChatMessage(ChatRole.User, pdfContent)
};
var result = await ChatClient.CompleteAsync(history);
this.SaveMarkDownFile(destFilePath, result.ToString());
}
}

对于对话主入口,则使用 Qwen2.5-7B-Instruct 模型即可:

Console.WriteLine("Now loading the configuration...");
var config = new ConfigurationBuilder()
.AddJsonFile($"appsettings.json")
.Build();
Console.WriteLine("Now loading the chat client...");
var apiKeyCredential = new ApiKeyCredential(config["MainChatModel:ApiKey"]);
var aiClientOptions = new OpenAIClientOptions();
aiClientOptions.Endpoint = new Uri(config["MainChatModel:EndPoint"]);
var aiClient = new OpenAIClient(apiKeyCredential, aiClientOptions)
.AsChatClient(config["MainChatModel:ModelId"]);
var chatClient = new ChatClientBuilder(aiClient)
.UseFunctionInvocation()
.Build();
Console.WriteLine("Now loading the plugins...");
var plugins = new PaperAssistantPlugins(config);
var chatOptions = new ChatOptions()
{
Tools =
[
AIFunctionFactory.Create(plugins.ExtractPdfContent),
AIFunctionFactory.Create(plugins.SaveMarkDownFile),
AIFunctionFactory.Create(plugins.GeneratePaperSummary)
]
};
Console.WriteLine("Now starting chatting...");
var prompt = """
You're one smart agent for reading the content of a PDF paper and summarizing it into a markdown note.
User will provide the path of the paper and the path to create the note.
Please make sure the file path is in the following format:
"D:\Documents\xxx.pdf"
"D:\Documents\xxx.md"
Please summarize the abstract, introduction, literature review, main points, research methods, results, and conclusion of the paper.
The tile should be 《[Title]》, Authour should be [Author] and published in [Year].
Please make sure the summary should include the following:
(1) Abstrat
(2) Introduction
(3) Literature Review
(4) Main Research Questions and Background
(5) Research Methods and Techniques Used
(6) Main Results and Findings
(7) Conclusion and Future Research Directions
""";
var history = new List<ChatMessage>
{
new ChatMessage(ChatRole.System, prompt)
};
bool isComplete = false;
Console.WriteLine("AI> I'm Ready! What can I do for you?");
do
{
Console.Write("You> ");
string? input = Console.ReadLine();
if (string.IsNullOrWhiteSpace(input))
continue; if (input.Trim().Equals("EXIT", StringComparison.OrdinalIgnoreCase))
{
isComplete = true;
break;
} if (input.Trim().Equals("Clear", StringComparison.OrdinalIgnoreCase))
{
history.Clear();
Console.WriteLine("Cleared our chatting history successfully!");
continue;
} history.Add(new ChatMessage(ChatRole.User, input));
Console.WriteLine();
var result = await chatClient.CompleteAsync(input, chatOptions);
Console.WriteLine(result.ToString());
history.Add(new ChatMessage(ChatRole.Assistant, result.ToString() ?? string.Empty));
} while (!isComplete);

这里测试一下,我让它帮我总结一个pdf并将总结内容生成到一个md文件中输出到我指定的目录下保存。

可以看出,它成功地调用了Plugin完成了PDF读取、内容提取总结 和 生成Markdown文件。

eShopSupport

ShopSupport 是一个开源的AI示例应用程序,客户可以使用它来与AI客户对话查询产品,实现网站系统的“智能客服”的场景。

这个开源项目就使用了 Microsoft.Extensions.AI 作为和AI服务集成的抽象层,值得我们参考学习。

值得一提的是,它并没有说全部统一.NET技术栈,而是保留了Python作为机器学习模型训练和推理的,展示了技术异构在这个场景下的融合。

此外,基于Aspire来生成可观察和可靠的云原生应用也是这个项目带来的一个亮点,可以学习下。

小结

本文介绍了Microsoft.Extensions.AI的基本概念 和 基本使用,如果你也是.NET程序员希望参与AI应用的开发,那就快快了解和使用起来吧。

示例源码

GitHub:https://github.com/Coder-EdisonZhou/EDT.Agent.Demos

参考内容

mingupupu的文章:https://www.cnblogs.com/mingupupu/p/18651932

更多

Microsoft Learn: https://learn.microsoft.com/zh-cn/dotnet/ai/ai-extensions

eShopSupport:  https://github.com/dotnet/eShopSupport

devblogs: https://devblogs.microsoft.com/dotnet/e-shop-infused-with-ai-comprehensive-intelligent-dotnet-app-sample

作者:周旭龙

出处:https://edisonchou.cnblogs.com

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。

.NET程序员AI开发基座:Microsoft.Extensions.AI的更多相关文章

  1. 一名Delphi程序员的开发习惯

    一名Delphi程序员的开发习惯 有关开发习惯的一些想法,如鲠在喉,不吐不快.究其发贴动机,当然不排除有骗取参与分的可能,但另一方面,也希望能给同行(念Xing)者提供一些 建议,或者参考(希望不是误 ...

  2. 程序员网站开发时应该注意的SEO问题

    一.链接的统一性 搜索引擎排名最主要的因素就是网站内容和链接,假如网站内部链接不一致,在很大程度上直接影响着网站在搜索引擎中的排名.例如彩票专营店导航栏中的“首页”链接,程序员在开发时可能会有以下几种 ...

  3. Unity游戏设计与实现 南梦宫一线程序员的开发实例

    图灵程序设计丛书 Unity游戏设计与实现:南梦宫一线程序员的开发实例(修订版)     加藤政树 (作者) 罗水东 (译者)  c# 游戏 unity   <内容提要>本书的作者是日本知 ...

  4. freecplus框架,Linux平台下C/C++程序员提高开发效率的利器

    目录 一.freecplus框架简介 二.freecplus开源许可协议 三.freecplus框架内容 字符串操作 2.xml解析 3.日期时间 4.目录操作 5.文件操作 6.日志文件 7.参数文 ...

  5. .NET程序员项目开发必知必会—Dev环境中的集成测试用例执行时上下文环境检查(实战)

    Microsoft.NET 解决方案,项目开发必知必会. 从这篇文章开始我将分享一系列我认为在实际工作中很有必要的一些.NET项目开发的核心技术点,所以我称为必知必会.尽管这一系列是使用.NET/C# ...

  6. JAVA程序员常用开发工具

    1.JDK (Java Development Kit)Java开发工具集 SUN的Java不仅提了一个丰富的语言和运行环境,而且还提了一个免费的Java开发工具集(JDK).开发人员和最终用户可以利 ...

  7. 课程10:《黑马程序员_Hibernate开发资料》视频目录--没有细看

    老师很厉害,讲得蛮详细的 \Hibernate视频教程\01_黑马程序员_Hibernate教程__Hibernate入门基础.avi; \Hibernate视频教程\02_黑马程序员_Hiberna ...

  8. 程序员便于开发的一些工具、网站、App。

    http://www.kancloud.cn 关于文档,各种技术,框架的学习指南,API文档搜索方便. https://leetcode.com/ 程序员刷题面试网站,无聊的时候可以做一做.

  9. 程序员带你一步步分析AI如何玩Flappy Bird

    以下内容来源于一次部门内部的分享,主要针对AI初学者,介绍包括CNN.Deep Q Network以及TensorFlow平台等内容.由于笔者并非深度学习算法研究者,因此以下更多从应用的角度对整个系统 ...

  10. Android Java 程序员必备开发工具

    对于Java,有两种截然不同的观点:一种认为Java是最简单功能最强大的编程语言之一,另一种则表示这种编程语言既难用又复杂. 下面这些工具或许功能和作用不同,但是有着一个共同的主旨,那就是——它们都是 ...

随机推荐

  1. vue使用docxtemplater导出word

    安装 // 安装 docxtemplater npm install docxtemplater pizzip --save // 安装 jszip-utils npm install jszip-u ...

  2. File was changed on disk

    刚开始从eclipse转到idea,发现idea从svn同步代码后,点开一个java类报错 说是某个方法不存在,以为是别人代码没有提全,就点到别人代码里面去看,顶行出现"File was c ...

  3. consul discovery prefer-ip-address

    spring.cloud.consul.discovery.prefer-ip-address: 'true'

  4. Qt编写地图综合应用34-生成区域轮廓图

    一.前言 区域轮廓图的前提是,如何拿到这些轮廓的js文件,网络上其实能够找到各省市的轮廓的json数据,这些json数据对应内容是各种边界的一些类似 @@CGIUCACAAAAA@Q@ 字符的东西,每 ...

  5. Qt通用方法及类库1

    函数名 //桌面宽度高度 static int deskWidth(); static int deskHeight(); //程序文件名称+当前所在路径 static QString appName ...

  6. [转]C#的二进制文件操作及关于Encoding类与汉字编码转换的问题

    1.数值应保存在二进制文件 首先列举文本.二进制文件的操作(读写)方法: 方式1: //文本文件操作:创建/读取/拷贝/删除 using System; using System.IO; class ...

  7. 闲话即时通讯:腾讯的成长史本质就是一部QQ成长史

    1.前言 在猴年新春的时候,腾讯当时推出了新春广告片(点击观看视频),作为<弹指间 心无间>的延续.片中通过春节期间发送QQ红包让家人打车回家团聚,让我们感受到了"最温暖的红包, ...

  8. C#中如何使用异步编程

    在 C# 中,异步编程主要通过 async 和 await 关键字来实现.异步编程的目的是让程序在执行耗时操作(如 I/O 操作.网络请求等)时不会阻塞主线程,从而提高程序的性能. 1. 异步编程的核 ...

  9. 题解:AT_abc386_d [ABC386D] Diagonal Separation

    分析题面,发现题目求的是是否存在一个白点被 \((1, 1)\) 和任意一个黑点围成的矩形内. 先将所有黑点按 \(x\) 坐标排序. 枚举所有的白点. 找到所有横坐标不比该白点横坐标小的所有黑点的纵 ...

  10. [转载]「服务」WCF中NetNamedPipeBinding的应用实例

    「服务」WCF中NetNamedPipeBinding的应用实例 WCF中有很多种绑定,根据官方的说法,NetNamedPipeBinding是适用于同一台主机中不同进程之间的通信的. 今天终于实现了 ...