RAG 检索增强生成入门教程
零基础掌握RAG(Retrieval-Augmented Generation)技术,从原理到实战,构建企业级知识问答系统。
一、什么是RAG?
1.1 RAG的定义
RAG(Retrieval-Augmented Generation,检索增强生成)是一种将信息检索与大语言模型生成相结合的技术架构。它的核心思想是:在大模型生成回答之前,先从外部知识库中检索相关信息,然后将检索到的内容作为上下文提供给模型,从而生成更准确、更有依据的回答。
1.2 为什么需要RAG?
大语言模型(如GPT-4、Claude等)虽然强大,但存在以下局限性:
| 问题 | 说明 | RAG如何解决 |
|---|---|---|
| 知识截止 | 模型训练数据有时间限制 | 实时检索最新信息 |
| 幻觉问题 | 模型可能编造不存在的事实 | 基于真实文档生成 |
| 领域知识不足 | 通用模型缺乏专业领域知识 | 接入专业知识库 |
| 数据隐私 | 企业数据不能用于外部训练 | 数据保留在本地知识库 |
1.3 RAG vs 微调 vs 长上下文
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| RAG | 知识可更新、可溯源、成本低 | 检索质量影响生成效果 | 知识问答、客服系统 |
| 微调 | 深度理解特定领域 | 成本高、知识难更新 | 特定风格/格式生成 |
| 长上下文 | 实现简单 | Token消耗大、有长度限制 | 少量文档分析 |
二、RAG核心架构
2.1 整体流程
用户提问
↓
┌─────────────────────────────────┐
│ 索引阶段 (Indexing) │
│ 文档加载 → 文本分块 → 向量化 → 存储 │
└─────────────────────────────────┘
↓
┌─────────────────────────────────┐
│ 检索阶段 (Retrieval) │
│ 查询向量化 → 相似度搜索 → 返回Top-K │
└─────────────────────────────────┘
↓
┌─────────────────────────────────┐
│ 生成阶段 (Generation) │
│ 拼接Prompt → LLM生成 → 返回答案 │
└─────────────────────────────────┘
↓
返回带引用的答案
2.2 核心组件
- 文档加载器(Document Loader):读取各种格式的文档
- 文本分块器(Text Splitter):将长文档切分为合适大小的块
- 嵌入模型(Embedding Model):将文本转换为向量表示
- 向量数据库(Vector Store):存储和检索向量
- 检索器(Retriever):执行相似度搜索
- LLM(大语言模型):基于检索结果生成回答
三、文本分块策略
3.1 为什么需要分块?
- 大模型上下文窗口有限
- 检索精度与块大小相关
- 不同内容需要不同的分块策略
3.2 常见分块方法
固定大小分块
from langchain.text_splitter import CharacterTextSplitter
splitter = CharacterTextSplitter(
chunk_size=1000, # 每块最大字符数
chunk_overlap=200, # 块之间重叠字符数
separator="\n" # 分隔符
)
text = """这里是需要分块的长文本内容..."""
chunks = splitter.split_text(text)
print(f"共分为 {len(chunks)} 个块")
递归字符分块(推荐)
from langchain.text_splitter import RecursiveCharacterTextSplitter
splitter = RecursiveCharacterTextSplitter(
chunk_size=1000,
chunk_overlap=200,
separators=["\n\n", "\n", "。", "!", "?", ",", " ", ""]
)
chunks = splitter.split_text(long_text)
语义分块
from langchain_experimental.text_splitter import SemanticChunker
from langchain_openai import OpenAIEmbeddings
embeddings = OpenAIEmbeddings()
splitter = SemanticChunker(embeddings)
chunks = splitter.split_text(long_text)
3.3 分块最佳实践
- 块大小:通常500-1500个字符,根据内容类型调整
- 重叠:建议10%-20%的重叠,避免上下文断裂
- 元数据:保留来源、页码等信息
- 中文处理:注意按句子边界分割,避免截断句子
四、向量嵌入与向量数据库
4.1 什么是向量嵌入?
向量嵌入是将文本转换为高维数值向量的过程。语义相似的文本在向量空间中距离更近。
from sentence_transformers import SentenceTransformer
model = SentenceTransformer('BAAI/bge-base-zh-v1.5') # 中文嵌入模型
sentences = [
"什么是机器学习?",
"机器学习的定义是什么?",
"今天天气怎么样?"
]
embeddings = model.encode(sentences)
print(f"向量维度: {embeddings.shape}") # (3, 768)
# 计算相似度
from sklearn.metrics.pairwise import cosine_similarity
sim_matrix = cosine_similarity(embeddings)
print(f"'机器学习' vs '机器学习定义': {sim_matrix[0][1]:.4f}") # 高相似度
print(f"'机器学习' vs '天气': {sim_matrix[0][2]:.4f}") # 低相似度
4.2 常用嵌入模型
| 模型 | 特点 | 维度 | 推荐场景 |
|---|---|---|---|
BAAI/bge-base-zh-v1.5 |
中文优秀、开源 | 768 | 中文场景首选 |
text-embedding-3-small |
OpenAI、性价比高 | 1536 | 多语言场景 |
text-embedding-3-large |
OpenAI、精度最高 | 3072 | 高精度需求 |
m3e-base |
中文、轻量 | 768 | 资源受限场景 |
4.3 向量数据库对比
| 数据库 | 特点 | 部署方式 | 推荐场景 |
|---|---|---|---|
| ChromaDB | 轻量、易用 | 嵌入式 | 快速原型开发 |
| FAISS | Facebook开源、高性能 | 嵌入式 | 大规模向量搜索 |
| Milvus | 分布式、云原生 | 独立服务 | 生产环境 |
| Pinecone | 全托管SaaS | 云服务 | 无需运维 |
| Weaviate | 支持混合搜索 | 独立服务 | 复杂检索需求 |
五、实战:用LangChain构建RAG系统
5.1 环境准备
pip install langchain langchain-openai langchain-community chromadb pypdf
5.2 完整RAG实现
import os
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_community.document_loaders import PyPDFLoader, TextLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.vectorstores import Chroma
from langchain.chains import RetrievalQA
from langchain.prompts import PromptTemplate
# ========== 第一步:加载文档 ==========
def load_documents(file_paths):
"""加载多个文档"""
documents = []
for path in file_paths:
if path.endswith('.pdf'):
loader = PyPDFLoader(path)
elif path.endswith('.txt'):
loader = TextLoader(path, encoding='utf-8')
else:
continue
documents.extend(loader.load())
print(f"共加载 {len(documents)} 页文档")
return documents
# ========== 第二步:文本分块 ==========
def split_documents(documents):
"""将文档分块"""
splitter = RecursiveCharacterTextSplitter(
chunk_size=1000,
chunk_overlap=200,
separators=["\n\n", "\n", "。", "!", "?", ";", ",", " ", ""]
)
chunks = splitter.split_documents(documents)
print(f"共分为 {len(chunks)} 个文本块")
return chunks
# ========== 第三步:创建向量数据库 ==========
def create_vectorstore(chunks, persist_dir="./chroma_db"):
"""创建并持久化向量数据库"""
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
vectorstore = Chroma.from_documents(
documents=chunks,
embedding=embeddings,
persist_directory=persist_dir
)
print(f"向量数据库已创建,保存至 {persist_dir}")
return vectorstore
# ========== 第四步:构建RAG链 ==========
def create_rag_chain(vectorstore):
"""创建RAG问答链"""
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
prompt_template = """基于以下上下文信息回答用户的问题。
如果上下文中没有相关信息,请说明你无法根据已有信息回答。
上下文:
{context}
问题:{question}
回答:"""
prompt = PromptTemplate(
template=prompt_template,
input_variables=["context", "question"]
)
qa_chain = RetrievalQA.from_chain_type(
llm=llm,
chain_type="stuff",
retriever=vectorstore.as_retriever(
search_type="similarity",
search_kwargs={"k": 4}
),
chain_type_kwargs={"prompt": prompt},
return_source_documents=True
)
return qa_chain
# ========== 主程序 ==========
if __name__ == "__main__":
# 1. 加载文档
files = ["./docs/company_intro.pdf", "./docs/product_manual.txt"]
documents = load_documents(files)
# 2. 分块
chunks = split_documents(documents)
# 3. 创建向量数据库
vectorstore = create_vectorstore(chunks)
# 4. 创建RAG链
qa_chain = create_rag_chain(vectorstore)
# 5. 提问
while True:
question = input("\n请输入问题(输入q退出):")
if question.lower() == 'q':
break
result = qa_chain.invoke({"query": question})
print(f"\n回答:{result['result']}")
print(f"\n参考来源:")
for doc in result['source_documents']:
print(f" - {doc.metadata.get('source', '未知')}")
5.3 运行效果示例
请输入问题:公司的核心产品有哪些?
回答:根据公司介绍文档,公司目前有三大核心产品:
1. 智能客服系统 - 基于大模型的7x24小时客服解决方案
2. 知识管理平台 - 企业级RAG知识库系统
3. AI写作助手 - 面向内容创作者的AI辅助工具
参考来源:
- ./docs/company_intro.pdf (第3页)
- ./docs/product_manual.txt (第1页)
六、进阶优化技巧
6.1 混合检索
结合关键词检索和语义检索,提高召回率:
from langchain.retrievers import EnsembleRetriever
from langchain_community.retrievers import BM25Retriever
# BM25关键词检索器
bm25_retriever = BM25Retriever.from_documents(chunks)
bm25_retriever.k = 4
# 向量检索器
vector_retriever = vectorstore.as_retriever(search_kwargs={"k": 4})
# 混合检索
ensemble_retriever = EnsembleRetriever(
retrievers=[bm25_retriever, vector_retriever],
weights=[0.4, 0.6] # BM25权重0.4,向量权重0.6
)
6.2 查询改写
优化用户查询,提高检索质量:
from langchain.prompts import ChatPromptTemplate
rewrite_prompt = ChatPromptTemplate.from_template(
"请将以下用户问题改写为更适合搜索的形式,"
"保持原意但使用更准确的关键词:\n\n"
"原始问题:{question}\n"
"改写后的搜索查询:"
)
6.3 重排序
对检索结果进行重排序,将最相关的内容排在前面:
from sentence_transformers import CrossEncoder
reranker = CrossEncoder('BAAI/bge-reranker-base')
def rerank(query, documents, top_k=3):
pairs = [(query, doc.page_content) for doc in documents]
scores = reranker.predict(pairs)
ranked = sorted(zip(documents, scores), key=lambda x: x[1], reverse=True)
return [doc for doc, score in ranked[:top_k]]
七、常见问题与解决方案
Q1:检索结果不相关怎么办?
- 检查分块策略是否合理
- 尝试不同的嵌入模型
- 使用混合检索
- 添加查询改写
Q2:回答质量不高如何优化?
- 增加检索的文档数量(k值)
- 优化Prompt模板
- 使用重排序
- 确保上下文信息完整
Q3:如何处理多轮对话?
from langchain.memory import ConversationBufferMemory
memory = ConversationBufferMemory(
memory_key="chat_history",
return_messages=True
)
八、总结
RAG技术是当前企业落地大模型应用的最重要技术之一。通过本文的学习,你应该掌握了:
- ✅ RAG的核心概念和工作原理
- ✅ 文本分块的各种策略
- ✅ 向量嵌入和向量数据库的使用
- ✅ 使用LangChain构建完整RAG系统
- ✅ 混合检索、查询改写、重排序等优化技巧
下一步学习建议:
- 尝试用你自己的文档构建RAG系统
- 探索多模态RAG(图片、表格)
- 学习RAG评估指标(如Faithfulness、Relevancy)
- 了解Agentic RAG(带Agent的RAG系统)
本教程内容基于公开技术文档和开源项目整理,仅供学习参考。