RAG 技术深度解析:打造高效的 LLM 知识库系统
全面讲解检索增强生成(RAG)的核心原理、架构设计与优化策略,手把手教你构建企业级知识库系统。
· 更新于 Mar 12, 2025
为什么需要 RAG?
LLM 虽然强大,但存在几个固有限制:
- 知识截止:训练数据截止后的事件无法了解
- 领域知识缺失:缺乏特定领域的专业知识
- 幻觉问题:会生成看似合理但不准确的内容
- 更新成本高:重新训练成本巨大
RAG(Retrieval-Augmented Generation)通过外部知识检索,有效解决了这些问题。
RAG 系统架构
┌─────────────────────────────────────────────────────────────┐
│ User Query │
└──────────────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Query Processing │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ 1. Query Rewriting (可选) │ │
│ │ 2. Query Expansion (同义词扩展) │ │
│ │ 3. HyDE (假设文档嵌入) │ │
│ └──────────────────────────────────────────────────────┘ │
└──────────────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Vector Retrieval │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Embedding Model (text-embedding-3-large, │ │
│ │ bge-m3, e5-large-v2) │ │
│ └──────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Vector Database (Chroma, Pinecone, Weaviate, │ │
│ │ Qdrant, Milvus) │ │
│ └──────────────────────────────────────────────────────┘ │
└──────────────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Context Fusion & Reranking │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ 1. Cross-Encoder Reranker (bge-reranker) │ │
│ │ 2. Re-ranking with LLM (LLM-as-a-Judge) │ │
│ │ 3. Diversity Filtering (去重) │ │
│ └──────────────────────────────────────────────────────┘ │
└──────────────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ LLM Generation │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Context + Query → Prompt → GPT-4/Claude/Llama │ │
│ │ │ │
│ │ 提示词模板: │ │
│ │ "基于以下上下文:{context} │ │
│ │ 问题:{query} │ │
│ │ 请回答问题,如上下文无相关信息请明确说明。" │ │
│ └──────────────────────────────────────────────────────┘ │
└──────────────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Final Answer │
└─────────────────────────────────────────────────────────────────┘
一、文档处理管道
1.1 多格式文档加载
from langchain_community.document_loaders import (
PyPDFLoader,
UnstructuredWordDocumentLoader,
UnstructuredExcelLoader,
CSVLoader,
TextLoader,
WebBaseLoader,
YoutubeLoader
)
# 批量加载不同格式文档
loaders = {
"*.pdf": PyPDFLoader,
"*.docx": UnstructuredWordDocumentLoader,
"*.xlsx": UnstructuredExcelLoader,
"*.csv": CSVLoader,
"*.txt": TextLoader,
}
documents = []
for pattern, loader_cls in loaders.items():
for file_path in Path("./data").glob(pattern):
loader = loader_cls(str(file_path))
documents.extend(loader.load())
1.2 智能分块策略
from langchain_text_splitters import (
RecursiveCharacterTextSplitter,
MarkdownHeaderTextSplitter,
TokenTextSplitter
)
# 方案1:递归字符分块(通用)
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=1000,
chunk_overlap=200,
separators=["\n\n", "\n", "。", ".", " ", ""],
keep_separator=True
)
# 方案2:Markdown 标题分块(适合文档)
markdown_splitter = MarkdownHeaderTextSplitter(
headers_to_split_on=[
("#", "Header 1"),
("##", "Header 2"),
("###", "Header 3"),
]
)
# 方案3:按 Token 分块(更精确)
token_splitter = TokenTextSplitter(
encoding_name="cl100k_base", # GPT-4 tokenizer
chunk_size=500,
chunk_overlap=50
)
# 应用分块
chunks = text_splitter.split_documents(documents)
# 添加元数据
for i, chunk in enumerate(chunks):
chunk.metadata.update({
"chunk_id": i,
"total_chunks": len(chunks),
"source": chunk.metadata.get("source", ""),
"file_name": Path(chunk.metadata.get("source", "")).name,
})
二、向量嵌入与检索
2.1 嵌入模型选型
from langchain_openai import OpenAIEmbeddings
from sentence_transformers import SentenceTransformer
# OpenAI 方案(质量高,成本高)
embeddings = OpenAIEmbeddings(
model="text-embedding-3-large", # 3072 维
dimensions=3072
)
# 本地方案(开源,免费)
local_model = SentenceTransformer("BAAI/bge-m3") # 多语言,1024 维
# 更优选择:双语模型
bilingual_model = SentenceTransformer("BAAI/bge-large-zh-v1.5") # 中文优化 1024 维
2.2 向量数据库对比
| 数据库 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| Chroma | 本地、简单、免费 | 规模小 | 开发测试、中小项目 |
| Pinecone | 云服务、自动扩容 | 收费、网络延迟 | 生产环境、大规模 |
| Qdrant | 高性能、支持过滤 | 部署复杂 | 需要复杂过滤 |
| Milvus | 超大规模、功能全 | 资源消耗大 | 亿级向量 |
| Weaviate | 内置混合搜索 | 学习曲线陡 | 混合检索场景 |
2.3 实践:Chroma 向量库
from langchain_community.vectorstores import Chroma
from langchain_openai import OpenAIEmbeddings
# 创建持久化向量库
vectorstore = Chroma.from_documents(
documents=chunks,
embedding=OpenAIEmbeddings(model="text-embedding-3-small"),
persist_directory="./chroma_db",
collection_name="knowledge_base"
)
# 检索
results = vectorstore.similarity_search(
query="什么是 RAG?",
k=5 # 返回前 5 个最相似文档
)
三、检索优化策略
3.1 混合检索
from langchain.retrievers.multi_query import MultiQueryRetriever
from langchain.retrievers import ContextualCompressionRetriever
from langchain.retrievers.document_compressors import CrossEncoderReranker
# 多查询检索(提升召回)
multi_retriever = MultiQueryRetriever.from_llm(
retriever=vectorstore.as_retriever(search_kwargs={"k": 10}),
llm=ChatOpenAI(model="gpt-4")
)
# 重排序(提升精确度)
compressor = CrossEncoderReranker(
model="BAAI/bge-reranker-large",
top_n=5
)
compression_retriever = ContextualCompressionRetriever(
base_compressor=compressor,
base_retriever=vectorstore.as_retriever(search_kwargs={"k": 20})
)
3.2 元数据过滤
# 带过滤条件的检索
filtered_results = vectorstore.similarity_search(
query="Transformer 原理",
k=10,
filter={
"category": "technical",
"doc_type": "tutorial",
"created_date": {"$gte": "2024-01-01"}
}
)
四、Prompt Engineering for RAG
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
# 优化后的 RAG Prompt
prompt = ChatPromptTemplate.from_messages([
("system", """你是一个专业的 AI 助手,请基于提供的上下文信息准确回答用户问题。
## 规则
1. **严格基于上下文**:只使用提供的上下文信息,不编造
2. **诚实回答**:如果上下文中没有相关信息,明确告知"根据现有资料,我无法回答这个问题"
3. **引用来源**:每个关键陈述都要标注来源文档
4. **结构化输出**:
- 简明扼要的回答
- 相关引用(文档标题 + 页码)
- 如有歧义,说明不同观点
## 上下文:
{context}
## 对话历史:
{chat_history}"""),
MessagesPlaceholder(variable_name="chat_history"),
("human", "{input}"),
])
五、高级优化技巧
5.1 自适应分块
from langchain_text_splitters import SemanticChunker
# 基于语义的分块(更智能)
semantic_splitter = SemanticChunker(
embeddings=OpenAIEmbeddings(),
breakpoint_threshold_type="percentile",
breakpoint_threshold_amount=95
)
chunks = semantic_splitter.create_documents([text])
5.2 查询扩展
def expand_query(query: str, llm):
"""使用 LLM 扩展查询以提高召回率"""
expansion_prompt = f"""
请为以下查询生成 3 个不同表述的版本,以改善检索效果:
原始查询:{query}
要求:
- 保持语义一致
- 使用不同表达方式
- 包括同义词替换
- 格式:JSON list
"""
response = llm.invoke(expansion_prompt)
return [query] + json.loads(response.content)
5.3 压缩上下文
from langchain.retrievers.document_compressors import LLMChainExtractor
compressor = LLMChainExtractor.from_llm(llm)
compressed_docs = compressor.compress_documents(
docs=retrieved_docs,
query=query
)
六、评估指标
def evaluate_rag(qa_chain, test_cases):
"""评估 RAG 系统质量"""
metrics = {
"retrieval_precision": [],
"answer_relevance": [],
"factuality": [],
"latency": []
}
for case in test_cases:
start = time.time()
result = qa_chain.invoke({"input": case["question"]})
latency = time.time() - start
# 人工评分或自动评分
metrics["retrieval_precision"].append(
evaluate_retrieval(result["source_documents"], case["ground_truth"])
)
metrics["answer_relevance"].append(
semantic_similarity(result["answer"], case["expected_answer"])
)
metrics["factuality"].append(
check_factual_accuracy(result["answer"], case["source_documents"])
)
metrics["latency"].append(latency)
return {k: sum(v)/len(v) for k, v in metrics.items()}
七、生产环境最佳实践
-
缓存机制:
from langchain.cache import InMemoryCache llm = ChatOpenAI().with_config({"cache": True}) -
异步处理:
async def async_retrieve(query): results = await retriever.aget_relevant_documents(query) return results -
监控告警:
- 记录检索耗时、命中率、token 消耗
- 设置异常告警阈值
- 定期手动审核问答质量
-
A/B 测试:
- 同时运行多个检索策略
- 对比效果,持续优化
总结
RAG 技术是连接 LLM 与外部知识的关键桥梁。通过精心设计的检索策略、高质量的知识库构建和持续的优化迭代,可以打造出真正实用、可靠的 AI 知识助手系统。
进一步学习
- LangChain RAG 教程
- LlamaIndex 高级用法
- “Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks”