AI知识库

53AI知识库

学习大模型的前沿技术与行业应用场景


OLLama通过pdf文档构建知识问答应用:使用OLLama和LangChain构建PDF Chat简易问答聊天助手
发布日期:2024-04-12 21:37:21 浏览次数: 4246 来源:峰哥Python笔记


昨天写了『OLLama 如何通过已有文档建立知识库进行问答?OLLama + LangChain给你助力』 ,关于图形界面下的OLLama知识库问答有个streamlit由于代码没跑起来,就没有写出来,今天把这部分内容补出来。最后还有一段关于离线安装ollama模型的方法,如果有需要可以到最后去参考一下。

构建一个类似于chatPDF,但更简单的应用,用户可以上传PDF文件,基于已有的大模型,再加上提交的文档,进行简单提问。主要使用LangChain、OLLama和Streamlit,代码也并不多,提交文档,还是使用embedding,进行分割,向量转化,存储在chroma数据库。

1.运行OLLama

在ollama官网安装相应版本的ollama,并在后台开启服务。在浏览器输入http://localhost:11434/,提示Ollama is running,表示后台服务开启。也可以手动开启ollama服务。在命令行输入ollama serve,这个命令窗口不要关闭,后台一些信息都会在这里显示。

个人感觉,通过这个窗口的服务,比右下角图标服务,效率更高一点,支持gpu好一点,仅个人感觉。右下角图标服务时,cpu跑满,有卡死状态,命令行运行服务时,cpu占用降下来,gpu占用提高。

拉取Mistral-7B,命令行输入:

ollama pull mistral

是否拉取成功,可以通过:

ollama list

进行查看:

2.构建RAG

检索增强生成 (RAG) 是一种使用来自私有或专有数据源的信息来辅助文本生成的技术。它将检索模型(设计用于搜索大型数据集或知识库)和生成模型(例如大型语言模型 (LLM),此类模型会使用检索到的信息生成可供阅读的文本回复)结合在一起。

流程用这张图来总结:

首先,它将文档拆分为更小的块以适应 LLM 的标记限制;其次,它使用Embeddings 对这些块进行矢量化并存储到 Chroma 中。该方法处理用户查询。用户可以提出一个问题,然后 Retrieval使用向量相似性搜索技术检索相关上下文(文档块)。通过用户的问题和检索到的上下文,我们可以编写一个提示,并请求 LLM 服务器进行预测。

加载pdf文件

from langchain_community.document_loaders import PyPDFLoader

loader = PyPDFLoader("./你不知道的JavaScript(上卷).pdf")
pages = loader.load_and_split()
print(pages)

(https://stackoverflow.com/questions/76431655/langchain-pypdfloader)

加载pdf文件后,后面还是进行分割,转矢量,存储到数据库,步骤和代码和昨天一样,只是数据源进行简单修改。可以对已有的pdf进行加载。

通过streamlit加载文件

Streamlit 是一个用于创建数据应用的开源 Python 库。它允许开发人员使用 Python 语言来快速构建交互式的数据科学和机器学习应用,而无需编写大量的前端代码。通过 Streamlit,开发人员可以轻松地将数据处理、可视化、机器学习模型部署等功能集成到一个用户友好的 Web 应用中。

Streamlit 提供了简单易用的 API,可以通过简单的 Python 脚本来创建交互式组件,如滑动条、下拉菜单、复选框等,以及数据可视化组件,如图表、地图等。开发人员只需要编写一些 Python 代码,Streamlit 就会自动将其转换成一个 Web 应用,用户可以通过浏览器访问。

使用用stramlit进行上传pdf文件:

import streamlit as st
from PyPDF2 import PdfReader

uploaded_file = st.file_uploader("Upload your PDF")
if uploaded_file is not None:
   reader = PdfReader(uploaded_file)

model_local = ChatOllama(model="mistral")
uploaded_file = st.file_uploader("Upload your PDF")
docs = []
if uploaded_file is not None:
    
    reader = PdfReader(uploaded_file)
    i = 1
    for page in reader.pages:
        docs.append(Document(page_content=page.extract_text(), metadata={'page':i}))
        i += 1

加上streamlit的侧边栏

with st.sidebar:
    st.title('?? PDF Chat App')
    st.markdown('## About')
    st.markdown('This app is an LLM-powered chatbot built using:')
    st.markdown('- [Streamlit](https://streamlit.io/)')
    st.markdown('- [LangChain](https://python.langchain.com/)')
    st.markdown('- [OpenAI](https://platform.openai.com/docs/models) LLM model')
    #add_vertical_space(5)
    st.write('Made with 峰哥Python笔记')

streamlit代码运行时,需要在命令行输入streamlit run app.py,类似这种格式运行代码。

3.最后交互界面

4.代码

class ChatPDF:
    vector_store = None
    retriever = None
    chain = None

    def __init__(self):
        self.model = ChatOllama(model="mistral")
        self.text_splitter = RecursiveCharacterTextSplitter(chunk_size=1024, chunk_overlap=100)
        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]
            "
""
        )

    def ingest(self, pdf_file_path: str):
        docs = PyPDFLoader(file_path=pdf_file_path).load()
        chunks = self.text_splitter.split_documents(docs)
        chunks = filter_complex_metadata(chunks)

        vector_store = Chroma.from_documents(documents=chunks, embedding=FastEmbedEmbeddings())
        self.retriever = vector_store.as_retriever(
            search_type="similarity_score_threshold",
            search_kwargs={
                "k": 3,
                "score_threshold": 0.5,
            },
        )

        self.chain = ({"context": self.retriever, "question": RunnablePassthrough()}
                      | self.prompt
                      | self.model
                      | StrOutputParser())

    def ask(self, query: str):
        if not self.chain:
            return "Please, add a PDF document first."

        return self.chain.invoke(query)

