构建基础RAG管道:文档加载 -> 分割 -> 存储 -> 检索
核心知识点讲解
什么是RAG?
RAG(Retrieval-Augmented Generation)是一种结合了信息检索和生成模型的技术,它的主要特点包括:
- 外部知识增强:通过检索外部知识库的相关信息来增强生成模型的回答
- 减少幻觉:基于检索到的事实性信息生成回答,减少模型的幻觉
- 知识更新:无需重新训练模型,通过更新知识库即可更新模型的知识
- 领域适应:可以快速适应特定领域的知识
- 可解释性:可以提供回答的知识来源,增强可解释性
在AI智能体系统中,RAG主要用于:
- 知识库问答:基于企业或个人知识库回答问题
- 信息整合:整合多个来源的信息生成综合回答
- 实时信息获取:结合实时检索的信息生成回答
- 个性化服务:基于用户的历史数据和偏好提供个性化回答
RAG管道的核心组件
一个完整的RAG管道包含以下核心组件:
1. 文档加载器(Document Loader)
文档加载器负责从不同来源加载文档,支持多种文件格式:
- 文本文件:TXT、MD、JSON等
- 办公文档:PDF、DOCX、PPTX、XLSX等
- 网页内容:HTML、URL等
- 数据库:关系型数据库、NoSQL数据库等
- 其他来源:API、邮件、代码仓库等
2. 文本分割器(Text Splitter)
文本分割器负责将长文档分割成短文本块,以便于:
- 适应上下文窗口:符合语言模型的上下文窗口大小
- 提高检索精度:更精确地匹配相关内容
- 减少噪声:减少无关信息的干扰
- 提高处理速度:加快嵌入和检索速度
3. 嵌入模型(Embedding Model)
嵌入模型负责将文本转换为向量表示:
- 语义捕捉:捕捉文本的语义信息
- 向量生成:生成低维稠密的向量
- 相似度计算:支持基于向量的相似度计算
4. 向量数据库(Vector Database)
向量数据库负责存储和检索向量:
- 向量存储:存储文本块的向量表示
- 索引构建:构建高效的索引加速检索
- 相似度搜索:根据查询向量检索最相似的文本块
5. 检索器(Retriever)
检索器负责根据用户查询检索相关信息:
- 查询处理:处理用户的自然语言查询
- 向量转换:将查询转换为向量
- 相似度匹配:匹配最相关的文本块
- 结果排序:根据相似度排序返回结果
6. 生成器(Generator)
生成器负责基于检索结果生成回答:
- 上下文构建:构建包含检索结果的上下文
- 提示词设计:设计有效的提示词引导生成
- 回答生成:生成准确、连贯的回答
- 引用标注:标注回答的知识来源
实用案例分析
案例1:构建基础RAG管道
# 安装必要的库
# pip install langchain chromadb pypdf sentence-transformers
from langchain.document_loaders import PyPDFLoader, TextLoader, WebBaseLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.embeddings import HuggingFaceEmbeddings
from langchain.vectorstores import Chroma
from langchain.chains import RetrievalQA
from langchain.llms import OpenAI
import os
# 设置环境变量
os.environ["OPENAI_API_KEY"] = "your-openai-api-key"
# 1. 文档加载
print("=== 文档加载 ===")
# 加载PDF文件
pdf_loader = PyPDFLoader("example.pdf")
pdf_docs = pdf_loader.load()
print(f"PDF文档数量: {len(pdf_docs)}")
# 加载文本文件
text_loader = TextLoader("example.txt", encoding="utf-8")
text_docs = text_loader.load()
print(f"文本文档数量: {len(text_docs)}")
# 加载网页内容
web_loader = WebBaseLoader("https://example.com")
web_docs = web_loader.load()
print(f"网页文档数量: {len(web_docs)}")
# 合并所有文档
all_docs = pdf_docs + text_docs + web_docs
print(f"总文档数量: {len(all_docs)}")
# 2. 文本分割
print("\n=== 文本分割 ===")
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=1000, # 每个块的大小
chunk_overlap=200, # 块之间的重叠
length_function=len, # 长度计算函数
separators=["\n\n", "\n", " ", ""] # 分割符
)
# 分割文档
split_docs = text_splitter.split_documents(all_docs)
print(f"分割后的文档数量: {len(split_docs)}")
print(f"第一个文档块: {split_docs[0].page_content[:200]}...")
# 3. 嵌入模型
print("\n=== 嵌入模型 ===")
embeddings = HuggingFaceEmbeddings(
model_name="sentence-transformers/all-MiniLM-L6-v2", # 轻量级模型
model_kwargs={"device": "cpu"}, # 运行设备
encode_kwargs={"normalize_embeddings": True} # 归一化嵌入
)
# 测试嵌入
query = "测试嵌入模型"
query_embedding = embeddings.embed_query(query)
print(f"查询嵌入维度: {len(query_embedding)}")
print(f"嵌入示例: {query_embedding[:3]}...")
# 4. 向量存储
print("\n=== 向量存储 ===")
# 创建Chroma向量库
vectorstore = Chroma.from_documents(
documents=split_docs,
embedding=embeddings,
persist_directory="./chroma_db"
)
vectorstore.persist()
print("向量库创建成功并持久化")
# 测试相似度搜索
search_results = vectorstore.similarity_search(
query="测试相似度搜索",
k=3
)
print(f"相似度搜索结果数量: {len(search_results)}")
for i, result in enumerate(search_results):
print(f"结果 {i+1}: {result.page_content[:150]}...")
# 5. 构建RAG链
print("\n=== 构建RAG链 ===")
# 初始化语言模型
llm = OpenAI(temperature=0.7)
# 构建检索器
retriever = vectorstore.as_retriever(
search_type="similarity", # 搜索类型
search_kwargs={"k": 3} # 搜索参数
)
# 构建RAG链
qa_chain = RetrievalQA.from_chain_type(
llm=llm,
chain_type="stuff", # 链类型
retriever=retriever,
return_source_documents=True, # 返回源文档
verbose=True # 启用详细输出
)
# 6. 测试RAG链
print("\n=== 测试RAG链 ===")
# 测试问题
questions = [
"文档的主要内容是什么?",
"文档中提到了哪些关键概念?",
"文档的作者是谁?"
]
for question in questions:
print(f"\n问题: {question}")
result = qa_chain(question)
print(f"回答: {result['result']}")
print(f"源文档数量: {len(result['source_documents'])}")
for i, doc in enumerate(result['source_documents']):
print(f"源文档 {i+1}: {doc.page_content[:100]}...")
print("\n基础RAG管道构建完成!")案例2:高级RAG配置
from langchain.document_loaders import PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter, TokenTextSplitter
from langchain.embeddings import OpenAIEmbeddings, HuggingFaceEmbeddings
from langchain.vectorstores import FAISS
from langchain.chains import RetrievalQA, ConversationalRetrievalChain
from langchain.chat_models import ChatOpenAI
from langchain.memory import ConversationBufferMemory
import os
# 设置环境变量
os.environ["OPENAI_API_KEY"] = "your-openai-api-key"
# 1. 文档加载与处理
print("=== 文档加载与处理 ===")
# 加载PDF文档
loader = PyPDFLoader("technical_document.pdf")
documents = loader.load()
print(f"原始文档数量: {len(documents)}")
# 2. 高级文本分割
print("\n=== 高级文本分割 ===")
# 选项1:递归字符分割器(推荐)
recursive_splitter = RecursiveCharacterTextSplitter(
chunk_size=1000,
chunk_overlap=200,
separators=["\n\n", "\n", " ", ""]
)
# 选项2:Token分割器
token_splitter = TokenTextSplitter(
chunk_size=512,
chunk_overlap=100
)
# 使用递归字符分割器
split_docs = recursive_splitter.split_documents(documents)
print(f"分割后的文档数量: {len(split_docs)}")
# 3. 嵌入模型选择
print("\n=== 嵌入模型选择 ===")
# 选项1:OpenAI嵌入模型
openai_embeddings = OpenAIEmbeddings(
model="text-embedding-ada-002"
)
# 选项2:HuggingFace嵌入模型
hf_embeddings = HuggingFaceEmbeddings(
model_name="sentence-transformers/all-mpnet-base-v2"
)
# 使用OpenAI嵌入模型
embeddings = openai_embeddings
# 4. 向量存储配置
print("\n=== 向量存储配置 ===")
# 创建FAISS向量库
vectorstore = FAISS.from_documents(
documents=split_docs,
embedding=embeddings
)
# 保存向量库
vectorstore.save_local("./faiss_db")
print("FAISS向量库创建并保存成功")
# 5. 高级检索配置
print("\n=== 高级检索配置 ===")
# 选项1:相似度搜索
retriever_similarity = vectorstore.as_retriever(
search_type="similarity",
search_kwargs={"k": 3}
)
# 选项2:最大边际相关性搜索(MMR)
retriever_mmr = vectorstore.as_retriever(
search_type="mmr",
search_kwargs={"k": 3, "lambda_mult": 0.5}
)
# 选项3:相似度分数阈值搜索
retriever_threshold = vectorstore.as_retriever(
search_type="similarity_score_threshold",
search_kwargs={"score_threshold": 0.7, "k": 3}
)
# 使用MMR检索器
retriever = retriever_mmr
# 6. 不同链类型测试
print("\n=== 不同链类型测试 ===")
# 初始化语言模型
llm = ChatOpenAI(temperature=0.7, model="gpt-3.5-turbo")
# 选项1:stuff链
chain_stuff = RetrievalQA.from_chain_type(
llm=llm,
chain_type="stuff",
retriever=retriever,
return_source_documents=True,
verbose=True
)
# 选项2:map_reduce链
chain_map_reduce = RetrievalQA.from_chain_type(
llm=llm,
chain_type="map_reduce",
retriever=retriever,
return_source_documents=True,
verbose=True
)
# 选项3:refine链
chain_refine = RetrievalQA.from_chain_type(
llm=llm,
chain_type="refine",
retriever=retriever,
return_source_documents=True,
verbose=True
)
# 7. 对话式RAG
print("\n=== 对话式RAG ===")
# 初始化记忆系统
memory = ConversationBufferMemory(
memory_key="chat_history",
return_messages=True,
output_key="answer"
)
# 构建对话式RAG链
conversational_chain = ConversationalRetrievalChain.from_llm(
llm=llm,
retriever=retriever,
memory=memory,
return_source_documents=True,
verbose=True
)
# 测试对话式RAG
print("\n测试对话式RAG:")
print("你可以连续提问,测试对话上下文理解能力")
print("输入'退出'结束对话")
while True:
question = input("\n问题: ")
if question.lower() == "退出":
print("对话结束!")
break
result = conversational_chain({
"question": question
})
print(f"回答: {result['answer']}")
print(f"源文档数量: {len(result['source_documents'])}")
for i, doc in enumerate(result['source_documents']):
print(f"源文档 {i+1}: {doc.page_content[:100]}...")
print("\n高级RAG配置测试完成!")案例3:RAG与智能体集成
from langchain.agents import initialize_agent, AgentType
from langchain.chat_models import ChatOpenAI
from langchain.tools import Tool
from langchain.utilities import SerpAPIWrapper
from langchain.chains import RetrievalQA
from langchain.vectorstores import Chroma
from langchain.embeddings import OpenAIEmbeddings
from langchain.memory import ConversationBufferMemory
import os
# 设置环境变量
os.environ["OPENAI_API_KEY"] = "your-openai-api-key"
os.environ["SERPAPI_API_KEY"] = "your-serpapi-api-key"
# 1. 加载向量库
print("=== 加载向量库 ===")
embeddings = OpenAIEmbeddings()
vectorstore = Chroma(
persist_directory="./chroma_db",
embedding_function=embeddings
)
print("向量库加载成功")
# 2. 构建RAG工具
print("\n=== 构建RAG工具 ===")
# 初始化语言模型
llm = ChatOpenAI(temperature=0.7)
# 构建检索器
retriever = vectorstore.as_retriever(
search_type="mmr",
search_kwargs={"k": 3, "lambda_mult": 0.5}
)
# 构建RAG链
rag_chain = RetrievalQA.from_chain_type(
llm=llm,
chain_type="stuff",
retriever=retriever,
return_source_documents=True
)
# 创建RAG工具
def rag_tool(query):
"""使用RAG回答关于文档的问题"""
result = rag_chain(query)
answer = result["result"]
sources = result["source_documents"]
# 格式化结果
formatted_sources = "\n".join([f"- {source.page_content[:100]}..." for source in sources[:2]])
return f"回答: {answer}\n\n参考来源:\n{formatted_sources}"
# 3. 初始化其他工具
print("\n=== 初始化其他工具 ===")
search = SerpAPIWrapper()
# 4. 定义工具列表
tools = [
Tool(
name="Search",
func=search.run,
description="用于搜索网络上的实时信息"
),
Tool(
name="DocumentQA",
func=rag_tool,
description="用于回答关于文档内容的问题"
)
]
# 5. 初始化记忆系统
print("\n=== 初始化记忆系统 ===")
memory = ConversationBufferMemory(
memory_key="chat_history",
return_messages=True
)
# 6. 初始化智能体
print("\n=== 初始化智能体 ===")
agent = initialize_agent(
tools=tools,
llm=llm,
agent=AgentType.CHAT_ZERO_SHOT_REACT_DESCRIPTION,
verbose=True,
memory=memory
)
print("\n=== 测试智能体 ===")
print("你可以询问关于文档的问题或其他问题,例如:")
print("1. 文档的主要内容是什么?")
print("2. 文档中提到了哪些技术?")
print("3. 今天北京的天气怎么样?")
print("4. 文档的作者是谁?")
print("输入'退出'结束对话")
print("===================")
# 7. 测试智能体
while True:
user_input = input("\n用户: ")
if user_input.lower() == "退出":
print("智能体: 再见!如果有任何问题,随时再来咨询我。")
break
try:
response = agent.run(user_input)
print(f"智能体: {response}")
except Exception as e:
print(f"智能体: 抱歉,我在处理您的问题时遇到了一些困难。错误信息: {str(e)}")
print("\n智能体测试完成!")代码解析
基础RAG管道
文档加载:
- 使用不同的加载器加载不同类型的文档
- 支持PDF、文本文件、网页等多种格式
- 合并多个来源的文档
文本分割:
- 使用RecursiveCharacterTextSplitter进行递归分割
- 配置合适的chunk_size和chunk_overlap
- 确保分割后的文本块大小适中
嵌入与存储:
- 使用HuggingFaceEmbeddings生成文本嵌入
- 创建Chroma向量库并持久化
- 测试相似度搜索功能
RAG链构建:
- 初始化语言模型
- 构建检索器
- 使用RetrievalQA构建RAG链
- 测试RAG链的回答能力
高级RAG配置
高级文本分割:
- 比较不同的文本分割器
- 根据文档类型选择合适的分割策略
嵌入模型选择:
- 比较OpenAI和HuggingFace嵌入模型
- 根据性能和成本选择合适的模型
向量存储优化:
- 使用FAISS提高检索性能
- 保存和加载向量库
高级检索策略:
- 实现相似度搜索、MMR搜索和阈值搜索
- 根据场景选择合适的检索策略
不同链类型:
- 测试stuff、map_reduce和refine链类型
- 分析不同链类型的优缺点
对话式RAG:
- 集成记忆系统
- 实现多轮对话能力
RAG与智能体集成
向量库加载:
- 加载已有的向量库
- 准备RAG功能
RAG工具创建:
- 构建RAG链
- 封装为工具函数
- 定义清晰的工具描述
智能体配置:
- 集成RAG工具和搜索工具
- 配置记忆系统
- 初始化智能体
交互测试:
- 测试智能体对不同问题的响应
- 观察智能体选择工具的策略
- 验证RAG功能的有效性
高级技巧
1. 文档加载优化
针对不同类型的文档,使用合适的加载策略:
from langchain.document_loaders import (
PyPDFLoader, PDFPlumberLoader, UnstructuredPDFLoader,
Docx2txtLoader, UnstructuredWordDocumentLoader,
TextLoader, CSVLoader, JSONLoader,
WebBaseLoader, SeleniumURLLoader,
GitHubRepoLoader, NotionDirectoryLoader
)
import os
# 1. PDF文档加载
print("=== PDF文档加载 ===")
# 选项1:PyPDFLoader(基础)
pdf_loader = PyPDFLoader("document.pdf")
# 选项2:PDFPlumberLoader(高级)
# pdf_loader = PDFPlumberLoader("document.pdf")
# 选项3:UnstructuredPDFLoader(支持OCR)
# pdf_loader = UnstructuredPDFLoader("document.pdf", mode="elements")
pdf_docs = pdf_loader.load()
print(f"PDF文档页数: {len(pdf_docs)}")
# 2. Word文档加载
print("\n=== Word文档加载 ===")
docx_loader = Docx2txtLoader("document.docx")
docx_docs = docx_loader.load()
print(f"Word文档数量: {len(docx_docs)}")
# 3. CSV文档加载
print("\n=== CSV文档加载 ===")
csv_loader = CSVLoader(
file_path="data.csv",
csv_args={
"delimiter": ",",
"quotechar": '"',
"fieldnames": ["column1", "column2", "column3"]
}
)
csv_docs = csv_loader.load()
print(f"CSV文档数量: {len(csv_docs)}")
# 4. 网页加载
print("\n=== 网页加载 ===")
# 选项1:WebBaseLoader(基础)
web_loader = WebBaseLoader("https://example.com")
# 选项2:SeleniumURLLoader(支持JavaScript渲染)
# web_loader = SeleniumURLLoader(urls=["https://example.com"])
web_docs = web_loader.load()
print(f"网页文档数量: {len(web_docs)}")
# 5. GitHub仓库加载
print("\n=== GitHub仓库加载 ===")
if os.environ.get("GITHUB_TOKEN"):
github_loader = GitHubRepoLoader(
repo="username/repository",
access_token=os.environ.get("GITHUB_TOKEN"),
recursive=True,
ignore_directories=[".git", "node_modules", "venv"]
)
github_docs = github_loader.load()
print(f"GitHub文档数量: {len(github_docs)}")
# 6. 批量加载
print("\n=== 批量加载 ===")
document_loaders = [
PyPDFLoader("doc1.pdf"),
TextLoader("doc2.txt"),
WebBaseLoader("https://example.com")
]
all_docs = []
for loader in document_loaders:
try:
docs = loader.load()
all_docs.extend(docs)
print(f"加载成功: {type(loader).__name__}")
except Exception as e:
print(f"加载失败 {type(loader).__name__}: {str(e)}")
print(f"\n总加载文档数量: {len(all_docs)}")2. 文本分割策略
根据文档类型选择合适的分割策略:
from langchain.text_splitter import (
RecursiveCharacterTextSplitter, CharacterTextSplitter,
TokenTextSplitter, MarkdownTextSplitter,
PythonCodeTextSplitter, NLTKTextSplitter
)
# 1. 递归字符分割器(通用推荐)
print("=== 递归字符分割器 ===")
recursive_splitter = RecursiveCharacterTextSplitter(
chunk_size=1000,
chunk_overlap=200,
separators=["\n\n", "\n", " ", ""]
)
# 2. 字符分割器(简单分割)
print("\n=== 字符分割器 ===")
character_splitter = CharacterTextSplitter(
separator="\n",
chunk_size=1000,
chunk_overlap=200,
length_function=len
)
# 3. Token分割器(基于Token计数)
print("\n=== Token分割器 ===")
token_splitter = TokenTextSplitter(
chunk_size=512,
chunk_overlap=100
)
# 4. Markdown分割器(保留Markdown结构)
print("\n=== Markdown分割器 ===")
markdown_splitter = MarkdownTextSplitter(
chunk_size=1000,
chunk_overlap=200
)
# 5. Python代码分割器(保留代码结构)
print("\n=== Python代码分割器 ===")
python_splitter = PythonCodeTextSplitter(
chunk_size=1000,
chunk_overlap=200
)
# 测试不同分割器
text = """# 标题
这是一段正文内容,包含多个句子。
## 子标题
这是子标题下的内容,也包含多个句子。
```python
def hello():
print("Hello world!")这是代码后的内容。"""
print("\n=== 分割结果对比 ===")
递归字符分割
recursive_chunks = recursive_splitter.split_text(text)
print(f"递归字符分割: {len(recursive_chunks)} 块")
for i, chunk in enumerate(recursive_chunks):
print(f"块 {i+1}: {chunk[:100]}...")
Markdown分割
markdown_chunks = markdown_splitter.split_text(text)
print(f"\nMarkdown分割: {len(markdown_chunks)} 块")
for i, chunk in enumerate(markdown_chunks):
print(f"块 {i+1}: {chunk[:100]}...")
Python代码分割
python_chunks = python_splitter.split_text(text)
print(f"\nPython代码分割: {len(python_chunks)} 块")
for i, chunk in enumerate(python_chunks):
print(f"块 {i+1}: {chunk[:100]}...")
选择合适的分割器
print("\n=== 分割器选择建议 ===")
print("- 通用文档: 递归字符分割器")
print("- Markdown文档: Markdown分割器")
print("- 代码文件: Python代码分割器(或相应语言的分割器)")
print("- 对Token敏感的模型: Token分割器")
### 3. 检索优化
优化检索策略,提高检索精度:
```python
from langchain.vectorstores import FAISS
from langchain.embeddings import OpenAIEmbeddings
from langchain.docstore.document import Document
import numpy as np
# 1. 创建测试文档
documents = [
Document(
page_content="人工智能是研究、开发用于模拟、延伸和扩展人的智能的理论、方法、技术及应用系统的一门新的技术科学。",
metadata={"category": "ai", "type": "definition"}
),
Document(
page_content="机器学习是人工智能的一个分支,它使计算机系统能够从数据中学习并改进,而无需明确编程。",
metadata={"category": "ai", "type": "branch"}
),
Document(
page_content="深度学习是机器学习的一个分支,它使用多层神经网络来模拟人脑的学习过程。",
metadata={"category": "ai", "type": "branch"}
),
Document(
page_content="自然语言处理是人工智能的一个领域,它使计算机能够理解、解释和生成人类语言。",
metadata={"category": "ai", "type": "field"}
),
Document(
page_content="计算机视觉是人工智能的一个领域,它使计算机能够理解和解释图像和视频。",
metadata={"category": "ai", "type": "field"}
)
]
# 2. 创建向量库
embeddings = OpenAIEmbeddings()
vectorstore = FAISS.from_documents(
documents=documents,
embedding=embeddings
)
# 3. 不同检索策略对比
print("=== 不同检索策略对比 ===")
query = "什么是机器学习和深度学习?"
# 策略1:相似度搜索
print("\n策略1:相似度搜索")
similarity_results = vectorstore.similarity_search(
query=query,
k=3
)
for i, result in enumerate(similarity_results):
print(f"{i+1}. {result.page_content}")
# 策略2:带分数的相似度搜索
print("\n策略2:带分数的相似度搜索")
similarity_score_results = vectorstore.similarity_search_with_score(
query=query,
k=3
)
for i, (result, score) in enumerate(similarity_score_results):
print(f"{i+1}. {result.page_content} (分数: {score:.4f})")
# 策略3:MMR搜索
print("\n策略3:MMR搜索")
mmr_results = vectorstore.max_marginal_relevance_search(
query=query,
k=3,
lambda_mult=0.5
)
for i, result in enumerate(mmr_results):
print(f"{i+1}. {result.page_content}")
# 策略4:基于向量的搜索
print("\n策略4:基于向量的搜索")
query_vector = embeddings.embed_query(query)
vector_results = vectorstore.similarity_search_by_vector(
embedding=query_vector,
k=3
)
for i, result in enumerate(vector_results):
print(f"{i+1}. {result.page_content}")
# 4. 元数据过滤
print("\n=== 元数据过滤 ===")
# 过滤特定类型的文档
filtered_results = vectorstore.similarity_search(
query="人工智能领域",
k=3
)
# 手动过滤元数据
filtered_by_metadata = [
doc for doc in filtered_results
if doc.metadata.get("type") == "field"
]
print("过滤后的结果(只显示field类型):")
for i, result in enumerate(filtered_by_metadata):
print(f"{i+1}. {result.page_content} (类型: {result.metadata.get('type')})")
# 5. 组合检索策略
print("\n=== 组合检索策略 ===")
def hybrid_search(query, k=3):
"""混合检索策略"""
# 1. 首先使用MMR获取多样化结果
mmr_results = vectorstore.max_marginal_relevance_search(
query=query,
k=k*2, # 获取更多结果
lambda_mult=0.3
)
# 2. 然后对结果进行相似度排序
query_vector = embeddings.embed_query(query)
# 计算每个结果与查询的相似度
results_with_similarity = []
for doc in mmr_results:
doc_vector = embeddings.embed_query(doc.page_content)
similarity = np.dot(query_vector, doc_vector) / (
np.linalg.norm(query_vector) * np.linalg.norm(doc_vector)
)
results_with_similarity.append((doc, similarity))
# 按相似度排序并返回前k个
results_with_similarity.sort(key=lambda x: x[1], reverse=True)
top_results = [result[0] for result in results_with_similarity[:k]]
return top_results
# 测试混合检索
hybrid_results = hybrid_search(query, k=3)
print("混合检索结果:")
for i, result in enumerate(hybrid_results):
print(f"{i+1}. {result.page_content}")
print("\n检索优化完成!")4. RAG评估
评估RAG系统的性能:
from langchain.vectorstores import Chroma
from langchain.embeddings import OpenAIEmbeddings
from langchain.chains import RetrievalQA
from langchain.llms import OpenAI
import json
import os
# 1. 加载向量库
embeddings = OpenAIEmbeddings()
vectorstore = Chroma(
persist_directory="./chroma_db",
embedding_function=embeddings
)
# 2. 构建RAG链
llm = OpenAI(temperature=0.7)
retriever = vectorstore.as_retriever(
search_type="mmr",
search_kwargs={"k": 3}
)
rag_chain = RetrievalQA.from_chain_type(
llm=llm,
chain_type="stuff",
retriever=retriever,
return_source_documents=True
)
# 3. 准备评估数据集
evaluation_data = [
{
"question": "文档的主要内容是什么?",
"ground_truth": "文档主要介绍了人工智能的基本概念、发展历史、核心技术和应用领域。"
},
{
"question": "文档中提到了哪些人工智能技术?",
"ground_truth": "文档中提到了机器学习、深度学习、自然语言处理、计算机视觉等人工智能技术。"
},
{
"question": "人工智能有哪些应用领域?",
"ground_truth": "人工智能的应用领域包括医疗、金融、教育、交通、制造、娱乐等。"
}
]
# 4. 评估指标
from sklearn.metrics import f1_score, accuracy_score
import re
def normalize_text(text):
"""标准化文本"""
text = text.lower()
text = re.sub(r'\s+', ' ', text)
text = re.sub(r'[\n\r]', ' ', text)
return text.strip()
def compute_metrics(predictions, ground_truths):
"""计算评估指标"""
# 简单准确率(基于关键词匹配)
correct = 0
for pred, gt in zip(predictions, ground_truths):
pred_norm = normalize_text(pred)
gt_norm = normalize_text(gt)
# 检查预测是否包含 ground truth 的主要内容
if all(keyword in pred_norm for keyword in gt_norm.split()[:5]):
correct += 1
accuracy = correct / len(predictions)
return {
"accuracy": accuracy
}
# 5. 执行评估
print("=== RAG系统评估 ===")
predictions = []
ground_truths = []
evaluation_results = []
for item in evaluation_data:
question = item["question"]
ground_truth = item["ground_truth"]
# 获取预测结果
result = rag_chain(question)
prediction = result["result"]
sources = result["source_documents"]
# 保存结果
predictions.append(prediction)
ground_truths.append(ground_truth)
# 记录详细结果
evaluation_results.append({
"question": question,
"prediction": prediction,
"ground_truth": ground_truth,
"sources": [source.page_content[:200] for source in sources]
})
print(f"\n问题: {question}")
print(f"预测: {prediction}")
print(f"真实答案: {ground_truth}")
# 6. 计算指标
metrics = compute_metrics(predictions, ground_truths)
print("\n=== 评估指标 ===")
for metric_name, metric_value in metrics.items():
print(f"{metric_name}: {metric_value:.4f}")
# 7. 保存评估结果
with open("./rag_evaluation_results.json", "w", encoding="utf-8") as f:
json.dump({
"metrics": metrics,
"results": evaluation_results
}, f, ensure_ascii=False, indent=2)
print("\n评估完成,结果已保存到 rag_evaluation_results.json")最佳实践
1. RAG管道优化
| 组件 | 优化策略 | 推荐配置 |
|---|---|---|
| 文档加载 | 选择合适的加载器 | 根据文档类型选择专门的加载器 |
| 文本分割 | 调整分割参数 | chunk_size=1000-2000, chunk_overlap=100-200 |
| 嵌入模型 | 选择合适的模型 | 开发环境:all-MiniLM-L6-v2;生产环境:text-embedding-ada-002 |
| 向量存储 | 选择合适的数据库 | 开发环境:Chroma;生产环境:FAISS或专业向量数据库 |
| 检索策略 | 组合多种策略 | 小规模:相似度搜索;大规模:MMR搜索 |
| 链类型 | 根据文档长度选择 | 短文档:stuff;长文档:map_reduce或refine |
2. 性能优化
- 批处理:批量处理文档加载和嵌入
- 缓存机制:缓存频繁查询的结果和嵌入
- 并行处理:并行执行文档分割和嵌入
- 索引优化:为向量数据库创建合适的索引
- 硬件加速:使用GPU加速嵌入生成和检索
3. 常见问题与解决方案
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 回答质量差 | 检索结果不相关 | 优化检索策略,调整分割参数 |
| 响应速度慢 | 嵌入和检索耗时 | 使用轻量级嵌入模型,优化向量数据库 |
| 记忆溢出 | 文档过大 | 增加分割的chunk_size,使用流式处理 |
| 知识过时 | 知识库未更新 | 定期更新向量数据库,集成实时搜索 |
| 幻觉仍存在 | 检索结果不足 | 增加检索结果数量,优化提示词 |
4. 部署建议
开发环境:
- 使用Chroma内存模式快速测试
- 启用详细日志便于调试
- 使用轻量级嵌入模型
生产环境:
- 使用FAISS或专业向量数据库
- 实现向量库的备份和恢复
- 监控系统性能和响应时间
- 考虑使用容器化部署
- 实现自动扩展机制
总结与展望
本集要点总结
RAG基础:
- RAG的概念和优势
- RAG在智能体系统中的应用
- RAG管道的核心组件
RAG管道构建:
- 文档加载:支持多种文件格式
- 文本分割:适应语言模型的上下文窗口
- 向量存储:使用Chroma和FAISS
- 相似度检索:实现准确的信息检索
- 生成回答:基于检索结果生成准确回答
高级配置:
- 不同分割策略的选择
- 多种检索策略的对比
- 不同链类型的应用
- 对话式RAG的实现
智能体集成:
- 将RAG封装为工具
- 集成到智能体系统
- 实现多工具协同
高级技巧:
- 文档加载优化
- 文本分割策略
- 检索优化
- RAG评估
未来发展方向
RAG技术演进:
- 更先进的检索策略
- 多模态RAG(结合文本、图像、音频等)
- 自适应RAG(根据查询自动调整策略)
智能体集成:
- 更深层次的RAG与智能体集成
- 多智能体RAG系统
- 个性化RAG(基于用户偏好)
性能优化:
- 更高效的嵌入和检索
- 分布式RAG系统
- 边缘设备上的RAG
应用扩展:
- 企业级知识库系统
- 实时信息获取与整合
- 跨语言RAG系统
研究方向:
- RAG的理论基础
- 最优检索策略的研究
- RAG与微调的结合
通过本集的学习,我们了解了如何构建基础的RAG管道,掌握了文档加载、文本分割、向量存储和相似度检索等核心技术,以及如何将RAG集成到智能体系统中。RAG作为一种强大的知识增强技术,将在构建更智能、更可靠的AI系统中发挥越来越重要的作用。
在后续的课程中,我们将深入探讨RAG的高级应用,以及如何构建更复杂、更强大的智能体记忆系统。