7 赞同
18 收藏


  1. PDF对话助手:该工具促进用户和PDF文档之间的交互。用户可以提出与PDF文件内容相关的问题,助手从文档中检索相关信息以提供准确的答复。
  2. LangChain:LangChain 提供了开发人工智能应用程序的框架,特别是那些利用语言模型的应用程序。在PDF对话助手的背景下,LangChain处理文档摄取、文本处理以及与语言模型交互等任务。它通过提供用于管理数据、与语言模型集成以及编排信息流的模块来简化开发过程。
  3. LLM(大型语言模型):LLM 构成了对话助理理解和生成类人响应能力的支柱。这些模型(例如 GPT-3.5 和 GPT-4)已经过大量文本数据的预训练,可以根据提供给它们的上下文生成连贯的响应。在 PDF 对话助手中,LLM 用于处理用户查询、从 PDF 文档中检索相关信息并以自然语言制定响应。
  4. Streamlit:Streamlit 充当用户与我们的 PDF 对话助手交互的界面。


逐步过程:

第1步:导入必要的库

# Import necessary libraries
import os
import tempfile
import streamlit as st
from streamlit_chat import message
from langchain.vectorstores import Chroma
from langchain.chat_models import ChatOllama
from langchain.embeddings import FastEmbedEmbeddings
from langchain.schema.output_parser import StrOutputParser
from langchain.document_loaders import PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.schema.runnable import RunnablePassthrough
from langchain.prompts import PromptTemplate
from langchain.vectorstores.utils import filter_complex_metadata





解释:

  • 我们导入 ChatPDF 应用程序所需的各种库和模块。
  • Streamlit 用于创建用户界面。
  • Streamlit Chat 用于显示聊天消息。
  • 导入 LangChain 模块用于 PDF 文档处理、文本处理和语言模型交互。


如何?-

  1. PDF文档处理:LangChain提供了一个用于处理PDF文档的模块PyPDFLoaderPyPDFLoaderPyPDF2pdfminer。该模块允许开发人员加载 PDF 文件并提取其内容以进行进一步处理。该模块可能在底层使用 Python 库来解析 PDF 文件并从中提取文本和元数据。
  2. 文本处理:LangChain中的文本处理涉及各种任务,例如分割、清理和格式化文本数据,以使其适合进一步分析或与语言模型交互。LangChain 提供了一些模块,例如RecursiveCharacterTextSplitter将大型文本文档分解为较小的块。此外,它还可以提供用于清理和预处理文本数据的工具,例如删除特殊字符、停用词或不相关的元数据。
  3. 语言模型交互:与语言模型的交互是构建 LLM 支持的应用程序的一个重要方面。LangChain 通过其模块促进这种交互chat_modelsChatOllama,该模块包括. 这些类封装了用于初始化、配置以及与各种语言模型交互的功能。LangChain可能支持不同的LLM,包括来自OpenAI、Hugging Face或定制训练模型的LLM。它抽象化了与不同 LLM API 集成的复杂性,并为开发人员提供了与其交互的统一接口。


步骤 2:定义 ChatPDF 类

# Define the ChatPDF class
class ChatPDF:
    vector_store = None
    retriever = None
    chain = None

    def __init__(self):
        # Initialize the ChatOllama model
        self.model = ChatOllama(model="mistral")
        # Initialize the text splitter
        self.text_splitter = RecursiveCharacterTextSplitter(chunk_size=1024, chunk_overlap=100)
        # Define the prompt template for conversation
        self.prompt = PromptTemplate.from_template(
            """
            <s> [INST] 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, just say that you don't know. Use three sentences
             maximum and keep the answer concise. [/INST] </s> 
            [INST] Question: {question} 
            Context: {context} 
            Answer: [/INST]
            """
        )

解释:

  • 我们定义ChatPDF类来封装应用程序的功能。
  • 在构造函数 ( __init__) 中,我们初始化用于对话响应的 ChatOllama 模型。
  • 我们初始化文本分割器以将 PDF 文档分解成更小的块进行处理。
  • 定义提示模板用于格式化用户和模型之间的对话。


