RAG 与混合检索全面解析:从零基础到上手实践

本文将以通俗易懂的方式,详细讲解 RAG(检索增强生成)的定义、完整流程,以及混合检索在 RAG 中的作用。内容结合生动案例和 Python 代码示例,适合初学者和开发者学习!

一、什么是 RAG?

RAG(Retrieval-Augmented Generation,检索增强生成)是一种结合了信息检索和生成式大模型的技术框架,

旨在提升大模型生成答案的准确性、相关性和实时性。它通过从外部知识库中检索相关信息,结合大模型的语言能力, 生成更高质量的回答。

通俗比喻
RAG 就像一个图书管理员和作家的完美搭档。你提出问题,图书管理员从图书馆里找出最相关的书籍章节(检索模块),

然后作家根据这些内容写出流畅的回答(生成模块)。相比只靠作家自己的记忆,RAG 的回答更精准、更贴近最新信息。

为什么需要 RAG?

  • 大模型的局限:大模型的知识基于训练数据,可能过时。例如,2025 年 5 月的最新政策,大模型可能不知道。
  • 外部知识库的优势:通过检索文档、数据库或网页,RAG 引入实时信息。
  • 应用场景:智能客服、学术助手、企业文档查询等。例如,企业 RAG 系统可从内部文档检索最新福利政策,回答员工问题。

二、RAG 的完整流程

RAG 的核心是 检索生成 的协作。以下是 RAG 的五个步骤,我们以“旅游问答助手”为例,讲解每个步骤。

2.1 流程步骤

  1. 用户输入查询(Query)
    用户提问,例如:“2025 年去瑞士旅游需要注意哪些新政策?”

  2. 检索相关文档(Retrieval)
    系统将问题转为查询向量,使用检索算法从知识库中找到相关文档。知识库可能包含政策、新闻等。
    技术细节

    • 使用嵌入模型(如 Sentence-BERT)将查询和文档转为向量。
    • 通过向量数据库(如 Chroma)进行相似性搜索。
  3. 文档处理与上下文构建(Context Building)
    筛选检索到的文档,构建简洁的上下文。
    例子:提取瑞士 2025 年签证政策,去掉无关景点介绍。

  4. 生成答案(Generation)
    将查询和上下文输入大模型,生成回答。
    技术细节:提示示例:“根据以下文档,回答用户问题:{文档内容} 用户问题:{查询}”。

  5. 输出与优化(Output and Refinement)
    输出答案,并通过后处理优化,确保简洁、准确。

2.2 举例:旅游问答助手的 RAG 流程

用户问题:“2025 年去瑞士旅游需要注意哪些新政策?”

  1. 输入:系统接收问题。
  2. 检索:在旅游知识库中搜索,找到签证新规和入境要求。
  3. 上下文:提取:“2025 年瑞士免签国家增至 70 个;入境需健康码。”
  4. 生成:“2025 年瑞士免签国家增至 70 个,停留不超过 90 天。入境需健康码,建议通过官方 APP 申请。例如,张女士 5 月访日,

需提前 7 天提交健康码。”
5. 输出:返回友好、易懂的答案。

2.3 Python 代码示例:RAG 检索流程

以下是一个简化的 Python 程序,展示向量搜索的检索过程。

 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
import numpy as np
from typing import List, Dict

# 模拟文档结构
class Document:
    def __init__(self, id: int, content: str, vector: np.ndarray):
        self.id = id
        self.content = content
        self.vector = vector

# 计算余弦相似度
def cosine_similarity(vec1: np.ndarray, vec2: np.ndarray) -> float:
    dot_product = np.dot(vec1, vec2)
    norm1 = np.linalg.norm(vec1)
    norm2 = np.linalg.norm(vec2)
    if norm1 == 0 or norm2 == 0:
        return 0.0
    return dot_product / (norm1 * norm2)

# 检索相关文档
def retrieve_documents(query_vector: np.ndarray, knowledge_base: List[Document], top_k: int) -> List[Document]:
    scored_docs = []
    
    # 计算每个文档与查询的相似度
    for doc in knowledge_base:
        score = cosine_similarity(query_vector, doc.vector)
        scored_docs.append((doc, score))
    
    # 按相似度排序并取 top_k
    scored_docs.sort(key=lambda x: x[1], reverse=True)
    return [doc for doc, _ in scored_docs[:top_k]]

def main():
    # 模拟知识库
    knowledge_base = [
        Document(1, "2025年瑞士免签国家增至70个", np.array([0.1, 0.9, 0.2])),
        Document(2, "瑞士入境需提供健康码", np.array([0.3, 0.4, 0.5])),
    ]
    
    # 模拟查询向量
    query_vector = np.array([0.15, 0.85, 0.25])
    
    # 检索 Top-1 文档
    results = retrieve_documents(query_vector, knowledge_base, 1)
    for doc in results:
        print(f"检索到文档:ID={doc.id}, 内容={doc.content}")

