如何在Langchain+Streamlit PDF问答应用中实现答案来源页面检索与渲染?
解决方案
要实现检索并渲染答案来源的PDF页面,核心是在文本处理阶段保留页码元数据,并在检索后关联回PDF页面进行渲染。以下是具体实现步骤和修改后的代码:
关键修改点
- 拆分文本时为每个文本块绑定对应的页码元数据
- 使用带元数据的
Document对象构建向量索引 - 检索后提取来源页码,利用Streamlit的PDF渲染组件展示对应页面
修改后的完整代码
from dotenv import load_dotenv import streamlit as st from PyPDF2 import PdfReader from langchain.text_splitter import CharacterTextSplitter from langchain.embeddings.openai import OpenAIEmbeddings from langchain.vectorstores import FAISS from langchain.chains.question_answering import load_qa_chain from langchain.llms import OpenAI from langchain.callbacks import get_openai_callback from langchain.schema import Document def main(): load_dotenv() st.set_page_config(page_title="Ask your PDF") st.header("Ask your PDF 💬") # 上传文件 pdf = st.file_uploader("Upload your PDF", type="pdf") if pdf is not None: pdf_reader = PdfReader(pdf) # 存储带页码的文档列表 documents = [] # 按页面提取文本并绑定页码元数据 for page_num, page in enumerate(pdf_reader.pages, start=1): page_text = page.extract_text() if page_text: documents.append(Document( page_content=page_text, metadata={"page": page_num} )) # 拆分文本为块(保留元数据) text_splitter = CharacterTextSplitter( separator="\n", chunk_size=500, chunk_overlap=100, length_function=len ) split_docs = text_splitter.split_documents(documents) # 创建带元数据的向量知识库 embeddings = OpenAIEmbeddings() knowledge_base = FAISS.from_documents(split_docs, embeddings) # 用户提问 user_question = st.text_input("Ask a question about your PDF:") if user_question: docs = knowledge_base.similarity_search(user_question) # 提取来源页码(去重) source_pages = list({doc.metadata["page"] for doc in docs}) source_pages.sort() # 生成回答 llm = OpenAI() chain = load_qa_chain(llm) with get_openai_callback() as cb: response = chain.run(input_documents=docs, question=user_question) # 展示回答和来源页码 st.subheader("回答") st.write(response) st.subheader("来源页面") st.write(f"答案来自PDF的第:{', '.join(map(str, source_pages))}页") # 渲染对应PDF页面 for page_num in source_pages: st.subheader(f"第 {page_num} 页内容") # 使用Streamlit的pdf_display组件渲染指定页码 st.pdf_display(pdf, pages=[page_num]) if __name__ == '__main__': main()
代码解释
- 带页码的文档构建:遍历PDF每页时,用
Document对象存储页面文本和页码元数据,确保后续拆分的每个文本块都能追溯到原始页码。 - 带元数据的文本拆分:使用
split_documents方法拆分,会自动保留每个文本块的页码元数据。 - 向量索引构建:用
from_documents替代from_texts,将带元数据的文档传入FAISS,实现检索时能获取来源页码。 - 页码提取与渲染:检索完成后,从匹配的文档中提取页码并去重,使用
st.pdf_display组件直接渲染指定页码的PDF内容,无需额外转换格式。
注意事项
- 确保Streamlit版本≥1.28.0,
st.pdf_display是较新版本新增的组件,若版本过低可升级:pip install --upgrade streamlit - 若PDF存在扫描件(无文本内容),需先进行OCR处理提取文本,否则无法关联页码和生成回答
内容的提问来源于stack exchange,提问作者Learner
相关产品推荐
相关产品推荐

