You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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()

代码解释

  1. 带页码的文档构建:遍历PDF每页时,用Document对象存储页面文本和页码元数据,确保后续拆分的每个文本块都能追溯到原始页码。
  2. 带元数据的文本拆分:使用split_documents方法拆分,会自动保留每个文本块的页码元数据。
  3. 向量索引构建:用from_documents替代from_texts,将带元数据的文档传入FAISS,实现检索时能获取来源页码。
  4. 页码提取与渲染:检索完成后,从匹配的文档中提取页码并去重,使用st.pdf_display组件直接渲染指定页码的PDF内容,无需额外转换格式。

注意事项

  • 确保Streamlit版本≥1.28.0,st.pdf_display是较新版本新增的组件,若版本过低可升级:pip install --upgrade streamlit
  • 若PDF存在扫描件(无文本内容),需先进行OCR处理提取文本,否则无法关联页码和生成回答

内容的提问来源于stack exchange,提问作者Learner

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 14:06:31