欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

在C#中基于Semantic?Kernel的檢索增強生成(RAG)實踐記錄

 更新時間:2024年10月21日 08:57:58   作者:dax.net  
SemanticKernel是一個用于集成和操作大語言模型的應(yīng)用程序框架,支持C#、Python和Java等多種編程語言,通過SemanticKernel,開發(fā)者可以輕松構(gòu)建基于最新AI技術(shù)的應(yīng)用程序

Semantic Kernel簡介

玩過大語言模型(LLM)的都知道OpenAI,然后微軟Azure也提供了OpenAI的服務(wù):Azure OpenAI,只需要申請到API Key,就可以使用這些AI服務(wù)。使用方式可以是通過在線Web頁面直接與AI聊天,也可以調(diào)用AI的API服務(wù),將AI的能力集成到自己的應(yīng)用程序中。不過這些服務(wù)都是在線提供的,都會需要根據(jù)token計費,所以不僅需要依賴互聯(lián)網(wǎng),而且在使用上會有一定成本。于是,就出現(xiàn)了像Ollama這樣的本地大語言模型服務(wù),只要你的電腦足夠強悍,應(yīng)用場景允許的情況下,使用本地大語言模型也是一個不錯的選擇。

既然有這么多AI服務(wù)可以選擇,那如果在我的應(yīng)用程序中需要能夠很方便地對接不同的AI服務(wù),應(yīng)該怎么做呢?這就是Semantic Kernel的基本功能,它是一個基于大語言模型開發(fā)應(yīng)用程序的框架,可以讓你的應(yīng)用程序更加方便地集成大語言模型。Semantic Kernel可用于輕松生成 AI 代理并將最新的 AI 模型集成到 C#、Python 或 Java 代碼庫中。因此,它雖然在.NET AI生態(tài)中扮演著非常重要的角色,但它是支持多編程語言跨平臺的應(yīng)用開發(fā)套件。

Semantic Kernel主要包含以下這些核心概念:

  • 連接(Connection):與外部 AI 服務(wù)和數(shù)據(jù)源交互,比如在應(yīng)用程序中實現(xiàn)Open AI和Ollama的無縫整合
  • 插件(Plugins):封裝應(yīng)用程序可以使用的功能,比如增強提示詞功能,為大語言模型提供更多的上下文信息
  • 規(guī)劃器(Planner):根據(jù)用戶行為編排執(zhí)行計劃和策略
  • 內(nèi)存(Memory):抽象并簡化 AI 應(yīng)用程序的上下文管理,比如文本向量(Text Embedding)的存儲等

有關(guān)Semantic Kernel的具體介紹可以參考微軟官方文檔。

演練:通過Semantic Kernel使用Microsoft Azure OpenAI Service

話不多說,直接實操。這個演練的目的,就是使用部署在Azure上的gpt-4o大語言模型來實現(xiàn)一個簡單的問答系統(tǒng)。

微軟于2024年10月21日終止面向個人用戶的Azure OpenAI服務(wù),企業(yè)用戶仍能繼續(xù)使用。參考:https://finance.sina.com.cn/roll/2024-10-18/doc-incsymyx4982064.shtml

在Azure中部署大語言模型

登錄Azure Portal,新建一個Azure AI service,然后點擊Go to Azure OpenAI Studio,進入OpenAI Studio:

進入后,在左側(cè)側(cè)邊欄的共享資源部分,選擇部署標(biāo)簽頁,然后在模型部署頁面,點擊部署模型按鈕,在下拉的菜單中,選擇部署基本模型

選擇模型對話框中,選擇gpt-4o,然后點擊確認(rèn)按鈕:

在彈出的對話框部署模型 gpt-4o中,給模型取個名字,然后直接點擊部署按鈕,如果希望對模型版本、安全性等做一些設(shè)置,也可以點擊自定義按鈕展開選項。

部署成功后,就可以在模型部署頁面的列表中看到已部署模型的版本以及狀態(tài):

