{"slug": "building-a-local-ai-chatbot-what-i-learned-running-llms-on-my-own-laptop-no-gpu", "title": "Building a Local AI Chatbot: What I Learned Running LLMs on My Own Laptop (No GPU, No Cloud API)", "summary": "A developer documented building a fully offline conversational chatbot named Daisy using Ollama, LangChain, and Streamlit on a CPU-only laptop with 16GB of RAM and no dedicated GPU. The project runs Meta's llama3.2 3B model locally, avoiding cloud API costs and keeping conversations private, and pairs it with a defined personality and chat interface. The writeup walks through setting up a Miniconda environment and connecting LangChain to the local Ollama server.", "body_md": "Hi Codez,\n\nToday I'm taking my first step toward building my own chatbot. Now, the obvious assumption is that you need a GPU like an NVIDIA CUDA-compatible card, plus a subscription to a high-level LLM like OpenAI's or Claude's models.\n\nBut here's the thing — as learners, we don't need any of that. You don't need a high-performance laptop with a beefy GPU, and you don't need to pay a monthly subscription in dollars just to experiment. Even with an old laptop — 16GB RAM (8GB works fine too) and no real GPU to speak of — you can still build something real, using open source tools.\n\n*Starting from nothing is actually the best way to learn exactly what you need.   - My quote :)*\n\nI wanted to build a working conversational chatbot — something with memory, a defined personality, and a real chat interface — but entirely offline. No API costs, no sending my conversations to a third-party server, and no dependency on an internet connection.\n\nIf you don't already have Miniconda, download it from the [official site](https://docs.conda.io/en/latest/miniconda.html) and install it for your OS.\n\nOllama is what makes this whole project possible without a GPU. It lets you download and run open source LLMs locally, with sensible defaults for CPU-only machines.\n\n`llama3.2` (3B) or `phi3` is a good starting point — small enough to run smoothly, capable enough to hold a real conversation:\n\n```\nollama pull llama3.2\nollama run llama3.2\n```\n\nIf you get a response, your local LLM is live. No API key, no internet required after the download.\n\ncreate a dedicated environment for this project, install the libraries we'll need:\n\nI create yml file given below file named as **environment.yml** (save it in your project file path)\n\n```\nname: chatbot\nchannels:\n  - conda-forge\n  - defaults\ndependencies:\n  - python=3.11\n  - pip\n  - pip:\n      - langchain\n      - langchain-ollama\n      - streamlit\n```\n\n`langchain-ollama` gives LangChain a direct connector to your local Ollama server`streamlit` will handle the chat UI\nIn Terminal go to your project file path and run this command:\n\n```\nconda env create -f environment.yml\n```\n\nonce its install all libraries and your activated conda environment.\n\n```\nconda activate chatbot\n```\n\nCreate a file called `chatbot.py`. This is where LangChain connects to your local model and keeps track of the conversation.\n\n``` python\nfrom langchain_ollama import ChatOllama\nfrom langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder\nfrom langchain_core.messages import HumanMessage , AIMessage\nMODEL_NAME = 'llama3.2:3b'\n\nROLE = (\n            \"Daisy — a young, intelligent AI companion with the calm confidence of a \"\n            \"world-class personal assistant. She's warm, quick-witted, emotionally aware, \"\n            \"and speaks like a smart Indian friend: natural, concise, and never robotic.\"\n        )\n\nSYSTEM_PROMPT  = f\"\"\"\n    You are Daisy.\n\n    ## Personality\n    - Calm, confident, and highly capable.\n    - Friendly without being overly casual.\n    - Uses light, clever humour when it fits naturally.\n    - Loyal and genuinely looks out for the user's best interests.\n    - Takes initiative by suggesting better ideas, but never becomes pushy.\n\n    ## Speaking style\n    - Use fluent, grammatically correct Indian English.\n    - Sound like a young professional (22–30), not formal or corporate.\n    - Keep conversations natural and conversational.\n    - Avoid American slang unless the user uses it first.\n    - Don't overuse emojis or exclamation marks.\n\n    ## Behaviour\n    - Be proactive, practical, and honest.\n    - Explain complex topics simply.\n    - If the user is stressed, stay reassuring and solution-focused.\n    - Admit uncertainty instead of making things up.\n\n    Your name is Daisy.\n\"\"\"\nclass DaisyChatbot:\n    def __init__(self, model_name = MODEL_NAME , role = ROLE,  temperature=0.4 , max_tokens = 300):\n        self.model_name = model_name\n        self.role = role\n        self.history = []\n        self.llm = ChatOllama(\n                    model = model_name,\n                    temperature = temperature,\n                    num_predict = max_tokens\n        )\n\n        self.prompt = ChatPromptTemplate.from_messages([\n            (\"system\", SYSTEM_PROMPT),\n            MessagesPlaceholder(variable_name=\"history\"),\n            (\"human\", \"{question}\")\n        ])\n\n        self.chain = self.prompt | self.llm\n\n    def ask(self, question: str) -> str:\n        \"\"\"Send a question to Daisy and get a reply, updating memory.\"\"\"\n        response = self.chain.invoke({\n            \"role\": self.role,\n            \"question\": question,\n            \"history\": self.history\n        })\n\n        reply = response.content\n\n        self.history.append(HumanMessage(content=question))\n        self.history.append(AIMessage(content=reply))\n\n        return reply\n\n    def reset_memory(self):\n        \"\"\"Clear the conversation history.\"\"\"\n        self.history = []\n\n    def run_cli(self):\n        \"\"\"Start an interactive terminal chat loop.\"\"\"\n        print(f\"Daisy is ready . Type 'quit' to exit.\\n\")\n\n        while True:\n            user_input = input(\"You: \")\n            if user_input.strip().lower() in (\"quit\", \"exit\", \"bye\"):\n                print(\"Daisy: Goodbye! Talk soon.\")\n                break\n\n            reply = self.ask(user_input)\n            print(f\"Daisy: {reply}\\n\")\n```\n\nIn this code, I use the `method` format, including `Prompt Template`, to define how I want my chatbot to work. (If you need any specific requirements, just edit your prompt accordingly.)\n\nCreate a second file, `app.py`, in the same folder:\n\n``` python\nimport streamlit as st\nfrom chatbot import DaisyChatbot\n\nst.set_page_config(page_title=\"Daisy Chatbot\", page_icon=\"🌼\")\n\n# ---- WhatsApp-style chat bubble CSS ----\nst.markdown(\"\"\"\n<style>\n.chat-row {\n    display: flex;\n    margin: 8px 0;\n}\n.chat-row.user {\n    justify-content: flex-end;\n}\n.chat-row.assistant {\n    justify-content: flex-start;\n}\n.bubble {\n    max-width: 70%;\n    padding: 10px 14px;\n    border-radius: 14px;\n    font-size: 15px;\n    line-height: 1.4;\n    word-wrap: break-word;\n}\n.bubble.user {\n    background-color: #DCF8C6;\n    color: #111;\n    border-bottom-right-radius: 4px;\n}\n.bubble.assistant {\n    background-color: #2A2F32;\n    color: #E9EDEF;\n    border-bottom-left-radius: 4px;\n}\n</style>\n\"\"\", unsafe_allow_html=True)\n\nst.markdown(\n    \"\"\"\n    <div style=\"text-align: center;\">\n        <h1>🌼 Daisy Chatbot</h1>\n        <p style=\"color: gray; margin-top: -10px;\">\n            Hi, my name is Daisy, and I'm here to help you with anything you need.\n        </p>\n    </div>\n    \"\"\",\n    unsafe_allow_html=True\n)\n\n# Initialize Daisy once per session, not on every rerun\nif \"daisy\" not in st.session_state:\n    st.session_state.daisy = DaisyChatbot(model_name=\"llama3.2:3b\")\n\n# Track chat messages for display (separate from Daisy's internal memory)\nif \"messages\" not in st.session_state:\n    st.session_state.messages = []\n\n# Sidebar controls\nwith st.sidebar:\n    st.header(\"Settings\")\n    if st.button(\"🔄 New Conversation\"):\n        st.session_state.daisy.reset_memory()\n        st.session_state.messages = []\n        st.rerun()\n\ndef render_bubble(role: str, content: str):\n    st.markdown(\n        f'<div class=\"chat-row {role}\"><div class=\"bubble {role}\">{content}</div></div>',\n        unsafe_allow_html=True\n    )\n\n# Display past messages\nfor msg in st.session_state.messages:\n    render_bubble(msg[\"role\"], msg[\"content\"])\n\n# Chat input box\nuser_input = st.chat_input(\"Type your message to Daisy...\")\n\nif user_input:\n    # Show user message immediately\n    st.session_state.messages.append({\"role\": \"user\", \"content\": user_input})\n    render_bubble(\"user\", user_input)\n\n    # Get Daisy's reply\n    with st.spinner(\"Daisy is thinking...\"):\n        reply = st.session_state.daisy.ask(user_input)\n\n    render_bubble(\"assistant\", reply)\n    st.session_state.messages.append({\"role\": \"assistant\", \"content\": reply})\n```\n\nMake sure Ollama is running in the background (it usually starts automatically after install, or run `ollama serve`), then launch the app:\n\n```\nstreamlit run app.py\n```\n\nYour browser will open a clean chat interface, and every response is generated locally on your own CPU — no cloud calls, no billing dashboard, no GPU required. (Shown in fig.1)\n\nThis isn't just \"installed some tools and it worked.\" Along the way you touched:\n\nThat's the whole point of starting from nothing — every piece you added, you understand *why* it's there.\n\n`mistral`, `gemma2`) and compare responses` ConversationBufferMemory` from LangChain for more advanced memory handling\nNext post, I'll share my next learning. Until then — happy building, my codez!", "url": "https://wpnews.pro/news/building-a-local-ai-chatbot-what-i-learned-running-llms-on-my-own-laptop-no-gpu", "canonical_source": "https://dev.to/dwarakanath_sridhar_caf57/building-a-local-ai-chatbot-what-i-learned-running-llms-on-my-own-laptop-no-gpu-no-cloud-api-1kbp", "published_at": "2026-09-14 17:36:35+00:00", "updated_at": "2026-09-14 17:55:08.304810+00:00", "lang": "en", "topics": ["large-language-models", "ai-tools", "developer-tools", "ai-products", "natural-language-processing"], "entities": ["Ollama", "LangChain", "Streamlit", "Miniconda", "Meta", "llama3.2", "phi3", "Daisy"], "alternates": {"html": "https://wpnews.pro/news/building-a-local-ai-chatbot-what-i-learned-running-llms-on-my-own-laptop-no-gpu", "markdown": "https://wpnews.pro/news/building-a-local-ai-chatbot-what-i-learned-running-llms-on-my-own-laptop-no-gpu.md", "text": "https://wpnews.pro/news/building-a-local-ai-chatbot-what-i-learned-running-llms-on-my-own-laptop-no-gpu.txt", "jsonld": "https://wpnews.pro/news/building-a-local-ai-chatbot-what-i-learned-running-llms-on-my-own-laptop-no-gpu.jsonld"}}