SemanticKernel/C#:检索增强生成(RAG)简易实践

简介: SemanticKernel/C#:检索增强生成(RAG)简易实践

检索增强生成(RAG)是什么?

RAG是“Reference-based Generative model with Attention”的缩写,也可以被称为“Retrieval-Augmented Generation”,是一种结合了检索技术和生成模型的方法,主要用于自然语言处理任务,如文本生成、对话系统、机器翻译等。RAG模型通过从外部知识库中检索相关信息,并将其与输入文本结合,以生成更准确、更丰富的输出。这种方法可以提高模型的准确性和可解释性,因为它可以明确地指出生成的文本与哪些外部知识相关。RAG模型在处理需要大量背景知识的任务时特别有用,如专业领域的问答系统或对话代理。

本示例实现的效果

在使用大语言模型的过程中,会发现大语言模型在通用知识上很强,但是如果你问的是跟私有数据有关的事情,它就不知道了。比如有一段私有文本数据如下所示:

小X于2000年创建了一家名为“小X的世界”的公司,公司总部在湖北武汉,员工有300人。小X最喜欢的编程语言是C#,小X最喜欢的书是《平凡的世界》。

这只是个简单的例子,所以文本先取的很短,实际上可以换成是你的一些私有文档,然后让大语言模型根据你的私有文档进行回答,现在你如果问大语言模型,“小X创建的公司叫什么?”、”小X最喜欢的编程语言是什么?“等等一些根据私有文档才能回答的问题,大语言模型是不知道的,但是通过RAG就可以让大语言模型回答诸如此类的需要根据私有文档才能回答的问题。

实现的思路是通过嵌入模型将文本转化为向量,将向量存入数据库,检索时基于输入查询的向量表示,从知识库中检索出最相关的文档或片段。将获取的相关片段,嵌入到Prompt中,让大语言模型根据获取到的片段进行回答。

开始实践

安装所需的nuget包:

首先先初始化一个Kernel,这里我使用的大语言模型是硅基流动平台提供的开源的Qwen/Qwen2-7B-Instruct。

private readonly Kernel _kernel;
 public SemanticKernelService()
 {
     var handler = new OpenAIHttpClientHandler();
     var builder = Kernel.CreateBuilder()
     .AddOpenAIChatCompletion(
       modelId: "Qwen/Qwen2-7B-Instruct",
       apiKey: "api key",
       httpClient: new HttpClient(handler));         
     var kernel = builder.Build();
     _kernel = kernel;
 }

由于硅基流动平台已经提供了与OpenAI兼容的格式,只需要在传入一个HttpClient将请求转发到硅基流动平台的api即可,OpenAIHttpClientHandler类如下所示:

public class OpenAIHttpClientHandler : HttpClientHandler
 {
     protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
     {
         UriBuilder uriBuilder;
         switch (request.RequestUri?.LocalPath)
         {
             case "/v1/chat/completions":
                 uriBuilder = new UriBuilder(request.RequestUri)
                 {
                     // 这里是你要修改的 URL
                     Scheme = "https",
                     Host = "api.siliconflow.cn",
                     Path = "v1/chat/completions",
                 };
                 request.RequestUri = uriBuilder.Uri;
                 break;
             case "/v1/embeddings":
                 uriBuilder = new UriBuilder(request.RequestUri)
                 {
                     // 这里是你要修改的 URL
                     Scheme = "https",
                     Host = "api.siliconflow.cn",
                     Path = "v1/embeddings",
                 };
                 request.RequestUri = uriBuilder.Uri;
                 break;
         }
         HttpResponseMessage response = await base.SendAsync(request, cancellationToken);
         return response;
     }
 }

现在需要将文本转化为向量,需要先构建一个ISemanticTextMemory:

现在先来看看如何构建一个ISemanticTextMemory:

public async Task<ISemanticTextMemory> GetTextMemory2()
  {
      var memoryBuilder = new MemoryBuilder();
      memoryBuilder.WithOpenAITextEmbeddingGeneration("text-embedding-ada-002", "api key");           
      IMemoryStore memoryStore = await SqliteMemoryStore.ConnectAsync("memstore.db");
      memoryBuilder.WithMemoryStore(memoryStore);
      var textMemory = memoryBuilder.Build();
      return textMemory;
  }

首先需要有一个嵌入模型,这里使用的是OpenAI的text-embedding-ada-002模型,也尝试过使用硅基流动平台提供的嵌入模型,生成向量是没有问题的,但是在搜索的时候会报错,还没有解决。

使用SQLite来存储生成的向量。

var lines = TextChunker.SplitPlainTextLines(input, 100);
 var paragraphs = TextChunker.SplitPlainTextParagraphs(lines, 1000);
 foreach (var para in paragraphs)
 {
     await textMemory.SaveInformationAsync(index, id: Guid.NewGuid().ToString(), text: para, cancellationToken: default);
 }

