RAG 的基本原理
RAG(Retrieval-Augmented Generation)是目前解决 LLM 知识更新和幻觉问题的主流方案。其核心思路是:在模型生成答案前,先从外部知识库中检索相关内容,将检索结果作为上下文注入提示词。
为什么需要 RAG
| 问题 | 纯 LLM 方案 | RAG 方案 |
|---|---|---|
| 知识截止日期 | 训练数据截止后无法更新 | 实时检索最新信息 |
| 幻觉 | 模型可能编造事实 | 答案有检索结果支撑 |
| 领域知识 | 通用模型不擅长垂直领域 | 可接入企业知识库 |
| 成本 | 微调成本高 | 无需重新训练 |
标准 RAG 流程
用户问题 → 向量化 → 向量检索 → 文档重排序 → LLM 生成 → 返回回答
↑
向量数据库(存储 Embedding)
1. 文档预处理
from langchain.text_splitter import RecursiveCharacterTextSplitter
def prepare_documents(docs):
splitter = RecursiveCharacterTextSplitter(
chunk_size=500,
chunk_overlap=50,
separators=["\n\n", "\n", "。", "!", "?", ".", "!", "?", " "]
)
chunks = splitter.split_documents(docs)
return chunks
选择合适的 chunk 大小是关键。太小的 chunk 会丢失上下文,太大会引入噪声。中文场景建议使用分句符号作为分隔符。
2. Embedding 模型选择
Embedding 模型将文本转换为向量。选择时需考虑:
- 维度:高维度保留更多信息但存储和检索成本高
- 语言支持:中文场景需要中英双语或多语言模型
- 最大输入长度:决定单次编码的文本上限
常用 Embedding 模型:
| 模型 | 维度 | 最大长度 | 特点 |
|---|---|---|---|
| text-embedding-3-small | 1536 | 8192 | OpenAI,性价比高 |
| BGE-large-zh | 1024 | 512 | 中文优化,开源免费 |
| m3e-base | 768 | 512 | 轻量中文模型 |
| jina-embeddings-v2 | 768 | 8192 | 支持长文本 |
from langchain.embeddings import OpenAIEmbeddings
embeddings = OpenAIEmbeddings(
model="text-embedding-3-small",
dimensions=512 # 通过 API 参数压缩维度
)
3. 向量数据库
向量数据库负责存储 Embedding 并支持高效的近似最近邻(ANN)搜索。
# 使用 Chroma(本地开发推荐)
from langchain.vectorstores import Chroma
vector_store = Chroma.from_documents(
documents=chunks,
embedding=embeddings,
persist_directory="./chroma_db"
)
# 检索
results = vector_store.similarity_search_with_score(
query="什么是 RAG?",
k=4 # 返回 top-4
)
生产环境推荐使用 Qdrant、Milvus 或 Pinecone,它们支持分布式部署和更复杂的过滤逻辑。
4. 重排序(Re-ranking)
初次检索结果可能包含不相关的内容。使用重排序模型对结果重新打分,可以显著提升最终质量。
from langchain.retrievers import ContextualCompressionRetriever
from langchain.retrievers.document_compressors import CohereRerank
compressor = CohereRerank(model="rerank-multilingual-v2.0")
compression_retriever = ContextualCompressionRetriever(
base_compressor=compressor,
base_retriever=vector_store.as_retriever(search_kwargs={"k": 10})
)
compressed_docs = compression_retriever.get_relevant_documents(query)
5. 生成增强回答
from langchain.chains import RetrievalQA
from langchain.chat_models import ChatOpenAI
qa_chain = RetrievalQA.from_chain_type(
llm=ChatOpenAI(model="gpt-4o-mini", temperature=0),
chain_type="stuff", # 将检索结果全部塞入上下文
retriever=vector_store.as_retriever(search_kwargs={"k": 4}),
return_source_documents=True
)
result = qa_chain.invoke("为什么 RAG 能减少幻觉?")
print(result["result"])
print("来源:", [doc.metadata["source"] for doc in result["source_documents"]])
进阶优化
HyDE(假设性文档检索)
先用 LLM 生成一个假设性答案,再用这个答案去检索相似文档。当用户问题简短或措辞模糊时效果显著。
多路召回
同时从向量数据库、全文搜索引擎(如 Elasticsearch)和知识图谱中检索,合并结果后重排序。
查询改写
用户的原始问题可能不适合直接检索。先用 LLM 将其改写成更适合搜索的形式。
def rewrite_query(original_query, history):
prompt = f"""
历史对话:{history}
用户原始问题:{original_query}
任务:将用户问题改写成更适合检索的形式,保持原意。
改写结果:
"""
return llm.invoke(prompt)
评估 RAG 系统
| 指标 | 测量内容 | 优化方向 |
|---|---|---|
| Recall@k | 检索结果是否覆盖正确答案 | chunk 大小、检索算法 |
| MRR | 正确答案在结果中的排位 | 重排序模型 |
| 忠实度 | 生成内容是否基于检索结果 | 提示词设计 |
| 答案相关性 | 回答是否匹配问题 | 生成模型选择 |