第1节:欢迎来到RAG的世界!它是什么,为什么你需要它
1.1 解码RAG
你可以简单解释想象一下,有一位非常聪明的学生(我们称之为大语言模型,LLM),他博览群书,记忆力惊人。
但是,他有一个限制:他所学的知识都截止于某个时间点,并且无法连接互联网或查阅最新的报纸。如果你问他最近发生的事情或者某个特定领域(比如你公司的内部规定)的知识,他可能就答不上来了。
检索增强生成(Retrieval-Augmented Generation, RAG) 技术就像是给了这位聪明的学生一个实时搜索引擎。在他回答你的问题之前,他可以先用这个搜索引擎查找最新、最相关的信息。
简单来说,RAG是一种优化大语言模型输出的方法,它让模型在生成回答之前,能够先从其训练数据之外的、权威的知识库中检索信息。
这个概念最早可以追溯到 2020 年由 Facebook(现 Meta)的研究人员提出。
那么,为什么需要 RAG 呢?核心问题在于大语言模型知识的“静态”特性。LLM 通过学习海量的文本数据来获得理解和生成语言的能力。
然而,这些训练数据是固定的,有明确的时间节点,并且通常不包含特定组织的内部私有信息或最新的实时资讯。这就导致了 LLM 在回答某些问题时,可能会提供过时的信息,甚至“一本正经地胡说八道”(这被称为“幻觉”现象),或者干脆承认自己不知道。
RAG 通过在生成答案之前,引入一个信息检索步骤,直接解决了这个问题。检索到的信息作为新鲜、相关的“上下文”被提供给LLM。因此,RAG 使得 LLM 能够生成更准确、更新、更贴合具体情境的回答,克服了它们知识静态的固有局限性,这对于需要高可靠性的现实世界应用至关重要。
1.2 为什么选择 RAG?为语言模型充电
不使用 RAG 的标准 LLM 有其局限性。正如前面提到的,它们可能产生“幻觉”,即编造不正确的信息。
它们无法访问实时更新的数据(比如今天的新闻)或私有信息(比如你公司的内部知识库)。
此外,当 LLM 给出一个答案时,你很难知道它是基于什么信息得出的,缺乏可验证性。RAG 通过将 LLM 的回答“锚定”在检索到的具体事实上,极大地改善了这些情况。
当 LLM 被要求基于提供的“事实”(即检索到的上下文)来回答问题时,它产生幻觉的可能性就会降低。这使得 LLM 在处理需要事实准确性的任务时更加可靠和值得信赖。
1.3 RAG 的超能力:关键优势
RAG 为 LLM 带来了许多显著的好处,使其在各种应用中表现更佳:
1️⃣获取最新信息:RAG 系统可以连接到实时更新的知识源,确保 LLM 的回答基于最新数据,而不是仅仅依赖于其可能过时的训练知识。
2️⃣减少幻觉:通过将回答建立在检索到的真实数据基础上,RAG 显著降低了 LLM “编造”信息的风险,提高了回答的事实准确性。
3️⃣答案可验证:RAG 系统通常可以提供用于生成答案的信息来源,用户可以据此进行事实核查,增加了透明度和可信度。
4️⃣领域专业知识:RAG 可以让 LLM 回答特定领域(如公司政策、特定产品信息、专业研究)的问题,即使这些知识不在其原始训练数据中。
5️⃣成本效益高: 更新 RAG 系统中的知识库远比重新训练整个 LLM 要便宜得多,也快得多。这使得保持信息更新更加经济高效。
6️⃣更强的可控性: 企业可以精确控制 RAG 系统访问的知识源,确保 LLM 只使用经过审核和批准的数据来生成回答,这对于保证信息安全和合规性非常重要。
第 2 节:RAG 如何工作:深入了解内部机制
2.1 RAG 之旅:从提问到回答
理解 RAG 的工作流程就像是了解一次信息的旅程。这个过程通常包含以下几个关键步骤:
1️⃣用户提问(Query):一切始于你向系统提出的问题或指令。例如,你可能会问:“RAG 是什么?”。
2️⃣搜索开始(Retrieval):系统接收到你的问题后,并不会立刻让 LLM 回答。相反,它首先会启动“检索”过程。系统会分析你的问题,然后在预先建立好的知识库(这可能包含各种文档、数据库记录、网页内容等)中搜索与你的问题最相关的信息。
3️⃣找到上下文(Context):检索系统会找到一些信息片段,这些片段被认为与你的问题最相关。这些信息片段被称为“上下文”。
4️⃣提示词变聪明(Augmentation):接下来是“增强”步骤。系统会将你最初的问题和刚刚检索到的上下文信息组合在一起,形成一个新的、内容更丰富的“提示词”(Prompt)发送给 LLM。这个增强后的提示词大概是这样的:“请根据以下信息回答这个问题:[你的问题]。相关信息如下:[检索到的上下文]”。
5️⃣LLM 思考(Generation):LLM 接收到这个增强后的提示词。现在,它不仅知道你的问题是什么,还拥有了专门为回答这个问题而检索到的相关背景信息。LLM 会利用这些信息来生成一个全面、准确且符合上下文的回答。
6️⃣答案送达(Response):最后,LLM 生成的回答会呈现给你。这个回答是基于检索到的信息和你原始问题的结合体,因此通常比没有 RAG 的 LLM 直接生成的回答更可靠、更相关。
整个过程是在“生成时”(generation time)动态发生的,这意味着每次提问都会触发一次新的信息检索和增强过程。
2.2 认识团队:检索器与生成器
RAG系统能够顺利工作,主要归功于两个核心组件的协同合作:检索器(Retriever)和生成器(Generator)。你可以把它们想象成一个高效的工作团队:
• 检索器(The Retriever):扮演着“图书管理员”或“搜索引擎”的角色。它的主要职责是理解用户的查询意图,并从庞大的知识库中快速、准确地找到最相关的资料片段。它不仅仅是进行简单的关键词匹配,更常用的是“语义搜索”(Semantic Search),即根据查询和文档内容的含义来查找信息,即使它们使用的词语不完全相同。
• 生成器(The Generator):扮演着“作家”或“发言人”的角色。这通常是一个强大的大语言模型(LLM),比如我们熟知的 GPT 系列、Claude、Llama 等。它的任务是接收用户的原始查询以及检索器找到的相关信息(上下文),然后将这两者融合起来,生成一个流畅、连贯、符合逻辑且能准确回答问题的文本回复。
这两个组件紧密配合:检索器负责“找对信息”,生成器负责“说好答案”。
值得注意的是,RAG 系统的最终输出质量高度依赖于检索器和生成器两者的表现。如果检索器找来的信息不相关或不完整(检索质量差),那么即使生成器再强大,也无法凭空捏造出好的答案,这会影响到诸如**上下文精确率(Context Precision)和召回率(Context Recall)**等评价指标。
反过来,如果检索器找到了完美的上下文信息,但生成器未能正确理解、有效利用这些信息,或者没能将其与用户问题流畅地结合起来(生成质量差),那么最终的答案也可能含糊不清或答非所问,这会影响到如**忠实度(Faithfulness)和答案相关性(Answer Relevancy)**等指标。
因此,优化一个 RAG 系统往往需要同时关注并改进检索和生成这两个环节,任何一个环节的短板都可能成为整体性能的瓶颈。这也是为什么像 RAGAS 这样的评估框架会分别考察检索和生成两个阶段的质量。
第 3 节:你的 RAG 工具箱:必备要素
要构建一个 RAG 系统,你需要准备一些关键的“原料”和“工具”。让我们来认识一下这些核心组成部分。
3.1 引擎:大语言模型 (LLM)
大语言模型(LLM)是 RAG 系统中的“生成器”,也就是负责最终撰写答案的“大脑”。
对于初学者来说,可以简单地将 LLM 理解为一种极其先进的文本预测系统。它们通过在海量的文本数据上进行训练,学会了理解语法、上下文、甚至一定程度的常识和推理能力。LLM 的能力非常广泛,它们可以执行各种自然语言任务,例如撰写文章、总结长篇文档、翻译语言、回答问题,甚至编写代码。
在 RAG 中,我们利用 LLM 的这些能力,让它基于我们提供的(检索到的)上下文信息来生成精确的回答。市面上有很多著名的 LLM,包括 OpenAI 的 GPT 系列(如 GPT-3.5, GPT-4)、Anthropic 的 Claude 系列、Meta 的 Llama 系列,以及许多优秀的开源模型,例如 Google 的 Flan-T5 或 BERT 等。
你可以根据自己的需求(例如性能要求、成本预算、是否需要本地部署等)来选择合适的 LLM 作为你的 RAG 系统的生成器。
3.2 理解词语的奥秘:嵌入 (Embeddings) 详解
计算机本身并不像人类一样理解词语的含义,它们更擅长处理数字。为了让计算机能够理解和比较文本的意义,我们引入了**“嵌入”**(Embeddings)的概念。
嵌入是将文本(可以是单词、句子,或者我们后面会提到的“文本块”)转换成一串数字的技术,这些数字组成一个向量(可以想象成空间中的一个点,由多个坐标值定义)。这种转换的神奇之处在于,它能够捕捉文本的语义含义或概念关系。
打个比方,就像在地图上,地理位置相近的地方(比如北京和天津)会有相似的经纬度坐标。类似地,在嵌入空间中,意义相近的词语或句子(比如“小狗”和“宠物犬”)它们的嵌入向量在数学上也会更“接近”。这种“接近度”通常用向量之间的距离或角度来衡量(例如余弦相似度)。
生成这些嵌入向量需要用到专门的“嵌入模型”。这些模型也通过在大量文本上训练学习得到。
常见的嵌入模型来源包括 OpenAI(如 text-embedding-ada-002)、Hugging Face 社区(提供了大量预训练模型,如 Sentence-Transformers 系列的 all-MiniLM-L6-v2,这是一个流行的、可以在本地免费使用的模型)等。
在 RAG 的检索环节,嵌入起着至关重要的作用。系统会将用户的查询转换成一个嵌入向量,然后去知识库中寻找那些嵌入向量与查询向量最接近的文本块。
这样就能找到与用户查询在意义上最相关的文档内容,而不仅仅是包含相同关键词的文档。对于 RAG 来说,通常使用句子嵌入(Sentence Embeddings)比简单的词嵌入(Word Embeddings)效果更好,因为句子嵌入能更好地捕捉整个句子的上下文信息,从而实现更精准的语义检索。
3.3 智能存储:向量数据库简介
有了文本的嵌入向量之后,我们需要一个地方来存储它们,并且能够高效地根据向量的相似性进行搜索。普通的数据库(如关系型数据库 SQL 或一些 NoSQL 数据库)并不擅长处理这种高维向量的相似性搜索任务。
这时,我们就需要用到“向量数据库”(Vector Database)。向量数据库是专门为存储、索引和查询高维向量而设计的数据库。它们使用特殊的索引结构和算法,比如基于图的方法(如 HNSW)或量化方法(如 Product Quantization),来实现近似最近邻(Approximate Nearest Neighbor, ANN)搜索。
ANN 搜索的目标是在巨大的向量集合中,快速找到与给定查询向量最相似的几个向量,它允许在速度和绝对精度之间做出一些权衡,但在实践中通常能以极快的速度找到非常接近的结果。
除了RAG,向量数据库还广泛应用于其他需要进行相似性搜索的场景,例如:
1️⃣推荐系统: 找到与用户喜欢的物品(如商品、电影)相似的其他物品。
2️⃣图像/音频检索: 通过上传一张图片或一段音频来搜索视觉或听觉上相似的内容。
3️⃣自然语言处理: 如文本聚类、语义搜索等。
3.4 快速比较:面向初学者的Chroma与FAISS
对于刚开始接触 RAG 并希望在本地进行开发的初学者来说,Chroma 和 FAISS 是两个常见的开源选择。它们都可以用来处理向量索引和搜索,但在设计和使用上有所不同。
对于初学者而言,选择哪种工具会影响到最初的开发体验。FAISS 本身是一个强大的库,提供了底层的索引和搜索算法,但它需要开发者自己处理数据的加载、存储以及与搜索逻辑的集成。你需要编写代码来创建索引、添加向量,并管理索引文件的保存和加载。
相比之下,Chroma 被设计成一个更完整的向量数据库,特别强调了对 RAG 应用的友好性和开发者体验。它通常会封装好存储、提供更简洁的 API,让开发者能更快地搭建起一个可用的 RAG 流程,而无需过多关注底层的数据库管理细节。
因此,如果你的目标是快速上手并构建第一个 RAG 应用,Chroma 可能会提供一个更平缓的学习曲线。当你对 RAG 流程更熟悉,或者有更复杂的性能调优需求时,再考虑使用 FAISS 这样的库可能会更合适。
第 4 节:动手实践!一步步构建你的第一个 RAG 系统
理论学习之后,是时候动手实践了!本节将指导你一步步构建一个基础的 RAG 系统。我们将使用 Python 语言,并借助流行的 RAG 框架(如 LangChain 或 LlamaIndex)来简化开发过程。
4.1 准备工作:基础 Python 环境设置
首先,你需要确保你的电脑上安装了 Python。如果你是 Python 新手,可以查找一些在线教程来安装它。为了保持项目依赖的整洁,强烈建议创建一个“虚拟环境”(Virtual Environment)来安装本项目所需的库。
接下来,你需要安装一些核心的 Python 库。打开你的终端或命令行工具,在激活虚拟环境后,使用 pip(Python 的包管理器)来安装以下库(具体库名可能略有不同,取决于你选择的框架和组件):
• RAG 框架: langchain 或 llamaindex
• LLM 接口: 如果使用 OpenAI 模型,需要 openai;如果使用 Hugging Face 的开源模型,可能需要transformers 和 torch (或其他后端)。
• 嵌入模型接口: sentence-transformers (用于 Hugging Face 模型) 或 openai。
• 向量存储: chromadb-client (如果选择 Chroma) 或 faiss-cpu (如果选择 FAISS,CPU 版本通常足够入门)。
• 文档加载器: pypdf(用于加载 PDF 文件), beautifulsoup4(用于加载网页内容) 等,根据你的数据源选择。
• 其他依赖: 可能还需要 datasets, numpy等基础库。
你可以参考一些 RAG 教程或 GitHub 项目中的 requirements.txt 文件来获取更精确的安装列表。例如,运行 pip install langchain openai chromadb-client sentence-transformers pypdf可以安装一套常用的库。
如果你对 Python 编程本身还不太熟悉,可以考虑先学习一些基础的 Python 课程,了解变量、函数、列表、字典等基本概念。
不过,我会尽量提供清晰、带有注释的代码示例。
4.2 第 1 步:收集和清理你的信息(数据加载与预处理)
RAG 的第一步是将你的知识源(也就是你希望 LLM 能够参考的资料)加载到系统中。这些资料可以是 PDF 文件、Word 文档、纯文本文件、网页内容,甚至是数据库中的数据。
RAG 框架(如 LangChain 和 LlamaIndex)提供了各种“文档加载器”(Document Loaders),它们可以方便地从不同的来源读取数据,并将其转换成统一的“文档”(Document)对象。一个文档对象通常包含文本内容(page_content)和一些元数据(metadata),比如来源文件名、页码等。
示例(使用 LangChain 加载 PDF):
# 假设你已经安装了 pypdf 和 langchainfrom langchain_community.document_loaders import PyPDFLoaderloader = PyPDFLoader("your_document.pdf")documents = loader.load()# 'documents' 现在是一个包含 PDF 页面内容的列表
加载数据后,有时需要进行一些基础的“清理”(Preprocessing)工作。这包括:• 去除无关内容:比如页眉、页脚、广告、导航栏等。
• 处理格式问题:比如去除多余的空格、空行,或者统一换行符。
• 修正错误:比如修复明显的拼写错误(虽然通常 RAG 对此不敏感)。
• 清理的目标是让输入给后续步骤的文本尽可能干净、包含核心信息,去除噪音。
4.3 第 2 步:切分信息:分块策略 (Chunking)
加载进来的文档内容通常很长,直接将整个文档喂给 LLM 或进行向量搜索效率不高,效果也不好。
主要原因有两个:
• LLM 上下文窗口限制: 大多数 LLM 都有一个输入长度限制(称为“上下文窗口”),无法一次处理过长的文本。
• 检索精度: 在一大段文本中搜索相关信息,不如在更小、更聚焦的文本块中搜索来得精确。
因此,我们需要将加载的文档“切块”(Chunking),分割成更小、更易于管理的单元。选择合适的切块策略很重要。以下是一些常见的方法:
1️⃣固定大小分块 (Fixed-size Chunking): 按固定的字符数或 Token 数(Token 是 LLM 处理文本的基本单元)来切分。这种方法简单直接,但缺点是可能在句子中间或词语中间断开,破坏语义完整性。
2️⃣递归字符分块 (Recursive Character Splitting): 这是更常用且推荐的方法。它会尝试按一系列分隔符(如段落 \n\n、句子 .、空格 )来递归地切分文本,直到块的大小符合要求。这种方法能更好地保持文本的语义结构。
3️⃣按文档结构分块 (Document Specific Chunking): 如果文档有明确的结构(如 Markdown 的标题、HTML 标签),可以利用这些结构来分块,比如按章节或段落分割。
4️⃣语义分块 (Semantic Chunking): 更高级的方法,试图根据内容的语义相关性来分块,确保每个块包含一个相对完整的意思单元。这通常需要额外的计算。
在分块时,通常还会设置一个“重叠”(Overlap)参数。这意味着每个块的末尾会包含下一个块开头的一部分内容。这样做的好处是,即使一个重要的信息点恰好被分割线切开,它也能完整地出现在相邻的两个块中,提高了被检索到的概率。
示例(使用 LangChain 的 RecursiveCharacterTextSplitter):
# 假设你已经加载了 documentsfrom langchain.text_splitter import RecursiveCharacterTextSplitter text_splitter = RecursiveCharacterTextSplitter( chunk_size=500, #每个块的目标大小(字符数) chunk_overlap=50 #块之间的重叠大小)chunks = text_splitter.split_documents(documents)# 'chunks' 现在是一个包含切分后文本块的列表
4.4 第 3 步:向量化!创建嵌入
现在我们有了一堆文本块(chunks),下一步是将它们转换成机器能够理解和比较的数字形式——也就是嵌入向量。
我们需要选择一个嵌入模型来完成这个任务。对于初学者,可以选择:
• Hugging Face 模型: 使用 sentence-transformers库可以方便地加载许多预训练好的嵌入模型,比如 all-MiniLM-L6-v2,它在性能和资源消耗之间取得了不错的平衡,并且可以在本地免费运行。
• OpenAI 模型: 如果你有 OpenAI API 密钥,可以使用他们的嵌入模型,如text-embedding-ada-002。这通常需要付费,但使用简单,性能也很好。
RAG 框架提供了方便的方式来调用这些模型。
示例(使用 LangChain 初始化 HuggingFace 嵌入模型):
# 假设你已经安装了 sentence-transformers 和 langchainfrom langchain_community.embeddings import HuggingFaceEmbeddings model_name = "sentence-transformers/all-MiniLM-L6-v2"embedding_model = HuggingFaceEmbeddings(model_name=model_name) # 现在可以用 embedding_model.embed_documents(list_of_texts) 来获取嵌入向量
这个步骤会对上一步生成的每一个文本块调用嵌入模型,得到一个对应的向量。
4.5 第 4 步:存储起来:建立简单的向量数据库 (例如 Chroma/FAISS)
有了文本块和它们对应的嵌入向量,我们需要将它们存储到一个向量数据库中,以便后续能够快速进行相似性搜索。
这个存储和索引的过程,就是构建我们 RAG 系统的“知识索引”。使用 Chroma 或 FAISS(通过 LangChain/LlamaIndex 的接口)可以让这个过程相对简单。框架通常提供了直接从文档块和嵌入模型创建向量存储索引的功能。
示例(使用 LangChain 和 Chroma 创建并填充向量存储):
# 假设你已经有了 chunks 和 embedding_model# 并且安装了 chromadb-client 和 langchainfrom langchain_community.vectorstores import Chroma # 定义一个持久化存储的路径(可选,也可以只在内存中)persist_directory = 'db_chroma' # 创建 Chroma 向量存储,传入文档块、嵌入模型和存储路径vector_store = Chroma.from_documents( documents=chunks, embedding=embedding_model, persist_directory=persist_directory) # 向量存储现在包含了所有块的嵌入,并已准备好被检索print(f"成功将 {len(chunks)} 个文本块存入 Chroma。")示例(使用 LangChain 和 FAISS 创建向量存储 - 仅内存):Python# 假设你已经有了 chunks 和 embedding_model# 并且安装了 faiss-cpu 和 langchainfrom langchain_community.vectorstores import FAISS # 直接从文档块和嵌入模型创建 FAISS 索引(在内存中)vector_store = FAISS.from_documents(chunks, embedding_model) # 向量存储现在包含了所有块的嵌入,并已准备好被检索print(f"成功将 {len(chunks)} 个文本块创建为 FAISS 索引。")
4.6 第 5 步:找到它:构建检索器
索引(向量存储)建立好之后,我们需要一个组件来执行实际的搜索任务。这个组件就是“检索器”(Retriever)。检索器的工作流程是:
1️⃣接收用户的查询(一个问题或关键词)。
2️⃣使用与索引数据时相同的嵌入模型,将查询也转换成一个嵌入向量。
3️⃣在向量数据库中执行相似性搜索(通常是查找 K 个最相似的向量,K 是一个可配置的参数)。
4️⃣返回与查询向量最相似的那些文本块(及其元数据)。
在 LangChain 和 LlamaIndex 中,通常可以非常方便地从已经创建好的向量存储对象中直接获取一个检索器实例。
示例(从 LangChain 的向量存储获取检索器):
# 假设你已经创建了 vector_store (无论是 Chroma 还是 FAISS)retriever = vector_store.as_retriever(search_kwargs={"k": 3}) # 配置为返回最相似的 3 个块# 现在可以用 retriever.invoke("你的查询") 来获取相关的文本块
这里的相似性搜索通常使用余弦相似度(Cosine Similarity)或点积(Dot Product)等数学方法来计算向量之间的“距离”或“角度”,从而判断它们的语义接近程度。
4.7 第 6 步:生成它:连接 LLM
检索器负责找到相关信息,而最终生成答案的任务则交给大语言模型(LLM),也就是 RAG 系统中的“生成器”。你需要初始化你选择的 LLM。
同样,框架提供了与各种 LLM(包括 OpenAI 的模型和 Hugging Face 上的开源模型)交互的接口。
示例(使用 LangChain 初始化 OpenAI LLM):
# 假设你已经安装了 openai 和 langchain,并设置了 OpenAI API 密钥环境变量from langchain_openai import ChatOpenAI llm = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0) # temperature=0 使输出更具确定性 # llm 对象现在可以用来生成文本示例(使用 LangChain 初始化 Hugging Face 开源 LLM,如 Flan-T5):# 假设你已经安装了 transformers, torch, accelerate 和 langchainfrom langchain_community.llms import HuggingFacePipelineimport torch # 选择一个适合的模型 IDmodel_id = "google/flan-t5-base"# 注意:运行本地模型可能需要较好的硬件配置llm = HuggingFacePipeline.from_model_id( model_id=model_id, task="text2text-generation", # T5 是文本到文本生成模型 model_kwargs={"temperature": 0.1, "max_length": 512}, device_map="auto" # 尝试自动使用 GPU (如果可用)) # llm 对象现在可以用来生成文本
选择哪个 LLM 取决于你的需求,比如对回答质量的要求、预算、是否需要离线运行等。
4.8 第 7 步:提示词魔法:指导 LLM 如何使用上下文
现在我们有了检索器(能找到相关信息)和 LLM(能生成答案),但还需要告诉 LLM 如何 利用检索到的信息来回答问题。
这就是“提示词工程”(Prompt Engineering)发挥作用的地方。我们需要创建一个“提示词模板”(Prompt Template)。
这个模板是一个预先定义好的文本结构,它会包含:
1️⃣指令(Instructions): 告诉 LLM 它的任务是什么,应该如何表现。例如:“请根据下面提供的上下文信息来回答问题。如果上下文中没有答案,请说你不知道。回答应简洁,不超过三句话。”
2️⃣上下文占位符(Context Placeholder): 一个标记,表示检索到的相关文本块将插入到这里。通常用 {context} 或类似语法表示。
3️⃣问题占位符(Question Placeholder): 一个标记,表示用户的原始问题将插入到这里。通常用 {question} 或 {query} 表示。
这个模板确保了每次调用 LLM 时,输入都遵循一致的、结构化的格式,将检索到的上下文和用户的问题清晰地呈现给 LLM。
示例(使用 LangChain 创建提示词模板):
from langchain.prompts import PromptTemplate template = """请根据以下上下文信息来回答最后的问题。如果你在上下文中找不到答案,就直接说你不知道,不要试图编造答案。保持答案简洁。 上下文:{context} 问题: {question} 答案:""" prompt_template = PromptTemplate( template=template, input_variables=["context", "question"]) # 这个 prompt_template 可以接收 context 和 question 作为输入,生成最终的提示词
提示词的设计对 RAG 系统的最终效果至关重要。 一个好的提示词能引导 LLM 更好地理解任务、专注于提供的上下文、并按照你的要求(如简洁性、语气)来生成回答。如果提示词写得不好,LLM 可能会忽略上下文信息,或者仍然产生幻觉。
因此,调整和优化提示词是 RAG 开发中一个常见的迭代过程。
4.9 第 8 步:代码示例:一个基础的 RAG 流程
下面是一个简化的 Python 代码示例,演示如何使用 LangChain 将前面所有步骤串联起来,构建一个基本的 RAG 应用。这个例子假设我们使用 Hugging Face 的嵌入模型和 LLM(可以在本地运行),以及 Chroma 作为向量存储。
import osfrom langchain_community.document_loaders import TextLoaderfrom langchain.text_splitter import RecursiveCharacterTextSplitterfrom langchain_community.embeddings import HuggingFaceEmbeddingsfrom langchain_community.vectorstores import Chromafrom langchain_community.llms import HuggingFacePipelinefrom langchain.prompts import PromptTemplatefrom langchain.chains import RetrievalQA # --- 0. 准备工作 ---# 创建一个示例文本文件 data.txtwith open("data.txt", "w", encoding="utf-8") as f: f.write("这是关于 RAG 的示例文档。\n") f.write("RAG 代表检索增强生成。\n") f.write("它结合了检索和生成来改进 LLM 的回答。\n") f.write("LangChain 和 LlamaIndex 是流行的 RAG 框架。\n") # --- 1. 加载数据 ---print("加载文档...")loader = TextLoader("data.txt", encoding="utf-8")documents = loader.load()print(f"加载了 {len(documents)} 个文档。") # --- 2. 切分文档 ---print("切分文档...")text_splitter = RecursiveCharacterTextSplitter(chunk_size=100, chunk_overlap=10)chunks = text_splitter.split_documents(documents)print(f"文档被切分成 {len(chunks)} 个块。") # --- 3. 初始化嵌入模型 ---print("初始化嵌入模型...")# 使用一个轻量级的、适合本地运行的模型model_name = "sentence-transformers/all-MiniLM-L6-v2"embedding_model = HuggingFaceEmbeddings(model_name=model_name)print("嵌入模型初始化完成。") # --- 4. 创建向量存储 ---print("创建向量存储 (Chroma)...")# 使用内存模式的 Chroma,或指定 persist_directory 进行持久化vector_store = Chroma.from_documents(chunks, embedding_model)print("向量存储创建完成。") # --- 5. 创建检索器 ---print("创建检索器...")retriever = vector_store.as_retriever(search_kwargs={"k": 2}) # 返回最相关的 2 个块print("检索器创建完成。") # --- 6. 初始化 LLM ---print("初始化 LLM (使用 Hugging Face 的 Flan-T5)...")# 注意:运行本地 LLM 可能需要安装额外的依赖并占用较多资源# 对于初学者,如果配置本地 LLM 遇到困难,可以考虑替换为 OpenAI (需要 API Key)try: # 尝试使用一个相对较小的模型 llm = HuggingFacePipeline.from_model_id( model_id="google/flan-t5-small", # 更小的模型,资源需求较低 task="text2text-generation", model_kwargs={"temperature": 0.1, "max_length": 128}, device_map="auto" # 尝试自动使用 GPU ) print("LLM 初始化完成。")except Exception as e: print(f"初始化本地 LLM 失败: {e}") print("请确保已安装 transformers, torch, accelerate 等库,并有足够资源。") print("或者考虑替换为 OpenAI LLM (需要设置 API Key)。") llm = None # 标记 LLM 未成功初始化 # --- 7. 定义提示词模板 ---template = """根据以下上下文回答问题。如果上下文没有提供答案,请回答不知道。 上下文:{context} 问题: {question} 回答:"""prompt = PromptTemplate(template=template, input_variables=["context", "question"]) # --- 8. 创建 RAG 链 ---if llm: # 仅在 LLM 初始化成功时继续 print("创建 RAG 链...") qa_chain = RetrievalQA.from_chain_type( llm=llm, chain_type="stuff", # "stuff" 将所有检索到的块放入提示词 retriever=retriever, chain_type_kwargs={"prompt": prompt}, return_source_documents=True # 可以选择返回源文档块 ) print("RAG 链创建完成。") # --- 9. 进行查询 --- query = "RAG 代表什么?" print(f"\n查询: {query}") try: result = qa_chain.invoke({"query": query}) print(f"回答: {result['result']}") # print(f"源文档块: {result['source_documents']}") # 可以取消注释查看检索到的块 except Exception as e: print(f"查询过程中出错: {e}")else: print("\nLLM 未能初始化,无法执行查询。")
这个示例代码整合了 RAG 的核心流程。你可以尝试修改 data.txt 的内容,或者改变查询 query,看看 RAG 系统如何根据你提供的文档来回答问题。请注意,运行本地 LLM 可能需要一定的计算资源,如果遇到困难,可以考虑使用需要 API 密钥的云服务 LLM(如 OpenAI)。
第 5 节:效果如何?评估你的 RAG 系统
到这一步,恭喜你!你已经成功构建了一个基础的 RAG 系统。但是,仅仅让它跑起来是不够的,我们还需要知道它运行得好不好。这就是“评估”(Evaluation)的作用。
5.1 为何要检查工作成果?评估的重要性
评估 RAG 系统至关重要,原因如下:
1️⃣了解性能:评估可以告诉你系统在回答问题时的准确性、相关性和可靠性如何。
2️⃣发现问题:通过评估,你可以诊断出系统的瓶颈所在。是检索器没找到正确的信息?还是生成器没能很好地利用信息?。
3️⃣衡量改进:当你对系统进行调整(比如更换模型、修改提示词、改变分块策略)后,评估可以量化地告诉你这些改动是带来了提升还是负面影响。
4️⃣建立信任:对于需要部署到实际应用中的 RAG 系统,定期的评估是确保其持续提供高质量服务的关键。
简单来说,没有评估,你就无法真正了解你的 RAG 系统表现如何,也难以有效地对其进行优化。
5.2 衡量成功的标准:简单指标
介绍评估 RAG 系统涉及多个维度。以下是一些核心的、相对容易理解的评估指标:
1️⃣上下文精确率 (Context Precision):
• 衡量什么: 检索器找回来的信息中,有多少是真正与问题相关的?
• 简单理解: 检索器是不是找了一堆“垃圾”信息回来?得分高表示找回来的大部分都有用。
2️⃣上下文召回率 (Context Recall)
• 衡量什么: 所有真正相关的信息,检索器找回来了多少?
• 简单理解: 检索器有没有漏掉重要的信息?得分高表示相关的基本都找到了。
3️⃣忠实度 (Faithfulness):
• 衡量什么: 生成的答案是否严格基于提供的上下文信息?
• 简单理解: LLM 是不是在“胡说八道”或者掺杂了上下文里没有的内容?得分高表示答案忠于事实依据。
4️⃣答案相关性 (Answer Relevancy):
• 衡量什么: 生成的答案是否直接、有效地回答了用户的问题。
• 简单理解: 答案是不是切题?有没有答非所问?得分高表示答案与问题高度相关。
这些指标通常需要一个“基准真相”(Ground Truth)数据集来进行比较,这个数据集包含问题、理想的答案以及相关的上下文片段。
5.3 评估工具:RAGAS 简介
手动评估这些指标可能非常耗时。幸运的是,有一些工具可以帮助自动化这个过程。RAGAS(Retrieval-Augmented Generation Assessment)就是一个专门为评估 RAG 流水线而设计的流行 Python 框架。
RAGAS 的特点包括:
1️⃣自动化指标计算: 它可以自动计算我们上面提到的核心指标(如 Faithfulness, Answer Relevancy, Context Precision, Context Recall)。
2️⃣利用 LLM 进行评估: 有趣的是,RAGAS 经常使用 LLM 本身(可以是你指定的评判 LLM)来辅助评估过程,例如判断答案是否忠实于上下文。
3️⃣组件级评估: 它不仅评估最终答案,还能帮助评估 RAG 流程中各个组件(如检索器和生成器)的表现。
虽然本入门教程不深入 RAGAS 的具体用法,但了解有这样的工具存在是很重要的。当你需要系统地评估和改进你的 RAG 应用时,RAGAS 是一个值得探索的强大工具。
你可以通过 RAGAS 提供的 Python 库,将你的 RAG 输出(问题、答案、检索到的上下文)输入进去,它就能为你生成量化的评估分数。
第 6 节:下一步是什么?你的 RAG 探索之旅
你已经完成了 RAG 的基础学习和实践,但这只是一个开始。RAG 是一个快速发展的领域,还有很多值得探索的地方。
6.1 关键要点回顾
让我们快速回顾一下本次旅程的核心内容:
• RAG 的核心价值: 通过在生成答案前检索外部知识,增强 LLM 的能力,使其回答更准确、更新、更可靠。
• 两大核心组件: 检索器(负责查找信息)和生成器(负责组织语言、生成答案)。
• 关键技术要素: LLM(大脑)、嵌入(理解语义)、向量数据库(高效存储和搜索语义信息)。
• 基本流程: 数据加载 -> 分块 -> 嵌入 -> 索引存储 -> 查询 -> 检索 -> 增强提示词 -> 生成答案。
• 实用框架: LangChain 和 LlamaIndex 等框架可以大大简化 RAG 应用的开发。• 评估的重要性: 了解系统性能、发现问题和指导优化的关键步骤,可以使用 RAGAS 等工具。
6.2 探索更远:高级技术概览
我主要分享了最基础的 RAG 实现。当你对基础掌握牢固后,可以探索一些更高级的技术来进一步提升 RAG 系统的性能和能力:
1️⃣更智能的分块策略: 例如语义分块(Semantic Chunking),根据内容的含义来切分,而不是固定大小。
2️⃣混合搜索 (Hybrid Search): 结合传统的关键词搜索和向量语义搜索的优势,对于包含特定术语或名称的查询可能效果更好。
3️⃣重排序 (Re-ranking): 在检索器初步找回一批文档后,使用另一个(通常更小、更快的)模型对这些文档进行重新排序,将最相关的放在最前面,提高最终送入 LLM 的上下文质量。
4️⃣查询转换 (Query Transformations): 在将用户的原始查询发送给检索器之前,先对其进行改写或扩展,生成多个不同的查询变体,以期从不同角度捕捉相关信息,提高检索的全面性。
5️⃣使用 Agent: 对于更复杂的任务,可以构建 RAG Agent,让 LLM 能够自主决定何时进行检索、检索什么内容,甚至执行多步检索和推理。
6️⃣上下文压缩: 在将检索到的上下文送入 LLM 前,进行压缩或摘要,以在有限的上下文窗口内包含更多有效信息。
这些高级技术通常需要更深入的理解和更复杂的实现,但它们可以显著提升 RAG 系统在特定场景下的表现。
6.3 实用资源与社区分享
在你继续深入 RAG 的学习和实践过程中,以下资源可能会对你有所帮助:
1️⃣框架文档:
• LangChain 官方文档: https://python.langchain.com/
• LlamaIndex 官方文档: https://docs.llamaindex.ai/en/stable/
2️⃣向量数据库:
• Chroma: https://docs.trychroma.com/
• FAISS GitHub: https://github.com/facebookresearch/faiss
3️⃣评估工具:
• RAGAS 文档: https://docs.ragas.io/
4️⃣教程与示例:
• GitHub 上有许多 RAG 相关的开源项目和教程,可以搜索 “LangChain RAG tutorial” 或 “LlamaIndex RAG example”。
• Awesome RAG 列表 (GitHub): 收集了大量 RAG 相关的论文、代码和资源。
5️⃣入门学习:DeepLearning.AI 的 Python 入门课程 (如果需要 Python 基础):
6️⃣社区:许多框架和工具(如 LangChain, LlamaIndex, RAGAS)都有 Discord 或其他社区论坛,可以在那里提问和交流。
写在最后
爱因斯坦说:
“我没有特殊的天赋,我只是极度好奇。”
其实学习很简单,就是保持好奇心,然后动手实践。
普通人如何抓住AI大模型的风口?
领取方式在文末
为什么要学习大模型?
目前AI大模型的技术岗位与能力培养随着人工智能技术的迅速发展和应用 , 大模型作为其中的重要组成部分 , 正逐渐成为推动人工智能发展的重要引擎 。大模型以其强大的数据处理和模式识别能力, 广泛应用于自然语言处理 、计算机视觉 、 智能推荐等领域 ,为各行各业带来了革命性的改变和机遇 。
目前,开源人工智能大模型已应用于医疗、政务、法律、汽车、娱乐、金融、互联网、教育、制造业、企业服务等多个场景,其中,应用于金融、企业服务、制造业和法律领域的大模型在本次调研中占比超过 30%。
随着AI大模型技术的迅速发展,相关岗位的需求也日益增加。大模型产业链催生了一批高薪新职业:
人工智能大潮已来,不加入就可能被淘汰。如果你是技术人,尤其是互联网从业者,现在就开始学习AI大模型技术,真的是给你的人生一个重要建议!
最后
如果你真的想学习大模型,请不要去网上找那些零零碎碎的教程,真的很难学懂!你可以根据我这个学习路线和系统资料,制定一套学习计划,只要你肯花时间沉下心去学习,它们一定能帮到你!
大模型全套学习资料领取
这里我整理了一份AI大模型入门到进阶全套学习包,包含学习路线+实战案例+视频+书籍PDF+面试题+DeepSeek部署包和技巧,需要的小伙伴文在下方免费领取哦,真诚无偿分享!!!
vx扫描下方二维码即可
加上后会一个个给大家发
部分资料展示
一、 AI大模型学习路线图
整个学习分为7个阶段
二、AI大模型实战案例
涵盖AI大模型的理论研究、技术实现、行业应用等多个方面。无论您是科研人员、工程师,还是对AI大模型感兴趣的爱好者,皆可用。
三、视频和书籍PDF合集
从入门到进阶这里都有,跟着老师学习事半功倍。
四、LLM面试题
五、AI产品经理面试题
六、deepseek部署包+技巧大全
😝朋友们如果有需要的话,可以V扫描下方二维码联系领取~