
本文深入探讨了在使用langchain和rag(检索增强生成)处理pdf格式faq文档时,检索结果不准确的问题。核心在于优化嵌入模型和文档分块策略。通过采用huggingface的预训练嵌入模型,并结合合适的文本分割方法,可以显著提升rag系统的检索精度,确保llm能够获取到与查询高度相关的文档片段,从而生成更准确的答案。
在使用Langchain构建基于RAG的问答系统时,尤其是在处理PDF格式的FAQ文档时,可能会遇到检索到的信息与用户查询不匹配的问题。即使查询的问题直接存在于文档中,系统也可能返回看似相似但实际无关的文档片段。这通常是由于嵌入模型选择不当或文档分块策略不合理导致的。本教程将详细解析这些问题,并提供一套优化的解决方案。
一个典型的Langchain RAG系统主要包含以下几个步骤:
检索不准确的问题,往往发生在第3步(文本嵌入)和第2步(文本分割)上。
嵌入模型是RAG系统准确性的基石。它负责将文本转换为能够有效代表其语义的向量。如果嵌入模型无法准确捕捉文档片段和查询之间的语义相似性,那么即使文档中存在正确答案,也可能无法被检索到。
原先的代码可能使用了如 GPT4AllEmbeddings 或 OllamaEmbeddings 等本地模型。虽然这些模型易于部署,但在某些场景下,其语义理解能力可能不如更专业的预训练模型。
推荐方案:使用HuggingFace嵌入模型
HuggingFace提供了大量高质量的预训练嵌入模型,它们在各种文本理解任务上表现出色。这些模型通常经过大规模语料库的训练,能够更好地理解文本的语义细微差别。
以下是使用 HuggingFaceEmbeddings 的示例代码:
from langchain.document_loaders import PyPDFLoader, DirectoryLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.vectorstores import Chroma
from langchain.embeddings import HuggingFaceEmbeddings
from langchain.chains import RetrievalQA
from langchain.llms import OpenAI # 示例,也可替换为其他LLM
# 1. 文档加载
# 如果是单个PDF文件,可以使用PyPDFLoader
# loader = PyPDFLoader("doc.pdf")
# documents = loader.load()
# 如果是目录下的所有PDF文件
loader = DirectoryLoader('./docs/', glob="./*.pdf", loader_cls=PyPDFLoader) # 假设PDF文件在当前目录的'docs'文件夹
documents = loader.load()
# 2. 文本分割
# 针对FAQ文档,chunk_size和chunk_overlap的设置尤为关键
# 确保一个完整的问答对尽可能在一个chunk中,或跨越少量chunk
text_splitter = RecursiveCharacterTextSplitter(chunk_size=700, # 适当调整大小
chunk_overlap=70) # 适当的重叠有助于保持上下文
texts = text_splitter.split_documents(documents)
# 3. 文本嵌入
# 推荐使用HuggingFace的预训练模型
# "bert-base-multilingual-cased" 适用于多语言,效果较好
# "sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2" 也是一个不错的选择
embeddings = HuggingFaceEmbeddings(
model_name="bert-base-multilingual-cased"
# 或者 model_name="sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2"
)
# 4. 向量存储
# persist_directory 用于将向量数据库持久化到磁盘,避免每次运行时重新生成
persist_directory = "./chromadb"
vectordb = Chroma.from_documents(documents=texts, embedding=embeddings, persist_directory=persist_directory)
vectordb.persist() # 确保数据被写入磁盘
print(f"成功加载并嵌入 {len(texts)} 个文本块。")
# 5. LLM集成与检索QA链
# 替换为你的LLM实例,这里以OpenAI为例
# llm = OpenAI(temperature=0, model_name="gpt-3.5-turbo-instruct")
# 如果使用HuggingFaceHub作为LLM
from langchain.llms import HuggingFaceHub
llm = HuggingFaceHub(repo_id="google/flan-t5-base",
model_kwargs={"temperature":0.1, "max_length": 500, "max_new_tokens": 200})
qa_chain = RetrievalQA.from_chain_type(
llm=llm,
retriever=vectordb.as_retriever(search_kwargs={"k": 3}), # k表示检索前k个最相关的文档
chain_type="stuff", # "stuff"链类型将所有检索到的文档塞入LLM的上下文
return_source_documents=True # 返回检索到的源文档
)
# 6. 执行查询
question = "How do I reset my password?" # 替换为你的实际问题
response = qa_chain({"query": question})
print("\n--- 回答 ---")
print(response["result"])
print("\n--- 来源文档 ---")
for doc in response["source_documents"]:
print(f"内容: {doc.page_content[:200]}...") # 打印前200字
print(f"来源: {doc.metadata.get('source')}, 页码: {doc.metadata.get('page')}")对于FAQ(常见问题解答)文档,文本分割策略至关重要。如果一个问答对被分割到不同的块中,或者一个块包含了太多不相关的问答对,都会影响检索的准确性。
RecursiveCharacterTextSplitter 是一种常用的文本分割器,它会尝试根据一系列分隔符(如换行符、空格)递归地分割文本。
调整建议:
除了OpenAI的LLM,你也可以选择其他开源的LLM,例如HuggingFace Hub上托管的模型。
from langchain.llms import HuggingFaceHub
# 使用Google的Flan-T5-base模型
llm_flan_t5 = HuggingFaceHub(
repo_id="google/flan-t5-base",
model_kwargs={"temperature": 0.6, "max_length": 500, "max_new_tokens": 200}
)
# 或者使用EleutherAI的GPT-Neo-2.7B模型
llm_gpt_neo = HuggingFaceHub(
repo_id="EleutherAI/gpt-neo-2.7B",
model_kwargs={"temperature": 0.7, "max_length": 500, "max_new_tokens": 200}
)
# 在RetrievalQA链中替换llm参数即可
# qa_chain = RetrievalQA.from_chain_type(llm=llm_flan_t5, ...)配置 model_kwargs 中的 temperature(控制生成文本的随机性)、max_length 和 max_new_tokens(控制生成文本的最大长度)可以影响LLM的输出行为。
通过上述优化,特别是选择一个更强大的嵌入模型(如HuggingFace提供的模型)并精细调整文本分块策略,你可以显著提升Langchain RAG系统在处理FAQ文档时的检索准确性,从而为用户提供更精准、更相关的答案。
以上就是优化Langchain RAG检索:解决PDF文档信息不匹配问题的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号