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

ops/2024/9/29 5:26:26/

检索增强生成(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){// 这里是你要修改的 URLScheme = "https",Host = "api.siliconflow.cn",Path = "v1/chat/completions",};request.RequestUri = uriBuilder.Uri;break;case "/v1/embeddings":uriBuilder = new UriBuilder(request.RequestUri){// 这里是你要修改的 URLScheme = "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


http://www.ppmy.cn/ops/89966.html

相关文章

基于whisper流式语音识别

为了实现持续监听麦克风并在检测到声音时进行转录&#xff0c;我们可以将流的监听时间设置为无限长。通过使用一个音量门限来检测是否有声音&#xff0c;然后进行转录。 安装依赖 确保安装必要的库&#xff1a; pip install torch torchaudio openai-whisper sounddevice nu…

计算机毕业设计选题推荐-遥感影像共享系统-Java/Python项目实战

✨作者主页&#xff1a;IT毕设梦工厂✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Py…

C++解析ini文件

目录 一.什么是ini文件二.ini文件的格式一般是什么样的1.节2.参数3.注释 三.C实现ini文件的解析四.其他 这篇文章简单讨论一下ini文件。 一.什么是ini文件 ini文件其实就是一种配置文件&#xff0c;常见于Windows的系统配置文件&#xff0c;当然也可以是其他用途&#xff0c;…

unity2D游戏开发17战斗精灵

导入 将PlayerFight32x32.png拖Player文件夹进去 设置属性 创建动画剪辑 选中前四帧,右键Create|Animation,将动画命名为player-ire-east 其他几个动画也创建好后,将其拖到Animations|Animations文件夹 选中PlayerController,再点击Animator 创建新的Blend Tree Graph,并重…

【C++】C++特性揭秘:引用与内联函数 | auto关键字与for循环 | 指针空值

C语法相关知识点可以通过点击以下链接进行学习一起加油&#xff01;命名空间缺省参数与函数重载 本章将分享C增加的几种常见特性&#xff0c;主要内容为引用与内联函数 | auto关键字与for循环 | 指针空值&#xff0c;这些知识看似很多&#xff0c;实际也不少。本章篇幅长&#…

C语言宠物系统

功能有增加宠物信息&#xff0c;显示宠物信息&#xff0c;删除宠物信息&#xff0c;修改功能和排序功能&#xff0c;可以选择姓名排序&#xff0c;年龄排序&#xff0c;价格排序。进阶的功能有文件操作&#xff0c;动态内存开辟。。 test.c源文件 #include "Pet.h"v…

Spring框架中的IoC(Inversion of Control,控制反转)和DI

Spring框架中的IoC&#xff08;Inversion of Control&#xff0c;控制反转&#xff09;和DI&#xff08;Dependency Injection&#xff0c;依赖注入&#xff09;是其核心思想之一&#xff0c;它们共同实现了对象的创建、配置和依赖关系的注入&#xff0c;从而极大地提高了应用程…

数据面试问题的记录——7.29

技术问题&#xff1a; 1、SQL中的where和having的区别 where 是取数范围 having是聚合后的结果的筛选范围 2、Python中pass、continue、break的区别 都是循环中使用 pass不起作用&#xff0c;可以在没有实际运行内容时占位 continue继续执行循环 break停止整个循环 逻…