Java面试题53:一文深入了解RAG(检索增强生成)核心概念

一、RAG 核心概念

1.1 什么是 RAG?

检索增强生成(Retrieval-Augmented Generation, RAG) 是一种融合信息检索技术大语言模型(LLM) 的 AI 生成框架。其核心逻辑是:先从外部知识库精准检索相关信息,再将检索结果作为上下文喂给大模型,由大模型结合检索内容生成准确、事实性强的回答

RAG 技术最初由 Meta AI 研究院(FAIR)在 2020 年提出,现已成为解决大模型 "幻觉" 问题、接入专属知识库的主流方案。

1.2 为什么需要 RAG?

RAG 主要解决大语言模型的三大核心痛点:

痛点 描述 RAG 解决方案
幻觉现象 模型自信地生成与事实不符的内容 基于检索到的真实信息生成答案
知识时效性 训练数据存在时间窗口限制,无法获取最新信息 动态更新外部知识库,无需重新训练模型
专属知识接入 无法访问企业内部文档、私有数据等 构建专属知识库,实现私有数据的安全利用
可解释性差 输出结果缺乏可追溯的知识来源 提供引用来源,支持答案验证

1.3 RAG 核心优势

  • 低成本知识更新:无需重新训练昂贵的大模型
  • 高准确性:显著减少幻觉,提升回答的事实性
  • 数据安全:私有数据无需上传用于模型训练
  • 可追溯性:答案可关联到具体的知识来源
  • 灵活性:支持多种数据源和文档格式

1.4 关键术语解释

  • 嵌入(Embedding):将文本转换为高维向量表示,保留语义信息
  • 向量数据库:专门存储和检索向量数据的数据库,支持高效的相似度搜索
  • 分块(Chunking):将长文档分割成适合向量化和检索的小片段
  • 召回(Retrieval):从知识库中找到与用户问题相关的文档片段
  • 重排序(Reranking):对初步召回的结果进行更精细的相关性排序
  • 上下文窗口:大模型一次能处理的最大文本长度

二、RAG 基本原理

2.1 混合记忆系统

RAG 的核心创新在于其独特的混合记忆系统架构,巧妙结合了两种记忆的优点:

  • 参数化记忆(Parametric Memory)

    • 存储在大语言模型的数十亿甚至数万亿个网络参数中
    • 包含预训练过程中学到的通用世界知识和语言能力
    • 优点:语言流畅性好、推理能力强
    • 缺点:静态、更新成本高、易产生幻觉
  • 非参数化记忆(Non-Parametric Memory)

    • 存储在外部知识库(如向量数据库)中
    • 包含企业文档、产品手册、最新资讯等特定知识
    • 优点:动态可更新、可解释性强、成本低
    • 缺点:缺乏语言生成能力

RAG 通过一个可微分的访问机制,使这两种记忆系统能够协同工作。模型不再仅仅依赖其 "大脑" 中的固有知识,而是学会在需要时 "查阅书籍"。

2.2 开卷考试类比

理解 RAG 最简单的方式是将其比作开卷考试

  • 大模型 = 聪明但记忆力有限的学生
  • 外部知识库 = 考试时可以查阅的课本和资料
  • RAG 系统 = 帮助学生快速找到相关知识点并组织答案的助手

在闭卷考试中(纯 LLM),学生只能依靠自己记住的知识回答问题,容易记错或答不上来。而在开卷考试中(RAG),学生可以先查阅相关资料,再基于资料给出准确的答案。

三、RAG 完整流程概览

RAG 系统的工作流程分为两个主要阶段:离线知识库构建阶段在线问答生成阶段

3.1 离线流程:知识库构建

这是一次性的预处理过程,当知识库内容更新时需要重新执行。

  1. 数据采集:收集各种格式的原始数据(PDF、Word、HTML、Markdown、数据库等)
  2. 文档解析:将不同格式的文档转换为纯文本格式
  3. 文本清洗:去除无关内容、格式标记、重复信息等
  4. 文本分块:将长文本分割成适合向量化的小片段(通常 100-1000 个 token)
  5. 向量化:使用嵌入模型将每个文本块转换为高维向量
  6. 索引构建:将向量和对应的原始文本存储到向量数据库中,建立索引

3.2 在线流程:问答生成

当用户提出问题时实时执行的过程。

  1. 问题向量化:使用相同的嵌入模型将用户问题转换为向量
  2. 检索召回:在向量数据库中搜索与问题向量最相似的 Top-K 个文本块
  3. 结果重排序(可选但推荐):使用更精确的模型对召回结果进行重新排序
  4. 上下文构建:将最相关的文本块拼接成上下文
  5. 提示词构建:将用户问题和上下文组合成符合大模型要求的提示词
  6. 答案生成:将提示词输入大模型,生成基于上下文的回答
  7. 结果返回:将生成的答案和引用来源返回给用户

3.3 朴素 RAG vs 高级 RAG

RAG 技术已经从最初的朴素版本演进到了更复杂的高级版本:

特性 朴素 RAG(Naive RAG) 高级 RAG(Advanced RAG)
检索方式 单一向量相似度搜索 混合搜索(向量 + 关键词)、多阶段检索
查询处理 直接使用原始查询 查询改写、多查询生成
结果处理 直接使用 Top-K 结果 重排序、上下文压缩、自动合并
分块策略 固定大小分块 语义分块、递归分块、滑动窗口
反馈机制 自我评估、纠错机制、迭代检索
适用场景 简单问答、小型知识库 复杂查询、大型知识库、生产环境

四、RAG 完整流程详细介绍

1. 数据处理(离线阶段)

1.1 数据采集

概念:数据采集是 RAG 系统的基础,指从多种来源获取不同格式的数据,为后续处理提供原始素材。

