检索增强生成(RAG)技术深度解析:原理、架构与实战代码

举报
柠檬🍋 发表于 2026/06/27 19:28:19 2026/06/27
【摘要】 检索增强生成(RAG)技术深度解析:原理、架构与实战代码在大型语言模型(LLM)爆发的今天,我们常常面临两个核心痛点:一是模型的知识时效性,训练数据截止后的新知识无法即时获取;二是模型的幻觉问题,即模型可能会自信地生成错误事实。为了解决这些问题,检索增强生成(Retrieval-Augmented Generation, RAG) 应运而生。它成为了连接静态知识库与动态大语言模型之间的桥梁...

检索增强生成(RAG)技术深度解析:原理、架构与实战代码

在大型语言模型(LLM)爆发的今天,我们常常面临两个核心痛点:一是模型的知识时效性,训练数据截止后的新知识无法即时获取;二是模型的幻觉问题,即模型可能会自信地生成错误事实。为了解决这些问题,检索增强生成(Retrieval-Augmented Generation, RAG) 应运而生。它成为了连接静态知识库与动态大语言模型之间的桥梁,是目前企业级 AI 应用落地最主流的技术架构之一。

本文将深入剖析 RAG 的核心原理,拆解其技术架构,并提供基于 Python 的完整实战代码,帮助读者从零构建一个基础的 RAG 系统。

一、 什么是 RAG?

RAG 是一种将外部知识库嵌入到大语言模型生成过程中的技术范式。其核心思想可以概括为:“先检索,后生成”。

传统的 LLM 推理过程仅依赖其内部参数中存储的知识。而 RAG 流程如下:

  1. 检索(Retrieval):当用户提出问题时,系统首先在一个外部文档库或向量数据库中搜索与问题最相关的片段。
  2. 增强(Augmentation):将这些检索到的相关片段作为上下文(Context),与用户原始问题拼接在一起。
  3. **生成(Generation)**将拼接后的提示词发送给 LLM,让 LLM基于提供的上下文生成准确、真实的回答。

通过这种方式,RAG 既保留了 LLM强大的自然语言理解和生成能力,又引入了外部数据的准确性和时效性。

二、 RAG 的核心技术架构

一个标准的 RAG 系统通常包含以下四个关键模块:

1. 数据加载与分块(Data Loading & Chunking)

原始数据(如 PDF、网页、数据库记录)通常篇幅巨大且语义连贯性强。直接全文检索效率极低且容易淹没关键信息。因此,需要先将文档切割成小的文本块(Chunks)。

  • 关键策略:块大小(Chunk Size)通常设定在 256-1000 个 Token 之间;块重叠(Overlap)用于保持上下文连续性,避免关键信息被切断。

2. 嵌入与向量化(Embedding & Vectorization)

计算机无法直接理解文本语义,但可以理解向量。Embedding 模型将文本块转化为高维向量表示。在这个向量空间中,语义相似的文本距离更近。

  • 常用模型:Sentence-BERT, OpenAI Embeddings, BGE 等。

3. 向量存储(Vector Store)

存储所有文本块对应的向量,并支持高效的相似度搜索。

  • 常用数据库:FAISS(轻量级)、Pinecone(云服务)、Chroma(本地嵌入式)、Milvus(大规模分布式)。

4. 检索与生成(Retrieval & Generation)

  • 检索:将用户查询同样转化为向量,在向量数据库中查找相似度最高的 K 个文本块。
  • 生成:构建 Prompt,格式通常为:

    “基于以下参考信息:[检索到的文本块],请回答用户问题:[用户问题]”

三、 RAG 的高级优化策略

简单的 RAG 往往效果有限,工业级应用通常采用以下进阶策略:

  1. 混合检索(Hybrid Search):结合向量检索(语义匹配)和关键词检索(BM25,精确匹配)。两者互补,能有效解决专有名词匹配和语义模糊的问题。
  2. 重排序(Re-ranking):初步检索可能返回大量相关但冗余的结果。使用专门的 Re-ranker 模型(如 BGE-Reranker)对初步结果进行精排,只保留最相关的 Top-N 结果。
  3. 查询重写(Query Rewriting):用户的问题可能指代不明或过于简略。利用 LLM 将原始查询重写为更清晰、更适合检索的句子。
  4. 多路召回:从不同的知识库、不同的时间粒度进行并行检索,最后融合结果。

四、 实战代码:从零构建简易 RAG 系统

下面我们将使用 Python 构建一个基于 LangChain 框架的简易 RAG 系统。我们将使用 LangChain 作为编排框架,OpenAI 作为 LLM 和 Embedding 提供商,ChromaDB 作为向量数据库。

1. 环境准备

首先安装必要的库:

pip install langchain langchain-openai langchain-community chromadb pypdf

2. 完整代码实现

import os
from langchain.document_loaders import PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.embeddings import OpenAIEmbeddings
from langchain.vectorstores import Chroma
from langchain.chains import RetrievalQA
from langchain.llms import OpenAI
from dotenv import load_dotenv

# 加载环境变量,确保 API Key 安全
load_dotenv()

