RAG (Retrieval Augmented Generation),即检索增强生成,是一种结合了检索系统与大型语言模型 (LLM) 的人工智能技术。它旨在提高 LLM 在回答问题、生成文本时的准确性、及时性和事实可靠性,尤其是在处理特定领域知识、最新信息或内部数据时。RAG 通过在生成答案之前,从外部知识库中检索相关信息,并将这些信息作为上下文提供给 LLM,从而“增强”其生成能力。

核心思想:克服大语言模型在知识时效性、幻觉和领域特异性方面的局限性。它通过动态地从权威数据源检索相关、准确的事实依据,并以此为基础指导 LLM 进行生成,使得 LLM 的输出更加准确、可追溯且富含最新信息。


一、为什么需要 RAG?大语言模型的局限性

大语言模型(LLMs)在处理自然语言任务方面展现出惊人的能力,但它们也存在一些固有的局限性,RAG 正是为了解决这些问题而生:

  1. 知识时效性与更新难题 (Knowledge Staleness)

    • LLM 的知识来源于其训练数据,这些数据在模型发布后就成为了静态的。它们无法获取最新的事件、实时数据或新形成的知识。
    • 每次需要更新知识时,都可能需要对整个模型进行成本高昂的再训练或微调。
  2. 幻觉 (Hallucinations) 与事实错误

    • LLM 可能会生成听起来合理但实际上是虚构、不准确或与事实不符的信息,即“幻觉”。这源于其生成模式而非对事实的严格遵守。
    • 在没有明确证据时,LLM 倾向于“编造”答案。
  3. 知识边界与特定领域限制 (Knowledge Boundary)

    • LLM 无法访问训练数据之外的特定领域知识、企业内部文档或个人私有信息。
    • 对于专业性极强或隐私敏感的场景,LLM 缺乏必要的背景知识。
  4. 可解释性差 (Lack of Explainability)

    • 用户难以追踪 LLM 输出的答案是基于何种“知识”或“事实”得到的,使得模型缺乏透明度和可信度。
  5. 计算与存储成本高昂

    • 训练和部署大型 LLM 需要巨大的计算资源和存储空间。为特定任务微调 LLM 依然成本不菲。

RAG 技术通过引入一个外部的、可扩展的知识检索机制,有效地缓解了这些问题。

二、RAG 的工作原理

RAG 的核心思想是将检索 (Retrieval)生成 (Generation) 两个阶段紧密结合。其基本工作流程如下:

  1. 用户查询 (User Query):用户提出一个问题或请求。
  2. 检索阶段 (Retrieval Phase)
    • 系统首先分析用户查询的意图。
    • 然后,利用这个查询作为检索条件,在一个预先构建好的外部知识库 (External Knowledge Base) 中搜索并提取出最相关的文档、段落或知识片段。
  3. 增强阶段 (Augmentation Phase)
    • 将检索到的相关信息(作为“上下文”)与用户的原始查询拼接起来,形成一个增强型提示词 (Augmented Prompt)
    • 这个增强型提示词包含了 LLM 生成答案所需的所有相关背景知识。
  4. 生成阶段 (Generation Phase)
    • 将增强型提示词输入给大型语言模型 (LLM)。
    • LLM 基于这些新鲜的、精确的上下文信息,以及自身固有的语言理解和生成能力,来产生最终的答案。

通过这个过程,LLM 不再仅仅依赖其内部参数中固化的知识,而是能够动态地获取和利用外部世界的最新、最准确的信息。

三、RAG 系统的核心组件

一个完整的 RAG 系统通常由以下关键组件构成:

3.1 外部知识源 (External Knowledge Base)

这是 RAG 机制获取事实依据的源头。它可以是:

  • 结构化数据:如关系型数据库、知识图谱。
  • 半结构化数据:如维基百科、JSON、XML 文件。
  • 非结构化数据:如大量文本文件 (PDF, Word, TXT)、网页内容、内部文档库、学术论文等。
    选择高质量、权威和领域相关的知识源是 RAG 系统成功的基石。

