Question Answering RAG System

Retrieval-Augmented Generation (RAG) pipelines are transforming the way we handle complex question-answering tasks. By combining the precision of information retrieval with the creativity of natural language generation, these systems can deliver contextually accurate answers from large collections of documents.

In this blog, we’ll show you how to build a robust QA system using RAG, which processes PDFs and lets users interact through a web interface built with Gradio.

Why RAG Pipelines Are Revolutionary

Traditional QA systems often struggle with:

  • Retrieving accurate context from large datasets.
  • Generating nuanced answers in natural language.

RAG pipelines address these challenges by:

  1. Retrieving relevant documents from a dataset.
  2. Augmenting these with powerful language models to generate detailed and context-aware answers.

With this approach, our QA system can handle complex queries and provide insightful answers, even from large document collections.

Project Overview

In this project, we leverage the following technologies:

Tech Stack

Workflow Steps:

  1. Parse uploaded PDF files to extract text.
  2. Generate document embeddings using Sentence Transformers.
  3. Index the embeddings with FAISS for fast retrieval.
  4. Use a lightweight text-generation model (Flan-T5) to generate answers.
  5. Combine everything into an interactive QA system with a Gradio-based UI.

Step-by-Step QA System with RAG Pipeline

1. Extracting Text from PDFs

We allow users to upload PDFs and extract their content using PyPDF:

from pypdf import PdfReader
def extract_text_from_pdfs(uploaded_files):
pdf_texts = []
for filename, file_content in uploaded_files.items():
reader = PdfReader(file_content)
for page in reader.pages:
pdf_texts.append(page.extract_text())
return pdf_texts

This step ensures that the raw text from all uploaded PDFs is available for further processing.

2. Embedding the Documents

To enable efficient retrieval, we convert the extracted text into numerical embeddings using Sentence Transformers:


from langchain.embeddings import HuggingFaceEmbeddings
from langchain.docstore.document import Document

embedding_model = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")


def create_documents(pdf_texts):
return [Document(page_content=text) for text in pdf_texts]

The “all-MiniLM-L6-v2” model is lightweight and optimized for sentence-level embeddings, ensuring both performance and accuracy.

3. Indexing with FAISS

FAISS allows us to store and retrieve embeddings efficiently:

from langchain.vectorstores import FAISS

def create_vector_store(documents, embedding_model):
    return FAISS.from_documents(documents, embedding_model)

This creates a searchable vector store, making it easy to find relevant documents based on user queries.


4. Text Generation with Flan-T5

For generating natural language answers, we use Hugging Face’s Flan-T5 model:

from transformers import pipeline

generator = pipeline("text2text-generation", model="google/flan-t5-small", device=0)

Flan-T5-small balances quality and speed, making it ideal for our QA system on Colab’s free GPU tier.

5. Building the RAG Pipeline

LangChain simplifies integrating retrieval and generation components. We define a prompt template and create a QA chain:

from langchain.prompts import PromptTemplate
from langchain.chains import RetrievalQA
from langchain.llms import HuggingFacePipeline

prompt_template = """
Given the following information, answer the question.

Context:
{context}

Question: {question}
Answer:
"""

prompt = PromptTemplate(template=prompt_template, input_variables=["context", "question"])

llm = HuggingFacePipeline(pipeline=generator)
retriever = vector_store.as_retriever()

qa_chain = RetrievalQA.from_chain_type(
    llm=llm,
    retriever=retriever,
    return_source_documents=False,
    chain_type_kwargs={"prompt": prompt},
)

The pipeline retrieves the most relevant documents and uses the Flan-T5 model to generate detailed answers.

6. Building a Gradio Interface

We replace the terminal-based query system with a user-friendly Gradio web interface:

import gradio as gr

def answer_query(query):
    if not query.strip():
        return "Please enter a valid query."

    try:
        response = qa_chain.run(query)
        return response if response.strip() else "No answer found for your query."
    except Exception as e:
        return f"Error processing the query: {e}."

interface = gr.Interface(
    fn=answer_query,
    inputs="text",
    outputs="text",
    title="Document QA System",
    description="Upload documents, then ask questions to get answers based on their content."
)

interface.launch()

This interface allows users to upload their documents and ask questions directly through a simple web app.

 

Conclusion

In this tutorial, you’ve seen how straightforward it is to build a QA system using the RAG approach. By following the steps outlined above, you can create a robust and effective QA system for your clients. At AITUDE, we specialize in developing advanced QA systems and would love to hear from you if you’re looking to hire AI experts for your project.