将文本分段,本示例文本内容很少,只有一段。

查看数据库:

已经将向量数据存入数据库了。

现在根据问题,搜索最相关的片段:

以“小X最喜欢的编程语言是什么?”这个问题为例。

将问题转化为向量并利用余弦相似度进行检索搜索最相关的片段:

将获取到的最相关的文本与问题嵌入到Prompt中,让大语言模型回答:

大语言模型的回答结果:

以上就基于SemanticKernel实现了一个简单的RAG应用。

下一步探索方向

虽然说我的电脑本地运行大语言模型不太行,但是在本地运行大语言模型还是有很多需求场景的,下一步探索如何在SemanticKernel中使用本地的大语言模型与嵌入模型。如果大语言模型运行不太行的话,再换成国内的平台,嵌入模型我试过,本地运行也还可以的。

本地运行使用的是Ollama,官方也有计划发布一个Ollama Connector:

网上查了一些资料,有些大佬已经实现了在SemanticKernel中使用Ollama中的对话模型与嵌入模型。可以等官方支持,也可以根据大佬们的分享,自己去实践一下。

Local Memory: C# Semantic Kernel, Ollama and SQLite to manage Chat Memories locally | by John Kane | Medium

Using local LLM with Ollama and Semantic Kernel - Learnings in IT (sachinsu.github.io)

Use Custom and Local AI Models with the Semantic Kernel SDK for .NET | Microsoft Learn

参考

1、https://github.com/microsoft/semantic-kernel/blob/main/dotnet/notebooks/06-memory-and-embeddings.ipynb

2、https://github.com/microsoft/semantic-kernel/blob/main/dotnet/notebooks/09-memory-with-chroma.ipynb

3、https://github.com/microsoft/semantic-kernel/blob/main/dotnet/samples/Concepts/Memory/MemoryStore_CustomReadOnly.cs

4、https://github.com/microsoft/semantic-kernel/blob/main/dotnet/samples/Concepts/Memory/SemanticTextMemory_Building.cs

5、https://github.com/microsoft/semantic-kernel/blob/main/dotnet/samples/Concepts/Memory/TextChunkingAndEmbedding.cs

目录
相关文章
|
4月前
|
存储 安全 数据库连接
C#深度揭秘:常量的魅力和实践,一文让你从新手到专家
C#深度揭秘:常量的魅力和实践,一文让你从新手到专家
42 0
|
4月前
|
网络协议 API C#
C# 中模拟 POST 和 GET 请求的原理与实践
【1月更文挑战第4天】在现代网络应用中,HTTP请求是客户端与服务器交互的基础。其中,GET和POST是最常用的两种请求方法。本文将介绍如何使用C#语言模拟这两种请求,并解释其背后的工作原理。我们将利用.NET框架中的HttpClient类来发送请求,并处理服务器的响应。通过本文,读者将能够理解HTTP请求的基本构成,学会在C#中编写代码来模拟这些请求,进而在开发过程中实现与Web服务的交互。
|
22天前
|
开发框架 安全 .NET
|
10天前
|
存储 C#
C# 一分钟浅谈:继承与多态性的实践
【9月更文挑战第2天】本文从基础入手,详细介绍了面向对象编程中继承与多态性的核心概念。通过 `Animal`、`Dog` 和 `Cat` 类的示例代码,展示了如何利用继承重用代码及多态性实现不同对象对同一方法的多样化响应,帮助读者更好地理解和应用这两个重要概念,提升面向对象编程能力。
25 3
|
1月前
|
API C# 数据库
SemanticKernel/C#:实现接口,接入本地嵌入模型
SemanticKernel/C#:实现接口,接入本地嵌入模型
43 1
|
1月前
|
API C#
SemanticKernel/C#:使用Ollama中的对话模型与嵌入模型用于本地离线场景
SemanticKernel/C#:使用Ollama中的对话模型与嵌入模型用于本地离线场景
59 0
|
4月前
|
存储 C# 开发者
掌握 C#编程:关键技术与实践
【4月更文挑战第20天】掌握C#编程的关键技术,包括面向对象编程、数据类型、控制流、异常处理、集合泛型、委托事件和异步编程。通过学习基础、实践项目、代码调试、参考优秀代码、社区交流和持续学习来提升技能。注重代码质量,以开发高质量应用程序。
29 2
|
存储 前端开发 C#
【C#编程最佳实践 八】MVC流程实践
【C#编程最佳实践 八】MVC流程实践
95 0
【C#编程最佳实践 八】MVC流程实践
|
XML 存储 SQL
【C#编程最佳实践 五】项目与代码流程实践
【C#编程最佳实践 五】项目与代码流程实践
149 0
【C#编程最佳实践 五】项目与代码流程实践