Deeptoai RAG系列教程

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) - 准备知识库

目标:将文档转换为可搜索的向量格式

核心步骤

  1. 文档加载 (Document Loading)
  2. 文本分块 (Text Splitting)
  3. 向量化 (Vectorization)
  4. 存储 (Storage)

2. 检索阶段 (Retrieval) - 找到相关信息

目标:根据用户查询找到最相关的文档片段

核心步骤

  1. 查询向量化
  2. 相似度计算
  3. 结果排序

3. 生成阶段 (Generation) - 产生答案

目标:基于检索到的信息生成准确答案

核心步骤

  1. 构建提示词
  2. LLM生成
  3. 答案返回

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 ChatPromptTemplate

Part 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爬取网页内容
PDFPyPDFLoader读取PDF文件
WordUnstructuredWordDocumentLoader读取.docx文件
MarkdownUnstructuredMarkdownLoader读取.md文件
CSVCSVLoader读取表格数据
JSONJSONLoader读取JSON数据

步骤2: 文本分块 (Text Splitting)

为什么需要分块?

  1. LLM上下文限制:大多数LLM有最大token限制(如GPT-4的8K/32K)
  2. 提高检索精度:小块文本更容易匹配特定查询
  3. 降低成本:只检索和处理相关部分
# 创建文本分块器
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-smallOpenAI1536$高性价比 ⭐
text-embedding-3-largeOpenAI3072$$最高质量
text-embedding-ada-002OpenAI1536$上一代
all-MiniLM-L6-v2HuggingFace384免费本地部署

步骤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系统的三大核心组件:

  1. 索引(Indexing)

    • 文档加载
    • 文本分块
    • 向量化
    • 向量数据库存储
  2. 检索(Retrieval)

    • 相似度搜索
    • MMR多样性检索
    • 阈值过滤
    • 自定义检索器
  3. 生成(Generation)

    • QA链的4种类型
    • 自定义提示词
    • 带来源的回答
    • 流式输出

📊 关键决策点

决策点选项选择建议
分块大小500/1000/15001000字符(默认)
嵌入模型small/largesmall(性价比高)
向量数据库Chroma/Pinecone/WeaviateChroma(开发)→Pinecone(生产)
LLM模型GPT-3.5/GPT-4GPT-3.5(一般)→GPT-4(复杂任务)
检索数量k3/5/103-5个(平衡性能与质量)
Chain类型stuff/map-reduce/refinestuff(默认首选)

🚀 下一步学习

在掌握了基础RAG系统后,你可以继续学习:

  1. [2] 多查询技术 - 查询扩展和改写
  2. [3] 路由与查询构建 - 智能路由和结构化查询
  3. [4] 索引与高级检索 - 高级索引策略
  4. [5] 检索与重排序 - 结果优化技术

💡 最佳实践清单

开发阶段

  • 使用小数据集快速迭代
  • 启用LangSmith监控调试
  • 记录不同参数的效果
  • 建立测试用例集

生产部署前

  • 评估检索准确率
  • 测试边界情况
  • 优化成本
  • 设置监控告警

持续优化

  • 收集用户反馈
  • A/B测试不同配置
  • 定期更新知识库
  • 监控性能指标

参考资源

官方文档

推荐阅读

工具资源

通过本章的学习,你已经掌握了构建基础RAG系统的所有必要知识。接下来,让我们继续深入学习更高级的技术!🚀