Build an AI Chatbot with Python: Complete Guide from Scratch to Deployment
โก Quick Answer
Build an AI chatbot with Python โ complete tutorial from OpenAI API integration to conversation memory, streaming responses, and deploying a production-ready chatbot application.
Get more content like this on Telegram!
Daily AI tips, notes & resources โ free
Advertisement
Build an AI Chatbot with Python: Complete Guide from Scratch to Deployment
A working AI chatbot in Python is a loop that sends a growing list of messages to a language model and prints whatever comes back. Building that first version takes about two hours. Building one that survives real users โ conversation memory, streaming, rate limiting, error handling, cost controls โ takes closer to two months of iteration.
This guide covers the full path: from the minimal 30-line chatbot that actually works, to a production system with memory, streaming, retrieval, and a web front end. Each part below builds on the last, so you can stop wherever your project needs.
Part 1: The Minimal Working Chatbot
A chatbot's "memory" is just a Python list of messages, re-sent to the API on every turn โ the model itself remembers nothing between calls.
# pip install openai
from openai import OpenAI
client = OpenAI() # Uses OPENAI_API_KEY environment variable
def chatbot():
"""Minimal chatbot with conversation memory."""
messages = [
{
"role": "system",
"content": "You are a helpful assistant. Be concise and clear."
}
]
print("Chatbot ready. Type 'quit' to exit.\n")
while True:
user_input = input("You: ").strip()
if user_input.lower() in ["quit", "exit", "bye"]:
print("Goodbye!")
break
if not user_input:
continue
# Add user message to history
messages.append({"role": "user", "content": user_input})
# Call API with full conversation history
response = client.chat.completions.create(
model="gpt-4o-mini", # Fast and cheap for development
messages=messages,
temperature=0.7,
max_tokens=500
)
assistant_message = response.choices[0].message.content
# Add response to history (this IS the memory)
messages.append({"role": "assistant", "content": assistant_message})
print(f"\nAssistant: {assistant_message}\n")
if __name__ == "__main__":
chatbot()This 35-line script has real memory: each message in messages is the entire context the model sees, resent in full on every call.
Part 2: Streaming Responses
Streaming delivers a response as a sequence of small text chunks instead of one blocked wait โ the same difference between watching a page load word by word versus staring at a blank screen until it's fully rendered.
def streaming_chatbot():
messages = [{"role": "system", "content": "You are a helpful assistant."}]
print("Streaming chatbot. Type 'quit' to exit.\n")
while True:
user_input = input("You: ").strip()
if user_input.lower() == "quit":
break
messages.append({"role": "user", "content": user_input})
print("\nAssistant: ", end="", flush=True)
# Stream the response
stream = client.chat.completions.create(
model="gpt-4o-mini",
messages=messages,
stream=True # Key parameter
)
full_response = ""
for chunk in stream:
content = chunk.choices[0].delta.content
if content:
print(content, end="", flush=True)
full_response += content
print("\n") # Newline after response
messages.append({"role": "assistant", "content": full_response})Part 3: Context Management
Every model has a fixed context window โ a maximum number of tokens it can read per call โ and a long conversation will eventually exceed it, the same way a sticky note runs out of room no matter how small you write.
import tiktoken
class ChatbotWithMemory:
def __init__(
self,
model: str = "gpt-4o-mini",
system_prompt: str = "You are a helpful assistant.",
max_tokens: int = 8000 # Leave room for response
):
self.model = model
self.max_tokens = max_tokens
self.enc = tiktoken.encoding_for_model("gpt-4o")
self.messages = [{"role": "system", "content": system_prompt}]
self.system_tokens = len(self.enc.encode(system_prompt))
def count_tokens(self) -> int:
total = self.system_tokens
for msg in self.messages[1:]: # Skip system message
total += len(self.enc.encode(msg["content"])) + 4 # Overhead per message
return total
def trim_history(self):
"""Remove oldest messages when approaching limit."""
while self.count_tokens() > self.max_tokens and len(self.messages) > 2:
# Keep system message + latest exchange, remove second oldest user message
if len(self.messages) > 3:
self.messages.pop(1) # Remove oldest non-system message
else:
break
def chat(self, user_message: str) -> str:
self.messages.append({"role": "user", "content": user_message})
self.trim_history()
response = client.chat.completions.create(
model=self.model,
messages=self.messages,
temperature=0.7,
max_tokens=500
)
assistant_message = response.choices[0].message.content
self.messages.append({"role": "assistant", "content": assistant_message})
return assistant_message
def get_stats(self) -> dict:
return {
"message_count": len(self.messages),
"token_count": self.count_tokens(),
"token_limit": self.max_tokens
}
# Usage
bot = ChatbotWithMemory(
system_prompt="You are a Python tutor. Explain concepts clearly with code examples."
)
print(bot.chat("What is a decorator in Python?"))
print(bot.chat("Can you show me a practical example?"))
print(bot.chat("How does it differ from a class-based decorator?"))
print(f"Stats: {bot.get_stats()}")Part 4: FastAPI Web Backend
A command-line chatbot only serves one user at a time; a FastAPI backend turns the same logic into an HTTP endpoint that any web or mobile client can call, with one session per user tracked by ID.
# pip install fastapi uvicorn redis
from fastapi import FastAPI, HTTPException
from fastapi.responses import StreamingResponse
from pydantic import BaseModel
from typing import Optional
import json
import uuid
app = FastAPI(title="AI Chatbot API")
# In-memory session storage (use Redis for production)
sessions: dict = {}
class ChatRequest(BaseModel):
message: str
session_id: Optional[str] = None
class ChatResponse(BaseModel):
response: str
session_id: str
@app.post("/chat", response_model=ChatResponse)
async def chat(request: ChatRequest):
session_id = request.session_id or str(uuid.uuid4())
if session_id not in sessions:
sessions[session_id] = [
{"role": "system", "content": "You are a helpful assistant."}
]
sessions[session_id].append({"role": "user", "content": request.message})
try:
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=sessions[session_id],
max_tokens=500
)
assistant_message = response.choices[0].message.content
sessions[session_id].append({"role": "assistant", "content": assistant_message})
return ChatResponse(response=assistant_message, session_id=session_id)
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@app.post("/chat/stream")
async def chat_stream(request: ChatRequest):
session_id = request.session_id or str(uuid.uuid4())
if session_id not in sessions:
sessions[session_id] = [
{"role": "system", "content": "You are a helpful assistant."}
]
sessions[session_id].append({"role": "user", "content": request.message})
async def generate():
full_response = ""
# Send session_id first
yield f"data: {json.dumps({'session_id': session_id})}\n\n"
stream = client.chat.completions.create(
model="gpt-4o-mini",
messages=sessions[session_id],
stream=True
)
for chunk in stream:
content = chunk.choices[0].delta.content
if content:
full_response += content
yield f"data: {json.dumps({'content': content})}\n\n"
sessions[session_id].append({"role": "assistant", "content": full_response})
yield f"data: {json.dumps({'done': True})}\n\n"
return StreamingResponse(generate(), media_type="text/event-stream")
@app.delete("/chat/{session_id}")
async def clear_session(session_id: str):
if session_id in sessions:
del sessions[session_id]
return {"message": "Session cleared"}
# Run: uvicorn chatbot_api:app --reloadPart 5: Simple Streamlit UI
Streamlit turns a Python script into a browser UI with no HTML or JavaScript โ useful for internal tools and demos where a full frontend framework is overkill.
# pip install streamlit
# Run: streamlit run chatbot_ui.py
import streamlit as st
from openai import OpenAI
client = OpenAI()
st.title("AI Chatbot")
st.caption("Powered by GPT-4o mini")
# System prompt customization
with st.sidebar:
st.header("Settings")
system_prompt = st.text_area(
"System Prompt",
value="You are a helpful assistant. Be concise and clear.",
height=100
)
model = st.selectbox("Model", ["gpt-4o-mini", "gpt-4o", "gpt-3.5-turbo"])
temperature = st.slider("Temperature", 0.0, 2.0, 0.7, 0.1)
if st.button("Clear Conversation"):
st.session_state.messages = []
st.rerun()
# Initialize conversation state
if "messages" not in st.session_state:
st.session_state.messages = []
# Display conversation history
for msg in st.session_state.messages:
with st.chat_message(msg["role"]):
st.write(msg["content"])
# Chat input
if prompt := st.chat_input("Type your message..."):
# Add user message
st.session_state.messages.append({"role": "user", "content": prompt})
with st.chat_message("user"):
st.write(prompt)
# Build messages for API call
api_messages = [{"role": "system", "content": system_prompt}]
api_messages.extend(st.session_state.messages)
# Stream response
with st.chat_message("assistant"):
response_placeholder = st.empty()
full_response = ""
stream = client.chat.completions.create(
model=model,
messages=api_messages,
temperature=temperature,
stream=True
)
for chunk in stream:
content = chunk.choices[0].delta.content or ""
full_response += content
response_placeholder.write(full_response + "โ")
response_placeholder.write(full_response)
st.session_state.messages.append({"role": "assistant", "content": full_response})Part 6: Adding Document Knowledge (RAG)
Retrieval-Augmented Generation (RAG) answers questions from your own documents by searching them first and pasting the relevant chunks into the prompt โ like handing an open-book exam taker the exact page before they answer, instead of expecting them to memorize the textbook.
# pip install chromadb sentence-transformers langchain-community
from langchain_community.vectorstores import Chroma
from langchain_community.embeddings import HuggingFaceEmbeddings
from langchain.text_splitter import RecursiveCharacterTextSplitter
class RAGChatbot:
def __init__(self, documents_dir: str = "./docs"):
self.embeddings = HuggingFaceEmbeddings(model_name="all-MiniLM-L6-v2")
self.vectorstore = None
self._load_documents(documents_dir)
self.messages = [{"role": "system", "content": "Answer based on the provided context."}]
def _load_documents(self, docs_dir: str):
from langchain_community.document_loaders import DirectoryLoader, TextLoader
loader = DirectoryLoader(docs_dir, glob="**/*.txt", loader_cls=TextLoader)
docs = loader.load()
splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)
chunks = splitter.split_documents(docs)
self.vectorstore = Chroma.from_documents(chunks, self.embeddings)
print(f"Loaded {len(chunks)} document chunks")
def chat(self, question: str) -> str:
# Retrieve relevant context
relevant_docs = self.vectorstore.similarity_search(question, k=3)
context = "\n\n".join([doc.page_content for doc in relevant_docs])
# Build message with context
augmented_message = f"Context:\n{context}\n\nQuestion: {question}"
self.messages.append({"role": "user", "content": augmented_message})
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=self.messages
)
answer = response.choices[0].message.content
# Store clean question in history (not the augmented one)
self.messages[-1] = {"role": "user", "content": question}
self.messages.append({"role": "assistant", "content": answer})
return answer
bot = RAGChatbot("./company_docs")
print(bot.chat("What is our return policy?"))Building on This Foundation
A functional chatbot is genuinely 30 lines of Python. A production one adds conversation management, streaming, rate limiting, persistent storage, and a web interface โ but each piece bolts on separately, so nothing here requires a rewrite.
Start with the minimal version, add features only as you need them, and resist over-engineering from day one. Most production chatbots started exactly this way.
For building more complex AI applications, see our LangChain tutorial. For deploying to production with proper infrastructure, see our deploy AI model guide.
Further Reading
- AI API Cost Management: How to Cut LLM Costs by 80% Without Losing Quality
- Deploy AI Model to Production: FastAPI, Docker, and Cloud Deployment Guide
- CrewAI Tutorial: Build Multi-Agent AI Systems That Work Together
- Streamlit Tutorial: Build and Deploy AI Apps with Python in Minutes
- Build a Personal AI Assistant: Complete Python Project with Memory and Tools
- How to Use Python for Data Science (Complete 2026 Roadmap)
- AI Agents Explained: How Autonomous AI Systems Work and What They Can Do
- AI Hallucination Explained: Why LLMs Make Things Up (and How to Fix It)
Advertisement
๐ฌ DiscussionPowered by GitHub Discussions
Frequently Asked Questions

