RAG 技术全景解析:原理、架构与实战代码实现

举报
柠檬🍋 发表于 2026/06/27 19:28:05 2026/06/27
【摘要】 RAG 技术全景解析:原理、架构与实战代码实现在大型语言模型(LLM)爆发式增长的今天,我们面临着两个核心挑战:幻觉问题(Hallucination)和知识滞后性。LLM 基于训练数据生成内容,一旦训练数据过时或涉及私有领域知识,模型往往无法给出准确答案,甚至产生看似合理实则错误的“幻觉”。检索增强生成(Retrieval-Augmented Generation, RAG)技术应运而生。...

RAG 技术全景解析:原理、架构与实战代码实现

在大型语言模型(LLM)爆发式增长的今天,我们面临着两个核心挑战:幻觉问题(Hallucination)知识滞后性。LLM 基于训练数据生成内容,一旦训练数据过时或涉及私有领域知识,模型往往无法给出准确答案,甚至产生看似合理实则错误的“幻觉”。

检索增强生成(Retrieval-Augmented Generation, RAG)技术应运而生。它通过引入外部知识库,让模型在生成回答前“查阅”相关资料,从而显著提升回答的准确性、时效性和可解释性。本文将深入剖析 RAG 的核心原理,并通过 Python 代码实现一个完整的 RAG 系统。

一、 RAG 的核心架构与工作原理

RAG 并非单一的技术,而是一种架构模式。其核心思想是将**检索(Retrieval)生成(Generation)**两个步骤解耦并串联起来。一个标准的 RAG 系统通常包含以下四个关键阶段:

  1. 数据准备与索引(Indexing)
    将非结构化数据(如 PDF、Word、网页文本)切分为较小的块(Chunks),并通过嵌入模型(Embedding Model)将每个文本块转换为高维向量。这些向量存储在向量数据库中,以便快速检索。

  2. 查询与检索(Retrieval)
    当用户提出问题时,系统首先将问题转化为向量,然后在向量数据库中搜索与问题语义最相似的文本块。除了基础的向量相似度搜索,还可以结合关键词搜索(如 BM25)以提升召回率。

  3. 上下文构建(Context Construction)
    将检索到的相关文本块与原始用户问题组合在一起,形成增强后的提示词(Prompt)。这一步至关重要,因为它为 LLM 提供了回答所需的“证据”。

  4. 生成回答(Generation)
    LLM 接收增强后的提示词,基于检索到的上下文信息生成最终回答。由于有了参考依据,模型的回答更加可信且具备可追溯性。

二、 为什么需要 RAG?

在传统的全监督微调(Fine-tuning)模式下,更新模型知识需要重新训练,成本高且耗时。而 RAG 的优势在于:

  • 实时性:只需更新向量数据库中的数据,即可让模型掌握最新知识。
  • 成本效益:无需昂贵的 GPU 集群进行模型训练,主要依赖向量存储和 API 调用。
  • 可解释性:每个回答都可以附带引用的来源文档,便于用户验证事实。
  • 数据隐私:私有数据可以存储在本地向量数据库中,无需放入公共模型训练集。

三、 关键技术组件详解

1. 文本切分(Chunking)

文本切分质量直接影响检索效果。常见的策略包括按字符数切分、按段落切分或基于语义边界的递归切分。过小的 chunk 可能丢失上下文,过大的 chunk 可能引入噪声。

2. 嵌入模型(Embedding)

嵌入模型将文本映射到向量空间,使得语义相似的文本在空间中距离更近。常用的开源模型包括 all-MiniLM-L6-v2(轻量级)和 text-embedding-3-small(OpenAI 最新模型,效果更佳)。

3. 向量数据库

用于高效存储和检索高维向量。主流选择包括 ChromaDB(轻量级,适合开发测试)、Pinecone(托管服务)、MilvusFAISS(Facebook 开源,高性能)。

四、 实战:使用 Python 构建 RAG 系统

为了演示 RAG 的完整流程,我们将使用以下开源技术栈:

  • LangChain:LLM 应用开发框架,简化组件编排。
  • Hugging Face Transformers:用于加载嵌入模型。
  • ChromaDB:轻量级向量数据库。
  • OpenAI APIOllama:用于生成回答(本文以 OpenAI 为例,也可替换为本地模型)。

环境准备

首先安装必要的依赖库:

pip install langchain langchain-community langchain-openai chromadb openai

代码实现

以下代码实现了一个完整的 RAG 管道,包括数据加载、嵌入、存储和检索生成。