提示词:提示词是一段文本,为语言模型提供指令或上下文,指导其预期什么样的响应。它通常由用户查询、指令、动态内容的占位符和格式化元素的组合组成。

用法:

  • 定义提示模板:首先定义概述对话结构的提示模板。该模板可能包含将在运行时动态填充的变量的占位符。
  • 自定义提示:使用与用户查询或应用程序要求相关的特定内容或上下文填充提示模板。这种定制可确保语言模型接收清晰且相关的输入。
  • 将提示传递给模型:构建提示后,将其与任何其他输入数据一起传递给语言模型进行处理。该模型将根据提供的提示和上下文生成响应。


第 3 步:实施该ingest方法

    def ingest(self, pdf_file_path: str):
        # Load PDF documents
        docs = PyPDFLoader(file_path=pdf_file_path).load()
        # Split the documents into smaller chunks
        chunks = self.text_splitter.split_documents(docs)
        # Filter out complex metadata
        chunks = filter_complex_metadata(chunks)

        # Create a vector store from the chunks with FastEmbed embeddings
        vector_store = Chroma.from_documents(documents=chunks, embedding=FastEmbedEmbeddings())
        # Convert the vector store into a retriever with specified search parameters
        self.retriever = vector_store.as_retriever(
            search_type="similarity_score_threshold",
            search_kwargs={
                "k": 3,
                "score_threshold": 0.5,
            },
        )

        # Define the conversation chain using the retriever and prompt
        self.chain = ({"context": self.retriever, "question": RunnablePassthrough()}
                      | self.prompt
                      | self.model
                      | StrOutputParser())

解释:

  • 该ingest方法负责处理上传的PDF文档。
  • 它加载 PDF 文档,将其分割成更小的块,并过滤掉复杂的元数据。
  • 使用 LangChain 的Chroma模块,它可以通过 FastEmbed 嵌入从块创建向量存储。
  • 然后,向量存储被转换为具有指定搜索参数的检索器。
  • 最后,使用检索器、提示、模型和输出解析器定义对话链。



  1. 加载PDF文档:LangChain使用PyPDFLoader模块加载用户上传的PDF文档。该模块允许LangChain以编程方式提取PDF文件的内容和结构。
  2. 分割成更小的块:加载PDF文档后,LangChain将其分割成更小、更易于管理的单元,称为块。块是根据预定义标准(例如字符数或段落边界)划分的文档部分。拆分过程有助于将大型 PDF 文档分解为更小的片段,使其更易于处理和分析。
  3. 过滤复杂的元数据:一旦文档被分割成块,LangChain就会过滤掉可能与后续处理步骤不相关或不兼容的复杂元数据。复杂元数据是指 PDF 文档中可能干扰下游分析的非文本或结构元素。
  4. LangChain Chroma模块:Chroma是LangChain的一个组件,有助于矢量商店的创建和管理。它允许 LangChain 将文本数据组织成适合基于向量的操作的结构化格式。Chroma 提供有效存储、索引和查询文本数据的功能。
  5. 向量空间:在LangChain的上下文中,向量空间是一个数学空间,其中每个文档或文本段落都表示为一个向量。向量捕获单词或文档之间的语义和关系,从而允许相似性比较和检索操作。
  6. FastEmbed Embeddings:FastEmbed是LangChain使用的一种嵌入模型,用于快速有效地将文本数据转换为数字表示(嵌入)。嵌入是连续向量空间中单词或文档的密集向量表示。FastEmbed 嵌入设计用于快速计算和低内存使用,使其适合大规模文本处理任务。
  7. 将向量存储转换为检索器:对文档块进行向量化后,我们通过指定搜索参数将向量存储转换为检索器。此过程涉及设置从矢量化文档块中检索相关信息的标准,例如相似性分数阈值和要返回的最大结果数。搜索参数可以包括诸如要检索的顶部结果的数量(k)、相似性分数阈值或其他过滤标准之类的标准。
  8. 检索过程:当用户向系统提交查询或提示时,检索器利用配置的搜索参数来查找相关文档或段落。
  9. 结果呈现:最后,检索到的文档或段落通常以文本片段或摘要的形式呈现给用户。这些结果可以直接作为生成对用户查询或提示的响应的基础,也可以作为下游组件进一步处理的上下文。
  10. 提示词:提示充当结构化模板,指导用户和模型之间的交互。它提供了如何使用检索到的上下文来回答用户问题的说明。
  11. 模型:模型表示用于生成对用户查询的响应的语言模型 (LLM)。在这种情况下,模型的任务是理解用户的问题,利用检索到的上下文,并生成相关且连贯的响应。使用的具体法学硕士可能会根据应用程序要求和可用资源而有所不同。
  12. 输出解析器:输出解析器负责将模型生成的原始输出处理为更结构化和可读的格式。它可以执行文本规范化、摘要或后处理等任务,以确保最终响应满足所需的质量和格式标准。
  13. 对话链:对话链将检索器、提示、模型和输出解析器组合成一个顺序管道,用于处理用户交互。当用户提交查询时,检索器从矢量存储中获取相关上下文。检索到的上下文以及用户的问题将根据提示进行格式化,并传递到模型以生成响应。该模型根据提供的输入生成响应,然后由输出解析器处理该响应以生成呈现给用户的最终输出。


