学习顺序说明:本文是AI学习路线的第7篇,建议按顺序学习:
- 01 入门基础 → 02 机器学习 → 03 深度学习 → 04 NLP基础 → 05 Transformer进阶 → 06 大模型应用 → 07 RAG系统(本文)→ 08 AI工具链
RAG (Retrieval-Augmented Generation) 是将检索与生成结合的技术,让大模型能够利用外部知识库。
RAG概述
为什么需要RAG?
大语言模型存在以下局限:
- 知识截止: 训练数据有时间限制
- 幻觉问题: 可能生成错误信息
- 领域知识: 缺乏特定领域专业知识
- 隐私数据: 无法访问私有数据
RAG通过检索相关知识,增强模型的回答能力。
RAG工作流程
用户查询 → 向量化 → 检索相关文档 → 构建Prompt → LLM生成 → 返回答案
↓
知识库 → 文档切分 → 向量化 → 存入向量数据库
参考资源:Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks - RAG原始论文
第一部分:向量嵌入与向量数据库
1.1 文本嵌入 (Embedding)
from sentence_transformers import SentenceTransformer
# 加载嵌入模型
model = SentenceTransformer('BAAI/bge-large-zh-v1.5')
# 生成向量
texts = ["机器学习是人工智能的核心技术", "深度学习是机器学习的分支"]
embeddings = model.encode(texts)
print(f"向量维度: {embeddings.shape}") # (2, 1024)
# 计算相似度
from numpy import dot
from numpy.linalg import norm
similarity = dot(embeddings[0], embeddings[1]) / (norm(embeddings[0]) * norm(embeddings[1]))
print(f"相似度: {similarity:.4f}")
常用嵌入模型:
| 模型 | 维度 | 特点 |
|---|---|---|
| text-embedding-ada-002 | 1536 | OpenAI |
| bge-large-zh-v1.5 | 1024 | 中文效果好 |
| m3e-base | 768 | 轻量级 |
1.2 向量数据库
ChromaDB
import chromadb
# 创建客户端
client = chromadb.Client()
collection = client.create_collection("documents")
# 添加文档
collection.add(
documents=["机器学习是AI的核心", "深度学习是机器学习的分支"],
metadatas=[{"source": "doc1"}, {"source": "doc2"}],
ids=["id1", "id2"]
)
# 查询
results = collection.query(
query_texts=["什么是AI的核心技术"],
n_results=2
)
print(results)
FAISS
import faiss
import numpy as np
# 创建索引
dimension = 768
index = faiss.IndexFlatL2(dimension)
# 添加向量
vectors = np.random.randn(1000, dimension).astype('float32')
index.add(vectors)
# 搜索
query = np.random.randn(1, dimension).astype('float32')
distances, indices = index.search(query, k=5)
Milvus
from pymilvus import connections, Collection, FieldSchema, CollectionSchema, DataType
# 连接
connections.connect("default", host="localhost", port="19530")
# 创建集合
fields = [
FieldSchema(name="id", dtype=DataType.INT64, is_primary=True),
FieldSchema(name="embedding", dtype=DataType.FLOAT_VECTOR, dim=768)
]
schema = CollectionSchema(fields, "documents")
collection = Collection("docs", schema)
# 创建索引
index_params = {"metric_type": "L2", "index_type": "IVF_FLAT", "params": {"nlist": 1024}}
collection.create_index("embedding", index_params)
参考资源:ChromaDB文档 Milvus文档
第二部分:文档处理
2.1 文档加载
from langchain.document_loaders import PyPDFLoader, TextLoader, DirectoryLoader
# PDF加载
pdf_loader = PyPDFLoader("document.pdf")
pdf_docs = pdf_loader.load()
# 文本加载
text_loader = TextLoader("document.txt")
text_docs = text_loader.load()
# 目录批量加载
dir_loader = DirectoryLoader("./docs", glob="**/*.pdf", loader_cls=PyPDFLoader)
all_docs = dir_loader.load()
2.2 文档切分
from langchain.text_splitter import RecursiveCharacterTextSplitter
# 创建切分器
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=500, # 每块大小
chunk_overlap=50, # 重叠大小
separators=["\n\n", "\n", "。", "!", "?", ";", " ", ""]
)
# 切分文档
chunks = text_splitter.split_documents(pdf_docs)
print(f"原始文档数: {len(pdf_docs)}")
print(f"切分后块数: {len(chunks)}")
切分策略对比:
| 策略 | 适用场景 | 特点 |
|---|---|---|
| 按字符数 | 通用 | 简单直接 |
| 按段落 | 结构化文档 | 保持语义完整 |
| 递归切分 | 混合文档 | 智能分割 |
| 语义切分 | 高精度需求 | 按语义边界分割 |
第三部分:构建RAG系统
3.1 使用LangChain
from langchain.vectorstores import Chroma
from langchain.embeddings import HuggingFaceEmbeddings
from langchain.chat_models import ChatOpenAI
from langchain.chains import RetrievalQA
from langchain.prompts import PromptTemplate
# 初始化嵌入模型
embeddings = HuggingFaceEmbeddings(model_name="BAAI/bge-large-zh-v1.5")
# 创建向量存储
vectorstore = Chroma.from_documents(
documents=chunks,
embedding=embeddings,
persist_directory="./chroma_db"
)
# 创建检索器
retriever = vectorstore.as_retriever(
search_type="similarity",
search_kwargs={"k": 4}
)
# 创建Prompt模板
prompt_template = """
请根据以下上下文回答问题。如果上下文中没有相关信息,请说"我不知道"。
上下文:
{context}
问题:{question}
回答:
"""
PROMPT = PromptTemplate(
template=prompt_template,
input_variables=["context", "question"]
)
# 创建RAG链
qa_chain = RetrievalQA.from_chain_type(
llm=ChatOpenAI(model="gpt-4", temperature=0),
chain_type="stuff",
retriever=retriever,
return_source_documents=True,
chain_type_kwargs={"prompt": PROMPT}
)
# 查询
result = qa_chain({"query": "什么是机器学习?"})
print(result["result"])
print("\n来源文档:", result["source_documents"])
3.2 高级检索策略
混合检索 (Hybrid Search)
from langchain.retrievers import EnsembleRetriever
from langchain.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]
)
重排序 (Reranking)
from sentence_transformers import CrossEncoder
# 加载重排序模型
reranker = CrossEncoder('BAAI/bge-reranker-large')
def rerank_results(query, documents, top_k=5):
# 计算重排序分数
pairs = [[query, doc.page_content] for doc in documents]
scores = reranker.predict(pairs)
# 按分数排序
scored_docs = list(zip(documents, scores))
scored_docs.sort(key=lambda x: x[1], reverse=True)
return [doc for doc, score in scored_docs[:top_k]]
3.3 完整RAG应用
class RAGApplication:
def __init__(self, docs_path, persist_directory="./chroma_db"):
self.embeddings = HuggingFaceEmbeddings(model_name="BAAI/bge-large-zh-v1.5")
self.vectorstore = None
self.qa_chain = None
# 加载或创建向量存储
if os.path.exists(persist_directory):
self.load_vectorstore(persist_directory)
else:
self.build_index(docs_path, persist_directory)
def build_index(self, docs_path, persist_directory):
"""构建索引"""
# 加载文档
loader = DirectoryLoader(docs_path, glob="**/*.pdf", loader_cls=PyPDFLoader)
docs = loader.load()
# 切分
text_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)
chunks = text_splitter.split_documents(docs)
# 创建向量存储
self.vectorstore = Chroma.from_documents(
documents=chunks,
embedding=self.embeddings,
persist_directory=persist_directory
)
def query(self, question, k=4):
"""查询"""
retriever = self.vectorstore.as_retriever(search_kwargs={"k": k})
docs = retriever.get_relevant_documents(question)
# 重排序
reranked_docs = rerank_results(question, docs, top_k=k)
# 生成回答
context = "\n\n".join([doc.page_content for doc in reranked_docs])
return self.generate_answer(question, context)
def generate_answer(self, question, context):
"""生成回答"""
# 使用LLM生成
pass
第四部分:优化技巧
4.1 提升检索质量
- 文档预处理: 清洗、去重、元数据提取
- 切片优化: 选择合适的chunk_size和overlap
- 多路召回: 结合关键词和语义检索
- 重排序: 使用CrossEncoder精排
4.2 提升生成质量
# Prompt优化
prompt = """
你是一个专业的问答助手。请根据提供的上下文准确回答问题。
要求:
1. 只使用上下文中的信息
2. 如果上下文不足以回答,明确说明
3. 回答要简洁准确
4. 引用相关的上下文片段
上下文:
{context}
问题:{question}
"""
4.3 评估指标
| 指标 | 说明 |
|---|---|
| 召回率 | 检索到相关文档的比例 |
| 精确率 | 检索文档中相关的比例 |
| MRR | 第一个相关文档的排名倒数 |
| 答案相关性 | 答案与问题的相关程度 |
学习资源
官方文档
- LangChain - 应用开发框架
- LlamaIndex - 数据框架
- ChromaDB - 向量数据库
推荐论文
最后更新: 2026年4月10日
本文参考了 LangChain文档 和 RAG论文 整理