RAG系统基础:从零开始构建检索增强生成系统
全面介绍RAG系统的核心概念、架构和实现,包括文档索引、向量检索和生成三大核心组件的详细讲解
RAG系统基础:从零开始构建检索增强生成系统
欢迎来到RAG(Retrieval-Augmented Generation,检索增强生成)系统的世界!本文将带你从零开始,深入理解并构建一个完整的RAG系统。
什么是RAG?
RAG是一种结合了信息检索和文本生成的AI技术。它解决了大型语言模型(LLM)的一个关键限制:知识截止日期和领域专业知识的缺乏。
传统LLM的局限性
# ❌ 传统LLM的问题
问题: "我们公司2024年Q3的销售数据是多少?"
LLM: "抱歉,我的知识截止到2023年,无法回答..."
问题: "根据我们的内部文档,新员工入职流程是什么?"
LLM: "我无法访问你们的内部文档..."RAG如何解决这些问题?
RAG的核心思想
在回答问题之前,先从你的知识库中检索相关信息,然后让LLM基于这些信息生成答案。
# ✅ RAG系统的工作流程
用户问题: "我们公司2024年Q3的销售数据是多少?"
1. 检索阶段:
→ 在公司文档库中搜索相关内容
→ 找到: "2024 Q3 销售报告.pdf"
2. 生成阶段:
→ LLM 读取检索到的文档
→ 基于真实数据生成答案: "根据2024年Q3销售报告..."RAG系统架构
一个完整的RAG系统由三个核心组件构成:
┌─────────────────────────────────────────────────────────────┐
│ RAG 系统架构 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌───────────┐ │
│ │ 索引阶段 │ ───> │ 检索阶段 │ ───> │ 生成阶段 │ │
│ │ (Indexing) │ │ (Retrieval) │ │(Generation)│ │
│ └──────────────┘ └──────────────┘ └───────────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ 文档分块+向量化 语义搜索 基于上下文生成 │
│ │
└─────────────────────────────────────────────────────────────┘1. 索引阶段 (Indexing) - 准备知识库
目标:将文档转换为可搜索的向量格式
核心步骤:
- 文档加载 (Document Loading)
- 文本分块 (Text Splitting)
- 向量化 (Vectorization)
- 存储 (Storage)
2. 检索阶段 (Retrieval) - 找到相关信息
目标:根据用户查询找到最相关的文档片段
核心步骤:
- 查询向量化
- 相似度计算
- 结果排序
3. 生成阶段 (Generation) - 产生答案
目标:基于检索到的信息生成准确答案
核心步骤:
- 构建提示词
- LLM生成
- 答案返回
Part 1: 环境准备
在开始之前,我们需要安装必要的工具包和配置环境。
安装依赖
# 创建虚拟环境(推荐)
python3 -m venv venv
source venv/bin/activate # macOS/Linux
# venv\Scripts\activate # Windows
# 安装核心包
pip install langchain langchain-openai langchain-community
pip install chromadb # 向量数据库
pip install tiktoken # Token计数
pip install python-dotenv # 环境变量管理配置环境变量
创建 .env 文件:
# OpenAI API
OPENAI_API_KEY=your_api_key_here
# LangSmith (可选,用于监控和调试)
LANGCHAIN_TRACING_V2=true
LANGCHAIN_ENDPOINT=https://api.smith.langchain.com
LANGCHAIN_API_KEY=your_langsmith_key_here
LANGCHAIN_PROJECT=rag-tutorial基础导入
import os
from dotenv import load_dotenv
# 加载环境变量
load_dotenv()
# 导入LangChain核心组件
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_community.vectorstores import Chroma
from langchain_community.document_loaders import WebBaseLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.chains import RetrievalQA
from langchain_core.prompts import ChatPromptTemplatePart 2: 索引 (Indexing) - 构建知识库
索引是RAG系统的基础。我们需要将原始文档转换为LLM可以理解和搜索的格式。
步骤1: 加载文档
首先,我们需要加载文档。LangChain支持多种文档源:
from langchain_community.document_loaders import (
WebBaseLoader, # 网页
PyPDFLoader, # PDF
TextLoader, # 文本文件
DirectoryLoader, # 目录
CSVLoader, # CSV
)
# 示例:加载网页
loader = WebBaseLoader("https://lilianweng.github.io/posts/2023-06-23-agent/")
docs = loader.load()
print(f"加载了 {len(docs)} 个文档")
print(f"第一个文档长度: {len(docs[0].page_content)} 字符")支持的文档类型:
| 文档类型 | Loader | 用途 |
|---|---|---|
| 网页 | WebBaseLoader | 爬取网页内容 |
PyPDFLoader | 读取PDF文件 | |
| Word | UnstructuredWordDocumentLoader | 读取.docx文件 |
| Markdown | UnstructuredMarkdownLoader | 读取.md文件 |
| CSV | CSVLoader | 读取表格数据 |
| JSON | JSONLoader | 读取JSON数据 |
步骤2: 文本分块 (Text Splitting)
为什么需要分块?
- LLM上下文限制:大多数LLM有最大token限制(如GPT-4的8K/32K)
- 提高检索精度:小块文本更容易匹配特定查询
- 降低成本:只检索和处理相关部分
# 创建文本分块器
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=1000, # 每块最大字符数
chunk_overlap=200, # 块之间的重叠
length_function=len, # 长度计算函数
is_separator_regex=False,
)
# 分块文档
splits = text_splitter.split_documents(docs)
print(f"原始文档: {len(docs)} 个")
print(f"分块后: {len(splits)} 个")
print(f"\n第一个分块示例:\n{splits[0].page_content[:200]}...")分块策略对比:
# 1. 字符分块(最基础)
CharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
# 2. 递归分块(推荐,智能识别段落)
RecursiveCharacterTextSplitter(
chunk_size=1000,
chunk_overlap=200,
separators=["\n\n", "\n", " ", ""] # 优先按段落分割
)
# 3. Token分块(精确控制token数量)
from langchain.text_splitter import TokenTextSplitter
TokenTextSplitter(chunk_size=256, chunk_overlap=50)
# 4. 语义分块(基于语义相似度)
from langchain_experimental.text_splitter import SemanticChunker
SemanticChunker(OpenAIEmbeddings())分块参数详解:
| 参数 | 说明 | 推荐值 | 影响 |
|---|---|---|---|
chunk_size | 每块大小 | 500-1500 | 太小→上下文不足 太大→匹配不精确 |
chunk_overlap | 重叠部分 | 10-20%大小 | 保持上下文连贯性 |
separators | 分隔符 | ["\n\n", "\n", " "] | 优先级:段落>行>词 |
步骤3: 向量化 (Vectorization)
将文本转换为数学向量,这是语义搜索的核心。
# 创建嵌入模型
embeddings = OpenAIEmbeddings(
model="text-embedding-3-small" # OpenAI的嵌入模型
)
# 示例:向量化一段文本
text = "RAG是一种强大的AI技术"
vector = embeddings.embed_query(text)
print(f"文本: {{text}}")
print(f"向量维度: {len(vector)}")
print(f"向量前5个值: {vector[:5]}")
# 输出: [0.123, -0.456, 0.789, ...]什么是向量嵌入?
文本 "苹果是一种水果"
↓ 嵌入模型
向量 [0.2, 0.8, -0.1, 0.5, ...] (1536维)
文本 "橙子是一种水果"
↓ 嵌入模型
向量 [0.3, 0.7, -0.2, 0.4, ...] (1536维)
# 这两个向量在向量空间中距离很近(语义相似)常用嵌入模型对比:
| 模型 | 提供商 | 维度 | 成本 | 性能 |
|---|---|---|---|---|
| text-embedding-3-small | OpenAI | 1536 | $ | 高性价比 ⭐ |
| text-embedding-3-large | OpenAI | 3072 | $$ | 最高质量 |
| text-embedding-ada-002 | OpenAI | 1536 | $ | 上一代 |
| all-MiniLM-L6-v2 | HuggingFace | 384 | 免费 | 本地部署 |
步骤4: 存储到向量数据库
# 创建向量数据库
vectorstore = Chroma.from_documents(
documents=splits, # 分块后的文档
embedding=embeddings, # 嵌入模型
persist_directory="./chroma_db" # 持久化目录
)
print(f"✅ 向量数据库创建成功!")
print(f" 存储了 {len(splits)} 个文档块")
print(f" 持久化路径: ./chroma_db")向量数据库选择:
| 数据库 | 类型 | 优势 | 适用场景 |
|---|---|---|---|
| Chroma | 嵌入式 | 简单易用,无需额外服务 | 开发、小规模应用 ⭐ |
| Pinecone | 云服务 | 高性能,托管服务 | 生产环境 |
| Weaviate | 自建 | 功能丰富,开源 | 大规模部署 |
| FAISS | 库 | 速度快,Meta开源 | 研究和原型 |
完整索引流程代码
将上面的步骤整合:
def create_vectorstore(url: str, chunk_size: int = 1000):
"""创建向量数据库的完整流程"""
# 1. 加载文档
loader = WebBaseLoader(url)
docs = loader.load()
# 2. 分块
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=chunk_size,
chunk_overlap=int(chunk_size * 0.1)
)
splits = text_splitter.split_documents(docs)
# 3. 向量化并存储
vectorstore = Chroma.from_documents(
documents=splits,
embedding=OpenAIEmbeddings(model="text-embedding-3-small"),
persist_directory=f"./chroma_db_{url.split('/')[-1]}"
)
return vectorstore
# 使用
vectorstore = create_vectorstore(
"https://lilianweng.github.io/posts/2023-06-23-agent/"
)Part 3: 检索 (Retrieval) - 找到相关信息
现在我们有了向量数据库,接下来学习如何检索相关信息。
基础检索
# 创建检索器
retriever = vectorstore.as_retriever(
search_type="similarity", # 相似度搜索
search_kwargs={"k": 3} # 返回前3个结果
)
# 执行检索
query = "什么是Agent?"
docs = retriever.get_relevant_documents(query)
# 查看结果
for i, doc in enumerate(docs, 1):
print(f"\n📄 结果 {{i}}:")
print(f"内容: {doc.page_content[:200]}...")
print(f"来源: {doc.metadata.get('source', 'N/A')}")检索类型详解
1. 相似度搜索 (Similarity Search)
最基础的搜索方式,返回与查询最相似的文档。
retriever = vectorstore.as_retriever(
search_type="similarity",
search_kwargs={"k": 5} # 返回top-5
)2. 最大边际相关性 (MMR)
平衡相关性和多样性,避免返回过于相似的结果。
retriever = vectorstore.as_retriever(
search_type="mmr",
search_kwargs={
"k": 5,
"fetch_k": 20, # 先获取20个候选
"lambda_mult": 0.5 # 多样性参数 (0=最多样, 1=最相关)
}
)MMR工作原理:
查询: "机器学习算法"
步骤1: 获取top-20相似文档
1. "深度学习是机器学习的一个分支..." (相似度: 0.95)
2. "深度学习使用神经网络..." (相似度: 0.94) ← 与第1个很相似
3. "决策树是一种机器学习算法..." (相似度: 0.90)
4. "支持向量机(SVM)用于分类..." (相似度: 0.88)
...
步骤2: MMR选择 (k=3, lambda=0.5)
选中: #1 (最相关)
选中: #3 (相关且与#1不同) ← 跳过#2因为太相似
选中: #4 (增加多样性)3. 相似度阈值 (Similarity Score Threshold)
只返回相似度超过阈值的文档。
retriever = vectorstore.as_retriever(
search_type="similarity_score_threshold",
search_kwargs={
"score_threshold": 0.5, # 只返回相似度>0.5的结果
"k": 5
}
)检索策略对比
| 策略 | 优势 | 劣势 | 适用场景 |
|---|---|---|---|
| Similarity | 简单快速 | 可能重复 | 默认选择 |
| MMR | 结果多样 | 稍慢 | 需要不同角度信息 |
| Threshold | 质量保证 | 可能无结果 | 严格匹配需求 |
高级检索:自定义检索器
from langchain_core.runnables import RunnablePassthrough
def custom_retriever(question: str) -> list:
"""自定义检索逻辑"""
# 1. 基础检索
base_docs = vectorstore.similarity_search(question, k=10)
# 2. 根据元数据过滤
filtered_docs = [
doc for doc in base_docs
if doc.metadata.get('type') == 'article'
]
# 3. 重排序(可以使用更复杂的模型)
scored_docs = [
(doc, calculate_relevance_score(doc, question))
for doc in filtered_docs
]
scored_docs.sort(key=lambda x: x[1], reverse=True)
# 4. 返回top-k
return [doc for doc, score in scored_docs[:5]]检索评估
如何评估检索质量?
def evaluate_retrieval(retriever, test_cases):
"""评估检索器性能"""
metrics = {
"precision": [], # 精确率
"recall": [], # 召回率
}
for query, expected_doc_ids in test_cases:
# 执行检索
retrieved_docs = retriever.get_relevant_documents(query)
retrieved_ids = [doc.metadata['id'] for doc in retrieved_docs]
# 计算指标
relevant_retrieved = set(retrieved_ids) & set(expected_doc_ids)
precision = len(relevant_retrieved) / len(retrieved_ids) if retrieved_ids else 0
recall = len(relevant_retrieved) / len(expected_doc_ids) if expected_doc_ids else 0
metrics["precision"].append(precision)
metrics["recall"].append(recall)
return {
"avg_precision": sum(metrics["precision"]) / len(metrics["precision"]),
"avg_recall": sum(metrics["recall"]) / len(metrics["recall"])
}
# 使用示例
test_cases = [
("什么是Agent?", ["doc1", "doc2", "doc5"]),
("LLM的局限性", ["doc3", "doc8"]),
]
results = evaluate_retrieval(retriever, test_cases)
print(f"平均精确率: {results['avg_precision']:.2%}")
print(f"平均召回率: {results['avg_recall']:.2%}")Part 4: 生成 (Generation) - 产生答案
最后一步:基于检索到的文档,让LLM生成答案。
基础QA链
from langchain.chains import RetrievalQA
# 创建LLM
llm = ChatOpenAI(
model="gpt-4",
temperature=0 # 0 = 更精确, 1 = 更有创意
)
# 创建QA链
qa_chain = RetrievalQA.from_chain_type(
llm=llm,
chain_type="stuff", # 将所有文档"填充"到提示词中
retriever=retriever,
return_source_documents=True # 返回源文档
)
# 提问
query = "什么是Agent?它有什么能力?"
result = qa_chain({"query": query})
print("问题:", result["query"])
print("\n答案:", result["result"])
print("\n来源文档数量:", len(result["source_documents"]))Chain Types 详解
LangChain提供了4种不同的链类型来处理检索到的文档:
1. Stuff(填充) - 最简单,推荐优先使用
chain_type="stuff"
# 工作原理:
提示词: """
基于以下文档回答问题:
文档1: ...
文档2: ...
文档3: ...
问题: {{question}}
答案:
"""优点:
- ✅ 简单直接
- ✅ 只需一次LLM调用
- ✅ 成本低
缺点:
- ❌ 文档过多会超过上下文限制
2. Map-Reduce(映射-归约) - 处理大量文档
chain_type="map_reduce"
# 工作原理:
# Map阶段:对每个文档分别提问
文档1 → LLM → 部分答案1
文档2 → LLM → 部分答案2
文档3 → LLM → 部分答案3
# Reduce阶段:合并所有部分答案
[部分答案1, 部分答案2, 部分答案3] → LLM → 最终答案优点:
- ✅ 可处理任意数量文档
- ✅ 可并行处理
缺点:
- ❌ 需要多次LLM调用(成本高)
- ❌ 可能丢失文档间的关联
3. Refine(精炼) - 迭代改进答案
chain_type="refine"
# 工作原理:
文档1 → LLM → 初始答案
[初始答案 + 文档2] → LLM → 改进答案1
[改进答案1 + 文档3] → LLM → 最终答案优点:
- ✅ 答案质量高
- ✅ 保持文档间的关联
缺点:
- ❌ 顺序敏感(文档顺序影响结果)
- ❌ 不能并行
4. Map-Rerank(映射-重排序) - 找最佳答案
chain_type="map_rerank"
# 工作原理:
文档1 → LLM → 答案1 (置信度: 0.8)
文档2 → LLM → 答案2 (置信度: 0.6)
文档3 → LLM → 答案3 (置信度: 0.9) ← 选择这个Chain Type 选择指南
| 场景 | 推荐类型 | 原因 |
|---|---|---|
| 文档少(<10) | Stuff | 最简单高效 |
| 文档多(>10) | Map-Reduce | 可扩展 |
| 需要高质量答案 | Refine | 迭代改进 |
| 需要最相关答案 | Map-Rerank | 自动选择最佳 |
自定义提示词
默认提示词可能不够好,我们可以自定义:
from langchain_core.prompts import PromptTemplate
# 定义自定义提示词
template = """你是一个AI助手,请基于以下文档回答问题。
要求:
1. 只使用提供的文档内容
2. 如果文档中没有相关信息,明确说明
3. 引用具体的文档片段
4. 用简洁清晰的语言回答
文档:
{{context}}
问题:{{question}}
答案:"""
prompt = PromptTemplate(
template=template,
input_variables=["context", "question"]
)
# 使用自定义提示词
qa_chain = RetrievalQA.from_chain_type(
llm=llm,
chain_type="stuff",
retriever=retriever,
chain_type_kwargs={"prompt": prompt}
)带来源的回答
让答案包含信息来源,提高可信度:
from langchain.chains import RetrievalQAWithSourcesChain
qa_chain = RetrievalQAWithSourcesChain.from_chain_type(
llm=llm,
chain_type="stuff",
retriever=retriever
)
result = qa_chain({"question": "什么是Agent?"})
print("答案:", result["answer"])
print("\n来源:", result["sources"])
# 输出:
# 来源: https://example.com/doc1, https://example.com/doc2流式输出
对于长答案,使用流式输出改善用户体验:
from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler
# 创建支持流式输出的LLM
llm = ChatOpenAI(
model="gpt-4",
temperature=0,
streaming=True,
callbacks=[StreamingStdOutCallbackHandler()]
)
qa_chain = RetrievalQA.from_chain_type(
llm=llm,
chain_type="stuff",
retriever=retriever
)
# 提问(答案会实时输出)
response = qa_chain({"query": "详细解释什么是Agent系统?"})完整RAG Pipeline
将所有组件整合成一个完整的RAG系统:
class RAGSystem:
"""完整的RAG系统"""
def __init__(self, urls: list[str], model: str = "gpt-4"):
"""初始化RAG系统
Args:
urls: 要索引的网页URL列表
model: LLM模型名称
"""
self.urls = urls
self.model = model
self.vectorstore = None
self.qa_chain = None
def build_index(self, chunk_size: int = 1000):
"""构建索引"""
print("🔨 开始构建索引...")
# 加载所有文档
all_docs = []
for url in self.urls:
loader = WebBaseLoader(url)
docs = loader.load()
all_docs.extend(docs)
print(f" ✓ 加载了 {len(all_docs)} 个文档")
# 分块
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=chunk_size,
chunk_overlap=int(chunk_size * 0.1)
)
splits = text_splitter.split_documents(all_docs)
print(f" ✓ 分块后共 {len(splits)} 个片段")
# 创建向量数据库
self.vectorstore = Chroma.from_documents(
documents=splits,
embedding=OpenAIEmbeddings(model="text-embedding-3-small")
)
print("✅ 索引构建完成!")
def setup_qa_chain(self, search_type: str = "similarity", k: int = 3):
"""设置QA链"""
if self.vectorstore is None:
raise ValueError("请先构建索引(调用build_index)")
# 创建检索器
retriever = self.vectorstore.as_retriever(
search_type=search_type,
search_kwargs={"k": k}
)
# 创建QA链
llm = ChatOpenAI(model=self.model, temperature=0)
self.qa_chain = RetrievalQA.from_chain_type(
llm=llm,
chain_type="stuff",
retriever=retriever,
return_source_documents=True
)
print("✅ QA链设置完成!")
def query(self, question: str, show_sources: bool = True) -> str:
"""查询问题"""
if self.qa_chain is None:
raise ValueError("请先设置QA链(调用setup_qa_chain)")
result = self.qa_chain({"query": question})
answer = result["result"]
if show_sources:
sources = [doc.metadata.get("source", "N/A")
for doc in result["source_documents"]]
unique_sources = list(set(sources))
answer += f"\n\n📚 来源: {', '.join(unique_sources)}"
return answer
# 使用示例
rag = RAGSystem(urls=[
"https://lilianweng.github.io/posts/2023-06-23-agent/",
"https://lilianweng.github.io/posts/2023-03-15-prompt-engineering/"
])
# 构建索引
rag.build_index(chunk_size=1000)
# 设置QA链
rag.setup_qa_chain(search_type="mmr", k=5)
# 查询
answer = rag.query("什么是Agent?它有哪些关键组件?")
print(answer)性能优化技巧
1. 缓存嵌入结果
避免重复计算相同文本的嵌入:
from langchain.embeddings import CacheBackedEmbeddings
from langchain.storage import LocalFileStore
# 创建缓存存储
store = LocalFileStore("./embedding_cache")
# 包装嵌入模型
cached_embeddings = CacheBackedEmbeddings.from_bytes_store(
underlying_embeddings=OpenAIEmbeddings(),
document_embedding_cache=store,
namespace="openai_embeddings"
)
# 使用缓存的嵌入
vectorstore = Chroma.from_documents(
documents=splits,
embedding=cached_embeddings
)2. 批量处理
一次处理多个文档,提高效率:
# 批量嵌入(更快)
embeddings.embed_documents([doc.page_content for doc in splits])
# vs 逐个嵌入(慢)
for doc in splits:
embeddings.embed_query(doc.page_content)3. 异步处理
使用异步API提高并发性能:
import asyncio
from langchain.schema import Document
async def async_rag_query(question: str):
"""异步RAG查询"""
# 异步检索
docs = await vectorstore.asimilarity_search(question, k=3)
# 异步生成
response = await llm.apredict(
f"基于以下文档回答问题:\n{{docs}}\n\n问题:{{question}}"
)
return response
# 并发查询多个问题
questions = ["问题1", "问题2", "问题3"]
answers = await asyncio.gather(*[async_rag_query(q) for q in questions])4. 选择合适的向量数据库
根据数据规模选择:
# 小规模(<100K文档):Chroma(嵌入式)
vectorstore = Chroma(...)
# 中规模(100K-1M文档):Pinecone(云服务)
from langchain_pinecone import Pinecone
vectorstore = Pinecone(...)
# 大规模(>1M文档):Weaviate(自建)
from langchain_weaviate import Weaviate
vectorstore = Weaviate(...)常见问题与解决方案
Q1: 检索到的文档不相关?
原因:
- 分块策略不当
- 嵌入模型不适合
- 查询表达不清
解决方案:
# 1. 调整分块大小
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=500, # 减小块大小
chunk_overlap=100
)
# 2. 使用更好的嵌入模型
embeddings = OpenAIEmbeddings(model="text-embedding-3-large")
# 3. 查询重写
from langchain.chains import LLMChain
query_rewriter = LLMChain(llm=llm, prompt=PromptTemplate(
template="将以下问题改写得更清晰:{{query}}",
input_variables=["query"]
))
improved_query = query_rewriter.run("原始问题")Q2: 答案质量不高?
解决方案:
# 1. 增加检索文档数量
retriever = vectorstore.as_retriever(
search_kwargs={"k": 10} # 从3增加到10
)
# 2. 使用更强大的LLM
llm = ChatOpenAI(model="gpt-4") # 从gpt-3.5升级到gpt-4
# 3. 优化提示词
prompt = PromptTemplate(template="""
你是一个专业的AI助手。请基于提供的文档内容,给出详细、准确的答案。
要求:
- 分点作答
- 引用原文
- 如有不确定的信息,明确指出
文档:{{context}}
问题:{{question}}
答案:
""")Q3: 成本过高?
解决方案:
# 1. 使用更便宜的嵌入模型
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
# 或使用开源模型
from langchain_community.embeddings import HuggingFaceEmbeddings
embeddings = HuggingFaceEmbeddings(model_name="all-MiniLM-L6-v2")
# 2. 减少检索文档数量
retriever = vectorstore.as_retriever(search_kwargs={"k": 3})
# 3. 使用GPT-3.5替代GPT-4
llm = ChatOpenAI(model="gpt-3.5-turbo")
# 4. 批量处理降低API调用次数Q4: 响应太慢?
解决方案:
# 1. 使用流式输出
llm = ChatOpenAI(streaming=True)
# 2. 减少检索文档数量
k = 3 # 而不是10
# 3. 使用更快的向量数据库
# FAISS比Chroma更快
from langchain_community.vectorstores import FAISS
vectorstore = FAISS.from_documents(splits, embeddings)
# 4. 预加载常见问题答案
cache = {}
if question in cache:
return cache[question]总结与下一步
🎯 本章要点回顾
我们学习了RAG系统的三大核心组件:
-
索引(Indexing)
- 文档加载
- 文本分块
- 向量化
- 向量数据库存储
-
检索(Retrieval)
- 相似度搜索
- MMR多样性检索
- 阈值过滤
- 自定义检索器
-
生成(Generation)
- QA链的4种类型
- 自定义提示词
- 带来源的回答
- 流式输出
📊 关键决策点
| 决策点 | 选项 | 选择建议 |
|---|---|---|
| 分块大小 | 500/1000/1500 | 1000字符(默认) |
| 嵌入模型 | small/large | small(性价比高) |
| 向量数据库 | Chroma/Pinecone/Weaviate | Chroma(开发)→Pinecone(生产) |
| LLM模型 | GPT-3.5/GPT-4 | GPT-3.5(一般)→GPT-4(复杂任务) |
| 检索数量k | 3/5/10 | 3-5个(平衡性能与质量) |
| Chain类型 | stuff/map-reduce/refine | stuff(默认首选) |
🚀 下一步学习
在掌握了基础RAG系统后,你可以继续学习:
- [2] 多查询技术 - 查询扩展和改写
- [3] 路由与查询构建 - 智能路由和结构化查询
- [4] 索引与高级检索 - 高级索引策略
- [5] 检索与重排序 - 结果优化技术
💡 最佳实践清单
✅ 开发阶段:
- 使用小数据集快速迭代
- 启用LangSmith监控调试
- 记录不同参数的效果
- 建立测试用例集
✅ 生产部署前:
- 评估检索准确率
- 测试边界情况
- 优化成本
- 设置监控告警
✅ 持续优化:
- 收集用户反馈
- A/B测试不同配置
- 定期更新知识库
- 监控性能指标
参考资源
官方文档
推荐阅读
工具资源
- LangSmith - RAG系统监控和调试
- LlamaIndex - 另一个RAG框架
- Semantic Kernel - 微软的RAG实现
通过本章的学习,你已经掌握了构建基础RAG系统的所有必要知识。接下来,让我们继续深入学习更高级的技术!🚀