cd /news/large-language-models/building-a-local-ai-chatbot-what-i-l… · home topics large-language-models article
[ARTICLE · art-129426] src=dev.to ↗ pub= topic=large-language-models verified=true sentiment=↑ positive

Building a Local AI Chatbot: What I Learned Running LLMs on My Own Laptop (No GPU, No Cloud API)

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.

by read5 min views1 publishedSep 14, 2026

Hi Codez,

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

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

Starting from nothing is actually the best way to learn exactly what you need. - My quote :)

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

If you don't already have Miniconda, download it from the official site and install it for your OS.

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

llama3.2 (3B) or phi3 is a good starting point — small enough to run smoothly, capable enough to hold a real conversation:

ollama pull llama3.2
ollama run llama3.2

If you get a response, your local LLM is live. No API key, no internet required after the download.

create a dedicated environment for this project, install the libraries we'll need:

I create yml file given below file named as environment.yml (save it in your project file path)

name: chatbot
channels:
  - conda-forge
  - defaults
dependencies:
  - python=3.11
  - pip
  - pip:
      - langchain
      - langchain-ollama
      - streamlit

langchain-ollama gives LangChain a direct connector to your local Ollama serverstreamlit will handle the chat UI In Terminal go to your project file path and run this command:

conda env create -f environment.yml

once its install all libraries and your activated conda environment.

conda activate chatbot

Create a file called chatbot.py. This is where LangChain connects to your local model and keeps track of the conversation.

from langchain_ollama import ChatOllama
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.messages import HumanMessage , AIMessage
MODEL_NAME = 'llama3.2:3b'

ROLE = (
            "Daisy — a young, intelligent AI companion with the calm confidence of a "
            "world-class personal assistant. She's warm, quick-witted, emotionally aware, "
            "and speaks like a smart Indian friend: natural, concise, and never robotic."
        )

SYSTEM_PROMPT  = f"""
    You are Daisy.

    ## Personality
    - Calm, confident, and highly capable.
    - Friendly without being overly casual.
    - Uses light, clever humour when it fits naturally.
    - Loyal and genuinely looks out for the user's best interests.
    - Takes initiative by suggesting better ideas, but never becomes pushy.

    ## Speaking style
    - Use fluent, grammatically correct Indian English.
    - Sound like a young professional (22–30), not formal or corporate.
    - Keep conversations natural and conversational.
    - Avoid American slang unless the user uses it first.
    - Don't overuse emojis or exclamation marks.

    ## Behaviour
    - Be proactive, practical, and honest.
    - Explain complex topics simply.
    - If the user is stressed, stay reassuring and solution-focused.
    - Admit uncertainty instead of making things up.

    Your name is Daisy.
"""
class DaisyChatbot:
    def __init__(self, model_name = MODEL_NAME , role = ROLE,  temperature=0.4 , max_tokens = 300):
        self.model_name = model_name
        self.role = role
        self.history = []
        self.llm = ChatOllama(
                    model = model_name,
                    temperature = temperature,
                    num_predict = max_tokens
        )

        self.prompt = ChatPromptTemplate.from_messages([
            ("system", SYSTEM_PROMPT),
            MessagesPlaceholder(variable_name="history"),
            ("human", "{question}")
        ])

        self.chain = self.prompt | self.llm

    def ask(self, question: str) -> str:
        """Send a question to Daisy and get a reply, updating memory."""
        response = self.chain.invoke({
            "role": self.role,
            "question": question,
            "history": self.history
        })

        reply = response.content

        self.history.append(HumanMessage(content=question))
        self.history.append(AIMessage(content=reply))

        return reply

    def reset_memory(self):
        """Clear the conversation history."""
        self.history = []

    def run_cli(self):
        """Start an interactive terminal chat loop."""
        print(f"Daisy is ready . Type 'quit' to exit.\n")

        while True:
            user_input = input("You: ")
            if user_input.strip().lower() in ("quit", "exit", "bye"):
                print("Daisy: Goodbye! Talk soon.")
                break

            reply = self.ask(user_input)
            print(f"Daisy: {reply}\n")

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

Create a second file, app.py, in the same folder:

import streamlit as st
from chatbot import DaisyChatbot

st.set_page_config(page_title="Daisy Chatbot", page_icon="🌼")

st.markdown("""
<style>
.chat-row {
    display: flex;
    margin: 8px 0;
}
.chat-row.user {
    justify-content: flex-end;
}
.chat-row.assistant {
    justify-content: flex-start;
}
.bubble {
    max-width: 70%;
    padding: 10px 14px;
    border-radius: 14px;
    font-size: 15px;
    line-height: 1.4;
    word-wrap: break-word;
}
.bubble.user {
    background-color: #DCF8C6;
    color: #111;
    border-bottom-right-radius: 4px;
}
.bubble.assistant {
    background-color: #2A2F32;
    color: #E9EDEF;
    border-bottom-left-radius: 4px;
}
</style>
""", unsafe_allow_html=True)

st.markdown(
    """
    <div style="text-align: center;">
        <h1>🌼 Daisy Chatbot</h1>
        <p style="color: gray; margin-top: -10px;">
            Hi, my name is Daisy, and I'm here to help you with anything you need.
        </p>
    </div>
    """,
    unsafe_allow_html=True
)

if "daisy" not in st.session_state:
    st.session_state.daisy = DaisyChatbot(model_name="llama3.2:3b")

if "messages" not in st.session_state:
    st.session_state.messages = []

with st.sidebar:
    st.header("Settings")
    if st.button("🔄 New Conversation"):
        st.session_state.daisy.reset_memory()
        st.session_state.messages = []
        st.rerun()

def render_bubble(role: str, content: str):
    st.markdown(
        f'<div class="chat-row {role}"><div class="bubble {role}">{content}</div></div>',
        unsafe_allow_html=True
    )

for msg in st.session_state.messages:
    render_bubble(msg["role"], msg["content"])

user_input = st.chat_input("Type your message to Daisy...")

if user_input:
    st.session_state.messages.append({"role": "user", "content": user_input})
    render_bubble("user", user_input)

    with st.spinner("Daisy is thinking..."):
        reply = st.session_state.daisy.ask(user_input)

    render_bubble("assistant", reply)
    st.session_state.messages.append({"role": "assistant", "content": reply})

Make sure Ollama is running in the background (it usually starts automatically after install, or run ollama serve), then launch the app:

streamlit run app.py

Your 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)

This isn't just "installed some tools and it worked." Along the way you touched:

That's the whole point of starting from nothing — every piece you added, you understand why it's there.

mistral, gemma2) and compare responses ConversationBufferMemory from LangChain for more advanced memory handling Next post, I'll share my next learning. Until then — happy building, my codez!

── more in #large-language-models 4 stories · sorted by recency
── more on @ollama 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/building-a-local-ai-…] indexed:0 read:5min 2026-09-14 ·