Software Testing Expert & Prompt Engineering
Ensures every release is bug-free through rigorous testing, and crafts high-precision prompts that power our AI-driven workflows. Abdullah Al Arman Emon leads QA and prompt engineering across AiTechWorlds.
Not sure yet? Ask AI about this article
Get an instant, unbiased AI summary of โBuild an AI Chatbot with Python: Complete Guide from Scratch to Deploymentโ.
Advertisement
Related Articles
AI API Cost Management: How to Cut LLM Costs by 80% Without Losing Quality
AI API cost management โ practical strategies to reduce OpenAI, Claude, and Gemini API costs by 80% using model selection, caching, RAG, prompt optimization, and batch processing.
Build a Personal AI Assistant: Complete Python Project with Memory and Tools
Build a personal AI assistant in Python with persistent memory, web search, file access, and calendar integration โ a complete project from architecture to working prototype.
CrewAI Tutorial: Build Multi-Agent AI Systems That Work Together
CrewAI tutorial โ build multi-agent AI systems where specialized agents collaborate to complete complex tasks, with practical Python examples for research, coding, and content workflows.
Deploy AI Model to Production: FastAPI, Docker, and Cloud Deployment Guide
Deploy AI model to production โ complete guide using FastAPI, Docker, and cloud platforms with monitoring, scaling, CI/CD, and best practices for production ML systems.