點擊新部署的模型的名稱,進入模型詳細(xì)信息頁面,在頁面的終結(jié)點部分,把目標(biāo)URI密鑰復(fù)制下來,待會要用。目標(biāo)URI只需要復(fù)制主機名部分即可,比如https://qingy-m2e0gbl3-eastus.openai.azure.com這樣:

在C#中使用Semantic Kernel實現(xiàn)問答應(yīng)用

首先創(chuàng)建一個控制臺應(yīng)用程序,然后添加Microsoft.SemanticKernel NuGet包的引用:

$ dotnet new console --name ChatApp
$ dotnet add package Microsoft.SemanticKernel

然后編輯Program.cs文件,加入下面的代碼:

using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;
using System.Text;
var apikey = Environment.GetEnvironmentVariable("azureopenaiapikey")!;
// 初始化Semantic Kernel
var kernel = Kernel.CreateBuilder()
    .AddAzureOpenAIChatCompletion(
        "gpt-4", 
        "https://qingy-m2e0gbl3-eastus.openai.azure.com", 
        apikey)
    .Build();
// 創(chuàng)建一個對話完成服務(wù)以及對話歷史對象,用來保存對話歷史,以便后續(xù)為大模型
// 提供對話上下文信息。
var chatCompletionService = kernel.GetRequiredService<IChatCompletionService>();
var chat = new ChatHistory("你是一個AI助手,幫助人們查找信息和回答問題");
StringBuilder chatResponse = new();
while (true)
{
    Console.Write("請輸入問題>> ");
    // 將用戶輸入的問題添加到對話中
    chat.AddUserMessage(Console.ReadLine()!);
    chatResponse.Clear();
    // 獲取大語言模型的反饋,并將結(jié)果逐字輸出
    await foreach (var message in
                   chatCompletionService.GetStreamingChatMessageContentsAsync(chat))
    {
        // 輸出當(dāng)前獲取的結(jié)果字符串
        Console.Write(message);
        // 將輸出內(nèi)容添加到臨時變量中
        chatResponse.Append(message.Content);
    }
    Console.WriteLine();
    // 在進入下一次問答之前,將當(dāng)前回答結(jié)果添加到對話歷史中,為大語言模型提供問答上下文
    chat.AddAssistantMessage(chatResponse.ToString());
    Console.WriteLine();
}

在上面的代碼中,需要將你的API Key和終結(jié)點URI配置進去,為了安全性,這里我使用環(huán)境變量保存API Key,然后由程序讀入。為了讓大語言模型能夠了解在一次對話中,我和它之間都討論了什么內(nèi)容,在代碼中,使用一個StringBuilder臨時保存了當(dāng)前對話的應(yīng)答結(jié)果,然后將這個結(jié)果又通過Semantic Kernel的AddAssistantMessage方法加回到對話中,以便在下一次對話中,大語言模型能夠知道我們在聊什么話題。

比如下面的例子中,在第二次提問時我問到“有那幾次遷徙?”,AI能知道我是在說人類歷史上的大遷徙,然后將我想要的答案列舉出來:

到這里,一個簡單的基于gpt-4o的問答應(yīng)用就完成了,它的工作流程大致如下:

AI能回答所有的問題嗎?

由于這里使用的gpt-4o大語言模型是在今年5月份發(fā)布的,而大語言模型都是基于現(xiàn)有數(shù)據(jù)經(jīng)過訓(xùn)練得到的,所以,它應(yīng)該不會知道5月份以后的事情,遇到這樣的問題,AI只能回答不知道,或者給出一個比較離譜的答案:

你或許會想,那我將這些知識或者新聞文章下載下來,然后基于上面的代碼,將這些信息先添加到對話歷史中,讓大語言模型能夠了解上下文,這樣回答問題的時候準(zhǔn)確率不是提高了嗎?這個思路是對的,可以在進行問答之前,將新聞的文本信息添加到對話歷史中:

using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;
using System.Text;
using Microsoft.SemanticKernel.Connectors.AzureOpenAI;
using Microsoft.SemanticKernel.Memory;
using Microsoft.SemanticKernel.Text;
#pragma warning disable SKEXP0010, SKEXP0001, SKEXP0050
const string CollectionName = "LatestNews";
var apikey = Environment.GetEnvironmentVariable("azureopenaiapikey")!;
// 初始化Semantic Kernel
var kernel = Kernel.CreateBuilder()
    .AddAzureOpenAIChatCompletion(
        "gpt-4", 
        "https://qingy-m2e0gbl3-eastus.openai.azure.com", 
        apikey)
    .Build();
