跳到主要内容

RAG 检索增强

RAG(Retrieval Augmented Generation,检索增强生成)是当前最流行的 LLM 应用架构。本章将详细介绍如何在 LangChain 中实现 RAG 系统。

什么是 RAG?

RAG 是一种结合检索和生成的架构,让 LLM 能够基于私有数据生成更准确、更可靠的答案。

RAG 工作原理

核心流程

  1. 索引阶段:将文档分割、嵌入、存入向量数据库
  2. 查询阶段:用户提问 → 检索相关文档 → 生成回答

为什么需要 RAG?

  • 知识更新:LLM 训练数据有截止日期,RAG 可以访问最新信息
  • 私有数据:让 LLM 能够使用企业内部文档
  • 减少幻觉:基于真实文档生成,减少编造答案
  • 成本效益:无需重新训练模型即可扩展知识

RAG 架构

完整 RAG 流程

实现 RAG

1. 文档加载

from langchain_community.document_loaders import TextLoader, PyPDFLoader, DirectoryLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter

# 加载文本文档
loader = TextLoader("document.txt")
documents = loader.load()

# 加载 PDF
loader = PyPDFLoader("document.pdf")
documents = loader.load()

# 加载目录中的所有文档
loader = DirectoryLoader("docs/", glob="*.md")
documents = loader.load()

# 查看加载的文档
for doc in documents:
print(f"内容: {doc.page_content[:100]}...")
print(f"元数据: {doc.metadata}\n")

2. 文本分割

from langchain.text_splitter import RecursiveCharacterTextSplitter

# 创建分割器
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=1000, # 每个块的最大字符数
chunk_overlap=200, # 块之间的重叠字符数
separators=["\n\n", "\n", "。", ".", " "], # 分割符
)

# 分割文档
splits = text_splitter.split_documents(documents)

print(f"分割成 {len(splits)} 个块")
for i, split in enumerate(splits[:3]):
print(f"块 {i+1}: {split.page_content[:100]}...")

3. 嵌入和向量化

from langchain_openai import OpenAIEmbeddings

# 创建嵌入模型
embeddings = OpenAIEmbeddings(
model="text-embedding-3-small"
)

# 对文本进行嵌入
text = "Python 是一种高级编程语言"
vector = embeddings.embed_query(text)

print(f"嵌入维度: {len(vector)}")
print(f"嵌入向量前5个值: {vector[:5]}")

4. 向量存储

from langchain_community.vectorstores import Chroma

# 创建向量存储
vectorstore = Chroma.from_documents(
documents=splits, # 分割后的文档块
embedding=embeddings, # 嵌入模型
collection_name="my_docs" # 集合名称
)

# 保存到磁盘
vectorstore = Chroma.from_documents(
documents=splits,
embedding=embeddings,
persist_directory="./chroma_db" # 持久化目录
)

5. 检索

# 相似度检索
results = vectorstore.similarity_search(
query="什么是 Python 装饰器?",
k=3 # 返回前3个最相关的文档块
)

for i, doc in enumerate(results):
print(f"结果 {i+1}:")
print(doc.page_content[:200])
print(f"来源: {doc.metadata}\n")

6. 生成回答

from langchain.chat_models import init_chat_model
from langchain.prompts import ChatPromptTemplate

model = init_chat_model(model="gpt-4o-mini", model_provider="openai")

# 构建 RAG prompt
template = """基于以下参考文档回答问题。如果文档中没有相关信息,请如实说明。

参考文档:
{context}

问题:{question}

回答:"""

prompt = ChatPromptTemplate.from_template(template)

# 创建 RAG Chain
def format_docs(docs):
return "\n\n".join(doc.page_content for doc in docs)

rag_chain = (
{"context": lambda x: format_docs(x["docs"]), "question": lambda x: x["question"]}
| prompt
| model
)

# 执行 RAG
docs = vectorstore.similarity_search("什么是 Python 装饰器?", k=3)
result = rag_chain.invoke({"docs": docs, "question": "什么是 Python 装饰器?"})
print(result.content)

使用 LangChain RAG 组件

create_retrieval_chain

from langchain.chains import create_retrieval_chain
from langchain.chains.combine_documents import create_stuff_documents_chain

# 问答链
qa_prompt = ChatPromptTemplate.from_template("""
基于以下上下文回答问题:

{context}

问题:{input}
""")

# 创建文档处理链
qa_chain = create_stuff_documents_chain(llm=model, prompt=qa_prompt)

# 创建检索链
retrieval_chain = create_retrieval_chain(
retriever=vectorstore.as_retriever(),
return_source_documents=True,
combine_docs_chain=qa_chain
)

# 执行
result = retrieval_chain.invoke({"input": "Python装饰器是什么?"})
print(f"回答: {result['answer']}")
print(f"来源: {result['source_documents']}")

create_history_aware_retriever

带对话历史的检索器:

from langchain.chains import create_retrieval_chain
from langchain.chains.retrieval import create_history_aware_retriever

# 历史感知检索器
history_prompt = ChatPromptTemplate.from_template("""
给定对话历史,将当前问题改写为一个独立的问题。

对话历史:
{chat_history}

当前问题:{input}

独立问题:""")

history_retriever = create_history_aware_retriever(
llm=model,
retriever=vectorstore.as_retriever(),
prompt=history_prompt
)

