抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

RAG(Retrieval-Augmented Generation,检索增强生成)是构建AI Agent和智能问答系统的核心技术。它让大语言模型能够访问外部知识库,突破训练数据的限制。本文详细介绍RAG的原理和实现方法。

为什么需要RAG

大语言模型的局限性

LLM(大语言模型)虽然强大,但存在几个关键问题:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
┌─────────────────────────────────────────────────────────┐
│ LLM的局限性 │
├─────────────────────────────────────────────────────────┤
│ │
│ 1. 知识截止日期 │
│ 训练数据有截止时间,无法获取最新信息 │
│ "2024年奥运会在哪举办?" → 可能回答错误 │
│ │
│ 2. 幻觉问题(Hallucination) │
│ 可能编造不存在的事实 │
│ "张三的论文主要观点是什么?" → 可能瞎编 │
│ │
│ 3. 私有数据不可知 │
│ 无法访问企业内部文档、个人笔记等 │
│ "我们公司的报销流程是什么?" → 无法回答 │
│ │
│ 4. 上下文窗口限制 │
│ 无法一次性处理大量文档 │
│ │
└─────────────────────────────────────────────────────────┘

RAG的解决方案

RAG通过检索外部知识来增强LLM的生成能力:

1
2
3
4
5
6
7
8
传统LLM:
用户问题 ──────────────────────▶ LLM ──▶ 回答(可能有幻觉)

RAG增强:
用户问题 ──▶ 检索相关文档 ──▶ 文档+问题 ──▶ LLM ──▶ 回答(有据可依)


知识库/向量数据库

RAG工作流程

整体架构

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
┌─────────────────────────────────────────────────────────────────┐
│ RAG系统架构 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 离线索引阶段 │ │
│ │ │ │
│ │ 文档 ──▶ 分割 ──▶ 向量化 ──▶ 存储到向量数据库 │ │
│ │ │ │ │ │ │ │
│ │ PDF Chunk Embedding Milvus │ │
│ │ Word 256字 768/1536维 Pinecone │ │
│ │ HTML Chroma │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 在线查询阶段 │ │
│ │ │ │
│ │ 用户问题 ──▶ 向量化 ──▶ 相似度检索 ──▶ Top-K文档 │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ 构建Prompt ──▶ LLM ──▶ 回答 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘

两个阶段

阶段1:离线索引(Indexing)

  1. 加载文档(PDF、Word、网页等)
  2. 文本分割(切分成小块)
  3. 向量嵌入(文本转向量)
  4. 存储到向量数据库

阶段2:在线查询(Querying)

  1. 用户提问
  2. 问题向量化
  3. 相似度检索,获取相关文档
  4. 构建增强Prompt
  5. LLM生成回答

核心组件详解

1. 文档加载(Document Loader)

将各种格式的文档加载为统一的文本格式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from langchain_community.document_loaders import (
PyPDFLoader,
TextLoader,
UnstructuredWordDocumentLoader,
WebBaseLoader
)

# PDF文档
pdf_loader = PyPDFLoader("document.pdf")
pdf_docs = pdf_loader.load()

# 文本文件
text_loader = TextLoader("notes.txt")
text_docs = text_loader.load()

# Word文档
word_loader = UnstructuredWordDocumentLoader("report.docx")
word_docs = word_loader.load()

# 网页
web_loader = WebBaseLoader("https://example.com/article")
web_docs = web_loader.load()

2. 文本分割(Text Splitter)

将长文档切分成适合检索的小块:

1
2
3
4
5
6
7
8
9
from langchain.text_splitter import RecursiveCharacterTextSplitter

splitter = RecursiveCharacterTextSplitter(
chunk_size=500, # 每块最大500字符
chunk_overlap=50, # 相邻块重叠50字符
separators=["\n\n", "\n", "。", ",", " ", ""]
)

chunks = splitter.split_documents(documents)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
原始文档:
┌─────────────────────────────────────────────────────────────┐
│ 第一段内容...第一段内容...第一段内容... │
│ 第二段内容...第二段内容...第二段内容... │
│ 第三段内容...第三段内容...第三段内容... │
└─────────────────────────────────────────────────────────────┘

分割后:
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Chunk 1 │ │ Chunk 2 │ │ Chunk 3 │
│ 第一段内容... │ │ ...第二段... │ │ ...第三段... │
│ (500字符) │◀─▶│ (500字符) │◀─▶│ (500字符) │
└──────────────┘ └──────────────┘ └──────────────┘
重叠50字符