多源数据类型:

  • 结构化数据:有固定 schema,如数据库表、Excel 表格、CSV 文件,易于查询和分析。
  • 半结构化数据:有一定结构但不严格,如 Markdown、HTML、JSON、XML,包含标签或标记来分隔语义元素。
  • 非结构化数据:无预定义结构,如纯文本、PDF 文档、图片、音频、视频,需额外处理才能提取语义信息。

数据来源:

  • 内部文档:企业 Wiki、技术文档、会议纪要、内部报告。
  • 公开网页:通过爬虫获取的行业资讯、技术博客、公开知识库。
  • 知识库:第三方专业知识库(如维基百科、行业数据库)。
  • API 接口:通过 RESTful API、GraphQL 等获取的动态数据(如新闻 API、金融数据 API)。
1.2 数据清洗与预处理

概念:对原始数据进行格式转换、去噪、去重等操作,将其转化为统一、干净的文本格式,提升后续处理质量。

核心步骤

  1. 格式转换

    • PDF 转文本:使用 PyPDF2pdfplumber 提取文本;复杂排版 PDF 可结合 LayoutLM 等模型理解布局。
    • OCR 识别:对扫描版 PDF、图片中的文字,使用 PaddleOCRTesseract 进行识别。
    • 音频转文字:使用 OpenAI Whisper、Azure Speech 等将音频 / 视频转录为文本。
  2. 数据清洗

    • 去重:通过哈希(如 MD5)或相似度计算(如 MinHash)去除重复内容。
    • 去噪:去除页眉页脚、广告、无关链接、特殊符号(如 )。
    • 格式统一:统一大小写、标点符号(如中文全角转英文半角)、日期格式。

示例代码(PDF 转文本 + 简单清洗)

python 复制代码
import pdfplumber
import re

def pdf_to_clean_text(pdf_path):
    text = ""
    with pdfplumber.open(pdf_path) as pdf:
        for page in pdf.pages:
            text += page.extract_text() or ""
    # 简单清洗:去除多余换行和空格
    text = re.sub(r'\n+', '\n', text)
    text = re.sub(r' +', ' ', text)
    return text

clean_text = pdf_to_clean_text("example.pdf")
1.3 文本分段 (Chunking)

概念:将长文本切分为较小的、语义相对完整的片段(Chunk),是 RAG 效果的关键环节 ------ 分段过大易丢失细节,过小易破坏语义连贯性。

核心技术与挑战

  • 挑战:平衡 "语义完整性" 与 "检索粒度",避免跨段落语义断裂。