# 使用
result = history_retriever.invoke({
"input": "它的主要特点是什么?",
"chat_history": [
("user", "Python 是什么?"),
("assistant", "Python 是一种高级编程语言")
]
})

检索优化

1. 检索器配置

# 创建可配置的检索器
retriever = vectorstore.as_retriever(
search_type="similarity", # similarity, similarity_threshold, mmr, similarity_score_threshold
search_kwargs={
"k": 5, # 返回文档数量
"score_threshold": 0.8, # 相似度阈值
"filter": {"source": "doc"} # 过滤条件
}
)

2. MMR 检索

# MMR (最大边际相关性) - 多样性检索
retriever = vectorstore.as_retriever(
search_type="mmr",
search_kwargs={
"k": 5, # 召回数量
"fetch_k": 20, # 候选数量
"lambda_mult": 0.5 # 0=最相似, 1=最多样
}
)

3. 混合检索

from langchain.retrievers import ContextualCompressionRetriever
from langchain.retrievers.document_compressors import LLMChainExtractor

# 使用 LLM 压缩检索结果
compressor = LLMChainExtractor.from_llm(model)

compression_retriever = ContextualCompressionRetriever(
base_retriever=retriever,
document_compressor=compressor
)

高级 RAG 技术

1. 父子文档检索

# 检索小块,返回关联的大块
from langchain.retrievers import ParentDocumentRetriever
from langchain.text_splitter import RecursiveCharacterTextSplitter

# 子块分割器
child_splitter = RecursiveCharacterTextSplitter(
chunk_size=400,
chunk_overlap=50
)

# 父块分割器
parent_splitter = RecursiveCharacterTextSplitter(
chunk_size=2000,
chunk_overlap=200
)

retriever = ParentDocumentRetriever(
vectorstore=vectorstore,
docstore=store, # 文档存储
child_splitter=child_splitter,
parent_splitter=parent_splitter
)

2. 句法感知检索

from langchain.retrievers import SentenceTransformersRetriever

# 使用句子级嵌入
retriever = SentenceTransformersRetriever(
model_name="all-MiniLM-L6-v2"
)

3. 知识图谱增强

# 使用知识图谱增强检索
from langchain.graphs import Neo4jGraph

graph = Neo4jGraph()
graph.query("CREATE (a:Article {title: 'Python'})")

# 在 RAG 中使用图谱
# ... 结合图谱关系进行检索

实际应用示例

1. 企业知识库

from langchain_community.document_loaders import DirectoryLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.vectorstores import Chroma
from langchain_openai import OpenAIEmbeddings
from langchain.chains import create_retrieval_chain
from langchain.chains.combine_documents import create_stuff_documents_chain

# 1. 加载文档
loader = DirectoryLoader("company_docs/", glob="**/*.md")
docs = loader.load()

# 2. 分割
splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)
splits = splitter.split_documents(docs)

# 3. 嵌入并存储
vectorstore = Chroma.from_documents(
documents=splits,
embedding=OpenAIEmbeddings()
)

# 4. 创建检索链
retrieval_chain = create_retrieval_chain(
vectorstore.as_retriever(),
create_stuff_documents_chain(model, qa_prompt)
)

# 5. 查询
def query_knowledgebase(question):
result = retrieval_chain.invoke({"input": question})
return result["answer"], result["source_documents"]

# 使用
answer, sources = query_knowledgebase("公司年假制度是什么?")
print(answer)

2. PDF 问答

from langchain_community.document_loaders import PyPDFLoader
from langchain.chains import create_retrieval_chain

# 加载 PDF
loader = PyPDFLoader("annual_report.pdf")
pages = loader.load()

# 分割
splits = text_splitter.split_documents(pages)

# 向量存储
vectorstore = Chroma.from_documents(splits, embeddings)

# 创建问答系统
qa_chain = create_retrieval_chain(vectorstore.as_retriever(), qa_chain)

# 询问关于 PDF 的问题
result = qa_chain.invoke({"input": "公司去年的收入是多少?"})

3. 多文档问答

# 从多个数据源构建 RAG
from langchain_community.document_loaders import TextLoader, CSVLoader

# 加载多种类型文档
loaders = [
TextLoader("policy.txt"),
TextLoader("faq.md"),
# CSVLoader("products.csv"),
]

docs = []
for loader in loaders:
docs.extend(loader.load())

# 统一处理
splits = text_splitter.split_documents(docs)
vectorstore = Chroma.from_documents(splits, embeddings)

# 问答
result = qa_chain.invoke({"input": "退货政策是什么?"})

RAG 评估

使用 LangSmith

import os
os.environ["LANGSMITH_TRACING"] = "true"

# 评估 RAG 系统
from langsmith.evaluation import evaluate

def evaluate_rag(dataset):
# 评估逻辑
pass

evaluate(
"my-rag-system",
evaluate_rag,
data=dataset
)

常见问题

1. 检索不到相关内容

  • 检查文档是否正确加载
  • 调整 chunk_size 和 overlap
  • 尝试不同的嵌入模型

2. 生成回答不准确

  • 增加检索的文档数量
  • 优化 prompt 模板
  • 使用更强大的 LLM

3. 处理长文档

  • 使用父子文档检索
  • 使用摘要式记忆
  • 分批处理

下一步

现在你已经掌握了 RAG 的核心概念,接下来学习: