Building a RAG Chatbot Using LangChain and Streamlit: Engage with Your PDFs

#LLM

#Programming

Sabber Soltani

Sabber Soltani

June 23, 2024

Building a RAG Chatbot Using LangChain and Streamlit: Engage with Your PDFs

What is RAG?

RAG stands for Retrieval Augmented Generation. It's a technique that combines the power of large language models (like GPT-3) with a retrieval system that can fetch relevant information from a database. This allows the AI to generate responses based on both its general knowledge and specific information from your documents.

The Tools We'll Use

We'll be using several powerful tools to build our chatbot:

  1. Streamlit: A Python library that makes it easy to create web apps for machine learning and data science projects.
  2. PyPDF2: A library for reading and manipulating PDF files.
  3. LangChain: A framework for developing applications powered by language models.
  4. FAISS: A library developed by Facebook AI for efficient similarity search and clustering of dense vectors.

Setting Up Our Environment

First, let's install the necessary libraries. Open your terminal and run:

pip install streamlit langchain langchain_community langchain_chroma

Now, let's create a new Python file called rag_chatbot.py and start coding!

Step 1: Importing Libraries and Setting Up

Let's start by importing the libraries we'll need:

import streamlit as st
from PyPDF2 import PdfReader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_core.prompts import ChatPromptTemplate
from langchain_community.embeddings.spacy_embeddings import SpacyEmbeddings
from langchain_community.vectorstores import FAISS
from langchain.tools.retriever import create_retriever_tool
from dotenv import load_dotenv
from langchain_anthropic import ChatAnthropic
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain.agents import AgentExecutor, create_tool_calling_agent
import os

os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE"

Step 2: Reading and Processing PDF Files

Now, let's create functions to read PDF files and split them into manageable chunks:

def read_pdf(pdf_doc):
    text = ""
    for pdf in pdf_doc:
        pdf_reader = PdfReader(pdf)
        for page in pdf_reader.pages:
            text += page.extract_text()
    return text

def get_chunks(text):
    text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
    chunks = text_splitter.split_text(text)
    return chunks

 

Step 3: Creating a Searchable Text Database with Embeddings

Next, we'll convert our text chunks into vector representations that can be efficiently searched:

embeddings = SpacyEmbeddings(model_name="en_core_web_sm")

def vector_store(text_chunks):
    vector_store = FAISS.from_texts(text_chunks, embedding=embeddings)
    vector_store.save_local("faiss_db")

Step 4: Setting Up the Conversational AI

Now, let's set up the core of our chatbot using OpenAI's GPT model:

def get_conversational_chain(tools, ques):
    llm = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0, api_key="your_openai_api_key")
    
    prompt = ChatPromptTemplate.from_messages(
        [
            (
                "system",
                """You are a helpful 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.""",
            ),
            ("placeholder", "{chat_history}"),
            ("human", "{input}"),
            ("placeholder", "{agent_scratchpad}"),
        ]
    )
    
    tool = [tools]
    agent = create_tool_calling_agent(llm, tool, prompt)
    
    agent_executor = AgentExecutor(agent=agent, tools=tool, verbose=True)
    response = agent_executor.invoke({"input": ques})
    print(response)
    st.write("Reply: ", response['output'])

Step 5: Handling User Input

Let's create a function to process user questions and retrieve relevant information:

def user_input(user_question):
    new_db = FAISS.load_local("faiss_db", embeddings, allow_dangerous_deserialization=True)
    
    retriever = new_db.as_retriever()
    retrieval_chain = create_retriever_tool(retriever, "pdf_extractor", "This tool is to give answers to queries from the PDF")
    
    get_conversational_chain(retrieval_chain, user_question)

Step 6: Creating the Streamlit User Interface

Finally, let's create a user-friendly interface using Streamlit:

def main():
    st.set_page_config("Chat PDF")
    st.header("RAG based Chat with PDF")

    user_question = st.text_input("Ask a Question from the PDF Files")
    if user_question:
        user_input(user_question)

    with st.sidebar:
        st.title("Menu:")
        pdf_doc = st.file_uploader("Upload your PDF Files and Click on the Submit & Process Button", accept_multiple_files=True)
        if st.button("Submit & Process"):
            with st.spinner("Processing..."):
                raw_text = read_pdf(pdf_doc)
                text_chunks = get_chunks(raw_text)
                vector_store(text_chunks)
                st.success("Done")

if __name__ == "__main__":
    main()

How It Works

Now that we've built our chatbot, let's break down how it works:

  1. PDF Upload and Processing: Users can upload one or more PDF files through the Streamlit interface. The read_pdf() function extracts text from these PDFs.
  2. Text Chunking: The extracted text is split into smaller, manageable chunks using the get_chunks() function. This is important because large language models have a limit on how much text they can process at once.
  3. Vector Embedding: Each text chunk is converted into a vector representation using the SpacyEmbeddings model. These vectors capture the semantic meaning of the text.
  4. Vector Storage: The vector representations are stored in a FAISS database, allowing fast and efficient similarity searches.
  5. User Interaction: Users can ask questions through the Streamlit interface. When a question is submitted, the system searches the vector database for the most relevant text chunks.
  6. Answer Generation: The relevant text chunks and the user's question are sent to the OpenAI GPT model. The model generates a concise answer based on this information.
  7. Response Display: The generated answer is displayed to the user in the Streamlit interface.

Conclusion

We've built a powerful RAG chatbot that can understand and answer questions about PDF documents. This technology opens up exciting possibilities for information retrieval and knowledge management. As you continue to work with and improve this system, you'll gain valuable insights into the intersection of natural language processing, information retrieval, and artificial intelligence.

Remember, continuous experimentation and refinement are the key to building effective AI systems. Don't be afraid to try new approaches, gather user feedback, and iterate on your design. Happy coding, and may your chatbot bring new insights to you and your users!