RAG(检索增强生成)技术详解
RAG (Retrieval Augmented Generation),即检索增强生成,是一种结合了检索系统与大型语言模型 (LLM) 的人工智能技术。它旨在提高 LLM 在回答问题、生成文本时的准确性、及时性和事实可靠性,尤其是在处理特定领域知识、最新信息或内部数据时。RAG 通过在生成答案之前,从外部知识库中检索相关信息,并将这些信息作为上下文提供给 LLM,从而“增强”其生成能力。
核心思想:克服大语言模型在知识时效性、幻觉和领域特异性方面的局限性。它通过动态地从权威数据源检索相关、准确的事实依据,并以此为基础指导 LLM 进行生成,使得 LLM 的输出更加准确、可追溯且富含最新信息。
一、为什么需要 RAG?大语言模型的局限性
大语言模型(LLMs)在处理自然语言任务方面展现出惊人的能力,但它们也存在一些固有的局限性,RAG 正是为了解决这些问题而生:
知识时效性与更新难题 (Knowledge Staleness)
- LLM 的知识来源于其训练数据,这些数据在模型发布后就成为了静态的。它们无法获取最新的事件、实时数据或新形成的知识。
- 每次需要更新知识时,都可能需要对整个模型进行成本高昂的再训练或微调。
幻觉 (Hallucinations) 与事实错误
- LLM 可能会生成听起来合理但实际上是虚构、不准确或与事实不符的信息,即“幻觉”。这源于其生成模式而非对事实的严格遵守。
- 在没有明确证据时,LLM 倾向于“编造”答案。
知识边界与特定领域限制 (Knowledge Boundary)
- LLM 无法访问训练数据之外的特定领域知识、企业内部文档或个人私有信息。
- 对于专业性极强或隐私敏感的场景,LLM 缺乏必要的背景知识。
可解释性差 (Lack of Explainability)
- 用户难以追踪 LLM 输出的答案是基于何种“知识”或“事实”得到的,使得模型缺乏透明度和可信度。
计算与存储成本高昂
- 训练和部署大型 LLM 需要巨大的计算资源和存储空间。为特定任务微调 LLM 依然成本不菲。
RAG 技术通过引入一个外部的、可扩展的知识检索机制,有效地缓解了这些问题。
二、RAG 的工作原理
RAG 的核心思想是将检索 (Retrieval) 和生成 (Generation) 两个阶段紧密结合。其基本工作流程如下:
- 用户查询 (User Query):用户提出一个问题或请求。
- 检索阶段 (Retrieval Phase):
- 系统首先分析用户查询的意图。
- 然后,利用这个查询作为检索条件,在一个预先构建好的外部知识库 (External Knowledge Base) 中搜索并提取出最相关的文档、段落或知识片段。
- 增强阶段 (Augmentation Phase):
- 将检索到的相关信息(作为“上下文”)与用户的原始查询拼接起来,形成一个增强型提示词 (Augmented Prompt)。
- 这个增强型提示词包含了 LLM 生成答案所需的所有相关背景知识。
- 生成阶段 (Generation Phase):
- 将增强型提示词输入给大型语言模型 (LLM)。
- LLM 基于这些新鲜的、精确的上下文信息,以及自身固有的语言理解和生成能力,来产生最终的答案。
通过这个过程,LLM 不再仅仅依赖其内部参数中固化的知识,而是能够动态地获取和利用外部世界的最新、最准确的信息。
graph TD
A[用户查询] --> B{检索器 Searcher}
B --> C["外部知识库 <br/>(文档 / 数据库 / 向量存储)"]
C --> D[检索到相关上下文]
D --> E["增强型提示词 <br/>(用户查询 + 上下文)"]
E --> F{大语言模型 LLM}
F --> G[最终答案]
三、RAG 系统的核心组件
一个完整的 RAG 系统通常由以下关键组件构成:
3.1 外部知识源 (External Knowledge Base)
这是 RAG 机制获取事实依据的源头。它可以是:
- 结构化数据:如关系型数据库、知识图谱。
- 半结构化数据:如维基百科、JSON、XML 文件。
- 非结构化数据:如大量文本文件 (PDF, Word, TXT)、网页内容、内部文档库、学术论文等。
选择高质量、权威和领域相关的知识源是 RAG 系统成功的基石。
3.2 索引模块 (Indexing Module) / 知识库准备阶段
在进行检索之前,需要对外部知识源进行预处理,使其能够被高效地搜索。
文档加载器 (Document Loaders):
- 作用:从各种来源(文件系统、URL、数据库等)加载原始文档。
- 示例:
PyPDFLoader、UnstructuredHTMLLoader、WebBaseLoader等。
分块策略 (Chunking Strategy):
- 作用:将长文档切分成大小适中、语义完整的文本块 (chunks)。这是检索性能的关键。
- 常见策略:
- 固定大小分块 (Fixed-size Chunking):按字符数或 Token 数切割,可能导致语义割裂。
- 递归字符分块 (RecursiveCharacterTextSplitter):尝试保留语义结构,先按段落、再按句子等方式切割。
- 语义分块 (Semantic Chunking):利用嵌入模型识别语义边界。
- 基于标题/结构分块 (Header/Markdown-based Chunking):利用文档结构(如Markdown标题)进行分块。
嵌入模型 (Embedding Model):
- 作用:将每个文本块(以及用户查询)转换成一个高维的向量表示 (Vector Embedding)。这些向量捕获了文本的语义信息。
- 示例:
OpenAIEmbeddings、SentenceTransformers(如all-MiniLM-L6-v2)、CohereEmbeddings、或针对特定语言/领域训练的私有模型。
向量数据库/存储 (Vector Database / Store):
- 作用:存储所有文本块的向量嵌入,以及对应的原始文本块内容。它能够根据查询向量高效地查找相似的文本块。
- 示例:
- 开源本地:
Chroma、Faiss(Facebook AI Similarity Search)。 - 开源分布式:
Milvus、Qdrant、Weaviate。 - 商业云服务:
Pinecone、Redis(with RediSearch)。
- 开源本地:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42from langchain_community.document_loaders import PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.embeddings import OpenAIEmbeddings
from langchain_community.vectorstores import Chroma
import os
# 1. 文档加载
# 假设你有一个名为 "example.pdf" 的文件
# loader = PyPDFLoader("example.pdf")
# docs = loader.load()
# 或者使用一个简单的文本作为示例
docs = [{"page_content": "RAG技术是检索增强生成的缩写,它结合了检索系统和大型语言模型。RAG旨在提高LLM在回答问题时的准确性。"},
{"page_content": "LLM常常出现幻觉,即生成不准确或虚构的信息。RAG可以通过提供外部知识来解决这个问题。"},
{"page_content": "外部知识库的准备包括文档加载、分块、嵌入和存储到向量数据库。这是一个核心步骤。"},
{"page_content": "LangChain提供了丰富的工具来构建RAG应用程序,包括各种加载器、文本分割器和向量存储集成。"},
{"page_content": "高效的分块策略对RAG系统的性能至关重要。例如,递归字符分割器可以很好地处理长文档。"}]
# 2. 分块
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=200, # 每个文本块的最大字符数
chunk_overlap=20, # 文本块之间重叠的字符数
length_function=len,
add_start_index=True,
)
chunks = []
for doc in docs:
split_docs = text_splitter.create_documents([doc["page_content"]])
for s_doc in split_docs:
chunks.append(s_doc)
print(f"原始文档数量: {len(docs)}, 切分后文本块数量: {len(chunks)}")
# print(f"第一个文本块: {chunks[0].page_content}")
# 3. 嵌入与向量存储 (需要设置 OPENAI_API_KEY)
# os.environ["OPENAI_API_KEY"] = "sk-..."
embeddings = OpenAIEmbeddings()
# 将文本块及其嵌入存储到 Chroma 向量数据库 (内存版)
# 也可以指定persist_directory保存到磁盘
vectorstore = Chroma.from_documents(documents=chunks, embedding=embeddings)
print("文本块已嵌入并存储到向量数据库。")
3.3 检索器 (Retriever)
检索器负责根据用户查询,从向量数据库中获取最相关的文本块。
向量相似度搜索 (Vector Similarity Search):
- 机制:将用户查询转换为向量,然后在向量数据库中寻找与查询向量距离最近(即最相似)的文本块向量。
- 常用距离指标:余弦相似度 (Cosine Similarity)、欧氏距离 (Euclidean Distance)、内积 (Dot Product)。
- 示例:
vectorstore.as_retriever(search_kwargs={"k": 3})会返回最相似的 k 个文本块。
关键词搜索 (Keyword Search):
- 机制:基于传统的文本匹配算法,如 BM25、TF-IDF,寻找包含查询关键词的文档。
- 优点:对于精确匹配的关键词查询非常有效,且不受嵌入模型质量影响。
混合搜索 (Hybrid Search):
- 机制:结合向量搜索和关键词搜索的优点,通常能获得更鲁棒的检索效果。
- 例如:先用关键词搜索缩小范围,再对结果进行向量相似度重排。
3.4 生成器 (Generator / LLM)
这是 RAG 系统的最终环节,负责根据增强型提示词生成答案。
大语言模型 (LLM):
- 选择:根据需求选择合适的 LLM 模型,如
GPT-4o、GPT-3.5-turbo、Claude、Llama等。 - 能力:模型需要具备强大的文本理解、逻辑推理和文本生成能力。
- 选择:根据需求选择合适的 LLM 模型,如
提示词工程 (Prompt Engineering):
- 作用:精心设计传递给 LLM 的增强型提示词,以最大化 LLM 利用检索信息的效率和生成高质量答案。
- 通常结构:
[系统指令] + [检索到的上下文] + [用户原始查询] + [输出格式要求] - 示例:
1
2
3
4
5
6
7
8
9
10你是一个知识渊博的助手。请根据下方提供的上下文信息,简洁清晰地回答用户的问题。
如果上下文没有提供足够的信息,请说明你无法回答。
上下文:
{context}
用户问题:
{question}
请根据上下文回答。
1 | from langchain_openai import ChatOpenAI |
四、RAG 的类型与演进 (Architectures and Evolution)
RAG 技术并非一成不变,它在不断演进,以解决基本的 RAG 架构中存在的挑战。
4.1 Naïve RAG (基本 RAG)
- 即上述描述的基础工作流:查询 -> 检索 -> 增强 -> 生成。
- 优点:实现简单,对 LLM 能力要求相对较低。
- 缺点:检索质量对最终答案影响巨大,可能面临“上下文太长”、“检索噪音”等问题。
4.2 Advanced RAG (高级 RAG / 优化的 RAG)
着眼于优化检索和增强的各个环节,以提高 RAG 系统的整体性能。
查询转换 (Query Transformation)
- 查询重写 (Query Rewriting):将原始用户查询进行改写,使其更适合检索。例如,将复杂查询分解为子查询。
- 查询扩充 (Query Expansion):添加同义词、相关概念等,增加检索的召回率。
- 查询分解 (Query Decomposition):将多跳或复杂问题分解为一系列简单的子问题,分别检索。
重排 (Re-ranking)
- 在初始检索器返回一组文档后,使用更复杂的模型(通常是小型语言模型或专门训练的排序模型)对这些文档进行二次排序。
- 目的:过滤掉不相关的文档,提升最相关文档的排名,减少 LLM 上下文中的噪音。
上下文压缩 (Contextual Compression)
- 从检索到的长文档中提取最关键的信息片段,再将其传递给 LLM。
- 方法:使用 LLM 自身进行摘要,或通过关键句提取、信息浓缩等技术。
- 目的:减少 LLM 输入 Token 数量,降低成本,提高 LLM 对核心信息的关注度。
多跳检索 (Multi-hop Retrieval)
- 对于需要多步推理的问题 (例如 “X 的创始人出生在哪里?”),RAG 系统可能需要进行多次检索。
- 第一次检索找到 “X 的创始人”,第二次检索再根据创始人的信息找到 “出生地”。
- 这通常涉及一个代理 (Agent) 决定何时以及如何进行下一次检索。
混合检索 (Hybrid Retrieval)
- 结合向量搜索和关键词搜索的优点,在不同场景下各取所长。
基于代理的 RAG (Agentic RAG)
- 引入一个 LLM 驱动的代理,让它在检索和生成之间进行动态决策。
- 代理可以决定:是否需要检索?使用哪个工具(不同的检索器或API)?检索什么?检索结果是否足够?是否需要迭代查询或进行多次推理?
4.3 知识图谱与 RAG 结合
- 将结构化的知识图谱作为 RAG 的外部知识库。
- 检索时,不仅返回文本片段,还可以返回知识图谱中的实体、关系和路径,为 LLM 提供更精准、结构化的事实上下文。
- 这种结合能有效提升复杂推理能力和答案的准确性。
五、RAG 的优缺点
5.1 优点:
- 提高事实准确性,减少幻觉:LLM 能够基于实际证据而非记忆或猜测生成答案。
- 处理新知识和特定领域知识:轻松更新外部知识库即可使 LLM 掌握最新信息,无需重新训练。
- 知识来源可追溯,提高可解释性:可以展示检索到的原始文档或片段,增强用户对答案的信任。
- 降低模型训练/微调成本:无需对 LLM 进行大规模微调即可适应新领域或新数据。
- 实现数据隐私和安全:敏感或私有数据可以存储在受控的知识库中,并通过权限管理访问。
5.2 缺点:
- 检索质量直接影响生成质量:如果检索到的信息不相关、不准确或包含噪音,LLM 可能会生成错误答案(“垃圾进,垃圾出”)。
- 知识库的构建和维护成本:需要投入资源去收集、清洗、分块和嵌入文档,并持续更新知识库。
- 复杂查询处理仍有挑战:对于需要多跳推理、聚合或深入分析的复杂问题,RAG 系统的设计更具挑战性。
- 上下文窗口限制:即使有 RAG,检索到的上下文仍可能过长,超出 LLM 的输入 Token 限制。需要优化分块和压缩策略。
- 效率/延迟问题:引入检索环节会增加生成答案的时间,影响实时性应用。
六、RAG 与微调 (Fine-tuning) 的比较与结合
RAG 和微调(Fine-tuning)是增强 LLM 性能的两种主要方法,它们解决的问题和作用机制不同,但可以互补。
6.1 微调 (Fine-tuning)
- 作用:通过在特定任务或特定领域数据上训练 LLM,改变模型的内部参数,使其更好地适应某个风格、任务或数据分布。它更新的是模型“通用知识”的权重分布和行为模式。
- 适用场景:
- 让模型学习新的格式或输出风格。
- 提高模型对特定类型指令的遵循能力。
- 调整模型生成内容的语气、创造性。
- 对模型进行能力引导(如编程、摘要)。
- 成本:通常需要大量的训练数据和较高的计算资源。
6.2 RAG (检索增强生成)
- 作用:为 LLM 提供最新、外部或特定领域的事实性知识作为上下文,而不改变模型本身的参数。它更新的是模型“事实知识”的来源。
- 适用场景:
- 需要回答最新的事实信息。
- 需要访问私有、企业内部或特定领域的知识。
- 要求答案可追溯来源。
- 避免幻觉,确保事实准确性。
- 成本:主要在于构建和维护知识库及检索系统。
6.3 结合使用
RAG 和微调不是互斥的,而是高度互补的。最佳实践通常是将两者结合:
- 微调 LLM:使其更好地理解用户的意图、处理长上下文、以及更有效地利用检索到的信息。例如,可以微调 LLM,使其在上下文不确凿时更倾向于表达“我不知道”。
- RAG 增强:提供最新的、特定领域的、可追溯的事实性内容。
通过结合,可以获得一个既能遵循指令和风格(微调),又能提供最新、准确事实(RAG)的强大系统。
七、总结
RAG 技术已经成为大语言模型时代解决知识时效性、幻觉和领域特异性难题的强大范式。它通过动态地从外部知识库中检索信息来增强 LLM 的生成能力,使得 LLM 的输出更加准确、可信和可追溯。
从朴素 RAG 到高级 RAG,再到与知识图谱和LLM微调的深度融合,RAG 的发展展现出其巨大的潜力和灵活性。随着 AI 技术的不断进步,我们可以预见 RAG 将在智能问答、内容创作、决策支持、教育等多个领域发挥越来越核心的作用,成为构建可靠、高效、智能 AI 应用的关键技术。