class SimpleRAGSystem:
    def __init__(self, pdf_path: str):
        """
        初始化 RAG 系统
        :param pdf_path: 本地 PDF 文件路径
        """
        self.pdf_path = pdf_path
        self.vectorstore = None
        self.retriever = None
        self.llm = OpenAI(temperature=0) # 使用 OpenAI LLM
        self.embeddings = OpenAIEmbeddings() # 使用 OpenAI Embedding 模型

    def load_and_split_documents(self):
        """
        1. 加载 PDF 文档
        2. 将文档切割成小块(Chunks)
        """
        print(f"正在加载文档: {self.pdf_path}")
        loader = PyPDFLoader(self.pdf_path)
        documents = loader.load()
        
        print("正在切割文档...")
        # 使用递归字符分割器,块大小 500,重叠 50
        text_splitter = RecursiveCharacterTextSplitter(
            chunk_size=500,
            chunk_overlap=50,
            length_function=len,
        )
        chunks = text_splitter.split_documents(documents)
        print(f"文档已切割为 {len(chunks)} 个文本块。")
        return chunks

    def create_vector_store(self, chunks):
        """
        3. 将文本块向量化并存储到 ChromaDB
        """
        print("正在创建向量数据库...")
        # 如果已有持久化数据可加载,否则新建
        self.vectorstore = Chroma.from_documents(
            documents=chunks,
            embedding=self.embeddings,
            persist_directory="./chroma_db"
        )
        print("向量数据库创建完成。")

    def setup_retriever(self):
        """
        4. 设置检索器
        """
        # 定义相似度搜索类型为 MMRR 以增强多样性,也可用 similarity
        self.retriever = self.vectorstore.as_retriever(
            search_type="similarity",
            search_kwargs={"k": 3} # 只取最相关的3个片段
        )
        print("检索器设置完成。")

    def ask_question(self, question: str):
        """
        5. 创建问答链并回答问题
        """
        # 使用 RetrievalQA 链,它自动处理 检索->上下文注入->生成 的过程
        qa_chain = RetrievalQA.from_chain_type(
            llm=self.llm,
            chain_type="stuff", # 将检索到的内容全部放入 prompt
            retriever=self.retriever,
            return_source_documents=True # 返回参考来源
        )
        
        print(f"\n用户问题: {question}")
        result = qa_chain({"query": question})
        
        print(f"\nAI 回答: {result['result']}")
        print("-" * 30)
        print("参考来源:")
        for doc in result['source_documents']:
            print(f"- 内容片段: {doc.page_content[:100]}...")

    def run(self, question: str):
        """
        执行完整流程
        """
        # 第一步:数据预处理
        chunks = self.load_and_split_documents()
        
        # 第二步:建立向量索引
        self.create_vector_store(chunks)
        
        # 第三步:配置检索
        self.setup_retriever()
        
        # 第四步:回答问题
        self.ask_question(question)

if __name__ == "__main__":
    # 注意:请确保有一个名为 sample.pdf 的文件在根目录下,或者修改路径
    # 如果没有真实 PDF,代码仍可运行,但建议下载一份技术文档测试
    pdf_file = "sample.pdf" 
    
    # 为了演示代码可运行性,这里做一个简单的占位检查
    if not os.path.exists(pdf_file):
        print(f"警告: 未找到文件 {pdf_file},请创建一个测试 PDF 文件或将路径修改为有效文件。")
        # 此处仅作逻辑演示,实际使用需替换为真实文件
        # exit() 
    
    rag_system = SimpleRAGSystem(pdf_file)
    query = "请总结这份文档的核心观点。"
    rag_system.run(query)

3. 代码解析

  • PyPDFLoader: 负责解析非结构化 PDF 文档,提取文本内容。
  • RecursiveCharacterTextSplitter: 这是 LangChain 中最常用的分割器。它首先尝试按字符分割,如果太长则按空格分割,以此类推,确保不会在单词中间切断。
  • Chroma.from_documents: 这是核心步骤。它自动调用 Embedding 模型将文本转为向量,并存入 ChromaDB。Chroma 是一个轻量级的、支持持久化的向量数据库,非常适合本地开发测试。
  • RetrievalQA: 这是 LangChain 提供的高级链。它封装了标准的 RAG 逻辑:
    1. 接收用户 Query。
    2. 调用 retriever 搜索相关文档。
    3. 构建 Prompt(包括 Query 和检索到的 Document 上下文)。
    4. 调用 llm 生成回答。
    5. 返回答案及源文档。

五、 常见问题与挑战

尽管 RAG 技术成熟,但在实际落地中仍面临挑战:

  1. 切割不当导致信息丢失:如果 Chunk 太小,上下文丢失;太大,噪声太多且可能超过 LLM 的上下文窗口。
    • 对策:使用父文档检索(Parent Document Retriever),检索小块,但返回大块父文档作为上下文。
  2. 检索精度不足:向量相似度并不总是等于语义相关性。
    • 对策:引入关键词混合检索(Hybrid Search)和重排序(Re-ranking)。
  3. 引用幻觉:LLM 可能会捏造不存在的引用。
    • 对策:在 Prompt 中明确指示模型“仅基于提供的上下文回答”,并在后处理阶段验证引用的真实性。
  4. 多跳推理(Multi-hop):用户问题需要结合多个分散的信息片段才能回答。
    • 对策:使用 Graph RAG(知识图谱 RAG)或迭代式检索。

六、 未来展望

RAG 正在向更智能的方向演进。未来的趋势包括:

  • Agentic RAG:RAG 系统不再被动检索,而是像智能体一样自主决定何时检索、检索什么、甚至调用工具来解决复杂问题。
  • Graph RAG:结合知识图谱的结构化优势与向量检索的语义优势,显著提升对复杂关系和实体间推理的能力。
  • 端到端微调:将检索过程作为 LLM 训练的一部分,通过微调让模型更好地理解何时触发检索,实现检索与生成的无缝协同。

结语

检索增强生成(RAG)不仅是解决 LLM 幻觉和知识滞后性的关键技术,更是将私有数据价值最大化的基础设施。通过理解其核心原理并掌握如 LangChain 等开发工具,开发者可以快速构建出准确、可靠且具备领域知识的 AI 应用。随着混合检索、重排序等技术的普及,RAG 将在企业智能助手、智能客服、代码助手等领域发挥越来越重要的作用。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。