Run This Ai
EN DE

Build a RAG Chatbot with Streamlit and LangChain

Step-by-step tutorial showing how to build a document Q&A chatbot using Streamlit's chat elements and LangChain's RAG pipeline.

Streamlit Logo

Building a RAG Chatbot with Streamlit and LangChain

Streamlit's chat elements — st.chat_message() and st.chat_input() — make it incredibly easy to build conversational AI interfaces. In this tutorial, we will build a Retrieval-Augmented Generation (RAG) chatbot that answers questions from your documents, using Streamlit as the frontend and LangChain for the orchestration.

Why Streamlit for LLM Apps?

Streamlit handles all the complexity of maintaining chat state, rendering message history, and managing user input. With just a few lines of code, you get a fully functional chat UI that would take hours to build with traditional web frameworks. Combined with the @st.cache_resource decorator, you can load expensive LLM models and vector stores once and reuse them across sessions.

Streamlit Interactive App

Prerequisites

  • Python 3.10+ installed
  • An OpenAI API key (or any LLM provider supported by LangChain)
  • Basic familiarity with Python

Step 1: Install Dependencies

pip install streamlit langchain langchain-community langchain-openai chromadb pypdf

Step 2: Build the Chat Interface

Create a file called rag_chatbot.py:

import streamlit as st
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain_community.vectorstores import Chroma
from langchain_community.document_loaders import PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.chains import create_retrieval_chain
from langchain.chains.combine_documents import create_stuff_documents_chain
from langchain_core.prompts import ChatPromptTemplate
import tempfile, os

st.set_page_config(page_title="RAG Chatbot", page_icon="📚")
st.title("📚 Document Q&A Chatbot")

# Initialize session state
if "messages" not in st.session_state:
    st.session_state.messages = []
if "vectorstore" not in st.session_state:
    st.session_state.vectorstore = None

# Upload documents
with st.sidebar:
    st.header("Upload Documents")
    uploaded_files = st.file_uploader(
        "Upload PDF files", type="pdf", accept_multiple_files=True
    )
    
    if uploaded_files and st.button("Process Documents"):
        with st.spinner("Processing documents..."):
            documents = []
            for f in uploaded_files:
                with tempfile.NamedTemporaryFile(delete=False, suffix=".pdf") as tmp:
                    tmp.write(f.getvalue())
                    tmp_path = tmp.name
                loader = PyPDFLoader(tmp_path)
                documents.extend(loader.load())
                os.unlink(tmp_path)
            
            splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
            chunks = splitter.split_documents(documents)
            
            embeddings = OpenAIEmbeddings()
            st.session_state.vectorstore = Chroma.from_documents(chunks, embeddings)
            st.success(f"Processed {len(chunks)} chunks from {len(uploaded_files)} files!")

# Display chat history
for msg in st.session_state.messages:
    with st.chat_message(msg["role"]):
        st.markdown(msg["content"])

# Chat input
if prompt := st.chat_input("Ask a question about your documents"):
    st.session_state.messages.append({"role": "user", "content": prompt})
    with st.chat_message("user"):
        st.markdown(prompt)
    
    if st.session_state.vectorstore:
        with st.chat_message("assistant"):
            with st.spinner("Thinking..."):
                llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
                retriever = st.session_state.vectorstore.as_retriever()
                
                system_prompt = ChatPromptTemplate.from_messages([
                    ("system", "Answer based on the context. If unsure, say you don't know."),
                    ("human", "Context: {context}\n\nQuestion: {input}")
                ])
                
                chain = create_stuff_documents_chain(llm, system_prompt)
                rag_chain = create_retrieval_chain(retriever, chain)
                result = rag_chain.invoke({"input": prompt})
                
                st.markdown(result["answer"])
                st.session_state.messages.append({"role": "assistant", "content": result["answer"]})
    else:
        st.info("Please upload documents first to enable Q&A.")

Step 3: Run Your Chatbot

export OPENAI_API_KEY="sk-your-key-here"
streamlit run rag_chatbot.py

Open your browser at http://localhost:8501. Upload PDF documents, process them, and start asking questions. The chatbot uses RAG to retrieve relevant chunks from your documents and generates accurate answers using your chosen LLM.

Deploying to Production

For self-hosted production deployment, use Docker Compose:

version: "3.8"
services:
  streamlit-app:
    build: .
    ports:
      - "8501:8501"
    environment:
      - OPENAI_API_KEY=\${OPENAI_API_KEY}
    volumes:
      - ./data:/app/data
    restart: unless-stopped

Conclusion

Streamlit eliminates the gap between building a Python script and deploying a full web application. With its chat elements, caching system, and seamless LangChain integration, you can build production-ready RAG chatbots in under 100 lines of code. Whether for internal knowledge bases, customer support, or research assistants, Streamlit + LangChain is one of the fastest ways to bring LLM-powered applications to life.

#streamlit #rag #chatbot #langchain #tutorial #python