分割策略选择:

策略 适用场景
固定大小 通用场景
按段落 结构清晰的文档
按句子 需要精确匹配的场景
递归分割 推荐,智能选择分割点
语义分割 保持语义完整性

3. 向量嵌入(Embedding)

将文本转换为稠密向量,使语义相似的文本在向量空间中距离更近:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from langchain_openai import OpenAIEmbeddings
from langchain_community.embeddings import HuggingFaceEmbeddings

# OpenAI Embedding
openai_embeddings = OpenAIEmbeddings(
model="text-embedding-3-small" # 1536维
)

# 本地模型(免费)
local_embeddings = HuggingFaceEmbeddings(
model_name="sentence-transformers/all-MiniLM-L6-v2" # 384维
)

# 文本转向量
vector = openai_embeddings.embed_query("什么是机器学习?")
print(len(vector)) # 1536
1
2
3
4
5
文本 ──▶ Embedding模型 ──▶ 向量

"猫是一种宠物" ──▶ [0.12, -0.34, 0.56, ..., 0.78] (1536维)
"狗是人类的朋友" ──▶ [0.15, -0.32, 0.58, ..., 0.75] 相似,距离近
"量子力学很难" ──▶ [-0.45, 0.67, -0.23, ..., 0.12] 不相似,距离远

常用Embedding模型:

模型 维度 特点
OpenAI text-embedding-3-small 1536 效果好,需付费
OpenAI text-embedding-3-large 3072 更精确,更贵
BGE-large-zh 1024 中文效果好,开源
all-MiniLM-L6-v2 384 轻量,免费
GTE-large 1024 阿里开源,效果好

4. 向量数据库(Vector Store)

存储向量并支持高效的相似度检索:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from langchain_community.vectorstores import Chroma, FAISS, Milvus

# Chroma(轻量级,适合开发)
vectorstore = Chroma.from_documents(
documents=chunks,
embedding=embeddings,
persist_directory="./chroma_db"
)

# FAISS(Facebook开源,高性能)
vectorstore = FAISS.from_documents(
documents=chunks,
embedding=embeddings
)
vectorstore.save_local("./faiss_index")

# Milvus(分布式,适合生产)
vectorstore = Milvus.from_documents(
documents=chunks,
embedding=embeddings,
connection_args={"host": "localhost", "port": "19530"}
)

常用向量数据库对比:

数据库 特点 适用场景
Chroma 轻量、易用、支持持久化 开发测试、小规模应用
FAISS Facebook开源、高性能 中等规模、单机部署
Milvus 分布式、云原生 大规模生产环境
Pinecone 全托管、开箱即用 快速上线、不想运维
Weaviate 支持混合搜索 需要关键词+向量混合
Qdrant Rust实现、高性能 高性能需求

5. 检索器(Retriever)

根据查询获取相关文档:

1
2
3
4
5
6
7
8
9
10
# 基础检索器
retriever = vectorstore.as_retriever(
search_type="similarity", # 相似度搜索
search_kwargs={"k": 5} # 返回Top 5
)

# 检索相关文档
docs = retriever.invoke("什么是RAG?")
for doc in docs:
print(doc.page_content[:100])

检索策略:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 1. 相似度搜索
retriever = vectorstore.as_retriever(search_type="similarity")

# 2. MMR(最大边际相关性)- 平衡相关性和多样性
retriever = vectorstore.as_retriever(
search_type="mmr",
search_kwargs={"k": 5, "fetch_k": 20, "lambda_mult": 0.5}
)

# 3. 相似度阈值过滤
retriever = vectorstore.as_retriever(
search_type="similarity_score_threshold",
search_kwargs={"score_threshold": 0.7}
)

6. 生成器(LLM)

基于检索到的文档生成回答:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from langchain_openai import ChatOpenAI
from langchain.prompts import ChatPromptTemplate

llm = ChatOpenAI(model="gpt-4o", temperature=0)

prompt = ChatPromptTemplate.from_template("""
基于以下上下文回答问题。如果上下文中没有相关信息,请说"我没有找到相关信息"。

上下文:
{context}

问题:{question}

回答:
""")

完整RAG实现

使用LangChain

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
61
62
63
64
65
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain_community.vectorstores import Chroma
from langchain_community.document_loaders import PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.chains import RetrievalQA
from langchain.prompts import PromptTemplate

# 1. 加载文档
loader = PyPDFLoader("knowledge_base.pdf")
documents = loader.load()

# 2. 分割文本
splitter = RecursiveCharacterTextSplitter(
chunk_size=500,
chunk_overlap=50
)
chunks = splitter.split_documents(documents)

# 3. 创建向量存储
embeddings = OpenAIEmbeddings()
vectorstore = Chroma.from_documents(
documents=chunks,
embedding=embeddings,
persist_directory="./chroma_db"
)

# 4. 创建检索器
retriever = vectorstore.as_retriever(search_kwargs={"k": 3})

# 5. 创建RAG链
llm = ChatOpenAI(model="gpt-4o", temperature=0)

prompt_template = """
你是一个专业的问答助手。请根据以下参考资料回答问题。

参考资料:
{context}

问题:{question}

要求:
1. 只基于参考资料回答,不要编造
2. 如果资料中没有相关信息,请明确说明
3. 回答要简洁准确

回答:
"""

prompt = PromptTemplate(
template=prompt_template,
input_variables=["context", "question"]
)

qa_chain = RetrievalQA.from_chain_type(
llm=llm,
chain_type="stuff",
retriever=retriever,
return_source_documents=True,
chain_type_kwargs={"prompt": prompt}
)

# 6. 查询
result = qa_chain.invoke({"query": "RAG的主要优势是什么?"})
print(result["result"])
print("来源文档:", result["source_documents"])

使用LCEL(LangChain Expression Language)

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
from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser

# 格式化文档
def format_docs(docs):
return "\n\n".join(doc.page_content for doc in docs)

# 构建RAG链
rag_chain = (
{
"context": retriever | format_docs,
"question": RunnablePassthrough()
}
| prompt
| llm
| StrOutputParser()
)

# 查询
answer = rag_chain.invoke("什么是向量数据库?")
print(answer)

# 流式输出
for chunk in rag_chain.stream("RAG如何工作?"):
print(chunk, end="", flush=True)

向量相似度计算

常用相似度度量

1
2
3
4
5
6
7
8
9
10
11
12
13
import numpy as np

def cosine_similarity(a, b):
"""余弦相似度:最常用"""
return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))

def euclidean_distance(a, b):
"""欧氏距离"""
return np.linalg.norm(a - b)

def dot_product(a, b):
"""点积"""
return np.dot(a, b)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
余弦相似度:
A·B
cos(θ) = ─────────
|A|×|B|

范围:[-1, 1]
1 = 完全相同方向
0 = 正交(无关)
-1 = 完全相反

▲ B
/│
/ │
/ │
/θ │
/ │
──────┼────▶ A


cos(θ)越大,越相似

近似最近邻搜索(ANN)

精确搜索在大规模数据上太慢,向量数据库使用ANN算法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
精确搜索:O(n) - 遍历所有向量
ANN搜索:O(log n) - 近似但快速

常用ANN算法:
┌─────────────────────────────────────────────────────────┐
│ │
│ HNSW(Hierarchical Navigable Small World) │
│ ├── 构建多层图结构 │
│ ├── 从顶层开始搜索,逐层下降 │
│ └── 查询速度快,内存占用大 │
│ │
│ IVF(Inverted File Index) │
│ ├── 聚类划分空间 │
│ ├── 只搜索相关聚类 │
│ └── 内存效率高 │
│ │
│ PQ(Product Quantization) │
│ ├── 向量压缩 │
│ ├── 减少内存占用 │
│ └── 适合超大规模数据 │
│ │
└─────────────────────────────────────────────────────────┘

RAG优化技巧

1. 分块策略优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 问题:分块太小丢失上下文,太大检索不精确

# 优化1:父子分块(Parent-Child Chunking)
# 检索小块,返回大块上下文
from langchain.retrievers import ParentDocumentRetriever
from langchain.storage import InMemoryStore

parent_splitter = RecursiveCharacterTextSplitter(chunk_size=2000)
child_splitter = RecursiveCharacterTextSplitter(chunk_size=400)

store = InMemoryStore()
retriever = ParentDocumentRetriever(
vectorstore=vectorstore,
docstore=store,
child_splitter=child_splitter,
parent_splitter=parent_splitter
)
1
2
3
4
5
6
7
8
9
10
11
12
13
父子分块:

