|
| 1 | +# Step-by-Step Guide: Building a Local Chatbot with Streamlit, LangChain, Ollama, and MongoDB Atlas |
| 2 | + |
| 3 | +In this tutorial, we'll set up a local chatbot using **Streamlit**, **LangChain**, **Ollama**, and **MongoDB Atlas Search**. This bot will leverage MongoDB's powerful Atlas Search capabilities alongside local LLMs (Large Language Models) via Ollama, allowing you to enhance user queries with context from chat history. |
| 4 | + |
| 5 | +## Prerequisites |
| 6 | +Before starting, make sure you have the following installed: |
| 7 | + |
| 8 | +* Docker |
| 9 | +* Docker Compose |
| 10 | + |
| 11 | +> [!NOTE] |
| 12 | +> For this tutorial, Docker is essential for containerized, isolated development. |
| 13 | +
|
| 14 | +## Step 1: Setting Up the Project |
| 15 | + |
| 16 | +### Project Overview |
| 17 | +We’ll start by creating a directory for the project files and setting up our working structure. |
| 18 | + |
| 19 | +```sh |
| 20 | +mkdir localai |
| 21 | +cd localai |
| 22 | +``` |
| 23 | + |
| 24 | +### Project Structure |
| 25 | + |
| 26 | +Organize your project files as shown: |
| 27 | + |
| 28 | +``` |
| 29 | +localai/ |
| 30 | +├── app.py |
| 31 | +├── Dockerfile |
| 32 | +├── compose.yaml |
| 33 | +└── requirements.txt |
| 34 | +``` |
| 35 | + |
| 36 | +### Tool Overview |
| 37 | + |
| 38 | +Here’s a quick rundown of the tools we’re using in this project: |
| 39 | + |
| 40 | +* *[Streamlit](https://streamlit.io)*: A Python library for easily creating data-based web applications. We'll use it to create a local chatbot interface. |
| 41 | +* *[LangChain](https://langchain.com)*: A framework that simplifies working with LLMs and document processing. It will assist processing user queries and generate responses. |
| 42 | +* *[Ollama](https://ollama.com)*: A solution for deploying LLMs locally without external API dependency. It to host our models. |
| 43 | +* *[MongoDB Atlas Search](https://www.mongodb.com/products/platform/atlas-search)*: Adds a powerful, flexible vector search functionality to our app. It will store user queries and responses in MongoDB. |
| 44 | + |
| 45 | +### Setting Up `requirements.txt` |
| 46 | + |
| 47 | +In `requirements.txt`, specify the dependencies needed for this project: |
| 48 | + |
| 49 | +```requirements.txt |
| 50 | +streamlit |
| 51 | +ollama |
| 52 | +langchain |
| 53 | +langchain_ollama |
| 54 | +pymongo |
| 55 | +langchain_mongodb |
| 56 | +langchain_community |
| 57 | +markdownify |
| 58 | +``` |
| 59 | + |
| 60 | +### Docker Configuration |
| 61 | + |
| 62 | +#### `Dockerfile` |
| 63 | +Create a Dockerfile and add the following content. This file will define the container setup, ensuring our app and its dependencies run consistently across environments. |
| 64 | + |
| 65 | +```Dockerfile |
| 66 | +FROM python:3.12 |
| 67 | +WORKDIR /opt/app |
| 68 | +ADD requirements.txt . |
| 69 | +RUN pip install -r requirements.txt |
| 70 | +ADD app.py . |
| 71 | + |
| 72 | +CMD ["streamlit", "run", "app.py", "--server.port=8501", "--server.address=0.0.0.0"] |
| 73 | +``` |
| 74 | + |
| 75 | +#### `compose.yaml` |
| 76 | +Define your Docker Compose configuration in `compose.yaml`: |
| 77 | + |
| 78 | +```yaml |
| 79 | +services: |
| 80 | + app: |
| 81 | + build: |
| 82 | + context: . |
| 83 | + ports: |
| 84 | + - 8501:8501/tcp |
| 85 | + environment: |
| 86 | + MONGO_URI: mongodb://root:root@mongo:27017/admin?directConnection=true |
| 87 | + ollama: |
| 88 | + image: ollama/ollama |
| 89 | + mongo: |
| 90 | + image: mongodb/mongodb-atlas-local |
| 91 | + environment: |
| 92 | + - MONGODB_INITDB_ROOT_USERNAME=root |
| 93 | + - MONGODB_INITDB_ROOT_PASSWORD=root |
| 94 | + ports: |
| 95 | + - 27017:27017 |
| 96 | +``` |
| 97 | +
|
| 98 | +> [!TIP] |
| 99 | +> If you are on macOS, install Ollama locally and use this modified version of `compose.yaml`: |
| 100 | +> ```yaml |
| 101 | +> services: |
| 102 | +> app: |
| 103 | +> build: |
| 104 | +> context: . |
| 105 | +> ports: |
| 106 | +> - 8501:8501/tcp |
| 107 | +> environment: |
| 108 | +> OLLAMA_HOST: host.docker.internal:11434 |
| 109 | +> MONGO_URI: mongodb://root:root@mongo:27017/admin?directConnection=true |
| 110 | +> extra_hosts: |
| 111 | +> - "host.docker.internal:host-gateway" |
| 112 | +> mongo: |
| 113 | +> image: mongodb/mongodb-atlas-local |
| 114 | +> environment: |
| 115 | +> - MONGODB_INITDB_ROOT_USERNAME=root |
| 116 | +> - MONGODB_INITDB_ROOT_PASSWORD=root |
| 117 | +> ports: |
| 118 | +> - 27017:27017 |
| 119 | +> ``` |
| 120 | + |
| 121 | +## Step 2: Creating the Initial App |
| 122 | + |
| 123 | +Create app.py with a simple “Hello World” message to ensure your environment is set up correctly. |
| 124 | + |
| 125 | +```python |
| 126 | +import streamlit as st |
| 127 | +
|
| 128 | +st.write('Hello, World!') |
| 129 | +``` |
| 130 | + |
| 131 | +With all files in place, you can now build and run the Docker containers: |
| 132 | + |
| 133 | +```sh |
| 134 | +docker compose up --build |
| 135 | +``` |
| 136 | + |
| 137 | +Access the app at http://localhost:8501 in your browser. You should see the message Hello, World! |
| 138 | + |
| 139 | +> _Expected Output_: A browser page displaying “Hello, World!” confirms your setup is correct. |
| 140 | + |
| 141 | +## Step 3: Build the Chatbot |
| 142 | + |
| 143 | +### Step 3.1: Setting Up MongoDB and Ollama |
| 144 | + |
| 145 | +In `app.py`, connect to MongoDB and Ollama. This will pull LLM models from Ollama and set up MongoDB for data storage. |
| 146 | + |
| 147 | +```python |
| 148 | +import os |
| 149 | +import streamlit as st |
| 150 | +from pymongo import MongoClient |
| 151 | +import ollama |
| 152 | +
|
| 153 | +# Model and embedding configurations |
| 154 | +MODEL = "llama3.2" |
| 155 | +EMBEDDING_MODEL = "nomic-embed-text" |
| 156 | +MONGO_URI = os.getenv("MONGO_URI", "mongodb://localhost:27017") |
| 157 | +
|
| 158 | +# Pull models from Ollama |
| 159 | +ollama.pull(MODEL) |
| 160 | +ollama.pull(EMBEDDING_MODEL) |
| 161 | +
|
| 162 | +# Initialize MongoDB client |
| 163 | +try: |
| 164 | + mongo_client = MongoClient(MONGO_URI) |
| 165 | + collection = mongo_client["bot"]["data"] |
| 166 | +except Exception as e: |
| 167 | + st.error(f"Failed to connect to MongoDB: {e}") |
| 168 | + st.stop() |
| 169 | +``` |
| 170 | + |
| 171 | +> [!NOTE] |
| 172 | +> After each step, you can test the app by re-running `docker compose up --build`. |
| 173 | + |
| 174 | +### Step 3.2: Loading Documents and Creating a Vector Search Index |
| 175 | + |
| 176 | +Now, load documents, process them with LangChain, and store them as vector embeddings in MongoDB. This setup allows MongoDB Atlas to perform fast vector-based searches. |
| 177 | + |
| 178 | +```python |
| 179 | +from langchain_ollama import OllamaEmbeddings |
| 180 | +from langchain_community.document_loaders import WebBaseLoader |
| 181 | +from langchain_community.document_transformers import MarkdownifyTransformer |
| 182 | +from langchain_text_splitters import RecursiveCharacterTextSplitter |
| 183 | +from langchain_mongodb import MongoDBAtlasVectorSearch |
| 184 | +
|
| 185 | +embedding = OllamaEmbeddings(model=EMBEDDING_MODEL) |
| 186 | +collection.drop() |
| 187 | +
|
| 188 | +loaders = [ |
| 189 | + WebBaseLoader("https://en.wikipedia.org/wiki/AT%26T"), |
| 190 | + WebBaseLoader("https://en.wikipedia.org/wiki/Bank_of_America") |
| 191 | +] |
| 192 | +docs = [] |
| 193 | +for loader in loaders: |
| 194 | + for doc in loader.load(): |
| 195 | + docs.append(doc) |
| 196 | +
|
| 197 | +md = MarkdownifyTransformer() |
| 198 | +text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200) |
| 199 | +converted_docs = md.transform_documents(docs) |
| 200 | +splits = text_splitter.split_documents(converted_docs) |
| 201 | +
|
| 202 | +vectorstore = MongoDBAtlasVectorSearch.from_documents(splits, embedding, collection=collection, index_name="default") |
| 203 | +vectorstore.create_vector_search_index(768) |
| 204 | +``` |
| 205 | + |
| 206 | +> _Expected Output_: After this step, MongoDB Atlas should contain indexed documents, enabling fast vector-based search capabilities. |
| 207 | + |
| 208 | +### Step 3.3: Setting Up the Chat Model |
| 209 | + |
| 210 | +Next, set up the chat model with a retrieval mechanism and define the chain of operations that will handle user queries. |
| 211 | + |
| 212 | +```python |
| 213 | +from langchain_ollama import ChatOllama |
| 214 | +from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder |
| 215 | +from langchain_core.output_parsers import StrOutputParser |
| 216 | +from langchain_core.runnables.history import RunnableWithMessageHistory |
| 217 | +from langchain_core.chat_history import BaseChatMessageHistory |
| 218 | +from langchain_mongodb import MongoDBChatMessageHistory |
| 219 | +
|
| 220 | +retriever = vectorstore.as_retriever() |
| 221 | +chat = ChatOllama(model=MODEL) |
| 222 | +
|
| 223 | +# System message for the chatbot |
| 224 | +SYSTEM_MESSAGE = """You're a helpful assistant. Answer all questions to the best of your ability. If you don't know the answer let the user know to find help on the internet. |
| 225 | +
|
| 226 | +Available context: |
| 227 | +{context} |
| 228 | +""" |
| 229 | +
|
| 230 | +prompt_template = ChatPromptTemplate.from_messages([ |
| 231 | + ("system", SYSTEM_MESSAGE), |
| 232 | + MessagesPlaceholder("history"), |
| 233 | + ("human", "{input}"), |
| 234 | +]) |
| 235 | +
|
| 236 | +chain = { |
| 237 | + "context": itemgetter("input") | retriever, |
| 238 | + "input": itemgetter("input"), |
| 239 | + "history": itemgetter("history") |
| 240 | +} | prompt_template | chat | StrOutputParser() |
| 241 | +
|
| 242 | +def get_session_history(session_id: str) -> BaseChatMessageHistory: |
| 243 | + return MongoDBChatMessageHistory(MONGO_URI, session_id, database_name="bot") |
| 244 | +
|
| 245 | +history_chain = RunnableWithMessageHistory(chain, get_session_history, input_messages_key="input", history_messages_key="history") |
| 246 | +``` |
| 247 | + |
| 248 | +### Step 3.4: Creating the Chat Interface |
| 249 | + |
| 250 | +Now, use Streamlit to create a chat interface for interacting with the chatbot. |
| 251 | + |
| 252 | +```python |
| 253 | +st.title("Chatbot") |
| 254 | +st.caption("A Streamlit chatbot") |
| 255 | +
|
| 256 | +history = get_session_history() |
| 257 | +for msg in history.messages: |
| 258 | + st.chat_message(msg.type).write(msg.content) |
| 259 | +
|
| 260 | +if prompt := st.chat_input(): |
| 261 | + st.chat_message("user").write(prompt) |
| 262 | + with st.chat_message("ai"): |
| 263 | + with st.spinner("Thinking..."): |
| 264 | + st.write_stream(history_chain.stream({"input": prompt})) |
| 265 | +``` |
| 266 | + |
| 267 | +At this point, you can start prompting with inputs like “Who started AT&T?” and see the chatbot respond! |
| 268 | + |
| 269 | +## Conclusion and Next Steps |
| 270 | + |
| 271 | +In this tutorial, we built a local chatbot setup using MongoDB Atlas Search and local LLMs via Ollama, integrated through Streamlit. This project forms a robust foundation for further development and deployment. |
| 272 | + |
| 273 | +Possible Extensions: |
| 274 | + |
| 275 | +* Add more sophisticated pre-processing for documents. |
| 276 | +* Experiment with different models in Ollama. |
| 277 | +* Deploy to a cloud environment like AWS or Azure for production scaling. |
| 278 | + |
| 279 | +Feel free to customize this setup to suit your needs. Happy coding! |
0 commit comments