import os
from langchain.document_loaders import TextLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.embeddings import HuggingFaceEmbeddings
from langchain.vectorstores import Chroma
from langchain.llms import Ollama  # 或者使用 ChatOpenAI
from langchain.chains import RetrievalQA
from langchain.prompts import PromptTemplate
import warnings

# 忽略一些无关警告
warnings.filterwarnings("ignore")

# 配置 API Key (如果使用 OpenAI)
# os.environ["OPENAI_API_KEY"] = "your-api-key-here"

class RAGSystem:
    def __init__(self, model_name="text-embedding-3-small"):
        """
        初始化 RAG 系统
        """
        print("初始化 RAG 系统...")
        
        # 1. 定义嵌入模型
        # 这里使用 HuggingFace 的 all-MiniLM-L6-v2,也可以使用 OpenAI 的嵌入
        self.embeddings = HuggingFaceEmbeddings(
            model_name="all-MiniLM-L6-v2"
        )
        
        # 2. 定义 LLM (这里以 Ollama 的 llama3 为例,实际项目中可替换为 OpenAI)
        # 如果使用 OpenAI: from langchain_openai import ChatOpenAI
        # self.llm = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0)
        
        # 为了代码通用性,这里仅定义接口,实际使用时需根据环境配置
        self.llm = self._get_llm()
        
        self.vectorstore = None
        self.qa_chain = None

    def _get_llm(self):
        """
        获取 LLM 实例,优先尝试 Ollama,其次 OpenAI
        """
        try:
            from langchain.llms import Ollama
            return Ollama(model="llama3")
        except ImportError:
            print("Ollama 未安装,尝试使用 OpenAI...")
            try:
                from langchain_openai import ChatOpenAI
                return ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0)
            except ImportError:
                raise Exception("请安装 Ollama 或配置 OpenAI API Key")

    def build_index(self, file_path: str):
        """
        加载文档,切分,生成嵌入并存入向量数据库
        """
        print(f"正在加载文档: {file_path}")
        
        # 1. 加载文档
        loader = TextLoader(file_path, encoding='utf-8')
        documents = loader.load()
        
        # 2. 文本切分
        # 将长文档切分为较小的块,以便嵌入和检索
        text_splitter = RecursiveCharacterTextSplitter(
            chunk_size=500,      # 每个块的大小
            chunk_overlap=50,    # 块之间的重叠部分,保留上下文
            length_function=len
        )
        chunks = text_splitter.split_documents(documents)
        print(f"文档被切分为 {len(chunks)} 个块")
        
        # 3. 创建向量数据库
        self.vectorstore = Chroma.from_documents(
            documents=chunks,
            embedding=self.embeddings,
            persist_directory="./chroma_db"  # 持久化存储路径
        )
        print("向量数据库构建完成")

    def create_qa_chain(self):
        """
        创建检索增强生成的问答链
        """
        if not self.vectorstore:
            raise Exception("请先构建索引")
            
        # 创建检索器
        retriever = self.vectorstore.as_retriever(
            search_type="similarity",       # 相似度搜索
            search_kwargs={"k": 3}          # 返回最相关的3个块
        )
        
        # 定义提示词模板
        # 强调基于上下文回答,避免幻觉
        template = """基于以下提供的上下文信息回答问题。如果上下文中没有相关信息,请回答“根据现有资料无法回答”。
        
        上下文:
        {context}
        
        问题:
        {question}
        
        回答:
        """
        
        PROMPT = PromptTemplate(
            template=template,
            input_variables=["context", "question"]
        )
        
        # 组装 QA Chain
        self.qa_chain = RetrievalQA.from_chain_type(
            llm=self.llm,
            chain_type="stuff",  # 将检索到的所有文本合并成一个 prompt
            retriever=retriever,
            chain_type_kwargs={"prompt": PROMPT},
            return_source_documents=True  # 返回源文档,便于追踪
        )
        print("问答链创建完成")

    def query(self, question: str) -> dict:
        """
        执行查询
        """
        if not self.qa_chain:
            self.create_qa_chain()
            
        print(f"用户问题: {question}")
        
        # 执行查询
        result = self.qa_chain.invoke({"query": question})
        
        # 提取结果
        answer = result['result']
        source_docs = result['source_documents']
        
        print(f"\n回答: {answer}")
        
        # 打印来源文档片段
        print("\n--- 参考来源 ---")
        for i, doc in enumerate(source_docs):
            print(f"来源 {i+1}: {doc.page_content[:100]}...")
            
        return {"answer": answer, "sources": source_docs}

