Skip to content

章节3:RAG检索增强生成


学习目标

  • 理解 RAG 架构原理与"检索+生成"协同优势
  • 掌握文档切片与分块策略(语义分块 / 重叠分块)
  • 了解 Embedding 模型与向量数据库(text2vec / BGE / ChromaDB)
  • 理解召回排序与重排序 Reranker(粗排 + 精排)
  • 掌握引用溯源与答案质量优化方法

核心知识点

3.1 RAG 架构原理与优势

定义

RAG(Retrieval-Augmented Generation,检索增强生成):在 LLM 生成回答前,先从外部知识库中检索相关文档片段,将其作为上下文注入提示词,让模型基于检索到的信息进行生成。

用户问题


┌─────────────┐    ┌──────────────┐
│  向量化检索  │───▶│  关联上下文   │
│ (Embedding)  │    │  (Top-K文档)  │
└─────────────┘    └──────┬───────┘


             ┌────────────────────┐
             │  LLM 生成回答      │
             │  (问题 + 上下文)   │
             └────────────────────┘

RAG 的核心优势

对比维度传统 LLMRAG 方案
知识时效性训练数据截止日期实时更新知识库
幻觉问题可能编造事实检索结果约束,降低幻觉
可解释性黑盒输出可溯源至原文
领域适配需全量微调只需更新知识库

代码示例:RAG 流程示意

python
# RAG 简化流程
def rag_query(query, knowledge_base, embedder, llm, top_k=3):
    """
    RAG 查询流程
    """
    # 1. 将查询向量化
    query_vector = embedder.encode(query)
    
    # 2. 在知识库中检索最相关片段
    results = knowledge_base.search(query_vector, top_k=top_k)
    
    # 3. 构建上下文
    context = "\n\n".join([r["content"] for r in results])
    
    # 4. 构造增强提示
    augmented_prompt = f"""
请基于以下参考信息回答问题。如果参考信息不足以回答,请诚实地说"信息不足"。

参考信息:
{context}

问题:{query}

回答:
"""
    
    # 5. 调用 LLM 生成
    answer = llm.generate(augmented_prompt)
    
    return {
        "answer": answer,
        "sources": [r["source"] for r in results]
    }

3.2 文档切片与分块策略

定义

文档分块(Chunking)是 RAG 的基础环节,直接影响检索质量:

策略方法适用场景
固定长度分块按字符/Token 数切分通用场景,实现简单
重叠分块相邻块保留部分重叠内容防止关键信息被截断
语义分块按自然段落/章节切分长文档、教材、论文
递归分块先按段落切,再调整大小LangChain 推荐策略

代码示例:不同分块策略实现

python
# 固定长度分块
def fixed_size_chunk(text, chunk_size=500, overlap=50):
    """固定长度分块,带重叠"""
    chunks = []
    start = 0
    while start < len(text):
        end = start + chunk_size
        chunk = text[start:end]
        chunks.append(chunk)
        start += chunk_size - overlap
    return chunks

# 语义分块(按段落)
def semantic_chunk(text):
    """按段落切分,保留语义完整性"""
    paragraphs = text.split("\n\n")
    chunks = []
    for para in paragraphs:
        para = para.strip()
        if len(para) > 50:  # 过滤过短段落
            chunks.append(para)
    return chunks

# LangChain 递归分块
# pip install langchain-text-splitters
from langchain_text_splitters import RecursiveCharacterTextSplitter

text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=500,
    chunk_overlap=50,
    separators=["\n\n", "\n", "。", ".", " ", ""],
    length_function=len,
)

# document_chunks = text_splitter.split_text(long_document_text)
# print(f"文档被切分为 {len(document_chunks)} 个块")

3.3 Embedding 模型与向量数据库

定义

Embedding 模型:将文本转换为固定维度的高维向量,语义相近的文本在向量空间中距离更近。

模型维度特点推荐场景
text2vec-base-chinese768中文语义理解好中文 RAG
BGE-large-zh1024BAAI出品,中文最佳企业级中文场景
text-embedding-3-small1536OpenAI 通用模型多语言、英文场景

向量数据库:专为向量相似度搜索优化(余弦相似度、内积、欧氏距离)。

代码示例:ChromaDB 完整使用

python
# pip install chromadb sentence-transformers

import chromadb
from sentence_transformers import SentenceTransformer

# 1. 初始化 Embedding 模型
encoder = SentenceTransformer("BAAI/bge-large-zh-v1.5")
print(f"向量维度: {encoder.get_sentence_embedding_dimension()}")

# 2. 创建 ChromaDB 客户端
client = chromadb.PersistentClient(path="./chroma_db")

# 3. 创建或获取集合
collection = client.get_or_create_collection(
    name="course_materials",
    metadata={"hnsw:space": "cosine"}  # 余弦相似度
)

