一、什么是RAG?为什么我们需要它?
RAG(Retrieval-Augmented Generation,检索增强生成) 是一种结合了信息检索与文本生成的大模型应用范式。简单来说,它的核心思想是:在让大语言模型(LLM)生成回答之前,先从一个可靠的知识库中检索出相关的资料,然后把这些资料作为上下文(Context)提供给模型,再让模型基于这些资料生成最终答案。
传统的LLM存在两大痛点:一是知识截止到训练数据的时间点,无法获知最新信息,容易产生“幻觉”;二是难以处理需要精确、私密知识(如公司内部文档、个人笔记)的问答。RAG正是为解决这些问题而生。它将LLM比作一个“博学但记忆可能出错的学生”,而检索系统则充当了“随时可查阅的权威参考书”。通过为模型提供实时的、确切的外部知识,RAG显著提高了回答的准确性、时效性和可追溯性。
提示:RAG不是模型本身,而是一个架构或流程。你可以把它理解为给LLM配备了一个“搜索引擎+资料包”。
二、RAG的核心组件与工作流程
一个典型的RAG流程可以拆解为三个核心阶段:数据处理、检索、生成。
- 数据处理与索引:这是准备“参考书”的阶段。首先需要将非结构化的知识源(如PDF、网页、Markdown文档)进行切片(Chunking),将其分解成大小适中的文本块。然后,使用文本嵌入模型(Embedding Model) 将每个文本块转换为一个高维向量(Vector)。这些向量连同原文,会被存储到专门的向量数据库(Vector Database,如FAISS, Chroma) 中,建立高效的可搜索索引。
- 检索(Retrieval):当用户提出问题时,系统首先将用户问题也进行向量化,然后在向量数据库中执行相似性搜索(Similarity Search),找出与问题向量在语义上最接近的K个文本块(Top-K)。这些文本块就是检索到的相关“参考资料”。
- 生成(Generation):最后,系统会构建一个精心设计的提示词(Prompt)。这个提示词通常包含两部分:指令部分(告诉模型要做什么)和上下文部分(塞入检索到的文本块)。将这个完整的提示词发送给LLM,模型就能基于给出的上下文资料,生成一个更加准确、有据可依的回答。
三、实践第一步:文档处理与向量化
在动手写代码前,处理好文档是基石。这里以处理一组Markdown文件为例。
首先,需要安装必要的库:langchain(提供了便捷的RAG工具链)、sentence-transformers(用于本地嵌入模型)和faiss-cpu(一个高效的向量检索库)。
pip install langchain sentence-transformers faiss-cpu
接下来是核心的切片与向量化代码。LangChain的RecursiveCharacterTextSplitter能智能地根据段落、换行符等分割文本。
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.vectorstores import FAISS
from langchain.embeddings import HuggingFaceEmbeddings
# 1. 准备知识库文本(这里用一个示例字符串代替实际文件读取)
knowledge_base_text = """RAG技术简介...(此处应为你的长文档内容)...
它是连接静态模型与动态世界的桥梁。"""
# 2. 文本切片
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=500, # 每个文本块约500字符
chunk_overlap=50 # 块之间重叠50字符,保持上下文连贯
)
chunks = text_splitter.split_text(knowledge_base_text)
print(f"共生成 {len(chunks)} 个文本块。")
# 3. 加载并使用本地嵌入模型(这里使用一个轻量级的多语言模型)
embedding_model = HuggingFaceEmbeddings(model_name="paraphrase-multilingual-MiniLM-L12-v2")
# 4. 创建向量数据库
vector_store = FAISS.from_texts(texts=chunks, embedding=embedding_model)
# 可以将索引保存到本地,下次直接加载
vector_store.save_local("my_faiss_index")
print("向量索引已创建并保存。")
四、检索策略:如何找到最相关的知识?
找到最相关的文本块是RAG成功的关键。除了基础的余弦相似度搜索,还有一些进阶策略:
- 元数据过滤:在存储向量时,可以为每个文本块附加元数据(如来源文件名、章节标题、日期)。检索时,可以先根据元数据进行过滤(例如只检索
source='技术白皮书'的文档),再在筛选后的子集里做向量搜索,这能极大提升精度。 - 混合检索(Hybrid Search):结合关键词检索(如BM25算法) 和语义向量检索。关键词检索擅长匹配确切术语,语义检索擅长理解同义词和概念。将两者结果融合,可以取长补短。
- 查询改写与分解:有时用户的问题比较模糊或复杂。可以先用LLM对用户原始问题进行改写,使其更具体、更适合检索;或者将一个复杂问题分解成几个子问题,分别检索后合并结果。
提示:没有一种检索策略是万能的。你需要根据你的知识库特点(是法律条文还是创意文本?)和用户问题类型进行测试和调优。
五、提示词工程:如何有效地“喂”资料给模型?
提示词是连接检索结果和LLM的指令桥梁,其设计直接决定生成质量。一个优秀的RAG提示词模板应包含:
- 明确的指令:清晰地告诉LLM要基于提供的上下文回答,并注明如果上下文未提及则应如何应对。
- 结构化的上下文:将检索到的文本块明确标记出来,方便模型区分。
- 问题:用户原始的提问。
一个常见的模板结构如下:
prompt_template = """
你是一个基于给定上下文进行回答的助手。请严格根据以下提供的上下文信息来回答用户的问题。
如果上下文中没有足够的信息,请明确说明‘根据给定的资料,我无法找到相关信息’。
不要编造任何不在上下文中的信息。
[上下文开始]
{context}
[上下文结束]
用户问题:{question}
请基于上述上下文,给出你的详细回答:
"""
在代码中,你需要将检索到的文本块(通常是一个列表)用换行符连接起来,填入{context}变量。
六、挑战与优化方向
RAG并非一劳永逸的银弹,在实际落地中会遇到挑战,也是持续优化的方向:
- 切片质量:切片太大,包含不相关信息;切片太小,可能破坏语义单元。需要根据文档类型反复实验
chunk_size和chunk_overlap。 - 检索精度:可能检索到语义相关但事实上不正确的文本块。这需要引入重排序(Re-ranking) 模型,对初步检索的Top-K结果进行二次精排。
- 答案忠实度:模型有时会“忽略”给定的上下文,依赖自身知识回答。可通过调整提示词、使用更强指令或采用约束解码技术来改善。
- 评估体系:如何量化评估一个RAG系统?需要建立综合评估指标,包括检索相关性(如命中率)和生成答案的准确性、完整性等。
RAG技术仍在快速演进,从简单的“检索-生成”已发展出更多高级模式,如自适应检索、迭代式RAG(模型自己判断是否需要再次检索)等。理解其基本原理,是跟进这些前沿应用的基础。