// 創(chuàng)建文本向量生成服務(wù)
var textEmbeddingGenerationService = new AzureOpenAITextEmbeddingGenerationService(
    "text-embedding-3-small",
    "https://qingy-m2e0gbl3-eastus.openai.azure.com",
    apikey);
// 創(chuàng)建用于保存文本向量的內(nèi)存向量數(shù)據(jù)庫
var memory = new MemoryBuilder()
    .WithMemoryStore(new VolatileMemoryStore())
    .WithTextEmbeddingGeneration(textEmbeddingGenerationService)
    .Build();
// 從外部文件以Markdown格式讀入內(nèi)容,然后根據(jù)語義產(chǎn)生多個段落
var markdownContent = await File.ReadAllTextAsync(@"input.md");
var paragraphs =
    TextChunker.SplitMarkdownParagraphs(
        TextChunker.SplitMarkDownLines(markdownContent.Replace("\r\n", " "), 128),
        64);
// 將各個段落進行量化并保存到向量數(shù)據(jù)庫
for (var i = 0; i < paragraphs.Count; i++)
{
    await memory.SaveInformationAsync(CollectionName, paragraphs[i], $"paragraph{i}");
}
// 創(chuàng)建一個對話完成服務(wù)以及對話歷史對象,用來保存對話歷史,以便后續(xù)為大模型
// 提供對話上下文信息。
var chatCompletionService = kernel.GetRequiredService<IChatCompletionService>();
var chat = new ChatHistory("你是一個AI助手,幫助人們查找信息和回答問題");
StringBuilder additionalInfo = new();
StringBuilder chatResponse = new();
while (true)
{
    Console.Write("請輸入問題>> ");
    var question = Console.ReadLine()!;
    additionalInfo.Clear();
    // 從向量數(shù)據(jù)庫中找到跟提問最為相近的3條信息,將其添加到對話歷史中
    await foreach (var hit in memory.SearchAsync(CollectionName, question, limit: 3))
    {
        additionalInfo.AppendLine(hit.Metadata.Text);
    }
    var contextLinesToRemove = -1;
    if (additionalInfo.Length != 0)
    {
        additionalInfo.Insert(0, "以下是一些附加信息:");
        contextLinesToRemove = chat.Count;
        chat.AddUserMessage(additionalInfo.ToString());
    }
    // 將用戶輸入的問題添加到對話中
    chat.AddUserMessage(question);
    chatResponse.Clear();
    // 獲取大語言模型的反饋,并將結(jié)果逐字輸出
    await foreach (var message in
                   chatCompletionService.GetStreamingChatMessageContentsAsync(chat))
    {
        // 輸出當(dāng)前獲取的結(jié)果字符串
        Console.Write(message);
        // 將輸出內(nèi)容添加到臨時變量中
        chatResponse.Append(message.Content);
    }
    Console.WriteLine();
    // 在進入下一次問答之前,將當(dāng)前回答結(jié)果添加到對話歷史中,為大語言模型提供問答上下文
    chat.AddAssistantMessage(chatResponse.ToString());
    // 將當(dāng)次問題相關(guān)的內(nèi)容從對話歷史中移除
    if (contextLinesToRemove >= 0) chat.RemoveAt(contextLinesToRemove);
    Console.WriteLine();
}

但是這樣做,會造成下面的異常信息:

這個問題其實就跟大語言模型的Context Window有關(guān)。當(dāng)今所有的大語言模型在一次數(shù)據(jù)處理上都有一定的限制,這個限制就是Context Window,在這個例子中,我們的模型一次最多處理12萬8千個token(token是大語言模型的數(shù)據(jù)處理單元,它可以是一個詞組,一個單詞或者是一個字符),而我們卻輸入了147,845個token,于是就報錯了。很明顯,我們應(yīng)該減少傳入的數(shù)據(jù)量,但這樣又沒辦法把完整的新聞文章信息發(fā)送給大語言模型。此時就要用到“檢索增強生成(RAG)”。