# 4. 添加文档
documents = [
    {"id": "doc1", "text": "深度学习是机器学习的一个分支...", "source": "intro.pdf"},
    {"id": "doc2", "text": "Transformer架构由编码器和解码器组成...", "source": "transformer.pdf"},
]

for doc in documents:
    embedding = encoder.encode(doc["text"]).tolist()
    collection.add(
        embeddings=[embedding],
        documents=[doc["text"]],
        metadatas=[{"source": doc["source"]}],
        ids=[doc["id"]]
    )

# 5. 检索
query = "什么是Transformer"
query_embedding = encoder.encode(query).tolist()
results = collection.query(
    query_embeddings=[query_embedding],
    n_results=2,
    include=["documents", "metadatas", "distances"]
)

print("检索结果:")
for i, (doc, meta, dist) in enumerate(zip(
    results["documents"][0],
    results["metadatas"][0],
    results["distances"][0]
)):
    print(f"{i+1}. [距离={dist:.4f}] {doc[:100]}...\n   来源: {meta['source']}")

3.4 召回排序与重排序 Reranker

定义

RAG 典型采用两阶段检索

阶段方法目的特点
粗排(Retrieval)Embedding 向量检索快速召回 Top-50(海选)速度快,精度一般
精排(Rerank)Cross-Encoder 重排序对 Top-50 精确排序精度高,速度慢

Reranker 模型:将查询和候选文档拼接输入,计算精确的相关性分数。

代码示例:Reranker 使用

python
# pip install sentence-transformers

from sentence_transformers import CrossEncoder

# 加载 Reranker 模型
reranker = CrossEncoder("BAAI/bge-reranker-v2-m3")

query = "Python中的装饰器是什么"

# 假设这是粗排召回的结果
candidates = [
    "装饰器是Python中一个非常强大的功能...",
    "Python中的列表推导式是一种简洁语法...",
    "Python装饰器本质上是一个函数,它接受一个函数作为参数...",
    "Python中的异常处理使用try-except语句..."
]

# 计算相关性分数
pairs = [[query, doc] for doc in candidates]
scores = reranker.predict(pairs)

# 按分数排序
ranked = sorted(zip(candidates, scores), key=lambda x: x[1], reverse=True)

print("重排序结果:")
for doc, score in ranked:
    print(f"分数: {score:.4f} | {doc[:60]}...")

3.5 引用溯源与答案质量优化

定义

RAG 的核心可信度保障——每个回答都必须能追溯到原始文档

质量优化策略

问题解决方案
检索不相关调整分块大小、增加 Top-K、使用 Reranker
上下文过长压缩 Prompt、关键信息提取
答案无引用要求 LLM 输出引用源(Source Citation)
多跳问题拆解为子问题,多轮检索(Multi-Hop RAG)

代码示例:带引用的 RAG 回答

python
def rag_with_citation(query, collection, encoder, llm, top_k=5):
    """
    带引用溯源的 RAG 查询
    """
    # 检索
    q_vec = encoder.encode(query).tolist()
    results = collection.query(
        query_embeddings=[q_vec],
        n_results=top_k,
        include=["documents", "metadatas", "distances"]
    )
    
    docs = results["documents"][0]
    metas = results["metadatas"][0]
    
    # 构建带编号的上下文
    context_parts = []
    for i, (doc, meta) in enumerate(zip(docs, metas)):
        context_parts.append(f"[文档{i+1}] (来源: {meta['source']})\n{doc}")
    
    context = "\n\n".join(context_parts)
    
    # 要求 LLM 引用来源
    prompt = f"""
请基于以下参考文档回答问题。在回答中,请在每个观点后用 [文档编号] 标注来源。

参考文档:
{context}

问题:{query}

回答(请标注引用来源):
"""
    
    answer = llm.generate(prompt)
    return answer

# 输出示例:
# "Python装饰器本质上是一个函数[文档1],它允许我们在不修改原函数代码的情况下扩展功能[文档3]。"

小结与练习

小结

  • RAG 通过"检索+生成"协同,解决了 LLM 知识时效性、幻觉、可解释性问题
  • 文档分块策略直接影响检索质量,重叠分块和语义分块是主流方案
  • Embedding 模型(BGE/text2vec)+ 向量数据库(ChromaDB)构成了 RAG 的检索基础
  • 两阶段检索(粗排+精排/重排序)能显著提升召回的精确度
  • 引用溯源是 RAG 可信度的关键保障

练习题

  1. 基础题:简述 RAG 架构的核心流程(至少 4 步),并说明每步的作用。
  2. 实践题:使用 ChromaDB + BGE Embedding 实现一个本地文件检索系统,支持文档添加和语义搜索。
  3. 实践题:在基础 RAG 上增加 Reranker 重排序,对比有无重排序的回答质量差异。
  4. 分析题:你的 RAG 系统回答总是不准确,请列出至少 3 种可能的故障原因和对应的排查方向。

Python 学习资料