分段策略

  1. 固定长度分段

    • 按字符数(如 500 字符)或 Token 数(如 512 Token)切分,简单高效但易切断语义。
    • 示例代码(按 Token 数分段,使用 tiktoken
python 复制代码
import tiktoken

def fixed_length_chunking(text, chunk_size=512, chunk_overlap=50):
    tokenizer = tiktoken.get_encoding("cl100k_base")
    tokens = tokenizer.encode(text)
    chunks = []
    for i in range(0, len(tokens), chunk_size - chunk_overlap):
        chunk_tokens = tokens[i:i + chunk_size]
        chunks.append(tokenizer.decode(chunk_tokens))
    return chunks

chunks = fixed_length_chunking(clean_text)
  1. 语义分段

    • 基于语义边界切分:按句子(spaCynltk)、段落(\n\n)、章节(Markdown 标题 #)切分,保留语义完整性。
    • 进阶:使用 LangChainRecursiveCharacterTextSplitter,按优先级尝试分隔符(["\n\n", "\n", "。", " ", ""])。
  2. 混合分段策略

    • 固定长度 + 语义重叠:在固定长度分段基础上,让相邻 Chunk 重叠部分 Token(如 50-100 Token),避免语义丢失。

分段大小与重叠率选择原则

  • 小文档(如短文):Chunk 大小 256-512 Token,重叠率 10%-20%。
  • 长文档(如书籍):Chunk 大小 512-1024 Token,重叠率 20%-30%。
  • 需结合 Embedding 模型的最大输入长度(如 BGE-base 最大 512 Token)调整。
1.4 向量化 (Embedding)

概念:将文本片段转换为高维稠密向量(数值数组),向量能捕捉文本的语义信息 ------ 语义相似的文本,向量距离更近。

向量表示原理

  • 底层逻辑:通过预训练语言模型(如 BERT 系列),将文本映射到一个语义空间中,向量的每一维代表某种抽象语义特征(如 "科技感"、"情感倾向")。

主流嵌入模型

模型 特点 适用场景
OpenAI Embedding 效果稳定,API 调用便捷 快速原型、通用场景
BGE (BAAI) 开源中英双语效果好,支持小 / 中 / 大版本 中文场景、私有化部署
M3E 轻量级,中文优化,速度快 移动端、低资源场景
Sentence-BERT 专注语义相似度,适合句子级匹配 问答、检索系统
Cohere Embedding 支持多语言,有轻量 / 高质量版本 企业级应用

嵌入维度选择

  • 维度越高:语义表达越丰富,但存储成本高、检索速度慢。
  • 常见维度:128(轻量)、256(平衡)、512(通用)、1024+(高精度)。
  • 建议:先试 512 维度,根据效果调整。

多模态嵌入

  • 文本 - 图像:如 CLIPBLIP-2,将图像和文本映射到同一向量空间,支持 "以文搜图"。
  • 文本 - 音频:如 Wav2Vec2 + 文本模型,实现音频与文本的联合检索。

示例代码(使用 BGE 向量化)

python 复制代码
from sentence_transformers import SentenceTransformer

# 加载 BGE 模型
model = SentenceTransformer('BAAI/bge-small-zh-v1.5')

# 对文本片段向量化
chunks = ["这是第一个文本片段", "这是第二个文本片段"]
embeddings = model.encode(chunks)

print(f"向量维度: {embeddings.shape[1]}")  # 输出: 512

2. 向量存储(离线阶段)

2.1 向量数据库基础

概念:专门用于存储、索引和检索高维向量的数据库,核心能力是 "相似性搜索"------ 给定查询向量,快速返回最相似的 Top-K 向量。

核心功能

  1. 向量存储:存储向量及其对应的元数据(如原始文本、来源、时间戳)。
  2. 相似性搜索:基于向量距离快速检索相似内容。
  3. 元数据过滤:先通过元数据(如 "来源 = 内部文档")过滤,再做向量搜索,提升精度。

相似性度量方法

方法 公式 / 原理 适用场景
余弦相似度 计算向量夹角的余弦值,范围 [-1, 1] 文本语义匹配(常用)
欧氏距离 计算向量空间中的直线距离 图像、数值型数据
点积 计算向量对应元素乘积之和 归一化后的向量(等价余弦)

索引技术

索引是加速相似性搜索的关键 ------ 通过对向量进行预处理,避免 "暴力搜索"(遍历所有向量,速度慢)。

索引类型 原理 优势 劣势
HNSW 基于层次化小世界图,构建多层近邻图 搜索速度快、精度高 内存占用大
IVF 将向量聚类成多个 "倒排列表",先搜聚类中心 内存占用小、适合大规模数据 精度略低于 HNSW
FAISS Facebook 开源,支持 HNSW/IVF 等多种索引 性能极致、优化完善 需结合其他数据库使用
Annoy 基于随机投影树,内存映射存储 内存占用极低、适合静态数据 构建索引慢
ScaNN Google 开源,通过各向异性量化优化 高精度、高吞吐 配置较复杂

向量数据库 vs 传统数据库

维度 向量数据库 传统数据库(MySQL/PostgreSQL)
核心能力 相似性搜索(Top-K) 精确匹配、范围查询
数据类型 高维向量 + 元数据 结构化 / 半结构化数据
索引方式 HNSW/IVF 等向量索引 B + 树、倒排索引
适用场景 RAG 检索、推荐系统、以图搜图 事务处理、结构化查询

2.2 主流向量数据库

开源向量数据库

数据库 特点 适用场景
Chroma 轻量级,API 简单,适合快速原型 个人项目、Demo
FAISS 性能极致,但需自行封装存储和元数据 大规模数据、性能敏感场景
Milvus 企业级,支持分布式、多种索引,功能全 生产环境、大规模部署
Qdrant 支持丰富的元数据过滤,API 友好 需复杂过滤的场景
Weaviate 结合知识图谱,支持语义搜索 知识增强型 RAG

商业向量数据库

数据库 特点 适用场景
Pinecone 全托管,无需运维,弹性扩展 企业级生产环境
Zilliz Cloud Milvus 的云服务版,提供托管和监控 不想自行运维 Milvus 的用户
Weaviate Cloud Weaviate 的云服务,结合知识图谱能力 知识密集型应用
2.3 向量存储优化

索引构建策略

  • 批量构建:离线阶段一次性构建索引,适合静态数据(如历史文档),构建效率高。
  • 增量构建:实时添加新向量时动态更新索引,适合动态数据(如新闻流),需平衡实时性与性能。

数据分片与分布式部署

  • 当数据量达亿级时,通过分片(Sharding)将数据分布到多个节点,并行检索提升速度(如 Milvus、Qdrant 支持分布式)。

冷热数据分离

  • 热数据(近期高频访问):使用高性能索引(如 HNSW),存内存。
  • 冷数据(历史低频访问):使用低成本索引(如 IVF),存磁盘。

元数据设计与过滤优化

  • 元数据设计:将常用过滤条件(如 sourcedatecategory)作为元数据字段,避免全量扫描。
  • 过滤优化:使用 "预过滤"(先过滤元数据,再搜向量)或 "后过滤"(先搜向量,再过滤元数据),根据数据量选择 ------ 元数据过滤后剩余数据少用 "预过滤",否则用 "后过滤"。

示例代码(使用 Chroma 存储向量)

python 复制代码
import chromadb
from chromadb.utils import embedding_functions

# 初始化 Chroma 客户端(持久化存储)
client = chromadb.PersistentClient(path="./chroma_db")

# 使用 BGE 作为嵌入函数(需先安装 sentence-transformers)
embedding_func = embedding_functions.SentenceTransformerEmbeddingFunction(
    model_name="BAAI/bge-small-zh-v1.5"
)

# 创建集合(类似表)
collection = client.create_collection(
    name="my_documents",
    embedding_function=embedding_func,
    metadata={"hnsw:space": "cosine"}  # 使用余弦相似度
)

# 添加文档(自动向量化并存储)
collection.add(
    documents=["这是第一个文档片段", "这是第二个文档片段"],
    metadatas=[{"source": "doc1"}, {"source": "doc2"}],
    ids=["chunk1", "chunk2"]
)

# 相似性搜索
results = collection.query(
    query_texts=["查询相关内容"],
    n_results=2
)
print(results)

3.查询预处理(在线阶段)

概念:用户输入的查询往往存在表述模糊、歧义、不完整、口语化等问题,直接用于检索会导致召回率低、相关性差。查询预处理通过对原始查询进行优化,使其更符合检索系统的 "理解习惯",从而大幅提升后续检索的准确性。

3.1 基础清洗

  • 去除无关字符:特殊符号、多余空格、换行符、表情符号
  • 错别字修正:使用pycorrectorTextBlob等工具修正中文 / 英文错别字
  • 格式统一:统一大小写、标点符号(中文全角转半角)

3.2 查询改写(Query Rewriting)

将模糊、口语化的查询转换为清晰、结构化的检索式。

  • 示例
    • 原始查询:"RAG 怎么弄?"
    • 改写后:"RAG 系统的搭建步骤和核心技术是什么?"
  • 实现方式
    • 规则改写:基于模板匹配(适合常见问题)

    • 大模型改写:用轻量大模型(如 Qwen-7B、Llama-3-8B)生成改写后的查询

    • 示例代码(大模型改写):

      python 复制代码
      from openai import OpenAI
      
      client = OpenAI(base_url="http://localhost:11434/v1", api_key="ollama")
      
      def rewrite_query(query):
          prompt = f"""
          将以下用户查询改写为更适合检索的清晰、具体的问题。
          要求:保留核心语义,去除口语化表达,补充必要的上下文。
          原始查询:{query}
          改写后的查询:
          """
          response = client.chat.completions.create(
              model="qwen:7b",
              messages=[{"role": "user", "content": prompt}],
              temperature=0.1
          )
          return response.choices[0].message.content.strip()

3.3 多查询生成(Multi-Query Generation)

一个查询生成多个不同表述的检索式,从多个角度召回相关内容,解决 "语义漂移" 问题。

  • 示例
    • 原始查询:"RAG 的优缺点"
    • 生成的多查询:
      1. RAG 技术的优势和局限性是什么?
      2. 检索增强生成相比纯大模型有哪些好处和不足?
      3. RAG 系统在实际应用中的挑战和优势?
  • 实现逻辑:让大模型基于原始查询生成 3-5 个不同角度的查询,然后分别进行检索,最后合并去重结果。

3.4 查询分类与路由

先判断查询类型,再决定后续处理流程:

  • 事实性问题:直接检索知识库回答(如 "Python 的列表和元组有什么区别?")
  • 推理问题:需要多步检索和推理(如 "如何用 RAG 搭建一个企业客服系统?")
  • 闲聊问题:直接由大模型回答,无需检索
  • 超出知识库范围:直接告知用户无法回答

4.混合检索

纯向量检索虽然擅长语义匹配,但存在以下问题:

  • 语义漂移:查询和文档语义相似但关键词不同时,可能漏检
  • 精确匹配差:对专有名词、代码、数字等精确内容的匹配效果差
  • 长尾问题:对低频、小众内容的召回率低

混合检索核心架构

向量检索 + 关键词检索 + 元数据过滤,三者结合,兼顾语义匹配和精确匹配。

4.1 向量检索(语义匹配)
  • 原理:将用户查询转换为向量,在向量数据库中搜索 Top-K 相似的 Chunk
  • 优化点:
    • 动态 Top-K:根据查询复杂度调整返回数量(简单问题返回 3-5 个,复杂问题返回 10-20 个)
    • 相似度阈值过滤:过滤掉相似度低于阈值的结果(如余弦相似度 < 0.6)
    • 多向量检索:一个 Chunk 生成多个向量(标题向量、摘要向量、全文向量),分别检索后合并
4.2 关键词检索(精确匹配)
  • 原理:基于倒排索引,匹配查询和文档中的关键词

  • 主流算法:BM25(比传统 TF-IDF 效果更好)

  • 实现工具:Elasticsearch、Whoosh、LangChain 的BM25Retriever

  • 示例代码(LangChain BM25 检索):

    python 复制代码
    from langchain.retrievers import BM25Retriever
    from langchain.text_splitter import RecursiveCharacterTextSplitter
    
    # 假设documents是离线阶段处理好的文档列表
    text_splitter = RecursiveCharacterTextSplitter(chunk_size=512, chunk_overlap=50)
    splits = text_splitter.split_documents(documents)
    
    # 构建BM25检索器
    bm25_retriever = BM25Retriever.from_documents(splits)
    bm25_retriever.k = 10
    
    # 检索
    bm25_results = bm25_retriever.invoke("RAG的分段策略")
4.3 元数据过滤(缩小检索范围)
  • 原理:先通过元数据过滤掉不相关的文档,再进行向量 / 关键词检索

  • 常用元数据字段:source(来源)、date(时间)、category(类别)、author(作者)

  • 示例(Chroma 元数据过滤):

    python 复制代码
    # 只检索2024年发布的内部文档
    results = collection.query(
        query_texts=["RAG的向量数据库选择"],
        n_results=10,
        where={"source": "内部文档", "date": {"$gte": "2024-01-01"}}
    )
4.4 结果融合

将向量检索和关键词检索的结果合并,去除重复项。常用融合算法:

  • Reciprocal Rank Fusion (RRF):根据两个检索结果的排名计算综合得分,效果最好
  • 加权求和:给向量检索和关键词检索分配不同的权重(如向量 0.7,关键词 0.3)

混合检索示例代码(LangChain)

python 复制代码
from langchain.retrievers import EnsembleRetriever
from langchain_community.vectorstores import Chroma
from langchain_community.embeddings import HuggingFaceEmbeddings

# 初始化向量检索器
embeddings = HuggingFaceEmbeddings(model_name="BAAI/bge-small-zh-v1.5")
vectorstore = Chroma.from_documents(splits, embeddings, persist_directory="./chroma_db")
vector_retriever = vectorstore.as_retriever(search_kwargs={"k": 10})

# 初始化BM25检索器(同上)
bm25_retriever = BM25Retriever.from_documents(splits)
bm25_retriever.k = 10

# 构建混合检索器(RRF融合)
ensemble_retriever = EnsembleRetriever(
    retrievers=[vector_retriever, bm25_retriever],
    weights=[0.6, 0.4]
)

# 混合检索
mixed_results = ensemble_retriever.invoke("RAG的分段策略")

5. 检索结果重排序

概念: 混合检索返回的 Top-K 结果中,往往有很多不相关或相关性较低的内容。重排序使用一个更强大的交叉编码器(Cross-Encoder) ,对查询和每个检索结果进行逐对相似度打分,然后重新排序,将最相关的结果排在前面。

为什么不直接用交叉编码器做检索?

  • 交叉编码器效果好,但速度慢(O (n) 复杂度),无法处理大规模数据
  • 正确的做法是:先用快速的检索器(向量 + BM25)召回前 20-50 个结果,再用重排序模型精排,平衡速度和效果

主流重排序模型

模型 特点 适用场景
BGE-Reranker 开源中英双语效果最好,支持小 / 中 / 大版本 中文场景、私有化部署
Cohere Rerank API 调用便捷,效果稳定 企业级应用、快速原型
CrossEncoder Sentence-BERT 系列,支持多语言 通用场景
Jina Reranker 轻量级,速度快 低延迟场景

重排序实现与优化

示例代码(BGE-Reranker)

python 复制代码
from sentence_transformers import CrossEncoder

# 加载重排序模型
reranker = CrossEncoder("BAAI/bge-reranker-small")

def rerank_results(query, results, top_n=5):
    # 构建查询-文档对
    pairs = [(query, doc.page_content) for doc in results]
    # 计算相似度得分
    scores = reranker.predict(pairs)
    # 按得分排序
    ranked_results = sorted(zip(scores, results), key=lambda x: x[0], reverse=True)
    # 返回前top_n个结果
    return [doc for score, doc in ranked_results[:top_n]]

# 使用
reranked_results = rerank_results("RAG的分段策略", mixed_results, top_n=5)

优化策略

  1. 多级重排序:先用轻量模型(如 BGE-Reranker-small)粗排,再用重模型(如 BGE-Reranker-large)精排
  2. 结合元数据重排序:给权威来源、最新文档额外加分(如内部文档 + 0.1 分,2024 年文档 + 0.05 分)
  3. 批量处理:一次处理多个查询 - 文档对,提升吞吐量

6.上下文构建

**概念:**将重排序后的 Chunk 拼接成大模型可以理解的上下文,核心挑战是:

  • 上下文窗口限制:大模型的上下文长度有限(如 GPT-3.5-turbo 是 16K,Llama-3-8B 是 8K)
  • Lost in the Middle 现象:大模型对上下文开头和结尾的内容记忆更好,中间的内容容易被忽略
  • 信息冗余:多个 Chunk 可能包含重复信息,浪费上下文窗口
6.1 核心技术

上下文拼接与组织

  • 按相关性排序:将最相关的 Chunk 放在最前面,次相关的放在后面

  • 添加元数据标注 :给每个 Chunk 添加来源信息,方便大模型引用和用户溯源

    plaintext

    复制代码
    [来源:RAG技术白皮书.pdf,第3页]
    文本分段是RAG效果的关键环节,分段过大易丢失细节,过小易破坏语义连贯性。
    
    [来源:LangChain官方文档,2024-01-15]
    推荐使用RecursiveCharacterTextSplitter进行文本分段,它会按优先级尝试不同的分隔符。
  • 添加逻辑分隔符 :用---\n\n分隔不同的 Chunk,让大模型清楚区分不同的信息块

上下文窗口管理

  • 动态截断:计算上下文的 Token 数,超过大模型窗口限制时,截断相关性最低的 Chunk
  • 滑动窗口:对于超长文档,使用滑动窗口逐段检索和生成
  • 上下文压缩 :用轻量大模型对每个 Chunk 进行摘要,保留核心信息,减少 Token 占用
    • 示例:LangChain 的ContextualCompressionRetriever

解决 "Lost in the Middle"

  • 将最相关的 Chunk 放在上下文的开头和结尾
  • 重要信息重复出现(如在开头和结尾都提到核心结论)
  • 使用结构化的上下文(如分点、编号),让大模型更容易提取信息

7. 提示词工程

7.1 RAG 专用提示词结构

一个好的 RAG 提示词应该包含以下 5 个部分:

  1. 系统角色定义:明确大模型的身份和任务
  2. 回答规则约束:核心是 "只能使用提供的上下文回答,不知道就说不知道"
  3. 上下文信息:检索到的相关内容
  4. 用户查询:用户的原始问题
  5. 输出格式要求:如分点回答、引用来源、使用 Markdown 格式
7.2 标准 RAG 提示词模板
python 复制代码
你是一个专业的知识助手,只能基于以下提供的上下文内容回答用户的问题。

【回答规则】
1. 严格使用上下文中的信息,不得编造内容
2. 如果上下文中没有相关信息,直接回答"抱歉,我没有找到相关信息"
3. 回答要简洁明了,逻辑清晰,分点阐述
4. 重要信息请标注来源,格式为[来源:文档名,页码/日期]

【上下文】
{context}

【用户问题】
{query}

【回答】
7.3 提示词优化技巧
  1. 明确禁止幻觉:反复强调 "不得编造内容"、"不知道就说不知道"

  2. 引用标注要求:强制要求大模型标注信息来源,方便后续校验

  3. 思维链引导 :对于复杂问题,要求大模型一步步推理

    复制代码
    请先分析上下文内容,然后分步骤回答问题,每一步都要有依据。
  4. 格式约束:明确要求输出格式(如 Markdown、JSON),提升回答的可读性

  5. 示例引导:给一个正确的回答示例,让大模型模仿

8.大模型生成与结果后处理

8.1 大模型参数调优

RAG 场景下,大模型参数的设置原则是保证准确性和一致性

  • 温度(temperature):0-0.3,越低越准确,避免生成随机内容
  • Top-p:0.1-0.5,限制生成的 Token 范围
  • 最大生成长度:根据回答长度需求设置,一般不超过 2048 Token
8.2 流式输出

为了提升用户体验,建议使用大模型的流式输出功能,让用户可以实时看到回答的生成过程。

  • 示例代码(OpenAI 流式输出): python

    运行

    python 复制代码
    response = client.chat.completions.create(
        model="gpt-3.5-turbo",
        messages=messages,
        stream=True
    )
    
    for chunk in response:
        if chunk.choices[0].delta.content:
            print(chunk.choices[0].delta.content, end="")
8.3 结果校验与幻觉检测

即使有 RAG,大模型仍然可能生成幻觉,需要进行结果校验:

  1. 引用校验:检查回答中的每个引用是否在上下文中存在

  2. 事实校验:用事实校验模型(如 FactScore)检查回答的准确性

  3. 自我检查 :让大模型自己检查回答是否符合上下文

    plaintext

    复制代码
    请检查以下回答是否严格基于提供的上下文,如果有编造的内容,请修改。
    上下文:{context}
    回答:{answer}
    修改后的回答:
  4. 多模型交叉验证:用多个不同的大模型生成回答,对比一致性

五、完整 RAG 系统全流程代码示例

以下是一个基于 LangChain+Chroma+BGE+Qwen 的完整 RAG 系统实现,整合了离线 + 在线所有核心环节:

python 复制代码
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.document_loaders import PyPDFLoader
from langchain_community.vectorstores import Chroma
from langchain_community.embeddings import HuggingFaceEmbeddings
from langchain.retrievers import EnsembleRetriever, BM25Retriever
from langchain_community.llms import Ollama
from langchain.chains import RetrievalQA
from langchain.prompts import PromptTemplate
from sentence_transformers import CrossEncoder

# ---------------------- 离线阶段(已提前执行) ----------------------
# 1. 数据加载与清洗
loader = PyPDFLoader("RAG技术白皮书.pdf")
documents = loader.load()

# 2. 文本分段
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=512,
    chunk_overlap=50,
    separators=["\n\n", "\n", "。", " ", ""]
)
splits = text_splitter.split_documents(documents)

# 3. 向量化与存储
embeddings = HuggingFaceEmbeddings(model_name="BAAI/bge-small-zh-v1.5")
vectorstore = Chroma.from_documents(
    documents=splits,
    embedding=embeddings,
    persist_directory="./chroma_db"
)
vectorstore.persist()

# ---------------------- 在线阶段 ----------------------
# 1. 初始化检索器
vector_retriever = vectorstore.as_retriever(search_kwargs={"k": 10})
bm25_retriever = BM25Retriever.from_documents(splits)
bm25_retriever.k = 10
ensemble_retriever = EnsembleRetriever(
    retrievers=[vector_retriever, bm25_retriever],
    weights=[0.6, 0.4]
)

# 2. 初始化重排序模型
reranker = CrossEncoder("BAAI/bge-reranker-small")

# 3. 自定义检索链(包含重排序)
class RerankRetrievalQA(RetrievalQA):
    def _get_docs(self, query):
        # 混合检索
        docs = self.retriever.invoke(query)
        # 重排序
        pairs = [(query, doc.page_content) for doc in docs]
        scores = reranker.predict(pairs)
        ranked_docs = sorted(zip(scores, docs), key=lambda x: x[0], reverse=True)
        return [doc for score, doc in ranked_docs[:5]]

# 4. 初始化大模型
llm = Ollama(model="qwen:7b", temperature=0.1)

# 5. 构建提示词
prompt_template = """
你是一个专业的RAG技术助手,只能基于以下提供的上下文内容回答用户的问题。

【回答规则】
1. 严格使用上下文中的信息,不得编造内容
2. 如果上下文中没有相关信息,直接回答"抱歉,我没有找到相关信息"
3. 回答要简洁明了,逻辑清晰,分点阐述
4. 重要信息请标注来源,格式为[来源:{source}]

【上下文】
{context}

【用户问题】
{question}

【回答】
"""
PROMPT = PromptTemplate(
    template=prompt_template,
    input_variables=["context", "question"]
)

# 6. 构建RAG链
rag_chain = RerankRetrievalQA.from_chain_type(
    llm=llm,
    chain_type="stuff",
    retriever=ensemble_retriever,
    return_source_documents=True,
    chain_type_kwargs={"prompt": PROMPT}
)

# 7. 运行RAG系统
query = "RAG系统的文本分段策略有哪些?如何选择合适的分段大小?"
result = rag_chain.invoke(query)

print("回答:")
print(result["result"])
print("\n来源文档:")
for doc in result["source_documents"]:
    print(f"- {doc.metadata['source']},第{doc.metadata['page']}页")

六、RAG 面试题全集

1. 什么是 RAG?全称 & 核心思想

解答 RAG:Retrieval-Augmented Generation,检索增强生成。核心思想:

  1. 大模型存在知识截止日期、幻觉、私有知识缺失问题;
  2. 不直接让大模型凭空回答,先从私有知识库 / 外部文档中检索和问题相关的真实上下文;
  3. 将「用户问题 + 检索到的上下文」一起塞入大模型 Prompt;
  4. 大模型基于给定真实资料生成答案,实现:
  • 减少幻觉
  • 实时 / 私有知识问答
  • 可溯源、可解释

2. RAG 与 微调(Fine-tune)的区别 & 选型

解答

表格

维度 RAG 微调 FT
原理 实时检索外部知识,上下文约束生成 修改模型权重,内化知识 / 风格
知识更新 增量更新文档即可,实时生效 需重新训练,成本高、更新慢
数据要求 少量文档即可,无需标注 需要大量高质量标注数据
成本 低,推理阶段增加检索开销 算力 / 时间 / 人力成本极高
可控性 答案可溯源、内容可控 容易过拟合、难溯源
适用场景 私有知识库、文档问答、实时资讯 对话风格、任务对齐、特定能力强化

选型结论

  • 知识问答、企业知识库、文档咨询 → 优先 RAG
  • 固定话术、角色定制、指令跟随强化 → 搭配微调

3. RAG 能解决大模型哪些核心问题?

  1. 幻觉问题:强制基于检索真实文档生成,减少编造;
  2. 知识时效性:模型训练数据有时间截止,RAG 接入最新文档;
  3. 私有域知识:企业内部文档、业务数据不泄露、不依赖模型预训练;
  4. 上下文长度限制:通过分段检索,突破单轮上下文窗口限制;
  5. 合规可追溯:回答内容来自指定文档,便于审计合规。

4. 原生大模型、RAG、Agent 的关系

  • 原生大模型:仅靠预训练知识,能力上限固定;
  • RAG:给模型喂参考资料,解决知识缺失;
  • Agent:给模型规划 & 工具调用能力(检索、联网、代码、数据库);

实际业务:Agent + RAG 是主流,RAG 是 Agent 最核心的工具之一。

5. 标准 RAG 完整流程是什么?

两大阶段:离线预处理阶段 + 在线推理阶段

(1)离线数据预处理

  1. 文档加载:PDF/Word/Markdown/ 网页 / 数据库文本解析;
  2. 文本清洗:去空格、乱码、页眉页脚、特殊符号、无效内容;
  3. 文本分块(Chunk):长文档切分成固定长度片段;
  4. Embedding 向量化:将文本块转为向量;
  5. 向量入库:向量存入向量数据库,同时保存原始文本、元数据。

(2)在线问答推理

  1. 用户问题输入;
  2. 问题向量化:Query 生成 Embedding;
  3. 向量检索:向量库相似度匹配,召回 Top-K 文本块;
  4. 重排序(Rerank):对召回结果精细化打分,过滤无关内容;
  5. Prompt 拼接:指令模板 + 检索上下文 + 用户问题;
  6. LLM 生成:大模型输出答案;
  7. (可选)答案引用、溯源、格式整理。

6. 什么是文本分块(Chunking)?为什么必须分块?

解答 Chunking:将超长文档切分为固定长度、语义相对完整的短文本片段。

必要性:

  1. Embedding 模型有最大输入长度限制
  2. 向量检索以「块」为最小单位,整块语义更完整;
  3. 避免上下文过长超出 LLM 窗口,降低推理成本;
  4. 提升检索精准度:细粒度片段更容易匹配局部问题。

常见分块方式

  • 固定长度切分(滑动窗口):简单粗暴,通用;
  • 语义分块:基于语义相似度分割,语义完整性更高;
  • 规则分块:按标题、段落、章节、特殊分隔符分割;
  • 层级分块:大块 + 小块混合(父子块 RAG)。

7. 分块大小如何选择?影响是什么?

  • 块太小:语义碎片化,上下文不足,回答不完整;
  • 块太大 :冗余信息多,检索噪声大,超出 LLM 上下文,推理慢;经验值
  • 通用场景:300~800 token;
  • 长文档 / 技术文档:500~1000 token;
  • 短问答 / 话术:200~400 token。

8. 什么是 Embedding?工作原理

解答 Embedding:将自然语言文本 映射为低维稠密向量,语义相似的文本,向量空间距离更近。

  • 输入:一段文本;
  • 输出:固定长度浮点向量(如 768/1024/2048 维);
  • 核心:语义向量化,让机器可计算文本相似度。

9. 常见向量相似度计算方式 & 区别

  1. **余弦相似度(Cosine)**最常用,忽略向量长度,只关注方向,适合文本 Embedding;
  2. **欧氏距离(Euclidean)**关注绝对距离,受向量模长影响大;
  3. **点积(Dot Product)**适合归一化后的向量,计算速度快;

工业级 RAG:统一使用余弦相似度

10. 为什么检索到相似文本,实际却不相关?(检索失效)

常见原因:

  1. 语义鸿沟:Embedding 模型能力弱,字面相似、语义不相关;
  2. 分块不合理:切分破碎、语义断裂;
  3. Query 未优化:用户问题口语化、太短、歧义多;
  4. 向量库召回策略单一:仅向量检索,缺乏关键词补充;
  5. 领域不匹配:通用 Embedding 不适配垂直业务(医疗 / 法律 / 金融)。

11. 什么是 稀疏检索 & 稠密检索?区别

  • 稠密检索(Dense) :基于 Embedding 向量匹配,语义级召回,擅长语义同义、改写问题
  • 稀疏检索(Sparse) :基于关键词匹配(BM25、TF-IDF),字面匹配,擅长专有名词、数字、专业术语

业界最优方案:混合检索(BM25 + 向量检索),互补短板。

12. 什么是 Rerank 重排序?作用是什么

解答 Rerank 重排序:向量检索粗召回 Top-K(如 10~20 条)后,使用轻量打分模型对所有召回文档和问题做精细语义匹配打分,重新排序、过滤低相关内容。

核心作用:

  1. 剔除向量召回的噪声、弱相关文档;
  2. 提升上下文质量,减少 LLM 负担;
  3. 大幅提升 RAG 答案准确率;
  4. 成本低:Rerank 模型小、推理快。

典型链路:向量召回Top20 → Rerank 打分筛选Top5 → 送入LLM

13. 常用向量数据库有哪些?选型对比

主流向量库

  • 开源轻量:FAISS、Chroma、Qdrant、Milvus Lite
  • 企业级分布式:Milvus、Weaviate、PGVector(Postgres 插件)
  • 云服务:阿里云向量检索、腾讯云向量库、Pinecone

选型原则

  • 小体量、快速落地:Chroma / Qdrant / FAISS
  • 大数据量、高并发、持久化:Milvus
  • 原有 PG 数据库生态:PGVector

14. 向量库索引类型?作用

  • 暴力检索(FLAT):全量比对,精准、慢,小数据量用;
  • ANN 近似最近邻索引 :牺牲一点点精度,换百倍提速,工业标配;如:IVF、HNSW、ANNOY,HNSW 是目前 RAG 最常用索引。

15. 向量数据库和传统数据库区别

  • 传统库:擅长结构化数据、精准条件查询;
  • 向量库:擅长亿级向量快速相似度检索,专为高维向量优化;
  • 向量库一般同时支持:向量检索 + 标量过滤(时间、文档来源、权限)。

16. 基础 RAG 有哪些缺点?

  1. 单轮向量检索语义能力有限;
  2. 上下文拼接过长,LLM 忽略关键信息;
  3. 无法处理复杂多轮、拆分问题;
  4. 无法理解文档层级、表格、图片、复杂结构;
  5. 容易引入无关上下文,造成干扰。

17. 主流高级 RAG 方案 原理简述

  1. Hybrid RAG 混合检索向量检索 + BM25 关键词检索融合,解决纯语义丢失关键词问题;
  2. Query 改写 / 扩展对用户问题做:同义改写、拆分、补充关键词、扩写,提升召回;
  3. Rerank 重排序粗召回 + 精排,过滤噪声;
  4. 上下文压缩 / 摘要对检索内容精简,剔除冗余,缩短上下文;
  5. **层级 RAG(父子块)**大块存概要,小块存细节,先搜大块定位章节,再搜小块精准内容;
  6. 多模态 RAG图文解析、表格解析、OCR + 向量检索;
  7. Self-RAG / 自适应 RAG模型自行判断是否需要检索、检索几轮、是否需要重查。

18. 多轮对话下的 RAG 如何实现?

难点:历史对话有上下文,单 Question 无法代表真实意图。方案:

  1. 问题压缩 & 重构 :将历史对话 + 当前问题,改写为独立完整问题
  2. 用重构后的问题做向量检索;
  3. Prompt 同时带入「历史对话 + 检索文档 + 当前问题」。

19. 如何处理表格 / 图片 / PDF 复杂文档 RAG?

  • PDF:版式解析、去除水印、按章节分块;
  • 表格:表格结构化解析,转为 Markdown/JSON 再分块嵌入;
  • 图片:OCR 提取文字 + 多模态 Embedding;
  • 版式复杂文档:使用专业文档解析器(LayoutLM、PDF-Parse)。

20. RAG 场景下 Prompt 模板核心要素

必备组成:

  1. 角色指令:你是专业知识库问答助手;
  2. 约束规则:只能基于参考资料回答,资料没有就说不知道,禁止编造;
  3. 参考上下文:RAG 检索回来的片段;
  4. 用户问题
  5. 输出要求:简洁、分点、标注来源、禁止冗余。

21. 如何抑制 RAG 幻觉?

  1. Prompt 强约束:无依据内容禁止回答、明确告知局限性;
  2. 提升检索质量:混合检索 + Rerank,保证上下文相关;
  3. 答案溯源:输出时标注引用文档片段;
  4. 上下文精简:过滤无关噪声,减少模型混淆;
  5. 事实校验:Agent 模式下二次检索验证关键信息。

22. RAG 性能瓶颈在哪里?

  1. Embedding 推理速度(大批量文档入库慢);
  2. 向量库检索延迟(高并发下);
  3. Rerank 模型推理耗时;
  4. LLM 生成速度;
  5. 上下文过长导致推理 Token 成本高。

23. 优化 RAG 推理速度的手段

  1. Embedding/Rerank 模型量化、蒸馏、部署加速(TensorRT/ONNX);
  2. 向量库合理建索引(HNSW),加缓存;
  3. 限制召回数量,精简上下文;
  4. 高频问题缓存问答结果;
  5. 离线预计算所有文档向量,线上只做检索。

24. RAG 如何做效果评估?

自动指标

  • 检索层:召回率 Recall、精准率 Precision、MRR;
  • 生成层:RAGAS、TruLens、BLEU、ROUGE、语义相似度;人工评估
  • 答案准确性、完整性、是否幻觉、引用正确性。

25. 什么是上下文窗口污染?如何解决

定义:检索大量无关、冗余上下文塞进 Prompt,模型被干扰,回答跑偏、遗漏重点。解决:

  • Rerank 过滤低相关内容;
  • 上下文压缩、摘要;
  • 限制最大上下文长度;
  • 层级检索缩小范围。

26. 垂直领域 RAG 效果差怎么优化?

  1. 替换领域专属 Embedding 模型(法律 / 医疗 / 金融);
  2. 优化分块策略,贴合行业文档结构;
  3. 加入行业词典、专有名词增强稀疏检索;
  4. 微调领域 Rerank 模型;
  5. 清洗高质量领域语料,优化文档质量。

27. RAG 安全与权限怎么做?

  • 文档打标签:部门、权限、用户角色;
  • 向量检索时附加标量过滤,只召回当前用户有权限的文档;
  • 敏感内容脱敏,入库前屏蔽隐私数据。
相关推荐
果汁华2 小时前
Claude Agent SDK Python:构建自主 AI 代理的官方引擎
开发语言·人工智能·python
常利兵2 小时前
安卓启动页Logo适配秘籍:告别“奇形怪状”的展示
android·java·开发语言
User_芊芊君子2 小时前
从零入门!MySQL 约束、范式设计与联合查询核心精讲
数据库·人工智能·mysql
生物信息与育种2 小时前
JIPB | 一个表观多组学整合分析与可视化工具OmicsCanvas
运维·人工智能·算法·自动化·transformer
xinlianyq2 小时前
DeFi监管框架落地,美国认定多数代币为大宗商品
大数据·人工智能·区块链
Eloudy2 小时前
ubuntuclaude code 基于 kimi 2.6 入门
机器学习
小仙女的小稀罕2 小时前
实习工作例会口碑方案推荐 | 经筛选的实用选择参考
人工智能
未来智慧谷2 小时前
HappyHorse-1.0全球登顶:AI视频生成技术拆解与API接入指南(2026年4月)
人工智能·阿里云·ai视频·happyhorse
程序员阿明2 小时前
spring boot3集成企业微信推送消息
java·spring boot·企业微信