Semantic Kernel的檢索增強生成(RAG)實踐

 其實,并不一定非要把整篇新聞文章發(fā)給大語言模型,可以換個思路:只需要在新聞文章中摘出跟提問相關(guān)的內(nèi)容發(fā)送給大語言模型就可以了,這樣就可以大大減小需要發(fā)送到大語言模型的token數(shù)量。所以,這里就出現(xiàn)了額外的一些步驟:

  • 對大量的文檔進行預(yù)處理,將文本信息量化并保存下來(Text Embedding)
  • 在提出新問題時,根據(jù)問題語義,從保存的文本量化信息(Embeddings)中,找到與問題相關(guān)的信息
  • 將這些信息發(fā)送給大語言模型,并從大語言模型獲得應(yīng)答
  • 將結(jié)果反饋給調(diào)用方

流程大致如下:

虛線灰色框中就是檢索增強生成(RAG)相關(guān)流程,這里就不針對每個標(biāo)號一一說明了,能夠理解上面所述的4個大的步驟,就很好理解這張圖中的整體流程。下面我們直接使用Semantic Kernel,通過RAG來增強模型應(yīng)答。

首先,在Azure OpenAI Studio中,按照上文的步驟,部署一個text-embedding-3-small的模型,同樣將終結(jié)點URI和API Key記錄下來,然后,在項目中添加Microsoft.SemanticKernel.Plugins.Memory NuGet包的引用,因為我們打算先使用基于內(nèi)存的文本向量數(shù)據(jù)庫來運行我們的代碼。Semantic Kernel支持多種向量數(shù)據(jù)庫,比如Sqlite,Azure AI Search,Chroma,Milvus,Pinecone,Qdrant,Weaviate等等。在添加引用的時候,需要使用--prerelease參數(shù),因為Microsoft.SemanticKernel.Plugins.Memory包目前還處于alpha階段。

將上面的代碼改成下面的形式:

using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;
using System.Text;
using Microsoft.SemanticKernel.Connectors.AzureOpenAI;
using Microsoft.SemanticKernel.Memory;
using Microsoft.SemanticKernel.Text;
#pragma warning disable SKEXP0010, SKEXP0001, SKEXP0050
const string CollectionName = "LatestNews";
var apikey = Environment.GetEnvironmentVariable("azureopenaiapikey")!;
// 初始化Semantic Kernel
var kernel = Kernel.CreateBuilder()
    .AddAzureOpenAIChatCompletion(
        "gpt-4", 
        "https://qingy-m2e0gbl3-eastus.openai.azure.com", 
        apikey)
    .Build();
// 創(chuàng)建文本向量生成服務(wù)
var textEmbeddingGenerationService = new AzureOpenAITextEmbeddingGenerationService(
    "text-embedding-3-small",
    "https://qingy-m2e0gbl3-eastus.openai.azure.com",
    apikey);
// 創(chuàng)建用于保存文本向量的內(nèi)存向量數(shù)據(jù)庫
var memory = new MemoryBuilder()
    .WithMemoryStore(new VolatileMemoryStore())
    .WithTextEmbeddingGeneration(textEmbeddingGenerationService)
    .Build();
// 從外部文件以Markdown格式讀入內(nèi)容,然后根據(jù)語義產(chǎn)生多個段落
var markdownContent = await File.ReadAllTextAsync(@"input.md");
var paragraphs =
    TextChunker.SplitMarkdownParagraphs(
        TextChunker.SplitMarkDownLines(markdownContent.Replace("\r\n", " "), 128),
        64);