原始文档
┌─────────────────────────────────────────────┐
│ 父块(2000字符) │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ 子块1 │ │ 子块2 │ │ 子块3 │ │
│ │ 400字符 │ │ 400字符 │ │ 400字符 │ │
│ └─────────┘ └─────────┘ └─────────┘ │
└─────────────────────────────────────────────┘

检索时:用子块的向量匹配
返回时:返回父块,保留完整上下文

2. 查询改写(Query Rewriting)

1
2
3
4
5
6
7
8
9
10
11
12
13
from langchain.retrievers import MultiQueryRetriever

# 将一个问题改写成多个角度的问题
multi_query_retriever = MultiQueryRetriever.from_llm(
retriever=base_retriever,
llm=llm
)

# 原问题:"RAG有什么优势?"
# 改写为:
# - "RAG相比传统LLM有什么改进?"
# - "检索增强生成的主要优点是什么?"
# - "为什么要使用RAG技术?"

3. 混合检索(Hybrid Search)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from langchain.retrievers import EnsembleRetriever
from langchain_community.retrievers import BM25Retriever

# 关键词检索(BM25)
bm25_retriever = BM25Retriever.from_documents(chunks)
bm25_retriever.k = 5

# 向量检索
vector_retriever = vectorstore.as_retriever(search_kwargs={"k": 5})

# 混合检索
ensemble_retriever = EnsembleRetriever(
retrievers=[bm25_retriever, vector_retriever],
weights=[0.3, 0.7] # 权重分配
)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
混合检索:

用户查询:"Python装饰器"

BM25(关键词匹配):
├── 精确匹配"Python"
├── 精确匹配"装饰器"
└── 适合专有名词

向量检索(语义匹配):
├── 理解"装饰器"的含义
├── 找到相关概念(高阶函数、元编程)
└── 适合语义相关内容

结合两者:精确 + 语义

4. 重排序(Re-ranking)

1
2
3
4
5
6
7
8
9
10
11
12
from langchain.retrievers import ContextualCompressionRetriever
from langchain.retrievers.document_compressors import CrossEncoderReranker
from langchain_community.cross_encoders import HuggingFaceCrossEncoder

# 使用Cross-Encoder重排序
model = HuggingFaceCrossEncoder(model_name="BAAI/bge-reranker-base")
compressor = CrossEncoderReranker(model=model, top_n=3)

reranking_retriever = ContextualCompressionRetriever(
base_compressor=compressor,
base_retriever=retriever
)
1
2
3
4
5
6
7
8
9
10
11
12
13
重排序流程:

初始检索:Top 20
┌────┬────┬────┬────┬────┬────┬────┬────┐
│ D1 │ D2 │ D3 │ D4 │ D5 │ D6 │...│D20 │
└────┴────┴────┴────┴────┴────┴────┴────┘
粗排序(Bi-Encoder,快但不精确)

重排序:Top 3
┌────┬────┬────┐
│ D3 │ D7 │ D1 │
└────┴────┴────┘
精排序(Cross-Encoder,慢但精确)

5. 查询路由(Query Routing)

1
2
3
4
5
6
7
8
9
10
from langchain.chains.router import MultiPromptChain

# 根据问题类型路由到不同的知识库
routes = {
"技术文档": tech_retriever,
"产品手册": product_retriever,
"FAQ": faq_retriever
}

# 使用LLM判断问题类型,选择合适的检索器

高级RAG架构

Self-RAG(自我反思RAG)

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
┌─────────────────────────────────────────────────────────────┐
│ Self-RAG │
│ │
│ 问题 ──▶ 是否需要检索? │
│ │ │
│ ┌────┴────┐ │
│ 需要 不需要 │
│ │ │ │
│ ▼ ▼ │
│ 检索文档 直接生成 │
│ │ │
│ ▼ │
│ 文档是否相关? ──▶ 不相关 ──▶ 丢弃 │
│ │ │
│ 相关 │
│ │ │
│ ▼ │
│ 生成回答 │
│ │ │
│ ▼ │
│ 回答是否有支撑? ──▶ 无支撑 ──▶ 重新生成 │
│ │ │
│ 有支撑 │
│ │ │
│ ▼ │
│ 输出回答 │
│ │
└─────────────────────────────────────────────────────────────┘

