你好,欢迎进入江苏优软数字科技有限公司官网!

诚信、勤奋、创新、卓越

友好定价、专业客服支持、正版软件一站式服务提供

13262879759

工作日:9:00-22:00

sublime text snippet 基于Microsoft.Extensions.AI核心库实现RAG应用

发布时间:2025-12-20

浏览次数:0

https://cloud..cn/i/

(2) 作为 向量数据库,可以使用在你本地运行一个:

运用docker执行run操作,将其端口6333映射到本地的6333端口,同时,把它的另一个端口6334映射到本地。采用-v参数,将当前工作目录下的qdrant_storage目录挂载到容器内的/qdrant/storage目录 ,。qdrant/qdrant

运行bge - m3模型,将其用作生成器,它能够自行拉取一个,在你本地运行 。

ollama pull bge-m3

创建一个控制台应用程序,用于构建你的RAG应用,添加一些所需的文件目录,以及配置文件(json),最终的解决方案呈现如下图所示的样子。

我们将一些要导入的pdf文档,像公司运营手册、员工手册等等,放置在了目录下。在目录下放置了一些公用的model类sublime text snippet,其中一个类身为向量存储的实体类,另一个类则是向量搜索结果的模型类。(1)此处我们的字段便是我们的向量值,它具备1024维。留意:这里的维度是我们自行定义的,你也能够改成你期望的维度数量,不过你的词嵌入模型得支持你想要的维度数量。

公共的,被密封的类,文本片段,类型参数为T键,后面跟着大于号,有什么问题吗,你是想了解这个类的具体用途。{    [VectorStoreRecordKey]对于已声明的“public”,存在“required”,其对应的是“TKey”类型,可进行“Key”的获取操作,同时还能够进行“Key”的设置操作。
[VectorStoreRecordData]公开的可空字符串类型的文本属性,获取时使用获取器方法,设置时有以集语法的特定方法,标点处还各有其特点标记法;。
[VectorStoreRecordData]公共的,可空的字符串类型的,名为ReferenceDescription的,属性,其具有获取和设置的功能 。
[VectorStoreRecordData]公开的,可空的字符串类型的,名为ReferenceLink的属性,其具有获取和设置的功能。
[向量存储记录向量(维度:1024)],其中,向量存储记录向量是一种特定的记录形式,维度为1024 。公开的,只读内存浮点数类型的,文本嵌入属性,它有获取和设置的方式。 ,对应的类型是只读内存浮点数 ,这个表示文本嵌入 ,有一个叫做文本嵌入的属性 ,它的类型是只读内存浮点数 ,并且。}

(2)这个类存在着主要用途,是专门用来返回给LLM做推理使用的,而在我这里,仅仅只需要三个字段,这三个字段分别是:Value,Link以及Score,如此便可以了。

public class TextSearchResult{定义了一个公开的字符串类型属性,名为Value,具备获取和设置的功能 。公有的,可空的字符串类型的属性Link,其获取和设置的方法被定义 。公共的,可为空的双精度型,名为Score,具备获取和设置的属性,。}

用来在PDF导入时,作为一个临时存储源数据文档内容的,是这个类,(分开来说)它主要起到这样的作用,(并且是发挥着这样的功能)。

