检索增强生成(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

SemanticKernel/C#:检索增强生成(RAG)简易实践的更多相关文章

  1. 以小25倍参数量媲美GPT-3的检索增强自回归语言模型:RETRO

    NLP论文解读 原创•作者 | 吴雪梦Shinemon 研究方向 | 计算机视觉 导读说明: 一个具有良好性能的语言模型,一定量的数据样本必不可少.现有的各种语言模型中,例如GPT3具有1750亿的参 ...

  2. docker简易实践

    docker简易实践 实验环境 操作系统:deepin 15.4 安装步骤 1.安装docker sudo apt-get install docker.io 2.启动docker服务 sudo se ...

  3. 基于OVS的VLAN虚拟化简易实践方案

    基于OVS的VLAN虚拟化简易实践方案 前言 本实验基于ovs的vlan流表匹配,根据端口进行vlan标签插入.手工配置ovs,使其具有vlan虚拟化方案. 实验拓扑 ---- ---- | h1 | ...

  4. 如何为已有的类没有生成toString的方法增强生成toString方法

    1:只要提到增强,我的第一思路就是代理,动态代理.但是仅仅是一个toString其实没必要使用代理模式了,有点大材小用了(动态代理其实也是最后通过反射生成toString的方法). 2:简单粗暴,可以 ...

  5. IDEA 中生成 MyBatis 逆向工程实践

    IDEA 逆向 MyBatis 工程时,不像支持 Hibernate 那样有自带插件,需要集成第三方的 MyBatis Generator. MyBatis Generator的详细介绍 http:/ ...

  6. IDEA 中生成 Hibernate 逆向工程实践

    谈起 Hibernate 应该得知道 Gavin King 大叔,他构建了 Hibernate ,并将其捐献给了开源社区. Hibernate 对象关系映射解决方案,为面向对象的领域模型到传统的关系型 ...

  7. 生成UUID简易版

    最近一体化项目的主键ID都要求使用UUID,生成方法如下:   import org.safehaus.uuid.UUIDGenerator;   /** UUID生成器对象 */  private ...

  8. 简易实践的vue自定义tab入门

    本文基于vue官方文档,分别为:动态组件 & 异步组件.插槽.进入/离开 & 列表过渡 章节链接描述 要想实现tab动画,首先要了解vue中哪些元素/那些组件适合在那些条件下实现动画效 ...

  9. 2023计算机领域顶会(A类)以及ACL 2023自然语言处理(NLP)研究子方向领域汇总

    2023年的计算语言学协会年会(ACL 2023)共包含26个领域,代表着当前前计算语言学和自然语言处理研究的不同方面.每个领域都有一组相关联的关键字来描述其潜在的子领域, 这些子领域并非排他性的,它 ...

  10. C# 8.0 添加和增强的功能【基础篇】

    .NET Core 3.x和.NET Standard 2.1支持C# 8.0. 一.Readonly 成员 可将 readonly 修饰符应用于结构的成员,来限制成员为不可修改状态.这比在C# 7. ...

随机推荐

  1. H264 H265 分析小工具

    1.在调试 H264 H265 编码的流数据的时候,有时候需要打印没有nalu的类型和数量,自己写了一个小工具 使用方式: p.p1 { margin: 0; font: 22px Menlo; co ...

  2. 语义分割评价指标(Dice coefficient, IoU)

    语义分割任务常用的评价指标为Dice coefficient和IoU.Dice和IoU都是用来衡量两个集合之间相似性的度量,对于语义分割任务而言即用来评估网络预测的分割结果与人为标注结果之间的相似度. ...

  3. uniapp 输入有值后按钮变色

    我们在开发中难免会遇到一些登录注册输入框中的值为空时,按钮的状态变成为不可点击的状态,当输入框有值后就把按钮变为可点击的状态 代码部分: <input placeholder="请输入 ...

  4. bash: _get_comp_words_by_ref: command not found 报错

    没有安装补全的包 错误信息 bash: _get_comp_words_by_ref: command not found 表明你的 shell 中可能存在补全功能的问题. 通常,这种错误发生在你的系 ...

  5. SELinux 安全模型——TE

    首发公号:Rand_cs SELinux 安全模型--TE 通过前面的示例策略,大家对 SELinux 应该有那么点感觉认识了,从这篇开始的三篇文章讲述 SELinux 的三种安全模型,会涉及一些代码 ...

  6. LeetCode 678. Valid Parenthesis String 有效的括号字符串 (C++/Java)

    题目: Given a string containing only three types of characters: '(', ')' and '*', write a function to ...

  7. ABC335

    输 E 对于 \((u,v)\): 若 \(a_u = a_v\),则把 \(u\) 和 \(v\) 扔到同一个并查集里 否则连接两个点 然后跑一遍 dp 即可. code F 根号分治 如果 \(a ...

  8. 使用 OpenTelemetry 构建可观测性 02 - 埋点

    这是讲解 OpenTelemetry 系列博客的第二篇.在上一篇博客中,我们介绍了 OpenTelemetry 是什么以及由什么组成.现在我们将讨论如何使用 OTel 准确收集遥测数据和链路追踪数据. ...

  9. ssh练习

    根据要求完成部署 根据如下要求,完成部署过程 1.恢复7.8.9.31.41所有机器的快照 7 8 9 web服务 nginx ​ 172.16.1.xx ​ ​ nfs-31 提供共享文件存储 ​ ...

  10. 《Android开发卷——开卷》

    打算在自己在工作中遇到的问题,技术难点都记录下来,让其他人可以借鉴或者指点,这样既可以成长自己也可以成长别人.因为自己已经在工作了,所以遇到的问题非常具有代表性,所以不能简单简单的像网上一些小学生一样 ...