Corrective RAG(纠正式RAG)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# CRAG:检索后评估文档质量,必要时进行网络搜索

def corrective_rag(question):
# 1. 检索文档
docs = retriever.invoke(question)

# 2. 评估相关性
relevant_docs = []
for doc in docs:
relevance = evaluate_relevance(question, doc)
if relevance == "relevant":
relevant_docs.append(doc)

# 3. 如果没有相关文档,进行网络搜索
if not relevant_docs:
web_results = web_search(question)
relevant_docs = web_results

# 4. 生成回答
return generate_answer(question, relevant_docs)

Graph RAG(图增强RAG)

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
传统RAG:文档 → 向量 → 检索

Graph RAG:文档 → 实体和关系 → 知识图谱 → 图检索

┌──────────────────────────────────────────────────────────┐
│ 知识图谱 │
│ │
│ [Python] ───创建者──▶ [Guido] │
│ │ │ │
│ 用于 国籍 │
│ │ │ │
│ ▼ ▼ │
│ [机器学习] ◀───包含─── [荷兰] │
│ │ │
│ 框架 │
│ │ │
│ ▼ │
│ [TensorFlow] │
│ │
└──────────────────────────────────────────────────────────┘

优势:
- 捕获实体关系
- 多跳推理
- 结构化知识

评估指标

检索评估

1
2
3
4
5
6
7
8
9
10
# 召回率(Recall):相关文档被检索到的比例
recall = len(retrieved & relevant) / len(relevant)

# 精确率(Precision):检索到的文档中相关的比例
precision = len(retrieved & relevant) / len(retrieved)

# MRR(Mean Reciprocal Rank):第一个相关结果的排名
mrr = 1 / rank_of_first_relevant

# NDCG:考虑排序的评估指标

生成评估

1
2
3
4
5
6
7
8
9
10
11
12
# 忠实度(Faithfulness):回答是否基于检索的文档
# 相关性(Relevance):回答是否切题
# 有害性(Harmfulness):是否包含有害内容

# 使用RAGAs框架评估
from ragas import evaluate
from ragas.metrics import faithfulness, answer_relevancy, context_precision

result = evaluate(
dataset,
metrics=[faithfulness, answer_relevancy, context_precision]
)

应用场景

1. 企业知识库问答

1
2
3
4
5
6
7
8
9
10
11
12
13
┌─────────────────────────────────────────────┐
│ 企业文档: │
│ - 员工手册 │
│ - 产品文档 │
│ - 技术规范 │
│ - 会议记录 │
└─────────────────────────────────────────────┘


RAG系统


"新员工入职需要准备什么?" → 精准回答

2. 客服机器人

1
2
3
4
5
6
7
8
# 基于产品FAQ和用户手册的客服系统
qa_bot = RetrievalQA.from_chain_type(
llm=llm,
retriever=product_docs_retriever,
chain_type_kwargs={
"prompt": customer_service_prompt
}
)

3. 代码助手

1
2
3
4
5
6
7
8
9
# 基于代码库的编程助手
code_retriever = vectorstore.as_retriever(
search_kwargs={
"k": 5,
"filter": {"file_type": "code"}
}
)

# "这个项目的数据库连接是怎么实现的?"

4. 学术研究助手

1
2
3
4
5
6
7
8
9
# 基于论文库的研究助手
paper_rag = RetrievalQA.from_chain_type(
llm=llm,
retriever=paper_retriever,
return_source_documents=True # 返回引用来源
)

# "Transformer架构的主要创新点是什么?"
# → 回答 + 引用的论文

总结

RAG核心流程

1
2
3
文档 → 分块 → 向量化 → 存储

问题 → 向量化 → 检索 → 增强Prompt → LLM → 回答

关键组件选型

组件 推荐选择
Embedding OpenAI / BGE / GTE
向量数据库 Chroma(开发) / Milvus(生产)
分块策略 递归分割 + 父子分块
检索策略 混合检索 + 重排序
LLM GPT-4o / Claude

优化方向

  1. 分块优化:父子分块、语义分块
  2. 检索优化:混合检索、重排序、查询改写
  3. 生成优化:Prompt工程、Self-RAG
  4. 评估优化:使用RAGAs等框架持续评估

RAG是构建可靠AI应用的关键技术,它让LLM能够基于真实数据生成回答,大大减少幻觉问题,是企业级AI应用的基础架构。