How to Create Your First AI Chatbot with OpenAI
Build a functional AI chatbot using OpenAI API in under 30 minutes.
Jay Banlasan
The AI Systems Guy
Building your first AI chatbot with OpenAI is one of the most direct ways to see what the build ai chatbot openai tutorial output looks like in a real product. The core pattern is simple: maintain a list of messages, append each new user input, send the full list to the API, get a reply, append that to the list, repeat. Understanding this loop is the foundation for every chatbot, assistant, and agent system I build for clients.
Most businesses do not need a fancy chatbot framework to start. A 50-line Python script with conversation memory will handle 80% of the use cases I see. This tutorial gives you that script, then layers on the pieces that make it production-ready.
What You Need Before Starting
- Python 3.9 or higher
- OpenAI API key (from platform.openai.com)
openaiandpython-dotenvpackages installed- A terminal you are comfortable working in
Step 1: Understand the Message Structure
OpenAI's chat API works with a list of message objects. Each message has a role and content:
system: Sets the bot's behavior and personality (sent once at the start)user: Messages from the humanassistant: Previous replies from the model
Every call sends the entire conversation history. The model has no memory by itself. You are the one tracking and passing the history.
# This is what a conversation looks like as data
conversation = [
{"role": "system", "content": "You are a helpful customer service agent for Acme Software."},
{"role": "user", "content": "How do I reset my password?"},
{"role": "assistant", "content": "You can reset your password by clicking 'Forgot Password' on the login page."},
{"role": "user", "content": "I don't see that option. I'm on the mobile app."},
# Next API call includes all of the above so context is preserved
]
Step 2: Build the Basic Terminal Chatbot
import os
from openai import OpenAI
from dotenv import load_dotenv
load_dotenv()
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
SYSTEM_PROMPT = """You are a helpful support agent for Acme Software.
Be concise. Answer questions directly. If you don't know something, say so honestly."""
def chat(user_message: str, history: list) -> tuple[str, list]:
"""
Send a message and get a reply. Returns (reply_text, updated_history).
Args:
user_message: What the user typed
history: Current conversation history
Returns:
Tuple of (assistant_reply, updated_history)
"""
# Add user message to history
history.append({"role": "user", "content": user_message})
# Send full history to API
response = client.chat.completions.create(
model="gpt-4o-mini", # Use mini for chatbots to keep costs low
messages=[{"role": "system", "content": SYSTEM_PROMPT}] + history,
temperature=0.7,
max_tokens=500
)
# Extract reply
reply = response.choices[0].message.content
# Add assistant reply to history
history.append({"role": "assistant", "content": reply})
return reply, history
def main():
"""Run the chatbot in the terminal."""
print("Chatbot ready. Type 'quit' to exit.\n")
history = []
while True:
user_input = input("You: ").strip()
if not user_input:
continue
if user_input.lower() in ["quit", "exit", "bye"]:
print("Goodbye!")
break
reply, history = chat(user_input, history)
print(f"\nBot: {reply}\n")
# Optional: Show conversation length for debugging
# print(f"[{len(history)} messages in context]")
if __name__ == "__main__":
main()
Run it:
python chatbot.py
You now have a working chatbot with conversation memory.
Step 3: Add a Web Interface with Flask
pip install flask
# app.py
import os
from flask import Flask, request, jsonify, render_template_string
from openai import OpenAI
from dotenv import load_dotenv
load_dotenv()
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
app = Flask(__name__)
SYSTEM_PROMPT = "You are a helpful customer support agent. Be concise and friendly."
# Store sessions in memory (use Redis or a database in production)
sessions = {}
HTML_TEMPLATE = """
<!DOCTYPE html>
<html>
<head>
<title>AI Chatbot</title>
<style>
body { font-family: Arial, sans-serif; max-width: 600px; margin: 50px auto; padding: 20px; }
#chat-box { height: 400px; border: 1px solid #ccc; padding: 15px; overflow-y: auto; margin-bottom: 15px; }
.user-msg { text-align: right; margin: 8px 0; }
.bot-msg { text-align: left; margin: 8px 0; color: #333; }
.user-msg span { background: #007bff; color: white; padding: 8px 12px; border-radius: 12px; display: inline-block; }
.bot-msg span { background: #f0f0f0; padding: 8px 12px; border-radius: 12px; display: inline-block; }
#input-area { display: flex; gap: 10px; }
#user-input { flex: 1; padding: 10px; border: 1px solid #ccc; border-radius: 6px; }
button { padding: 10px 20px; background: #007bff; color: white; border: none; border-radius: 6px; cursor: pointer; }
</style>
</head>
<body>
<h2>Support Chat</h2>
<div id="chat-box"></div>
<div id="input-area">
<input type="text" id="user-input" placeholder="Type your message..." onkeypress="if(event.key==='Enter') sendMessage()">
<button onclick="sendMessage()">Send</button>
</div>
<script>
const sessionId = Math.random().toString(36).substr(2, 9);
async function sendMessage() {
const input = document.getElementById('user-input');
const message = input.value.trim();
if (!message) return;
addMessage(message, 'user');
input.value = '';
const response = await fetch('/chat', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({message, session_id: sessionId})
});
const data = await response.json();
addMessage(data.reply, 'bot');
}
function addMessage(text, sender) {
const chatBox = document.getElementById('chat-box');
const div = document.createElement('div');
div.className = sender === 'user' ? 'user-msg' : 'bot-msg';
div.innerHTML = `<span>${text}</span>`;
chatBox.appendChild(div);
chatBox.scrollTop = chatBox.scrollHeight;
}
</script>
</body>
</html>
"""
@app.route("/")
def index():
return render_template_string(HTML_TEMPLATE)
@app.route("/chat", methods=["POST"])
def chat():
data = request.json
user_message = data.get("message", "")
session_id = data.get("session_id", "default")
if session_id not in sessions:
sessions[session_id] = []
history = sessions[session_id]
history.append({"role": "user", "content": user_message})
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "system", "content": SYSTEM_PROMPT}] + history,
temperature=0.7,
max_tokens=500
)
reply = response.choices[0].message.content
history.append({"role": "assistant", "content": reply})
# Trim history if it gets too long (keep last 20 messages)
if len(history) > 20:
sessions[session_id] = history[-20:]
return jsonify({"reply": reply})
if __name__ == "__main__":
app.run(debug=True, port=5000)
Run it:
python app.py
Open http://localhost:5000 in your browser.
Step 4: Add Conversation Reset and Basic Logging
@app.route("/reset", methods=["POST"])
def reset():
session_id = request.json.get("session_id", "default")
sessions.pop(session_id, None)
return jsonify({"status": "reset"})
# Add to the /chat route for logging
import json
from datetime import datetime
def log_conversation(session_id: str, user_msg: str, bot_reply: str):
log_entry = {
"timestamp": datetime.now().isoformat(),
"session_id": session_id,
"user": user_msg,
"bot": bot_reply
}
with open("chat_log.jsonl", "a") as f:
f.write(json.dumps(log_entry) + "\n")
What to Build Next
- Add a product knowledge base so the bot can answer questions about your specific offerings
- Connect the session storage to Redis so conversations persist across server restarts
- Add intent detection to route specific question types to different response logic
Related Reading
- How to Build a Multi-Turn Conversation with Claude - Same conversation history pattern with Claude
- How to Handle AI API Rate Limits Gracefully - Essential for any chatbot seeing real traffic
- How to Set Up OpenAI Function Calling - Let your chatbot take actions, not just answer questions
Want this system built for your business?
Get a free assessment. We will map every system your business needs and show you the ROI.
Get Your Free Assessment