# 主程序执行
if __name__ == "__main__":
    # 假设我们有一个名为 "sample.txt" 的文件,包含关于 Python 编程的知识
    # 在实际使用中,你需要先创建一个 sample.txt 文件
    sample_file = "sample.txt"
    
    # 如果没有文件,创建一个示例文件用于测试
    if not os.path.exists(sample_file):
        with open(sample_file, "w", encoding="utf-8") as f:
            f.write("""Python 是一种高级编程语言,由 Guido van Rossum 于 1991 年首次发布。
它的主要设计哲学是代码可读性,使用缩进来划分代码块。
Python 支持多种编程范式,包括面向对象、命令式、函数式和过程式编程。
它是目前世界上最流行的编程语言之一,广泛应用于 Web 开发、数据科学、人工智能等领域。
著名的 Python Web 框架包括 Django 和 Flask。
数据分析库包括 NumPy、Pandas 和 Matplotlib。
""")

    # 实例化 RAG 系统
    rag = RAGSystem()
    
    # 1. 构建索引
    rag.build_index(sample_file)
    
    # 2. 创建问答链
    rag.create_qa_chain()
    
    # 3. 提问测试
    test_questions = [
        "Python 是谁创建的?",
        "Python 的主要设计哲学是什么?",
        "哪些库用于数据分析?",
        "Python 支持哪些编程范式?"
    ]
    
    for q in test_questions:
        rag.query(q)
        print("\n" + "="*50 + "\n")

代码解析

  1. 初始化与嵌入:我们使用 HuggingFaceEmbeddings 加载 all-MiniLM-L6-v2 模型。这是一个轻量级但效果不错的嵌入模型,能够将文本转换为 384 维的向量。
  2. 文档处理RecursiveCharacterTextSplitter 是 LangChain 中推荐的切分器。它递归地根据分隔符(如空格、换行)切分文本,并设置 chunk_overlap 以确保相邻块之间有部分重叠,防止关键信息被切断。
  3. 向量存储Chroma.from_documents 自动为每个文本块计算嵌入向量,并将其存储在 ChromaDB 中。persist_directory 参数确保数据在程序重启后依然可用。
  4. 提示词工程template 中的提示词经过精心设计,明确要求模型“基于上下文回答”,这能有效抑制模型的幻觉行为。如果上下文中没有答案,模型会被引导为“无法回答”而不是编造事实。
  5. 检索与生成RetrievalQA 链自动处理检索和生成的流程。search_kwargs={"k": 3} 表示每次检索最相关的 3 个文本块。

五、 优化 RAG 性能的策略

基础 RAG 虽然有效,但在复杂场景下可能面临挑战。以下是一些优化策略:

  1. 混合检索(Hybrid Search)
    结合向量检索(语义匹配)和关键词检索(BM25)。例如,当用户查询专有名词或精确代码时,关键词检索往往优于向量检索。LangChain 提供了 MultiQueryRetriever 或自定义组合检索器。

  2. 重排序(Reranking)
    初检返回的文档可能包含噪声。引入一个重排序模型(如 Cohere Rerank 或 BGE Reranker),对初检结果进行精排,只将最相关的 Top-K 文档交给 LLM。

  3. 查询改写(Query Rewriting)
    用户的问题可能模糊或不完整。可以在检索前使用 LLM 对问题进行改写或扩展,生成多个变体问题,分别检索后合并结果。

  4. 元数据过滤(Metadata Filtering)
    在索引时保留文档的来源、日期、作者等元数据。检索时可通过元数据过滤缩小搜索范围,提高相关性。

六、 总结与展望

RAG 技术是当前解决 LLM 知识局限性和幻觉问题的最佳实践之一。通过本文的讲解和代码实现,我们可以看到 RAG 的核心在于数据的预处理有效的检索策略以及良好的提示词设计

随着技术的发展,RAG 正在向更智能的方向演进。例如,GraphRAG 将知识图谱与向量检索结合,用于处理复杂的多跳推理问题;自反思 RAG(Self-RAG)让模型在生成过程中自我评估和修正答案。

对于开发者而言,掌握 RAG 不仅是掌握一项技术,更是理解如何将 LLM 落地到实际业务场景的关键。希望本文能为你构建自己的智能问答系统提供坚实的基础。

注意:在生产环境中,建议使用更强大的嵌入模型(如 OpenAI text-embedding-3-large)和重排序模型,并进行充分的评估测试,以确保回答的质量。同时,需关注数据安全和隐私保护,确保私有数据不被泄露。

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

评论(0

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

全部回复

上滑加载中

设置昵称

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

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

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