3.2 索引模块 (Indexing Module) / 知识库准备阶段

在进行检索之前,需要对外部知识源进行预处理,使其能够被高效地搜索。

  1. 文档加载器 (Document Loaders)

    • 作用:从各种来源(文件系统、URL、数据库等)加载原始文档。
    • 示例:PyPDFLoaderUnstructuredHTMLLoaderWebBaseLoader 等。
  2. 分块策略 (Chunking Strategy)

    • 作用:将长文档切分成大小适中、语义完整的文本块 (chunks)。这是检索性能的关键。
    • 常见策略:
      • 固定大小分块 (Fixed-size Chunking):按字符数或 Token 数切割,可能导致语义割裂。
      • 递归字符分块 (RecursiveCharacterTextSplitter):尝试保留语义结构,先按段落、再按句子等方式切割。
      • 语义分块 (Semantic Chunking):利用嵌入模型识别语义边界。
      • 基于标题/结构分块 (Header/Markdown-based Chunking):利用文档结构(如Markdown标题)进行分块。
  3. 嵌入模型 (Embedding Model)

    • 作用:将每个文本块(以及用户查询)转换成一个高维的向量表示 (Vector Embedding)。这些向量捕获了文本的语义信息。
    • 示例:OpenAIEmbeddingsSentenceTransformers (如 all-MiniLM-L6-v2)、CohereEmbeddings、或针对特定语言/领域训练的私有模型。
  4. 向量数据库/存储 (Vector Database / Store)

    • 作用:存储所有文本块的向量嵌入,以及对应的原始文本块内容。它能够根据查询向量高效地查找相似的文本块。
    • 示例:
      • 开源本地ChromaFaiss (Facebook AI Similarity Search)。
      • 开源分布式MilvusQdrantWeaviate
      • 商业云服务PineconeRedis (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
    42
    from 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)

检索器负责根据用户查询,从向量数据库中获取最相关的文本块。

  1. 向量相似度搜索 (Vector Similarity Search)

    • 机制:将用户查询转换为向量,然后在向量数据库中寻找与查询向量距离最近(即最相似)的文本块向量。
    • 常用距离指标:余弦相似度 (Cosine Similarity)、欧氏距离 (Euclidean Distance)、内积 (Dot Product)。
    • 示例:vectorstore.as_retriever(search_kwargs={"k": 3}) 会返回最相似的 k 个文本块。
  2. 关键词搜索 (Keyword Search)

    • 机制:基于传统的文本匹配算法,如 BM25、TF-IDF,寻找包含查询关键词的文档。
    • 优点:对于精确匹配的关键词查询非常有效,且不受嵌入模型质量影响。
  3. 混合搜索 (Hybrid Search)

    • 机制:结合向量搜索和关键词搜索的优点,通常能获得更鲁棒的检索效果。
    • 例如:先用关键词搜索缩小范围,再对结果进行向量相似度重排。

3.4 生成器 (Generator / LLM)

这是 RAG 系统的最终环节,负责根据增强型提示词生成答案。

  1. 大语言模型 (LLM)

    • 选择:根据需求选择合适的 LLM 模型,如 GPT-4oGPT-3.5-turboClaudeLlama 等。
    • 能力:模型需要具备强大的文本理解、逻辑推理和文本生成能力。
  2. 提示词工程 (Prompt Engineering)

    • 作用:精心设计传递给 LLM 的增强型提示词,以最大化 LLM 利用检索信息的效率和生成高质量答案。
    • 通常结构:[系统指令] + [检索到的上下文] + [用户原始查询] + [输出格式要求]
    • 示例:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      你是一个知识渊博的助手。请根据下方提供的上下文信息,简洁清晰地回答用户的问题。
      如果上下文没有提供足够的信息,请说明你无法回答。

      上下文:
      {context}

      用户问题:
      {question}

      请根据上下文回答。
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
42
43
44
45
46
47
48
49
50
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser

# 4. 检索器
retriever = vectorstore.as_retriever(search_kwargs={"k": 2}) # 检索最相关的2个文本块
print("检索器已准备就绪。")

