一、前言:为什么要学习RAG?
在AI大模型(LLM)快速发展的今天,我们在使用LLM时会遇到一个核心痛点------幻觉问题。LLM的知识来源于训练阶段输入的数据集,其知识范围被局限在训练数据的时间范围和内容覆盖内,当我们向LLM提出其未学习过的问题时,它不会直接告知"不知道",而是会"认认真真地胡乱回答",这种看似合理却与事实不符的输出,就是LLM的幻觉。
幻觉问题严重影响了LLM在专业领域、企业场景中的应用,尤其是在需要精准信息输出的场景(如企业知识库查询、专家咨询、学术问答等),幻觉可能会导致严重的决策失误。而RAG技术的出现,正是为了解决LLM的幻觉问题,通过"检索+生成"的结合,让LLM能够基于真实、精准的外部知识进行回答,既保留了LLM的语言生成能力,又保证了输出内容的准确性和可靠性。
本次学习将围绕RAG的核心知识展开,从基础概念、核心组件、向量原理,到实操代码解析,全面拆解RAG技术,帮助自己建立系统的RAG学习框架,为后续的实际应用和深入学习打下基础。
二、LLM基础认知(RAG学习前置知识)
2.1 LLM的知识来源
LLM(大语言模型)的知识完全来源于训练阶段所使用的数据集。在模型训练过程中,开发者会向模型输入海量的文本数据(如书籍、网页、论文、对话记录等),模型通过对这些数据的学习,掌握语言的语法、语义、逻辑关系,以及各类知识(如常识、专业知识、事实信息等)。
需要注意的是,LLM的知识具有时效性和局限性:一方面,模型的知识截止到训练数据的最后更新时间,无法获取训练完成后出现的新信息(如2024年训练的模型,无法知晓2025年的新事件);另一方面,训练数据的覆盖范围有限,对于一些小众领域、私有领域的知识(如企业内部的规章制度、特定行业的专业技术细节),LLM往往无法覆盖,这也是导致LLM出现幻觉的重要原因之一。
2.2 LLM的幻觉问题详解
LLM的幻觉,本质上是模型在面对未知问题时,基于自身已学习的知识进行"合理推测",但这种推测缺乏真实依据,最终导致输出内容与事实不符。结合参考资料中的定义,幻觉的核心特征的是:当询问LLM不知道的问题时,它会"认认真真地胡乱回答"------即输出的内容语言流畅、逻辑连贯,看起来非常合理,但实际上是错误的、无依据的。
举例来说,如果向未学习过"光光和东东"相关故事的LLM提问"光光擅长什么?",LLM可能会回答"光光擅长画画",这种回答看似合理(符合一般儿童的兴趣爱好),但与真实事实不符,这就是典型的幻觉。而RAG技术通过引入外部检索机制,让LLM在回答问题前,先从指定的知识库中检索相关信息,再基于检索到的真实信息进行生成,从而从根本上解决幻觉问题。
2.3 解决LLM幻觉的核心思路
参考资料中明确提到,RAG是解决LLM幻觉的有效方案,其核心思路围绕"检索增强"展开,主要包含三个关键环节(参考资料中提及的核心要点补充完善):
- LLM Thinking Planning(LLM思考规划):当接收到用户的提问后,LLM首先会对问题进行分析,明确问题的核心需求,规划检索的方向和范围,确保检索的内容能够精准匹配问题需求。这一步是RAG的"大脑",决定了检索的效率和准确性。
- 检索增强(Retrieval Augmentation):这是RAG的核心环节。通过检索器(Retriever)从外部知识库中,检索与用户问题最相关的内容片段,将这些片段作为"参考资料"提供给LLM,让LLM的回答有章可循、有据可依。
- Augment Prompt(提示词增强):将检索到的相关内容片段,与用户的原始提问结合,构建一个更完整、更具体的提示词(Prompt),再将这个增强后的提示词输入给LLM,引导LLM基于检索到的真实信息进行回答,避免凭空生成内容。
补充说明:如果检索器在知识库中没有匹配到与用户问题相关的内容,此时RAG会引导LLM输出"不知道"或"没有相关信息",而不是让LLM胡乱回答,这也是RAG解决幻觉问题的关键逻辑之一。
三、RAG核心知识详解
3.1 RAG的定义
RAG(Retrieval-Augmented Generation,检索增强生成)是一种结合了"信息检索"和"语言生成"的AI技术,其核心逻辑是:在LLM生成回答之前,先从外部知识库中检索与问题相关的真实信息,将这些信息作为上下文补充到提示词中,再让LLM基于补充后的上下文生成回答。
简单来说,RAG相当于给LLM配备了一个"外挂知识库",让LLM在回答问题时,能够"查阅资料",而不是单纯依靠自己训练时记住的知识,从而有效解决LLM的幻觉问题,同时拓展LLM的知识范围(无需重新训练模型,只需更新知识库即可获取新信息)。
3.2 RAG的核心组件及功能
参考资料中详细拆解了RAG的核心组件,主要包括Retriever(检索器)、知识库(Knowledge Base)、Augment(增强)、Generator(生成器)四个部分,四个组件相互配合,构成了RAG的完整工作流程。下面对每个组件进行详细解析:
3.2.1 Retriever(检索器):RAG的"搜索引擎"
Retriever是RAG的核心检索组件,其作用是接收用户的原始提问,从知识库中检索出与问题最相关的内容片段。参考资料中提到,检索器的工作依赖于"向量嵌入"和"相似度计算",其核心工作流程如下:
- 原始Prompt Embedding(原始提问嵌入):首先将用户的原始提问(Prompt)转换为向量形式(即嵌入向量),这个过程通过嵌入模型(如参考资料中的OpenAIEmbeddings)完成,目的是将文本形式的提问,转化为计算机能够理解和计算的数字向量。
- 知识库提前Embedding(知识库嵌入):知识库中的所有内容,在使用前会提前通过嵌入模型转换为向量形式,并存储在向量数据库中。这样做的目的是,方便后续快速进行相似度计算,提高检索效率。
- Cosine相似度计算(余弦相似度计算):检索器通过计算"原始提问向量"与"知识库中各内容向量"的余弦相似度,判断两者的相关程度。余弦相似度的取值范围在[-1,1]之间,相似度越接近1,说明两者的语义越相似;越接近-1,说明语义越不相关;接近0则说明两者无明显关联。检索器会根据相似度排序,筛选出与提问最相关的内容片段(如参考资料中设置的k=3,即检索出3个最相关的片段)。
补充说明:检索器的检索效率和准确性,直接决定了RAG的整体性能。如果检索器无法检索到相关内容,即使LLM的生成能力再强,也无法输出准确的回答;如果检索到的内容与问题相关性较低,也会影响LLM的回答质量。
3.2.2 知识库(Knowledge Base):RAG的"资料宝库"
知识库是RAG的信息来源,相当于LLM的"外挂资料库",其核心作用是存储需要被检索的真实信息,为LLM提供回答依据。参考资料中对知识库的描述较为详细,结合自身理解,将知识库的核心特征和分类整理如下:
(1)知识库的核心特征
- 多样性:知识库可以包含多种类型的文件,如txt、pdf、MP3、video等,只要能够提取出文本信息(MP3、video需先进行语音转文字、字幕提取),都可以作为知识库的内容。
- 碎片化:对于篇幅较大的文件(如长篇文档、厚书),需要进行切片处理,将其拆分为多个文档碎片(Document)。这样做的目的是,避免因文件过大导致嵌入向量不准确,同时提高检索的精准度------用户的提问往往只涉及文件中的某个片段,碎片化后可以直接检索到具体的相关片段,而无需加载整个大文件。
- 嵌入化:知识库中的所有内容(包括碎片化后的文档),都需要通过嵌入模型转换为向量形式,存储在向量数据库中,以便检索器进行相似度计算。
(2)知识库的常见分类
- 专家知识库:包含某一领域的专业知识,如医学、法律、工程等领域的专家经验、学术论文、行业标准等,适用于专业领域的问答场景。
- 企业私有/安全知识库:包含企业内部的私有信息,如规章制度、员工手册、客户数据、业务资料等,这类知识库通常具有保密性要求,适用于企业内部的问答、培训等场景。
- 通用知识库:包含各类通用知识,如常识、历史、文化、科技等,适用于日常问答场景。
参考资料中给出了一个具体的知识库示例------光光和东东的友情故事,该知识库包含7个Document(文档碎片),每个Document都包含pageContent(文档内容)和metadata(元数据),其中元数据用于对文档内容进行分类标注(如章节、角色、类型、情绪),方便后续检索时进行精准筛选。
3.2.3 Augment(增强):RAG的"提示词优化器"
Augment(增强)是连接检索器和生成器的关键环节,其核心作用是将检索器检索到的相关文档片段,与用户的原始提问结合,构建一个增强版的提示词(Prompt)。
具体来说,增强的过程是:将检索到的多个相关文档片段,按照相似度排序,依次添加到原始提问的后面,同时标注每个片段的序号和内容,让LLM能够清晰地看到参考资料。这样做的目的是,为LLM提供明确的回答依据,引导LLM基于这些参考资料进行生成,避免凭空捏造内容。
参考资料中的实操代码的,就体现了Augment的过程:将检索到的retrievedDocs(相关文档),通过map方法整理为"片段+内容"的格式,再用分隔符连接,最终整合到Prompt中,形成增强后的提示词。例如:
片段 1:光光是一个活泼开朗的小男孩,他有一双明亮的大眼睛,总是带着灿烂的笑容。光光最喜欢的事情就是和朋友们一起玩耍,他特别擅长踢足球,每次在球场上奔跑时,就像一道阳光一样充满活力。
片段 2:东东是光光最好的朋友,他是一个安静而聪明的男孩。东东喜欢读书和画画,他的画总是充满了想象力。虽然性格不同,但东东和光光从幼儿园就认识了,他们一起度过了无数个快乐的时光。
将这些片段与原始提问"光光和东东各自的优势是什么?"结合,就构成了增强后的提示词,为LLM的回答提供了明确的依据。
3.2.4 Generator(生成器):RAG的"回答输出器"
Generator(生成器)通常由LLM担任,其核心作用是接收增强后的提示词,基于提示词中的原始提问和检索到的参考资料,生成准确、流畅、符合需求的回答。
与单纯使用LLM不同,RAG中的生成器(LLM)不需要依靠自身的训练知识进行回答,而是以检索到的参考资料为核心依据,结合自身的语言生成能力,将参考资料中的信息进行整理、归纳、润色,最终输出符合用户需求的回答。如果参考资料中没有相关信息,生成器会按照预设的规则,输出"这个故事里没有提到这个细节",从而避免幻觉。
参考资料中的实操代码的,使用ChatOpenAI作为生成器,通过invoke方法接收增强后的提示词,生成温暖生动的回答,完美体现了Generator的功能。
3.3 RAG的完整工作流程(整合梳理)
结合上述四个核心组件,将RAG的完整工作流程梳理如下,确保每个环节的逻辑连贯,贴合参考资料中的内容:
-
用户提问:用户向RAG系统输入具体的问题(如"光光和东东各自的优势是什么?")。
-
LLM思考规划:RAG系统中的LLM对用户的问题进行分析,明确问题的核心需求(如需要找出光光和东东各自的优势),规划检索方向(从光光和东东的友情故事知识库中检索相关内容)。
-
检索器工作:
- 将用户的原始提问转换为嵌入向量(通过嵌入模型);
- 从向量数据库中,检索与提问向量余弦相似度最高的N个文档片段(如参考资料中的k=3);
- 将检索到的文档片段返回给增强环节。
-
提示词增强:将检索到的N个文档片段,与用户的原始提问结合,构建增强版的提示词,明确告知LLM"基于以下故事片段回答问题"。
-
生成器生成回答:LLM接收增强后的提示词,基于其中的参考资料,整理归纳出符合问题需求的回答,同时确保回答语言流畅、准确,若没有相关信息则输出预设提示。
-
输出结果:将LLM生成的回答返回给用户,完成一次RAG问答流程。
补充说明:RAG的工作流程是一个闭环的过程,其中任何一个环节出现问题,都会影响最终的回答质量。例如,检索器检索到不相关的片段,会导致LLM输出错误的回答;增强环节整理的片段不清晰,会导致LLM无法准确提取关键信息。因此,每个组件的优化都至关重要。
四、向量表达与语义搜索(RAG的核心技术支撑)
参考资料中专门讲解了向量表达和语义搜索的相关知识,这是RAG能够实现精准检索的核心技术支撑------如果没有向量表达,检索器就无法实现"语义层面"的检索,只能进行简单的关键词匹配,而关键词匹配无法解决语义相似但关键词不同的问题。下面结合参考资料,详细拆解向量表达和语义搜索的核心知识。
4.1 为什么需要向量表达?------ 关键词匹配的局限性
在传统的信息检索中,人们通常使用"关键词匹配"的方式进行检索,即通过对比用户提问中的关键词与文档中的关键词,判断两者是否相关。但这种方式存在明显的局限性,无法实现"语义搜索"。
参考资料中给出了一个非常形象的例子:如果查询"文中提到的水果",关键词匹配只能检索到包含"水果"二字的文档,而无法检索到包含"苹果、香蕉、荔枝"的文档------虽然苹果、香蕉、荔枝都属于水果,语义上与"水果"高度相关,但关键词并不完全一致。这种情况下,关键词匹配就会出现漏检,无法满足用户的真实需求。
而向量表达的出现,正是为了解决这个问题。通过将文本转换为向量,能够捕捉文本的语义信息,而不仅仅是表面的关键词,从而实现"语义搜索"------即只要两个文本的语义相似,无论关键词是否一致,都能通过向量相似度计算被检索到。
4.2 向量表达的本质
向量(Vector)是一种由多个数字组成的序列,形式如[0.1, 0.2, 0.3, ...],其本质是将文本信息(或其他类型的信息)转换为计算机能够理解和计算的数字形式,每个数字(即向量的每个维度)都代表文本的一个语义特征。
参考资料中通过一个简单的例子,清晰地解释了向量表达的原理:以"食用性"和"硬度"为两个语义维度,用0-1之间的数字表示每个维度的特征,从而将不同的事物转换为向量:
- 食用性:0 = 无,1 = 高;
- 硬度:0 = 液体,1 = 骨头(此处可理解为"硬度极高");
- 水果:[0.9, 0.3] → 食用性高(0.9接近1),硬度偏低(0.3接近0);
- 苹果:[0.9, 0.4] → 食用性高,硬度中等;
- 香蕉:[0.9, 0.1] → 食用性高,硬度低;
- 石头:[0.1, 0.9] → 食用性无(0.1接近0),硬度高(0.9接近1)。
从这个例子中可以看出,语义相似的事物,其向量也会非常接近。例如,水果、苹果、香蕉的向量中,第一个维度(食用性)都接近0.9,第二个维度(硬度)都偏低,因此它们的向量相似度很高;而石头的向量与水果的向量差异很大,相似度很低。这就是向量表达能够实现语义搜索的核心逻辑。
补充说明:实际应用中,向量的维度远不止2个,通常会有几百甚至几千个维度,每个维度代表一个更细致的语义特征(如情感倾向、主题类别、语法结构等)。维度越多,向量能够捕捉的语义信息就越全面,相似度计算的准确性也就越高。
4.3 语义搜索的流程
语义搜索的核心是"向量相似度计算",结合参考资料中的内容,将语义搜索的流程拆解为三个关键步骤:
- 向量维度的语义定义:向量的每个维度都有独特的语义含义(如参考资料中的"食用性""硬度"),这些维度是由嵌入模型在训练过程中自动学习得到的,能够准确捕捉文本的各类语义特征。
- 向量空间可视化:将所有文本的向量都映射到一个多维空间中,语义相似的文本向量会聚集在同一区域,语义不相关的文本向量会分布在不同的区域。这种可视化方式,能够直观地反映出文本之间的语义关系。
- 余弦相似度计算:通过计算两个向量之间的余弦相似度,判断它们的语义相关程度。余弦相似度的计算逻辑是:两个向量的夹角越小,余弦相似度值越接近1,语义越相似;夹角越大,余弦相似度值越接近-1,语义越不相关。
结合RAG的检索环节,语义搜索的流程可以进一步细化为:用户提问→提问向量化→与知识库中所有文档向量计算余弦相似度→筛选出相似度最高的N个文档→返回给增强环节。可以说,语义搜索是RAG检索器能够实现精准检索的核心,没有语义搜索,RAG就无法解决LLM的幻觉问题。
4.4 向量数据库的作用
参考资料中提到了"内存向量数据库"(如MemoryVectorStore),其核心作用是存储知识库中所有文档的嵌入向量,为检索器的相似度计算提供高效的支持。
与传统的关系型数据库(如MySQL)不同,向量数据库专门用于存储和管理向量数据,能够高效地执行向量相似度计算、向量检索等操作。对于包含大量文档的知识库来说,如果没有向量数据库,检索器需要逐个计算提问向量与每个文档向量的相似度,效率极低;而向量数据库能够通过优化算法,快速筛选出与提问向量最相似的文档,大幅提升检索效率。
参考资料中的实操代码的,使用MemoryVectorStore作为向量数据库,通过fromDocuments方法,将知识库中的文档(documents)和嵌入模型(embeddings)传入,自动完成文档的向量转换和存储,为后续的检索操作提供支持。
五、RAG实操代码解析(基于参考资料)
参考资料中提供了一段完整的RAG实操代码,基于LangChain框架和OpenAI模型,实现了"光光和东东友情故事"的RAG问答功能。下面结合代码,逐行解析每个模块的功能、作用及逻辑,帮助自己理解RAG的实际应用方式。
5.1 代码前置准备:环境与依赖
代码的开头部分,主要是导入所需的依赖包和环境配置,具体解析如下:
javascript
import "dotenv/config";
import {
ChatOpenAI, // 聊天模型(作为RAG的Generator)
OpenAIEmbeddings, // 嵌入模型(用于将文本转换为向量)
} from "@langchain/openai"
// 知识库中一段知识的抽象概念
import {
Document,
} from "@langchain/core/documents";
// 内存向量数据库
import {
MemoryVectorStore, // 内存向量数据库(用于存储文档向量)
} from "@langchain/classic/vectorstores/memory";
解析:
- import "dotenv/config":导入环境变量配置,用于加载.env文件中的配置信息(如OpenAI的API密钥、模型名称、基础URL等),避免将敏感信息硬编码在代码中,提高代码的安全性和可维护性。
- ChatOpenAI:从LangChain的OpenAI模块中导入聊天模型,用于作为RAG的Generator(生成器),接收增强后的提示词,生成回答。
- OpenAIEmbeddings:导入嵌入模型,用于将用户提问和知识库文档转换为向量,为检索环节提供支持。
- Document:从LangChain的核心模块中导入Document类,用于定义知识库中的文档碎片------每个Document包含pageContent(文档内容)和metadata(元数据),对应参考资料中知识库的碎片化特征。
- MemoryVectorStore:导入内存向量数据库,用于存储知识库文档的嵌入向量,支持快速的向量检索和相似度计算。
5.2 模型与向量数据库初始化
这部分代码主要是初始化ChatOpenAI(生成器)、OpenAIEmbeddings(嵌入模型)和MemoryVectorStore(向量数据库),具体解析如下:
arduino
const model = new ChatOpenAI({
modelName: process.env.MODEL_NAME, // 从环境变量中获取模型名称(如gpt-3.5-turbo)
apiKey: process.env.OPENAI_API_KEY, // 从环境变量中获取OpenAI API密钥
configuration: {
baseURL: process.env.OPENAI_BASE_URL, // 从环境变量中获取OpenAI基础URL(可选,用于国内代理)
},
temperature: 0, // 温度参数,0表示输出内容更确定、更严谨,避免随机生成
})
const embeddings = new OpenAIEmbeddings({
modelName: process.env.EMBEDDINGS_API_NAME, // 从环境变量中获取嵌入模型名称(如text-embedding-ada-002)
apiKey: process.env.OPENAI_API_KEY, // OpenAI API密钥
configuration: {
baseURL: process.env.OPENAI_BASE_URL, // 基础URL
},
});
// 内存向量数据库初始化:将文档转换为向量并存储
const vectorStore = await MemoryVectorStore.fromDocuments(
documents, // 知识库文档(数组形式,包含多个Document对象)
embeddings // 嵌入模型
);
解析:
-
ChatOpenAI初始化:
- modelName:指定使用的OpenAI聊天模型,如gpt-3.5-turbo、gpt-4等,通过环境变量加载,方便后续修改。
- apiKey:OpenAI的API密钥,用于调用OpenAI的模型接口,必须保密,通过环境变量加载。
- configuration.baseURL:可选参数,用于设置OpenAI接口的基础URL,适合国内用户使用代理服务器调用接口。
- temperature:温度参数,取值范围0-1,温度越低,输出内容越确定、越严谨,不易出现幻觉;温度越高,输出内容越随机、越有创造性。此处设置为0,符合RAG精准回答的需求。
-
OpenAIEmbeddings初始化:与ChatOpenAI类似,需要指定嵌入模型名称、API密钥和基础URL,用于将文本转换为向量。常用的嵌入模型是text-embedding-ada-002,性价比高,适合大多数场景。
-
MemoryVectorStore初始化:通过fromDocuments方法,将知识库文档(documents数组)和嵌入模型(embeddings)传入,该方法会自动将每个Document的pageContent转换为向量,并存储在内存向量数据库中,完成知识库的向量化和存储。
5.3 知识库定义(documents数组)
参考资料中的documents数组,是RAG的知识库核心内容,包含7个Document对象,每个对象对应一个故事片段,具体解析如下:
go
const documents = [
new Document({
pageContent: `光光是一个活泼开朗的小男孩,他有一双明亮的大眼睛,总是带着灿烂的笑容。光光最喜欢的事情就是和朋友们一起玩耍,他特别擅长踢足球,每次在球场上奔跑时,就像一道阳光一样充满活力。`,
metadata: {
chapter: 1,
character: "光光",
type: "角色介绍",
mood: "活泼"
},
}),
// 其余6个Document对象省略(与第一个结构一致)
];
解析:
- pageContent:文档的核心内容,即故事片段,是检索和生成回答的核心依据。每个pageContent对应一个碎片化的故事片段,长度适中,便于向量转换和精准检索。
- metadata:文档的元数据,用于对文档内容进行分类标注,包含chapter(章节)、character(角色)、type(类型)、mood(情绪)四个字段。元数据的作用是,在检索时可以根据元数据进行筛选(如只检索角色为"光光"的文档),进一步提高检索的精准度。
补充说明:documents数组的长度和内容可以根据实际需求调整,只要是符合Document类格式的对象,都可以作为知识库的内容。对于大量文档,可以通过批量导入的方式生成documents数组,无需手动逐个创建。
5.4 检索器配置与检索操作
这部分代码主要是配置检索器(Retriever),并执行检索操作,获取与用户提问最相关的文档片段,具体解析如下:
javascript
// 检索器配置:k表示返回多少个最相关的文档片段
const retriever = vectorStore.asRetriever({ k: 3 });
const questions = ["光光和东东各自的优势是什么?"];
for (const question of questions) {
console.log("=".repeat(80));
console.log(`问题:${question}`);
console.log("=".repeat(80));
// 执行检索:获取与问题最相关的3个文档片段
const retrievedDocs = await retriever.invoke(question);
console.log(retrievedDocs);
// 执行相似度搜索,并获取相似度评分
const storeResults = await vectorStore.similaritySearchWithScore(question, 3);
console.log(storeResults);
// 打印检索到的文档及相似度信息
console.log("\n [检索到的文档及相似度评分]");
retrievedDocs.forEach((doc, i) => {
const scoreResult = storeResults.find(
([scoreDoc]) => scoreDoc.pageContent === doc.pageContent
);
const score = scoreResult ? scoreResult[1] : null;
const similarity = score ? (1 - score).toFixed(2) : "N/A";
console.log(`\n 文档 ${i+1} 相似度:${similarity}`);
console.log(`内容:${doc.pageContent}`);
console.log(`元数据:${JSON.stringify(doc.metadata)}`);
})
解析:
-
检索器配置:通过vectorStore.asRetriever()方法,将向量数据库转换为检索器,其中k: 3表示检索器返回3个与提问最相关的文档片段。k的值可以根据实际需求调整,k越大,检索到的相关文档越多,但可能会包含不相关的内容;k越小,检索越精准,但可能会漏检相关内容。
-
检索操作:
- retriever.invoke(question):调用检索器的invoke方法,传入用户提问,执行检索操作,返回检索到的3个最相关的文档片段(retrievedDocs)。
- vectorStore.similaritySearchWithScore(question, 3):调用向量数据库的相似度搜索方法,传入用户提问和k值,返回包含文档和相似度评分的数组(storeResults),其中评分越小,说明语义相似度越高(不同向量数据库的评分规则可能不同,此处需注意)。
-
结果打印:通过forEach循环,遍历retrievedDocs,获取每个文档的相似度评分(将storeResults中的评分转换为0-1之间的相似度值),并打印文档的内容和元数据,方便查看检索结果的准确性。
5.5 提示词增强与回答生成
这部分代码是RAG的核心环节,将检索到的文档片段与原始提问结合,构建增强版提示词,然后调用生成器(ChatOpenAI)生成回答,具体解析如下:
javascript
// 构建增强版提示词
const context = retrievedDocs
.map((doc,i) => `片段 ${i+1}\n${doc.pageContent}`)
.join("\n\n----\n\n");
const prompt = `
你是一个讲友情故事的老师。
基于一下故事片段回答问题,用温暖生动的语言。
如果故事中没有提及,就是"这个故事里没有提到这个细节"
故事片段:
${context}
问题:
${question}
老师的回答:
`;
// 调用生成器生成回答
console.log("\n [AI 回答]");
const response = await model.invoke(prompt);
console.log(response.content);
console.log("\n");
解析:
-
构建增强上下文(context):通过map方法,将retrievedDocs中的每个文档片段整理为"片段+序号+内容"的格式,再用"----"分隔符连接,形成完整的上下文内容。这样做的目的是,让LLM能够清晰地看到每个检索到的片段,便于提取关键信息。
-
构建增强提示词(prompt):提示词包含三个核心部分:
- 角色设定:"你是一个讲友情故事的老师",明确LLM的回答风格(温暖生动)。
- 回答规则:"基于以下故事片段回答问题""没有提及则输出指定提示",避免LLM凭空生成内容,确保回答的准确性。
- 上下文和问题:将整理好的context(检索到的文档片段)和用户的原始提问,嵌入到提示词中,为LLM提供回答依据。
-
生成回答:调用model.invoke(prompt)方法,将增强后的提示词传入ChatOpenAI模型,模型基于提示词中的上下文和问题,生成温暖生动的回答,最后打印回答内容。
5.6 代码运行结果分析(结合参考资料)
结合参考资料中的代码逻辑,当运行该代码时,会输出以下结果:
- 打印用户提问:"光光和东东各自的优势是什么?"。
- 打印检索到的3个最相关的文档片段(如片段1:光光的角色介绍,片段2:东东的角色介绍,片段4:光光教东东踢足球、东东给光光画画的情节)。
- 打印每个文档的相似度评分,例如片段1的相似度可能为0.98,片段2的相似度为0.97,片段4的相似度为0.95,说明这三个片段与提问的语义高度相关。
- 打印AI生成的回答,例如:"光光的优势是活泼开朗,擅长踢足球,而且非常有耐心、乐于帮助朋友;东东的优势是安静聪明,喜欢读书和画画,想象力丰富,并且懂得感恩和回报朋友。" 这个回答完全基于检索到的文档片段,没有出现幻觉,符合RAG的核心目标。
补充说明:如果用户的提问是"光光和东东后来成为了什么职业?",检索器会检索到片段7(尾声),LLM会基于该片段生成"光光成为了职业足球运动员,东东成为了优秀的插画师"的回答;如果用户的提问是"光光喜欢吃什么水果?",检索器无法检索到相关文档,LLM会输出"这个故事里没有提到这个细节",完美避免了幻觉。
六、学习总结与拓展思考
6.1 学习总结
通过本次学习,我全面掌握了RAG技术的核心知识、向量表达的原理,以及RAG的实操流程,具体总结如下:
- 核心认知:RAG是解决LLM幻觉的有效方案,其核心逻辑是"检索+生成",通过引入外部知识库,让LLM基于真实信息进行回答,既保留了LLM的语言生成能力,又保证了输出的准确性。
- 核心组件:RAG包含Retriever(检索器)、知识库、Augment(增强)、Generator(生成器)四个核心组件,四个组件相互配合,构成完整的工作流程------用户提问→思考规划→检索→增强提示词→生成回答。
- 核心技术:向量表达和语义搜索是RAG的核心技术支撑,通过将文本转换为向量,捕捉文本的语义信息,再通过余弦相似度计算,实现精准的语义检索,解决了传统关键词匹配的局限性。
- 实操能力:通过解析参考资料中的代码,掌握了基于LangChain和OpenAI模型的RAG实操方法,理解了各模块的功能和联动逻辑,能够看懂并运行简单的RAG代码。
6.2 拓展思考
在学习过程中,结合参考资料中的内容,我也产生了一些拓展思考,为后续的深入学习提供方向:
- RAG的优化方向:参考资料中的RAG代码使用的是内存向量数据库(MemoryVectorStore),适用于小规模知识库;对于大规模知识库,如何选择更高效的向量数据库(如Pinecone、Chroma等)?如何优化检索器的检索效率和准确性?
- 嵌入模型的选择:参考资料中使用的是OpenAI的嵌入模型,除了该模型,还有哪些开源的嵌入模型(如BERT、Sentence-BERT)?不同嵌入模型的性能、效率、成本有何差异?如何根据实际场景选择合适的嵌入模型?
- 知识库的维护:知识库的内容需要定期更新,如何实现知识库的自动更新?如何处理重复的文档内容?如何对知识库进行分区管理(如按领域、按权限)?
- RAG的应用场景:参考资料中的RAG应用于故事问答场景,除此之外,RAG还可以应用于哪些场景?如企业知识库问答、学术论文检索、客服对话、智能助手等,不同场景下的RAG配置有何差异?
- LLM与RAG的结合优化:如何优化增强提示词的格式,让LLM更精准地提取检索到的信息?如何结合LLM的思考能力,进一步优化检索方向,提高检索的精准度?
6.3 后续学习计划
基于本次学习的内容和拓展思考,后续将重点开展以下学习:
- 深入学习LangChain框架:LangChain是RAG开发的常用框架,后续将系统学习LangChain的核心功能、组件和API,掌握更复杂的RAG开发方法。
- 学习向量数据库的使用:重点学习Pinecone、Chroma等常用向量数据库的使用方法,掌握大规模知识库的存储和检索优化技巧。
- 实操练习:基于本次学习的代码,尝试修改和拓展,如更换知识库内容、调整检索参数、更换嵌入模型,观察不同配置对RAG性能的影响。
- 了解RAG的前沿技术:关注RAG的最新研究成果和技术趋势,如多模态RAG(结合文本、图片、语音等多模态数据)、RAG与Agent的结合等,拓宽自己的知识视野。
七、附录:关键术语对照表
为了方便后续复习,将本次学习中涉及的关键术语整理如下,结合参考资料中的定义,确保准确无误:
| 术语 | 英文全称 | 核心定义 |
|---|---|---|
| RAG | Retrieval-Augmented Generation | 检索增强生成,结合信息检索和语言生成的AI技术,用于解决LLM的幻觉问题。 |
| LLM | Large Language Model | 大语言模型,通过海量文本训练,具备强大的语言生成和理解能力,知识来源于训练数据集。 |
| 幻觉 | Hallucination | LLM在面对未知问题时,凭空生成看似合理但与事实不符的回答的现象。 |
| 检索器 | Retriever | RAG的核心组件,负责从知识库中检索与用户提问最相关的内容片段,依赖向量嵌入和相似度计算。 |
| 知识库 | Knowledge Base | RAG的信息来源,存储需要被检索的真实信息,可包含多种类型的文件,需进行碎片化和嵌入化处理。 |
| 向量嵌入 | Embedding | 将文本等信息转换为向量形式的过程,用于捕捉文本的语义信息,为语义搜索提供支持。 |
| 余弦相似度 | Cosine Similarity | 用于计算两个向量语义相似程度的指标,取值范围[-1,1],越接近1,语义越相似。 |
| 向量数据库 | Vector Database | 专门用于存储和管理向量数据的数据库,支持高效的向量检索和相似度计算。 |
| 生成器 | Generator | RAG的核心组件,通常由LLM担任,负责基于增强后的提示词,生成准确、流畅的回答。 |