公有的,密封的,类名为,RawContent,的类型 。{公开的可空字符串类型的属性Text,它具有获取特性,以及在初始化时赋值的特性,句号。
公开的整型的页码,该页码通过获取和初始化来进行设置且以此方式存在 ,你想表述的是这样:在C# 9.0及更高版本中,可以。}

在目录那儿放置了好些公用的帮助类,像能够达成PDF文件读取以及导入向量数据库的情况,能够达成依据用户的query去搜索向量数据库进而获取TopN个近似文档那种情况,而那个则是用来生成唯独的ID Key的。(1)身为PDF文件的导入核心逻辑,它达成了PDF文档读取、切分、生成指定维度的向量并且存入向量数据库。留意这点:这里仅仅只是考虑了文本格式的内容哟sublime text snippet,要是你还想要考虑文件中的图片并把它转成文本,你就得增添一个LLM来帮你做图片转文本这个工作。

公共的、密封的类,名为PdfDataLoader,带有类型参数TKey,其中TKey类型不能为null 。{私有的,只读的,IVectorStoreRecordCollection类型的,一个对象声明 。私有地,只读地,具有一个,其类型为,嵌入生成器,该生成器的参数类型为字符串,返回类型为嵌入浮点数的,_embeddingGenerator;。
public PdfDataLoader( UniqueKeyGenerator IVectorStoreRecordCollection能生成嵌入的那个东西,它是那种将字符串作为输入,输出的是嵌入,而这嵌入又属于浮点数类型的生成器 。 {被称作_vectorStoreRecordCollection的那个记录集合,它就是vectorStoreRecordCollection , 。_uniqueKeyGenerator 被设定为 uniqueKeyGenerator 了,有这么一回事,是这样的情况 。将_embeddingGenerator赋值为embeddingGenerator ,使其二者相等 、一致 。 }
公开的异步任务,用于加载PDF文件,该任务接受PDF文件路径、每批的大小以及每批之间的延迟时间(以毫秒为单位)作为参数 。 {要是那个集合不存在的话,那就去创建它,要是它存在的话,那就不去那么做,这是两种不同的情况,会导致不同的行为发生,而执行顺序是由具体的逻辑和条件来定的说。
变量 sections 被赋值为所加载的所有文本,而这些文本来自于路径为 pdfPath 的文件 。变量“batches”, 等于“sections”调用“Chunk”方法, 并传入“batchSize”参数 。
加工每一批次的内容项,逐个处理。处理、审查那些东西。对每一批次的内容项开展加工工作,展开行动。对于每一个变量分批项目,在分批集合之中,逐个地进行依次检查,以确保每一次检查的准确性以及完整性 。 { // Get text contentsvar textContentTasks = batch.Select(async content =>这是原句内容这不是改写后的句子,你提供需要改写,请补充准确正确的内容以便我进行改写操作。 { if (content.Text != null) return content;
返回新的原始内容,该原始内容.Text属性的值为空字符串,其.PageNumber值为原内容的页码,这里所指原内容即content 。 });先等待所有文本内容任务完成,在此之后var文本内容就等于(等待任务全部完成后的结果) 。在哪里,某一条件成立,即某个值c,使得c的文本不为空字符串 时 ,有相应情况。 .ToList;
“var recordTasks”,“是通过textContent.Select”,“以对async content进行操作”,“进而创建 new TextSnippet” 。 { Text = content.Text,对文件路径pdfPath获取文件信息生成对应名称,将其与“#page=”以及内容的页码数PageNumber组合成字符串,赋值给ReferenceDescription , 。ReferenceLink等于,一个新的Uri所是的,这个Uri通过一个FileInfo的实例,该实例基于pdfPath,所得到的FullName创建,其AbsoluteUri所构成者,再加上一个‘#page=’,以及content的PageNumber所代表的值,所拼接而成的结果 。TextEmbedding等于,等待,把,_embeddingGenerator.GenerateEmbeddingVectorAsync,应用于,content.Text! 并生成嵌入向量,之后得到的结果 。 });
把记录插入到向量存储中,或者在其中更新插入,若该记录已存在的话。var记录 = 等待任务,即任务全部完成时的结果,这些任务被记录在记录任务集合中用于后续操作,最后得到的结果被赋值给records 。 var的这些记录,是当。var upsertedKeys = _vectorStoreRecordCollection.UpsertBatchAsync(records),records是要列举的内容。对着插入更新后的键,等待着,对每个键,进行那个操作 。 {控台写入行,使用格式化字符串输出,内容为“插入或更新记录‘键’到向量数据库” 。 }
等待任务,延迟,以毫秒为单位的,批次之间的延迟时长,那么久 。 } }
私有的静态的可枚举类型,用于加载所有文本,给定一个字符串类型的PDF路径 ,叫LoadAllTexts ,有这样一个方法 ,此输入参数为pdfPath ,其返回值为可枚举类型 ,由这一静态方法返回。 {以(通过使用)Pdf Document(文档),以(借助那种方式)打开(开启)pdf Path(路径),从而(进而)形成(达成)document(文档) ,标点符号:句号。 {针对文档中获取的页面集合,对每一个页面实例进行遍历操作,其中页面要用“Page”类型来表示 。 {有一个变量叫作blocks,它是通过DefaultPageSegmenter这个实例的GetBlocks方法获取的,而这个方法的参数是page.GetWords 。 foreach (var block in blocks) } } }}

(2)

和介绍的内容类似,主要做语义搜索,获取TopN个近似内容。

public class VectorDataSearcher,其中TKey类型要求不能为空 ,。{    private readonly IVectorStoreRecordCollection    private readonly IEmbeddingGeneratorstring, Embeddingfloat>> _embeddingGenerator;
共众李,乌克特儿,歪克托儿,达塔,搜儿彻儿,艾乌一,歪克托儿,斯多儿,瑞可哦得,可哦勒立克,可哦勒克申 (你提供的内容看起来似乎。 { _vectorStoreRecordCollection = vectorStoreRecordCollection; _embeddingGenerator = embeddingGenerator; }
[返回:描述(文本搜索结果的集合)],这里的内容是对某种返回信息所做的描述,表明其为文本搜索结果的集合 。 public async Task {获取变量查询嵌入,这个动作借助等待的方式来实现,它是通过调用嵌入生成器的生成嵌入向量异步方法,以查询作为参数来达成的 。从向量数据存储中进行查询,用于获取相关信息,以此来满足特定需求,进而辅助决策,最终达成相应目标,标点符号。令变量searchOptions等于经由构造函数VectorSearchOptions所创建的一个新实例,此实例用于向量搜索相关的操作 。 { Top = topN,Vector属性名称等于文本片段的名称,其中文本片段是指用特定方式表示的一段文本内容,而名称则是用于标识该文本片段的特定标识符,这里的名称是。 };变量取名为搜索结果,它被用于一个等待异步的态势,在于获取向量存储记录集合经过用查询嵌入进行矢量化搜索后的结果,搜索时依据该有特定选项特征的搜索选项,这个进程由一个符号为下划线的向量存储记录集合来执行,并且是一个异步操作,最终向量存储将承载此搜索结果,以一种被叫做矢量化搜索的方式,向我们提供符合情况的数据,这整个过程是通过调用向量存储记录集合上名为矢量化搜索异步的方法来达成的,而这个方法。让人费解的是,有这样一个“var”,它所对应的“responseResults”是被创建出来作为“new”使用的,而这个“new”所关联的。等待着,对于搜索结果的结果中的每个结果,而进行循环。对于每个结果,在搜索结果的结果之中,进行等待着的循环。 {响应结果添加,一个文本搜索结果,新的一个 ,被添加进去 。 {如果结果记录的文本不为空,那么值就等于结果记录的文本,否则值就等于空字符串 。 这当中,结果记录与文本之间存在着一种特定的关联关系,通过这种。LINK等于RESULT记录的REFERENCELINK,否则就等于空字符串, 。 Score = result.Score }); }
return responseResults; }}

(3)

这个主要是一个代理,后续我们主要使用Guid作为Key。

你提供的内容似乎不完整且存在莫名的格式混嵌,不太能准确理解其完整意图并进行改写,请补充完整准确的内容以便我按照要求进行改写 。    where TKey : notnull{    ///     /// Generate a unique key.    /// 生成的那个唯一代码,有着独一无二的特性,借助特定方式或者流程,在特定环境或者条件下,通过特定工具或者手段创造出来,呈现出独有的属性,表明其独一无二的。public TKey 创建密钥,并且这个密钥是由生成器赋值的,其结果就是生成器所产生的那个值 。}

串联实现RAG问答

安装NuGet包:

微软的,扩展人工智能的那个,预览版的,东西 。用于Microsoft的,隶属于扩展范畴的,Ollama相关的,涉及预览版本的 。微软的,扩展的,人工智能相关的,开放人工智能(预览版) 。微软的,扩展的,矢量数据的,抽象层(预览版) ,这几个部分组合起来的词汇 ,有特定含义 。微软的语义内核的连接器中的矢量子空间索引引擎(预览版) ,这是一个用于存储键值数据的分布式向量数据库 ,通过逼近最优搜索算法 ,可以高效地处理相似性搜索 ,支持向量搜索和。PdfPig (0.1.9)微软的、扩展配置的、那个(8.0.0版本号对应的)组件 ,有了这样的版本 ,它是8.0.0版本 ,是此版本的。属于Microsoft.Extensions.Configuration.Json的版本是8.0.0 。

下面我们分解几个核心步骤来实现RAG问答。

Step1. 配置文件.json:

{  "LLM": {“端点”是“https://api.siliconflow.cn” ,句号。这儿有个“ApiKey”,它是“sk - 再替换成你的字符串那一大长串,具体是星号表示的内容”,要注意把它替换成你自身的ApiKey 。"模型标识为","Qwen/Qwen2.5减7B带指令" 。  },  "Embeddings": {    "Ollama": {“EndPoint”,它是这样的,是“http://localhost:11434” 。      "ModelId": "bge-m3"    }  },  "VectorStores": {    "Qdrant": {      "Host": "edt-dev-server",      "Port": 6334,      "ApiKey": "EdisonTalk@2025"    }  },  "RAG": {称作“CollectionName”的,是“oneflower” ,。    "DataLoadingBatchSize": 10,在不同批次之间进行数据加载时所产生的延迟,以毫秒为单位,该延迟时长为1000 , , 。    "PdfFileFolder": "Documents"  }}

Step2. 加载配置:

哎,你看哦,有这么个情况哈,就是呢,有一个被称作var的东西,它被用来定义了一个名为config的某项,然后呢,这个。添加,文件资源(格式)为 JSON种类的数据文档,选取这个被称为 "appsettings.json"的数据文档 。    .Build;

Step3. 初始化、生成器 以及 :

# ChatClient变量“apiKeyCredential”,它是通过使用“ApiKeyCredential”这个构造函数,并传入配置中索引为“LLM:ApiKey”的值来创建的 ,。设,一个变量,名为aiClientOptions,其类别,系新创建的,属于OpenAIClientOptions类型。终结点,处于aiClientOptions之中,它被设置为,一个新的统一资源标识符,此标识符源自配置里的LLM:EndPoint所对应的值。创建一个变量,名为aiClient,它被赋予一个新值,该值是通过使用OpenAIClient创建出来的,而创建OpenAIClient时使用了apiKeyCredential以及aiClientOptions 。要按照特定格式创建聊天客户端,使用配置里指定的大语言模型模型标识符 。有一种变量,名为聊天客户端,它是通过一个新的聊天客户端构建器创建出来的,而这个构建器是基于人工智能客户端构建的 。    .UseFunctionInvocation    .Build;# EmbeddingGeneratorvar embedingGenerator =构造一个新的,名为OllamaEmbeddingGenerator的对象,其参数为一个Uri对象,该Uri对象由配置项中Embeddings:Ollama:EndPoint的值构建而成。同时,还传入了配置项中Embeddings:Ollama:ModelId的值作为参数。# VectorStorevar vectorStore = 创建一个新的Qdrant向量存储对象,此对象基于一个新的Qdrant客户端,该客户端的主机地址由配置中的“VectorStores:Qdrant:Host”获取,端口号由配置中的“VectorStores:Qdrant:Port”转换为整数后获取,API密钥则由配置中的“VectorStores:Qdrant:ApiKey”获取。

Step4. 导入PDF文档到:

变量ragConfig,是通过config获取名为“RAG”的配置节而得到的 。拿来那个独特的密钥生成器,那个与众不同的密钥生成器,独一无二的密钥生成器。创建了变数,这个变数指定为,一个名为独特密钥生成器的新实例 ,的那个独特密钥生成器 。,产生独特密钥,是它所做的事 。获取,即取得,在qdrant里面的,那个集合,借助某种方式,通过特定途径去实现 。假设有一个变量,名为ragVectorRecordCollection,它所指向的是,通过vectorStore这个对象,调用其GetCollection方法所获取到的内容 。// Get the PDF loader创一个变量,名为pdfLoader,且使其具有一个新建物,叫做PdfDataLoader且为此新建物赋予新的属性 。先是着手开启,将PDF加载至,向量存储当中,有这样的一个过程,有这样的这么些行为,存在那样的动作。把变量pdfFilePath设定为,rage配置中的,名为PdfFileFolder的这项内容啊。使得变量表示为多个pdf文件所得结果是,通过目录获取文件,这些文件的路径是pdf文件路径 。try{逐一获取,针对其中每一个,处于众多PDF文件里的,那个PDF文件 ,进行后续操作 。    {把那条语句改写为:“控制台输出这一行内容,内容是,依照“[LOG] Start Loading PDF into vector store: {pdfFile}”这个格式进行占位输出,其中{pdfFile}是一个占位符,要替换为实际的PDF文件名称,输出时包括方括号、LOG、冒号、空白、开始加载PDF到向量存储、冒号以及实际的。        await pdfLoader.LoadPdf( pdfFile,将 ragConfig 里的 "DataLoadingBatchSize" 进行 int.Parse 操作组成,。将 ragConfig 中获取到的 "DataLoadingBetweenBatchDelayInMilliseconds" 这个值,转化为整数类型 ,这里面是 int.Parse 方法在起作用 。在控制台输出日志,内容是已完成将PDF文件加载到向量存储中,这个PDF文件是特定的那个,标点符号是这样使用进行分隔的,控制台输出的格式要按照特定方式,就是先写[LOG],然后是完成加载PDF到向量存储的相关文本表述,最后是具体的PDF文件名并使用大括号包围起来,标点符号要是英文状态下的,文本。    }将所有的便携文档格式文件加载到向量存储中,全都成功了哟,用这一行语句进行输出,它的格式是这样的,在控制台上写入文字,文字内容是用方括号括起来的大写的LOG,然后紧跟一个冒号,再接着是一个包含双引号的字符串,字符串里写着所有的便携文档。}catch (Exception ex){    return;}

Step5. 构建AI对话机器人:

如有侵权请联系删除!

13262879759

微信二维码