# 5. 生成器 (LLM)
llm = ChatOpenAI(model="gpt-4o", temperature=0)

# 6. 提示词工程
prompt_template = ChatPromptTemplate.from_template(
"""你是一个知识渊博的助手。请根据下方提供的上下文信息,简洁清晰地回答用户的问题。
如果上下文没有提供足够的信息,请说明你无法回答,不要编造信息。

上下文:
{context}

用户问题:
{question}

请根据上下文回答:
"""
)

# 构建 RAG Chain (使用 LangChain Expression Language)
rag_chain = (
{"context": retriever, "question": RunnablePassthrough()} # 检索上下文,用户问题直接传入
| prompt_template
| llm
| StrOutputParser() # 将LLM的输出解析为字符串
)

# 7. 用户查询与生成答案
user_question = "RAG 技术是什么?它能解决什么问题?"
print(f"\n用户问题: {user_question}")

print("\n---开始 RAG 检索与生成---")
response = rag_chain.invoke(user_question)
print("\n---RAG 生成的答案---")
print(response)

user_question_2 = "什么是地球的引力?" # 故意提问知识库中没有的信息
print(f"\n用户问题: {user_question_2}")
print("\n---开始 RAG 检索与生成---")
response_2 = rag_chain.invoke(user_question_2)
print("\n---RAG 生成的答案---")
print(response_2)

四、RAG 的类型与演进 (Architectures and Evolution)

RAG 技术并非一成不变,它在不断演进,以解决基本的 RAG 架构中存在的挑战。

4.1 Naïve RAG (基本 RAG)

  • 即上述描述的基础工作流:查询 -> 检索 -> 增强 -> 生成。
  • 优点:实现简单,对 LLM 能力要求相对较低。
  • 缺点:检索质量对最终答案影响巨大,可能面临“上下文太长”、“检索噪音”等问题。

4.2 Advanced RAG (高级 RAG / 优化的 RAG)

着眼于优化检索和增强的各个环节,以提高 RAG 系统的整体性能。

  1. 查询转换 (Query Transformation)

    • 查询重写 (Query Rewriting):将原始用户查询进行改写,使其更适合检索。例如,将复杂查询分解为子查询。
    • 查询扩充 (Query Expansion):添加同义词、相关概念等,增加检索的召回率。
    • 查询分解 (Query Decomposition):将多跳或复杂问题分解为一系列简单的子问题,分别检索。
  2. 重排 (Re-ranking)

    • 在初始检索器返回一组文档后,使用更复杂的模型(通常是小型语言模型或专门训练的排序模型)对这些文档进行二次排序。
    • 目的:过滤掉不相关的文档,提升最相关文档的排名,减少 LLM 上下文中的噪音。
  3. 上下文压缩 (Contextual Compression)

    • 从检索到的长文档中提取最关键的信息片段,再将其传递给 LLM。
    • 方法:使用 LLM 自身进行摘要,或通过关键句提取、信息浓缩等技术。
    • 目的:减少 LLM 输入 Token 数量,降低成本,提高 LLM 对核心信息的关注度。
  4. 多跳检索 (Multi-hop Retrieval)

    • 对于需要多步推理的问题 (例如 “X 的创始人出生在哪里?”),RAG 系统可能需要进行多次检索。
    • 第一次检索找到 “X 的创始人”,第二次检索再根据创始人的信息找到 “出生地”。
    • 这通常涉及一个代理 (Agent) 决定何时以及如何进行下一次检索。
  5. 混合检索 (Hybrid Retrieval)

    • 结合向量搜索和关键词搜索的优点,在不同场景下各取所长。
  6. 基于代理的 RAG (Agentic RAG)

    • 引入一个 LLM 驱动的代理,让它在检索和生成之间进行动态决策。
    • 代理可以决定:是否需要检索?使用哪个工具(不同的检索器或API)?检索什么?检索结果是否足够?是否需要迭代查询或进行多次推理?