if __name__ == "__main__":
    main()

代码说明

  • 使用 NumPy 计算余弦相似度,模拟向量搜索。
  • 实际应用中,向量由嵌入模型生成,知识库存储在向量数据库中。

三、混合检索:定义与在 RAG 中的作用

3.1 什么是混合检索?

混合检索(Hybrid Retrieval)结合了 语义检索(基于向量)和 关键词检索(基于词频或精确匹配)的检索方法,

通过融合两种方式,提升检索结果的全面性和准确性。

通俗比喻
语义检索像找与“瑞士旅游”主题相似的书,哪怕书名没提“旅游”;关键词检索像直接搜书名里带“瑞士旅游”的书。混合检索结合两者,

确保找到主题相关且关键词匹配的书。

3.2 混合检索解决的问题

混合检索主要解决以下问题:

  1. 语义检索的局限:可能忽略具体关键词。例如,“瑞士免签政策”可能漏掉明确提到“免签”的条款。
  2. 关键词检索的局限:无法理解语义相近但用词不同的文档,例如“免签”和“visa-free”。
  3. 用户需求多样性:用户可能需要精确政策或模糊背景,单一方式难以满足。
  4. 知识库复杂性:包含结构化和非结构化数据,混合检索更全面。

3.3 混合检索在 RAG 中的体现

在 RAG 的检索阶段,混合检索的流程如下:

  1. 并行检索
    • 语义检索:用向量数据库搜索语义相似的文档。
    • 关键词检索:用搜索引擎匹配关键词。
  2. 结果融合:通过加权平均或 RRF(Reciprocal Rank Fusion)生成最终文档列表。
  3. 上下文优化:提取最相关内容,供大模型生成答案。

例子
用户问:“2025 年去瑞士旅游需要注意哪些新政策?”

  • 语义检索:找到签证政策和旅游攻略。
  • 关键词检索:找到包含“免签”或“健康码”的条款。
  • 融合:优先选择免签条款,补充攻略建议。
  • RAG 输出:“2025 年瑞士免签国家增至 70 个,入境需健康码。建议提前申请健康码,并带伞应对 5 月东京多雨天气。”

3.4 Python 代码示例:混合检索实现

以下是一个简化的混合检索程序。

 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
51
52
53
54
55
56
57
58
59
60
import numpy as np
from typing import List, Dict

# 模拟文档结构
class Document:
    def __init__(self, id: int, content: str, vector: np.ndarray):
        self.id = id
        self.content = content
        self.vector = vector

# 计算余弦相似度
def cosine_similarity(vec1: np.ndarray, vec2: np.ndarray) -> float:
    dot_product = np.dot(vec1, vec2)
    norm1 = np.linalg.norm(vec1)
    norm2 = np.linalg.norm(vec2)
    if norm1 == 0 or norm2 == 0:
        return 0.0
    return dot_product / (norm1 * norm2)

# 模拟关键词匹配得分(简单词频统计)
def keyword_score(query: str, content: str) -> float:
    query_words = query.lower().split()
    content_words = content.lower().split()
    matches = sum(1 for qw in query_words if qw in content_words)
    return matches / len(query_words) if query_words else 0.0

# 混合检索
def hybrid_retrieve(query: str, query_vector: np.ndarray, ECF knowledge_base: List[Document], top_k: int, semantic_weight: float) -> List[Document]:
    scored_docs = []
    
    # 计算语义和关键词得分
    for doc in knowledge_base:
        semantic_score = cosine_similarity(query_vector, doc.vector)
        kw_score = keyword_score(query, doc.content)
        # 加权融合
        combined_score = semantic_weight * semantic_score + (1 - semantic_weight) * kw_score
        scored_docs.append((doc, combined_score))
    
    # 按得分排序
    scored_docs.sort(key=lambda x: x[1], reverse=True)
    return [doc for doc, _ in scored_docs[:top_k]]

def main():
    # 模拟知识库
    knowledge_base = [
        Document(1, "2025年瑞士免签国家增至70个", np.array([0.1, 0.9, 0.2])),
        Document(2, "瑞士入境需提供健康码", np.array([0.3, 0.4, 0.5])),
    ]
    
    # 模拟查询
    query = "瑞士免签政策"
    query_vector = np.array([0.15, 0.85, 0.25])
    
    # 混合检索,语义权重为 0.7
    results = hybrid_retrieve(query, query_vector, knowledge_base, 1, 0.7)
    for doc in results:
        print(f"检索到文档:ID={doc.id}, 内容={doc.content}")

if __name__ == "__main__":
    main()

代码说明

  • 语义检索通过余弦相似度计算。
  • 关键词 retrieved 通过词频匹配模拟 BM25。
  • 混合检索通过加权融合生成结果。

评论 0