文章 关于

RAG 技术深度解析:打造高效的 LLM 知识库系统

全面讲解检索增强生成(RAG)的核心原理、架构设计与优化策略,手把手教你构建企业级知识库系统。

为什么需要 RAG?

LLM 虽然强大,但存在几个固有限制:

  1. 知识截止:训练数据截止后的事件无法了解
  2. 领域知识缺失:缺乏特定领域的专业知识
  3. 幻觉问题:会生成看似合理但不准确的内容
  4. 更新成本高:重新训练成本巨大

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()}

七、生产环境最佳实践

  1. 缓存机制

    from langchain.cache import InMemoryCache
    llm = ChatOpenAI().with_config({"cache": True})
  2. 异步处理

    async def async_retrieve(query):
        results = await retriever.aget_relevant_documents(query)
        return results
  3. 监控告警

    • 记录检索耗时、命中率、token 消耗
    • 设置异常告警阈值
    • 定期手动审核问答质量
  4. A/B 测试

    • 同时运行多个检索策略
    • 对比效果,持续优化

总结

RAG 技术是连接 LLM 与外部知识的关键桥梁。通过精心设计的检索策略、高质量的知识库构建和持续的优化迭代,可以打造出真正实用、可靠的 AI 知识助手系统。


进一步学习