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.
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.
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.