st.set_page_config(page_title="ChatPDF")
with st.sidebar:
    st.title('?? PDF Chat App')
    st.markdown('## About')
    st.markdown('This app is an LLM-powered chatbot built using:')
    st.markdown('- [Streamlit](https://streamlit.io/)')
    st.markdown('- [LangChain](https://python.langchain.com/)')
    st.markdown('- [OpenAI](https://platform.openai.com/docs/models) LLM model')
    #add_vertical_space(5)
    st.write('Made with 峰哥Python笔记')

def display_messages():
    st.subheader("Chat")
    for i, (msg, is_user) in enumerate(st.session_state["messages"]):
        message(msg, is_user=is_user, key=str(i))
    st.session_state["thinking_spinner"] = st.empty()


def process_input():
    if st.session_state["user_input"] and len(st.session_state["user_input"].strip()) > 0:
        user_text = st.session_state["user_input"].strip()
        with st.session_state["thinking_spinner"], st.spinner(f"Thinking"):
            agent_text = st.session_state["assistant"].ask(user_text)

        st.session_state["messages"].append((user_text, True))
        st.session_state["messages"].append((agent_text, False))


def read_and_save_file():
    st.session_state["assistant"].clear()
    st.session_state["messages"] = []
    st.session_state["user_input"] = ""

    for file in st.session_state["file_uploader"]:
        with tempfile.NamedTemporaryFile(delete=False) as tf:
            tf.write(file.getbuffer())
            file_path = tf.name

        with st.session_state["ingestion_spinner"], st.spinner(f"Ingesting {file.name}"):
            st.session_state["assistant"].ingest(file_path)
        os.remove(file_path)


def main():
    if len(st.session_state) == 0:
        st.session_state["messages"] = []
        st.session_state["assistant"] = ChatPDF()

    st.header("ChatPDF")

    st.subheader("Upload a document")
    st.file_uploader(
        "Upload document",
        type=["pdf"],
        key="file_uploader",
        on_change=read_and_save_file,
        label_visibility="collapsed",
        accept_multiple_files=True,
    )

    st.session_state["ingestion_spinner"] = st.empty()

    display_messages()
    st.text_input("Message", key="user_input", on_change=process_input)

if __name__ == "__main__":
    main()

这段代码使用了 Streamlit 库来构建用户界面,并通过调用 ChatPDF 类的方法来处理上传的 PDF 文件和生成聊天消息。然后,用户可以在文本输入框中输入消息,程序将根据用户输入生成响应并显示在页面上。

  1. 使用 st.set_page_config 设置页面配置,包括页面标题为 "ChatPDF"。
  2. 使用 st.sidebar 创建侧边栏,并在侧边栏中显示应用程序的标题和关于信息。
  3. 定义了 display_messages 函数用于显示聊天消息,以及 process_input 函数用于处理用户输入和生成回复消息。
  4. 定义了 read_and_save_file 函数用于读取和保存上传的 PDF 文件,并调用 ChatPDF 类中的 ingest 方法来处理文档。
  5. 定义了 main 函数作为应用程序的主页面,其中包含了上传 PDF 文档的部分、显示聊天消息的部分以及用户输入消息的部分。

在 Streamlit 中,st.session_state 是一个字典,用于存储应用程序的会话状态数据。会话状态数据是指与特定用户会话相关的数据,它在不同的请求之间保持不变,并且在整个用户会话期间持续存在。使用 st.session_state 可以在 Streamlit 应用程序的不同部分之间共享数据,并且可以跨多个用户请求保持数据的状态。这对于跟踪用户输入、存储应用程序的配置选项或者保存用户的会话状态非常有用。st.session_state["message"]来管理用户输入的消息和返回的消息,st.session_state["assistant"]一个处理ollama的对象,包含了上传pdf,调用模型,分割,转成向量,存储到数据等操作。message用来显示对话过程,把st.session_state["message"]显示出来。

Streamlit 中的st.spinner(f"Thinking")是一个小部件,用于在应用程序中显示加载状态或者进行长时间运行的任务时的进度指示。它可以让用户知道应用程序正在处理某些任务,并且可以提高用户体验,让用户感觉应用程序在运行中。

5.代码获取

在后台回复『streamlit-chat』来获取相关的例子代码。

6.OLLama使用自己下载的模型

群里有人提问,如何使用huggingface下载的.gguf的模型,经过查询找到使用的方法:

1.手动下载

下载mistrallite.Q4_K_M.gguf这个文件到本地,可以是其他文件,需要ollama支持的模型。

2.编写Modelfile

里面正确路径写好这个文件的位置

FROM ./mistrallite.Q4_K_M.gguf

3.创建一个新模型

通过Modelfile创建新模型:

ollama create mistrallite -f Modelfile

-f 后面跟这个Modelfile文件,上面命令是通过这个Modelfile,创建一个名为mistrallite的模型,可以通过ollama run mistrallite来运行这个模型,然后进行提问。

如果网络不通畅,可以试试此方法手动安装模型。



53AI,企业落地应用大模型首选服务商

产品:大模型应用平台+智能体定制开发+落地咨询服务

承诺:先做场景POC验证,看到效果再签署服务协议。零风险落地应用大模型,已交付160+中大型企业

联系我们

售前咨询
186 6662 7370
预约演示
185 8882 0121

微信扫码

与创始人交个朋友

回到顶部

 
扫码咨询