4.3 知识图谱与 RAG 结合

  • 将结构化的知识图谱作为 RAG 的外部知识库。
  • 检索时,不仅返回文本片段,还可以返回知识图谱中的实体、关系和路径,为 LLM 提供更精准、结构化的事实上下文。
  • 这种结合能有效提升复杂推理能力和答案的准确性。

五、RAG 的优缺点

5.1 优点:

  1. 提高事实准确性,减少幻觉:LLM 能够基于实际证据而非记忆或猜测生成答案。
  2. 处理新知识和特定领域知识:轻松更新外部知识库即可使 LLM 掌握最新信息,无需重新训练。
  3. 知识来源可追溯,提高可解释性:可以展示检索到的原始文档或片段,增强用户对答案的信任。
  4. 降低模型训练/微调成本:无需对 LLM 进行大规模微调即可适应新领域或新数据。
  5. 实现数据隐私和安全:敏感或私有数据可以存储在受控的知识库中,并通过权限管理访问。

5.2 缺点:

  1. 检索质量直接影响生成质量:如果检索到的信息不相关、不准确或包含噪音,LLM 可能会生成错误答案(“垃圾进,垃圾出”)。
  2. 知识库的构建和维护成本:需要投入资源去收集、清洗、分块和嵌入文档,并持续更新知识库。
  3. 复杂查询处理仍有挑战:对于需要多跳推理、聚合或深入分析的复杂问题,RAG 系统的设计更具挑战性。
  4. 上下文窗口限制:即使有 RAG,检索到的上下文仍可能过长,超出 LLM 的输入 Token 限制。需要优化分块和压缩策略。
  5. 效率/延迟问题:引入检索环节会增加生成答案的时间,影响实时性应用。

六、RAG 与微调 (Fine-tuning) 的比较与结合

RAG 和微调(Fine-tuning)是增强 LLM 性能的两种主要方法,它们解决的问题和作用机制不同,但可以互补。

6.1 微调 (Fine-tuning)

  • 作用:通过在特定任务或特定领域数据上训练 LLM,改变模型的内部参数,使其更好地适应某个风格、任务或数据分布。它更新的是模型“通用知识”的权重分布和行为模式。
  • 适用场景
    • 让模型学习新的格式输出风格
    • 提高模型对特定类型指令的遵循能力
    • 调整模型生成内容的语气、创造性
    • 对模型进行能力引导(如编程、摘要)。
  • 成本:通常需要大量的训练数据和较高的计算资源。

6.2 RAG (检索增强生成)

  • 作用:为 LLM 提供最新、外部或特定领域的事实性知识作为上下文,而不改变模型本身的参数。它更新的是模型“事实知识”的来源。
  • 适用场景
    • 需要回答最新的事实信息。
    • 需要访问私有、企业内部或特定领域的知识。
    • 要求答案可追溯来源。
    • 避免幻觉,确保事实准确性。
  • 成本:主要在于构建和维护知识库及检索系统。

6.3 结合使用

RAG 和微调不是互斥的,而是高度互补的。最佳实践通常是将两者结合:

  1. 微调 LLM:使其更好地理解用户的意图、处理长上下文、以及更有效地利用检索到的信息。例如,可以微调 LLM,使其在上下文不确凿时更倾向于表达“我不知道”。
  2. RAG 增强:提供最新的、特定领域的、可追溯的事实性内容。

通过结合,可以获得一个既能遵循指令和风格(微调),又能提供最新、准确事实(RAG)的强大系统。

七、总结

RAG 技术已经成为大语言模型时代解决知识时效性、幻觉和领域特异性难题的强大范式。它通过动态地从外部知识库中检索信息来增强 LLM 的生成能力,使得 LLM 的输出更加准确、可信和可追溯。

从朴素 RAG 到高级 RAG,再到与知识图谱和LLM微调的深度融合,RAG 的发展展现出其巨大的潜力和灵活性。随着 AI 技术的不断进步,我们可以预见 RAG 将在智能问答、内容创作、决策支持、教育等多个领域发挥越来越核心的作用,成为构建可靠、高效、智能 AI 应用的关键技术。