首页 星云 工具 资源 星选 资讯 热门工具
:

PDF转图片 完全免费 小红书视频下载 无水印 抖音视频下载 无水印 数字星空

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

编程知识
2024年08月01日 08:58

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

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

本示例实现的效果

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

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

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

image-20240801082308221

image-20240801082408638

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

开始实践

安装所需的nuget包:

image-20240801082520136

首先先初始化一个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:

image-20240801083237027

现在先来看看如何构建一个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);
 }

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

查看数据库:

image-20240801084335696

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

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

image-20240801084459909

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

image-20240801084808470

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

image-20240801085241311

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

image-20240801085445873

大语言模型的回答结果:

image-20240801085524229

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

下一步探索方向

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

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

image-20240801090623386

image-20240801090825349

网上查了一些资料,有些大佬已经实现了在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

From:https://www.cnblogs.com/mingupupu/p/18336055
本文地址: http://shuzixingkong.net/article/656
0评论
提交 加载更多评论
其他文章 canvas实现截图功能
开篇 最近在做一个图片截图的功能。 因为工作时间很紧张, 当时是使用的是一个截图插件。 周末两天无所事事,来写一个简单版本的截图功能。 因为写的比较简单,如果写的不好,求大佬轻一点喷 读取图片并获取图片的宽度和高度思路 首先读取文件我们使用input中类型是file。 我们需要对读取的对象进行限制,
canvas实现截图功能 canvas实现截图功能 canvas实现截图功能
PHP转Go系列 | Carbon 时间处理工具的使用姿势
在日常的开发过程中经常会遇到对时间的处理,比如将时间戳进行格式化、获取昨天或上周或上个月的时间、基于当前时间进行加减等场景的使用
PHP转Go系列 | Carbon 时间处理工具的使用姿势
低代码如何借助 K8s 实现高并发支持?
引言 在当今这个数字化时代,互联网的普及和技术的飞速发展使得应用程序面临着前所未有的挑战,其中最为显著的就是高并发访问的需求。随着用户数量的激增和业务规模的扩大,如何确保应用在高并发场景下依然能够稳定运行、快速响应,成为了所有开发者和技术团队必须面对的重要课题。 Kubernetes(K8s),作为
低代码如何借助 K8s 实现高并发支持? 低代码如何借助 K8s 实现高并发支持? 低代码如何借助 K8s 实现高并发支持?
吃透 JVM 诊断方法与工具使用
JVM(Java虚拟机)是Java程序运行的基础环境,它提供了内存管理、线程管理和性能监控等功能。吃透JVM诊断方法,可以帮助开发者更有效地解决Java应用在运行时遇到的问题。以下是一些常见的JVM诊断方法: 使用JConsole: JConsole是一个可视化监控工具,可以连接到本地或远程的JVM
02.计算器存储器的原理
02.计算器存储器的原理 目录介绍 01.什么是存储器 1.1 了解存储器是什么 1.2 存储器类型 02.存储器系统设计 2.1 存储器分层设计 2.2 存储器层次结构 2.3 高速缓存设计思想 2.4 虚拟内存访问内存 03.存储器类型 3.1 按照材质划分 3.2 按芯片类型划分 3.3 内存
02.计算器存储器的原理
.NET 结果与错误处理利器 FluentResults
前言 在项目开发中,方法返回的结果(成功或失败)对我们开发来说很重要。传统方法,如通过异常来指示错误或使用特定的返回类型(如布尔值加输出参数),虽然有效,但可能缺乏直观性和灵活性。 FluentResults库应运而生,它以一种既流畅又富有表达力的方式,极大地优化了这一过程。通过使用FluentRe
.NET 结果与错误处理利器 FluentResults .NET 结果与错误处理利器 FluentResults .NET 结果与错误处理利器 FluentResults
自从用了这些监控工具,我连续几天没睡好觉!
大家好,我是程序员鱼皮,今天分享一些很实用的系统监控告警工具。 为什么要用监控告警? 说到监控告警,没有企业开发经验的同学非常容易忽视它,甚至会有同学觉得没有必要,大不了出了 Bug 再修就是了。 这种想法大错特错! 我们把系统想象成人的身体。有的时候,一个人表面看起来可能很健康,但可能只是没有机会
自从用了这些监控工具,我连续几天没睡好觉! 自从用了这些监控工具,我连续几天没睡好觉! 自从用了这些监控工具,我连续几天没睡好觉!
telegraf 常用命令总结
本文为博主原创,转载请注明出处: Telegraf 是一个灵活的服务器代理,用于收集和报告指标。它支持插件驱动,这意味着你可以根据需要添加或修改功能。 1.使用telegraf --help 查看telegraf提供的相关命令和参数 使用telegraf --help 可以查看telegraf提供的
telegraf 常用命令总结