第 4 步:实施该ask方法

    def ask(self, query: str):
        # Check if the conversation chain has been initialized
        if not self.chain:
            return "Please, add a PDF document first."

        # Invoke the conversation chain with the user query
        return self.chain.invoke(query)

解释:

  • 该ask方法允许用户根据上传的PDF文档提出问题。
  • 它检查会话链是否已初始化。
  • 如果该链存在,它将通过用户查询调用对话链并返回响应。


第 5 步:实施该clear方法

def clear(self):
        # Clear the vector store, retriever, and conversation chain
        self.vector_store = None
        self.retriever = None
        self.chain = None

解释:

  • 该clear方法用于重置应用程序状态。
  • 当上传新的 PDF 文档时,它会清除矢量存储、检索器和对话链。


第 6 步:将 Streamlit 与ChatPDF类集成:

# Initialize ChatPDF instance
chat_pdf = ChatPDF()

# Streamlit app layout
st.title("PDF Question-Answering System")

# PDF file upload
uploaded_file = st.file_uploader("Upload PDF", type="pdf")

# Function to handle PDF ingestion
def ingest_pdf(file):
    if file is not None:
        chat_pdf.ingest(file)
        st.success("PDF successfully ingested!")

# Function to handle user queries
def answer_query(query):
    if not chat_pdf.chain:
        st.warning("Please, add a PDF document first.")
        return
    answer = chat_pdf.ask(query)
    st.info(f"Answer: {answer}")

# Streamlit components for PDF ingestion and querying
if uploaded_file is not None:
    ingest_pdf(uploaded_file)

user_query = st.text_input("Enter your question:")
if st.button("Ask"):
    answer_query(user_query)

if st.button("Clear PDF Data"):
    clear_pdf_data()



  • streamlit导入该库以创建 Web 应用程序。
  • 该st.title函数设置 Web 应用程序的标题。
  • 该类ChatPDF封装了以下功能:获取 PDF 文档、处理它们以创建对话管道以及根据提供的文档回答用户查询。
  • Streamlit 组件(例如st.file_uploader、st.text_input、 和 )st.button用于创建用于上传 PDF、提出问题和清除数据的用户界面元素。
  • ingest_pdf、answer_query、 和 等函数clear_pdf_data被定义来处理用户交互并执行相应的操作。
  • 应用程序布局和功能在 Streamlit 脚本中定义,允许用户通过 Web 界面与 PDF 问答系统进行交互。