// 將各個段落進行量化并保存到向量數(shù)據(jù)庫
for (var i = 0; i < paragraphs.Count; i++)
{
    await memory.SaveInformationAsync(CollectionName, paragraphs[i], $"paragraph{i}");
}
// 創(chuàng)建一個對話完成服務(wù)以及對話歷史對象,用來保存對話歷史,以便后續(xù)為大模型
// 提供對話上下文信息。
var chatCompletionService = kernel.GetRequiredService<IChatCompletionService>();
var chat = new ChatHistory("你是一個AI助手,幫助人們查找信息和回答問題");
StringBuilder additionalInfo = new();
StringBuilder chatResponse = new();
while (true)
{
    Console.Write("請輸入問題>> ");
    var question = Console.ReadLine()!;
    additionalInfo.Clear();
    // 從向量數(shù)據(jù)庫中找到跟提問最為相近的3條信息,將其添加到對話歷史中
    await foreach (var hit in memory.SearchAsync(CollectionName, question, limit: 3))
    {
        additionalInfo.AppendLine(hit.Metadata.Text);
    }
    var contextLinesToRemove = -1;
    if (additionalInfo.Length != 0)
    {
        additionalInfo.Insert(0, "以下是一些附加信息:");
        contextLinesToRemove = chat.Count;
        chat.AddUserMessage(additionalInfo.ToString());
    }
    // 將用戶輸入的問題添加到對話中
    chat.AddUserMessage(question);
    chatResponse.Clear();
    // 獲取大語言模型的反饋,并將結(jié)果逐字輸出
    await foreach (var message in
                   chatCompletionService.GetStreamingChatMessageContentsAsync(chat))
    {
        // 輸出當(dāng)前獲取的結(jié)果字符串
        Console.Write(message);
        // 將輸出內(nèi)容添加到臨時變量中
        chatResponse.Append(message.Content);
    }
    Console.WriteLine();
    // 在進入下一次問答之前,將當(dāng)前回答結(jié)果添加到對話歷史中,為大語言模型提供問答上下文
    chat.AddAssistantMessage(chatResponse.ToString());
    // 將當(dāng)次問題相關(guān)的內(nèi)容從對話歷史中移除
    if (contextLinesToRemove >= 0) chat.RemoveAt(contextLinesToRemove);
    Console.WriteLine();
}

重新運行程序,然后提出同樣的問題,可以看到,現(xiàn)在的答案就正確了:

 現(xiàn)在看看向量數(shù)據(jù)庫中到底有什么。新添加一個對Microsoft.SemanticKernel.Connectors.Sqlite NuGet包的引用,然后,將上面代碼的:

.WithMemoryStore(new VolatileMemoryStore())

改為:

.WithMemoryStore(await SqliteMemoryStore.ConnectAsync("vectors.db"))

重新運行程序,執(zhí)行成功后,在bin\Debug\net8.0目錄下,可以找到vectors.db文件,用Sqlite查看工具(我用的是SQLiteStudio)打開數(shù)據(jù)庫文件,可以看到下面的表和數(shù)據(jù):

Metadata字段保存的就是每個段落的原始數(shù)據(jù)信息,而Embedding字段則是文本向量,其實它就是一系列的浮點值,代表著文本之間在語義上的距離。

使用基于Ollama的本地大語言模型

Semantic Kernel現(xiàn)在已經(jīng)可以支持Ollama本地大語言模型了,雖然它目前也還是預(yù)覽版。可以在項目中通過添加Microsoft.SemanticKernel.Connectors.Ollama NuGet包來體驗。建議安裝最新版本的Ollama,然后,下載兩個大語言模型,一個是Chat Completion類型的,另一個是Text Embedding類型的。我選擇了llama3.2:3bmxbai-embed-large這兩個模型:

代碼上只需要將Azure OpenAI替換為Ollama即可:

// 初始化Semantic Kernel
var kernel = Kernel.CreateBuilder()
    .AddOllamaChatCompletion(
        "llama3.2:3b", 
        new Uri("http://localhost:11434"))
    .Build();
// 創(chuàng)建文本向量生成服務(wù)
var textEmbeddingGenerationService = new OllamaTextEmbeddingGenerationService(
    "mxbai-embed-large:latest", 
    new Uri("http://localhost:11434"));

總結(jié)

通過本文的介紹,應(yīng)該可以對Semantic Kernel、RAG以及在C#中的應(yīng)用有一定的了解,雖然沒有涉及原理性的內(nèi)容,但基本已經(jīng)可以在應(yīng)用層面上提供一定的參考價值。Semantic Kernel雖然有些Plugins還處于預(yù)覽階段,但通過本文的介紹,我們已經(jīng)可以看到它的強大功能,比如,允許我們很方便地接入各種流行的向量數(shù)據(jù)庫,也允許我們很方便地切換到不同的AI大語言模型服務(wù),在AI的應(yīng)用集成上,Semantic Kernel發(fā)揮著重要的作用。

參考

本文部分內(nèi)容參考了微軟官方文檔《Demystifying Retrieval Augmented Generation with .NET》,代碼也部分參考了文中內(nèi)容。文章介紹得更為詳細(xì),建議有興趣的讀者移步閱讀。

到此這篇關(guān)于在C#中基于Semantic Kernel的檢索增強生成(RAG)實踐的文章就介紹到這了,更多相關(guān)C#檢索增強生成內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • C#程序員應(yīng)該養(yǎng)成的程序性能優(yōu)化寫法

    C#程序員應(yīng)該養(yǎng)成的程序性能優(yōu)化寫法

    工作和生活中經(jīng)??梢钥吹揭恍┏绦蛟?寫代碼的時候只關(guān)注代碼的邏輯性,而不考慮運行效率,其實這對大多數(shù)程序猿來說都是沒有問題的,不過作為一只有理想的CodeMonkey,我還是希望給大家分享一些性能優(yōu)化心得
    2017-08-08
  • C#索引屬性用法實例分析

    C#索引屬性用法實例分析

    這篇文章主要介紹了C#索引屬性用法,實例分析了C#聲明索引屬性的相關(guān)技巧,需要的朋友可以參考下
    2015-06-06
  • 基于WPF實現(xiàn)瀑布流控件

    基于WPF實現(xiàn)瀑布流控件

    這篇文章主要介紹了如何基于WPF實現(xiàn)簡單的瀑布流控件,文中的示例代碼講解詳細(xì),對我們的學(xué)習(xí)或工作有一定幫助,需要的小伙伴可以參考一下
    2024-02-02
  • C#實現(xiàn)串口示波器

    C#實現(xiàn)串口示波器

    這篇文章主要為大家詳細(xì)介紹了C#實現(xiàn)串口示波器,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-02-02
  • C#生成餅形圖及添加文字說明實例代碼

    C#生成餅形圖及添加文字說明實例代碼

    這篇文章主要介紹了C#生成餅形圖及添加文字說明的方法,非常實用的功能,需要的朋友可以參考下
    2014-07-07
  • C#使用StreamWriter寫入文件的方法

    C#使用StreamWriter寫入文件的方法

    這篇文章主要介紹了C#使用StreamWriter寫入文件的方法,涉及C#中StreamWriter類操作文件的相關(guān)技巧,需要的朋友可以參考下
    2015-05-05
  • C# WORD操作實現(xiàn)代碼

    C# WORD操作實現(xiàn)代碼

    在當(dāng)前項目開發(fā)過程中,客戶有根據(jù)數(shù)據(jù)庫數(shù)據(jù)生成WORD文檔的需求,在和同事溝通的過程中,找到了兩個解決方案
    2009-04-04
  • C#運算符之與,或,異或及移位運算小結(jié)

    C#運算符之與,或,異或及移位運算小結(jié)

    本文是對C#中的與,或,異或及移位運算進行了詳細(xì)的介紹,需要的朋友可以過來參考下,希望對大家有所幫助
    2013-10-10
  • C#使用IronPython庫調(diào)用Python腳本

    C#使用IronPython庫調(diào)用Python腳本

    這篇文章介紹了C#使用IronPython庫調(diào)用Python腳本的方法,文中通過示例代碼介紹的非常詳細(xì)。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-06-06
  • C#用Topshelf創(chuàng)建Windows服務(wù)的步驟分享

    C#用Topshelf創(chuàng)建Windows服務(wù)的步驟分享

    這篇文章主要給大家介紹了關(guān)于C#如何利用Topshelf創(chuàng)建Windows服務(wù)的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用C#具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-05-05

最新評論