[go: up one dir, main page]

0% found this document useful (0 votes)
8 views49 pages

Chatbot Guide

Uploaded by

ujienoah45
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
8 views49 pages

Chatbot Guide

Uploaded by

ujienoah45
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 49

Building an AI-Powered Customer

Support Chatbot: A Step-by-Step Guide

Introduction

This guide will walk you through the process of building an AI-powered customer
support chatbot integrated into a web application. We will cover both the backend
(Flask) and frontend (HTML/CSS/JavaScript) development, including AI integration
(using a placeholder for now, with options for advanced models), database setup, and
essential features like human escalation and feedback. By the end of this guide, you
will have a fully functional chatbot that can handle common customer inquiries and
provide a foundation for further enhancements.

Table of Contents

1. Project Setup and Environment Configuration

Setting up your development environment

Creating the Flask project

Installing dependencies

2. Backend Development: Flask API

Designing API endpoints

Implementing chat functionality

Handling human escalation

Managing feedback and analytics

Database integration with SQLAlchemy

3. Frontend Development: Chat Interface

Designing the user interface (HTML)


Styling the chatbot (CSS)

Adding interactivity (JavaScript)

4. Integrating Frontend and Backend

Connecting the chat interface to the Flask API

Real-time communication

5. AI Logic and Database Interaction

Implementing placeholder AI responses

Populating the FAQ database

Conversation logging

6. Testing and Verification

Manual testing of chatbot features

Verifying API endpoints

7. Deployment and Next Steps

Preparing for deployment

Future enhancements and advanced AI integration

1. Project Setup and Environment Configuration

Before we begin coding, it's crucial to set up a proper development environment. This
ensures that all necessary tools and libraries are available and that your project
dependencies are managed effectively.

1.1. Setting up your Development Environment

To follow this guide, you will need:

Python 3.8+: The primary language for our backend.

pip : Python's package installer, usually comes with Python.

A Code Editor: Visual Studio Code, PyCharm, or Sublime Text are recommended.
A Web Browser: Chrome, Firefox, or Edge for testing the frontend.

First, ensure Python is installed. You can check by opening your terminal or command
prompt and typing:

python3 --version

If Python is not installed, please download it from the official Python website
(python.org).

Next, we will create a virtual environment. A virtual environment is a self-contained


directory that contains a Python installation for a specific project. This helps manage
dependencies and avoids conflicts between different projects.

Navigate to your desired project directory in the terminal and run:

# Create a new directory for your project


mkdir ai-chatbot-project
cd ai-chatbot-project

# Create a virtual environment named 'venv'


python3 -m venv venv

# Activate the virtual environment


# On macOS/Linux:
source venv/bin/activate

# On Windows (Command Prompt):


venc\Scripts\activate.bat

# On Windows (PowerShell):
venc\Scripts\Activate.ps1

Once activated, your terminal prompt should show (venv) indicating that you are
operating within the virtual environment.

1.2. Creating the Flask Project

We will use Flask, a lightweight Python web framework, for our backend. We'll start by
creating a basic Flask application structure.

Inside your ai-chatbot-project directory, create a new folder named backend and
navigate into it:

cd backend
Now, let's create the main Flask application file, app.py :

# backend/app.py

from flask import Flask

app = Flask(__name__)

@app.route("/")
def hello_world():
return "<p>Hello, World! This is your AI Chatbot Backend.</p>"

if __name__ == "__main__":
app.run(debug=True)

This simple Flask application defines a single route ( / ) that returns a

simple "Hello, World!" message. This is just a starting point.

1.3. Installing Dependencies

We will need a few Python packages for our Flask application. With your virtual
environment activated, install them using pip:

pip install Flask Flask-SQLAlchemy Flask-CORS

Flask: The web framework.

Flask-SQLAlchemy: An extension for Flask that adds SQLAlchemy support, an


Object Relational Mapper (ORM) for interacting with databases.

Flask-CORS: A Flask extension for handling Cross-Origin Resource Sharing


(CORS), which is essential for allowing our frontend to communicate with our
backend.

After installation, it's good practice to save your dependencies to a requirements.txt


file. This allows others (or your future self) to easily install all necessary packages:

pip freeze > requirements.txt


2. Backend Development: Flask API

Now that our environment is set up, let's build the Flask backend. This will involve
defining our API endpoints, implementing the chatbot logic, and setting up database
interactions.

2.1. Project Structure

For a professional and maintainable project, we will organize our Flask application
into a modular structure. Create the following directories and empty files within your
backend folder:

backend/
├── venv/ # Python virtual environment
├── src/
│ ├── main.py # Main Flask application entry point
│ ├── models/ # Database models (e.g., user, conversation, FAQ)
│ │ ├── __init__.py
│ │ ├── user.py
│ │ └── conversation.py
│ ├── routes/ # API route blueprints
│ │ ├── __init__.py
│ │ ├── user.py
│ │ └── chatbot.py
│ ├── static/ # Frontend static files (HTML, CSS, JS)
│ │ ├── index.html
│ │ ├── styles.css
│ │ └── script.js
│ └── database/ # SQLite database file
│ └── app.db
├── populate_sample_data.py # Script to populate initial data
└── requirements.txt # Project dependencies

2.2. Main Flask Application ( src/main.py )

This file will be the entry point for our Flask application. It will initialize the Flask app,
configure the database, register our API blueprints, and serve static files for the
frontend.

Replace the content of backend/src/main.py with the following:


# backend/src/main.py

import os
import sys
from flask import Flask, send_from_directory
from flask_cors import CORS
from flask_sqlalchemy import SQLAlchemy

# Initialize SQLAlchemy outside of app creation for flexibility


db = SQLAlchemy()

# Import blueprints after db is defined to avoid circular imports


from src.routes.user import user_bp
from src.routes.chatbot import chatbot_bp

app = Flask(__name__, static_folder=os.path.join(os.path.dirname(__file__),


'static'))
CORS(app) # Enable CORS for all routes
app.config['SECRET_KEY'] = 'your_super_secret_key_here' # Change this in
production!

# Database configuration
app.config['SQLALCHEMY_DATABASE_URI'] =
f"sqlite:///{os.path.join(os.path.dirname(__file__), 'database', 'app.db')}"
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

db.init_app(app)

# Register blueprints
app.register_blueprint(user_bp, url_prefix='/api')
app.register_blueprint(chatbot_bp, url_prefix='/api')

# Create database tables if they don't exist


with app.app_context():
db.create_all()

# Serve static files for the frontend


@app.route('/', defaults={'path': ''})
@app.route('/<path:path>')
def serve(path):
static_folder_path = app.static_folder
if static_folder_path is None:
return "Static folder not configured", 404

if path != "" and os.path.exists(os.path.join(static_folder_path, path)):


return send_from_directory(static_folder_path, path)
else:
index_path = os.path.join(static_folder_path, 'index.html')
if os.path.exists(index_path):
return send_from_directory(static_folder_path, 'index.html')
else:
return "index.html not found", 404

if __name__ == '__main__':
app.run(host='0.0.0.0', port=5001, debug=True)

Explanation:
Flask and CORS are imported. SQLAlchemy is initialized to manage our
database.

user_bp and chatbot_bp are imported from their respective route files. These
are Flask Blueprints, which help organize routes into modular components.

app.config['SECRET_KEY'] is crucial for session management and security.


Remember to change this to a strong, unique key in a production
environment.

SQLALCHEMY_DATABASE_URI points to our SQLite database file. This will be


created automatically if it doesn't exist.

db.init_app(app) binds the SQLAlchemy instance to our Flask app.

db.create_all() creates all defined database tables when the application


starts (within the application context).

The @app.route decorators define how our application responds to different


URL paths. The serve function handles serving our frontend index.html and
other static assets.

app.run(host='0.0.0.0', port=5001, debug=True) starts the Flask


development server, making it accessible from any IP address on port 5001.
debug=True enables debugging features, which are useful during development
but should be False in production.

2.3. Database Models ( src/models/user.py and


src/models/conversation.py )

We will define our database tables using SQLAlchemy models. These models represent
the structure of our data and provide an object-oriented way to interact with the
database.

src/models/user.py

This file will contain a basic User model. While user authentication is a requirement,
for simplicity in this guide, we'll keep it minimal. You can expand upon this for full
authentication features.
# backend/src/models/user.py

from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy() # Re-initialize db here to avoid circular import issues if


user.py is imported first

class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)

def __repr__(self):
return f'<User {self.username}>'

def to_dict(self):
return {
'id': self.id,
'username': self.username,
'email': self.email
}

Note: The db = SQLAlchemy() line is duplicated here. This is a common pattern to


avoid circular import issues in Flask-SQLAlchemy when models are defined in separate
files. The db object from main.py will be the one used for the application context.

src/models/conversation.py

This file will define models for Conversation logs, Feedback , and FAQ entries. These
are central to our chatbot's functionality.
# backend/src/models/conversation.py

from datetime import datetime


from src.models.user import db # Import db from user.py to ensure it's the same
instance

class Conversation(db.Model):
"""Model for storing conversation logs"""
__tablename__ = 'conversations'

id = db.Column(db.Integer, primary_key=True)
session_id = db.Column(db.String(100), nullable=False, index=True)
user_message = db.Column(db.Text, nullable=True)
ai_response = db.Column(db.Text, nullable=True)
escalated = db.Column(db.Boolean, default=False)
timestamp = db.Column(db.DateTime, default=datetime.utcnow)
message_type = db.Column(db.String(20), default='chat') # 'chat',
'escalation', 'feedback'

def to_dict(self):
return {
'id': self.id,
'session_id': self.session_id,
'user_message': self.user_message,
'ai_response': self.ai_response,
'escalated': self.escalated,
'timestamp': self.timestamp.isoformat() if self.timestamp else
None,
'message_type': self.message_type
}

class Feedback(db.Model):
"""Model for storing user feedback"""
__tablename__ = 'feedback'

id = db.Column(db.Integer, primary_key=True)
session_id = db.Column(db.String(100), nullable=False, index=True)
rating = db.Column(db.Integer, nullable=False) # 1-5 scale
comment = db.Column(db.Text, nullable=True)
timestamp = db.Column(db.DateTime, default=datetime.utcnow)

def to_dict(self):
return {
'id': self.id,
'session_id': self.session_id,
'rating': self.rating,
'comment': self.comment,
'timestamp': self.timestamp.isoformat() if self.timestamp else None
}

class FAQ(db.Model):
"""Model for storing frequently asked questions"""
__tablename__ = 'faqs'

id = db.Column(db.Integer, primary_key=True)
question = db.Column(db.Text, nullable=False)
answer = db.Column(db.Text, nullable=False)
category = db.Column(db.String(50), nullable=True)
keywords = db.Column(db.Text, nullable=True) # Comma-separated keywords
for matching
created_at = db.Column(db.DateTime, default=datetime.utcnow)
updated_at = db.Column(db.DateTime, default=datetime.utcnow,
onupdate=datetime.utcnow)
active = db.Column(db.Boolean, default=True)

def to_dict(self):
return {
'id': self.id,
'question': self.question,
'answer': self.answer,
'category': self.category,
'keywords': self.keywords.split(',') if self.keywords else [],
'created_at': self.created_at.isoformat() if self.created_at else
None,
'updated_at': self.updated_at.isoformat() if self.updated_at else
None,
'active': self.active
}

Explanation:

Conversation : Stores each message exchange between the user and the AI,
including whether it was escalated.

Feedback : Records user ratings and comments on the chatbot's performance.

FAQ : Contains frequently asked questions and their answers, along with
keywords for matching user queries. This will be our primary

source of AI responses.

2.4. API Endpoints ( src/routes/user.py and src/routes/chatbot.py )

We will define our API endpoints using Flask Blueprints. Blueprints allow us to
organize our routes into separate modules, making the application more scalable and
maintainable.

src/routes/user.py

This blueprint will handle user-related API endpoints. For this project, we will keep it
simple, but it provides a foundation for full user authentication and management.
# backend/src/routes/user.py

from flask import Blueprint, jsonify, request


from src.models.user import User, db

user_bp = Blueprint("user", __name__)

@user_bp.route("/users", methods=["GET"])
def get_users():
users = User.query.all()
return jsonify([user.to_dict() for user in users])

@user_bp.route("/users", methods=["POST"])
def create_user():
data = request.json
user = User(username=data["username"], email=data["email"])
db.session.add(user)
db.session.commit()
return jsonify(user.to_dict()), 201

@user_bp.route("/users/<int:user_id>", methods=["GET"])
def get_user(user_id):
user = User.query.get_or_404(user_id)
return jsonify(user.to_dict())

@user_bp.route("/users/<int:user_id>", methods=["PUT"])
def update_user(user_id):
user = User.query.get_or_404(user_id)
data = request.json
user.username = data.get("username", user.username)
user.email = data.get("email", user.email)
db.session.commit()
return jsonify(user.to_dict())

@user_bp.route("/users/<int:user_id>", methods=["DELETE"])
def delete_user(user_id):
user = User.query.get_or_404(user_id)
db.session.delete(user)
db.session.commit()
return "", 204

Explanation:

This blueprint defines standard CRUD (Create, Read, Update, Delete) operations
for User objects. While not directly used by the chatbot frontend, it
demonstrates how to structure API endpoints.

src/routes/chatbot.py

This is the core of our backend, handling all chatbot-related interactions, including
processing messages, providing AI responses, managing escalations, and handling
feedback.
# backend/src/routes/chatbot.py

from flask import Blueprint, jsonify, request


from datetime import datetime, timedelta
from src.models.user import db
from src.models.conversation import Conversation, Feedback, FAQ

chatbot_bp = Blueprint("chatbot", __name__)

# Enhanced AI responses with FAQ integration


def get_ai_response_from_db(message):
"""Get AI response by checking FAQ database first, then fallback to keyword
matching"""
message_lower = message.lower()

# First, try to find a matching FAQ


faqs = FAQ.query.filter(FAQ.active == True).all()
for faq in faqs:
if faq.keywords:
keywords = [k.strip().lower() for k in faq.keywords.split(",")]
if any(keyword in message_lower for keyword in keywords):
return faq.answer, False

# Fallback to simple keyword matching


if any(keyword in message_lower for keyword in ["track", "order", "shipping
status"]):
return "To track your order, please provide your order number. You can
find it in your confirmation email.", False
elif any(keyword in message_lower for keyword in ["refund", "return",
"money back"]):
return "For refund requests, please contact our support team with your
order details. Refunds are processed within 5-7 business days.", False
elif any(keyword in message_lower for keyword in ["shipping", "delivery",
"ship"]):
return "We offer standard shipping (5-7 days) and express shipping (2-3
days). Shipping costs vary by location.", False
elif any(keyword in message_lower for keyword in ["account", "login",
"password", "profile"]):
return "For account-related issues, please check your email for
verification links or contact support.", False
elif any(keyword in message_lower for keyword in ["product", "item",
"catalog", "price"]):
return "For product information, please browse our catalog or ask
specific questions about items you\"re interested in.", False
elif any(keyword in message_lower for keyword in ["payment", "pay", "credit
card", "billing"]):
return "We accept all major credit cards, PayPal, and bank transfers.
Payment is processed securely.", False
else:
return "I understand you need help. Let me connect you with a human
agent who can better assist you.", True

@chatbot_bp.route("/chat", methods=["POST"])
def chat():
"""Handle chat messages from users"""
try:
data = request.json
user_message = data.get("message", "").strip()
session_id = data.get("session_id", "anonymous")

if not user_message:
return jsonify({"error": "Message cannot be empty"}), 400

# Get AI response
ai_response, needs_escalation = get_ai_response_from_db(user_message)

# Log the conversation to database


conversation = Conversation(
session_id=session_id,
user_message=user_message,
ai_response=ai_response,
escalated=needs_escalation,
message_type=\'chat\'
)
db.session.add(conversation)
db.session.commit()

response = {
"response": ai_response,
"escalated": needs_escalation,
"timestamp": conversation.timestamp.isoformat(),
"conversation_id": conversation.id
}

return jsonify(response)

except Exception as e:
db.session.rollback()
return jsonify({"error": "Internal server error"}), 500

@chatbot_bp.route("/escalate", methods=["POST"])
def escalate():
"""Handle escalation to human agent"""
try:
data = request.json
session_id = data.get("session_id", "anonymous")
reason = data.get("reason", "User requested human agent")

# Log escalation to database


escalation = Conversation(
session_id=session_id,
user_message=reason,
ai_response=\'Escalated to human agent\',
escalated=True,
message_type=\'escalation\'
)
db.session.add(escalation)
db.session.commit()

response = {
"message": "Your request has been escalated to a human agent.
Someone will be with you shortly.",
"escalation_id": f"ESC-{escalation.id}",
"estimated_wait": "5-10 minutes"
}

return jsonify(response)

except Exception as e:
db.session.rollback()
return jsonify({"error": "Internal server error"}), 500

@chatbot_bp.route("/conversations", methods=["GET"])
def get_conversations():
"""Get conversation logs (admin endpoint)"""
try:
# In production, this would require admin authentication
page = request.args.get("page", 1, type=int)
per_page = request.args.get("per_page", 50, type=int)
session_id = request.args.get("session_id")

query = Conversation.query
if session_id:
query = query.filter(Conversation.session_id == session_id)

conversations = query.order_by(Conversation.timestamp.desc()).paginate(
page=page, per_page=per_page, error_out=False
)

return jsonify({
"conversations": [conv.to_dict() for conv in conversations.items],
"total": conversations.total,
"pages": conversations.pages,
"current_page": page
})

except Exception as e:
return jsonify({"error": "Internal server error"}), 500

@chatbot_bp.route("/health", methods=["GET"])
def health_check():
"""Health check endpoint"""
return jsonify({
"status": "healthy",
"timestamp": datetime.now().isoformat(),
"service": "chatbot-api",
"database": "connected"
})

@chatbot_bp.route("/feedback", methods=["POST"])
def submit_feedback():
"""Handle user feedback on chatbot interactions"""
try:
data = request.json
session_id = data.get("session_id", "anonymous")
rating = data.get("rating", 0) # 1-5 scale
comment = data.get("comment", "")

if rating < 1 or rating > 5:


return jsonify({"error": "Rating must be between 1 and 5"}), 400

# Save feedback to database


feedback = Feedback(
session_id=session_id,
rating=rating,
comment=comment
)
db.session.add(feedback)
db.session.commit()

return jsonify({
"message": "Thank you for your feedback!",
"feedback_id": f"FB-{feedback.id}"
})
except Exception as e:
db.session.rollback()
return jsonify({"error": "Internal server error"}), 500

@chatbot_bp.route("/faqs", methods=["GET"])
def get_faqs():
"""Get all active FAQs"""
try:
faqs = FAQ.query.filter(FAQ.active == True).order_by(FAQ.category,
FAQ.question).all()
return jsonify({
"faqs": [faq.to_dict() for faq in faqs],
"total": len(faqs)
})

except Exception as e:
return jsonify({"error": "Internal server error"}), 500

@chatbot_bp.route("/faqs", methods=["POST"])
def create_faq():
"""Create a new FAQ (admin endpoint)"""
try:
data = request.json
faq = FAQ(
question=data.get("question"),
answer=data.get("answer"),
category=data.get("category"),
keywords=data.get("keywords", "")
)
db.session.add(faq)
db.session.commit()

return jsonify(faq.to_dict()), 201

except Exception as e:
db.session.rollback()
return jsonify({"error": "Internal server error"}), 500

@chatbot_bp.route("/analytics", methods=["GET"])
def get_analytics():
"""Get basic analytics about chatbot usage"""
try:
total_conversations = Conversation.query.count()
total_escalations = Conversation.query.filter(Conversation.escalated ==
True).count()
total_feedback = Feedback.query.count()
avg_rating = db.session.query(db.func.avg(Feedback.rating)).scalar() or
0

# Recent activity (last 24 hours)


yesterday = datetime.utcnow() - timedelta(days=1)
recent_conversations = Conversation.query.filter(Conversation.timestamp
>= yesterday).count()

return jsonify({
"total_conversations": total_conversations,
"total_escalations": total_escalations,
"escalation_rate": (total_escalations / total_conversations * 100)
if total_conversations > 0 else 0,
"total_feedback": total_feedback,
"average_rating": round(float(avg_rating), 2),
"recent_conversations_24h": recent_conversations
})

except Exception as e:
return jsonify({"error": "Internal server error"}), 500

Explanation:

get_ai_response_from_db(message) : This function is the heart of our AI logic. It


first attempts to find a matching FAQ in the database based on keywords. If no
FAQ is found, it falls back to a simple keyword-matching system. If neither
provides a suitable answer, it flags the conversation for human escalation.

/chat (POST): Receives user messages, processes them using


get_ai_response_from_db , stores the conversation in the database, and returns
the AI's response.

/escalate (POST): Handles requests to escalate a conversation to a human


agent, logging the escalation in the database.

/conversations (GET): (Admin endpoint) Retrieves conversation logs from the


database, with optional filtering by session ID and pagination.

/health (GET): A simple endpoint to check the health status of the API and
database connection.

/feedback (POST): Allows users to submit feedback (rating and comment) on


their chatbot experience, which is then stored in the database.

/faqs (GET/POST): (Admin endpoints) Allows retrieval and creation of FAQ


entries in the database.

/analytics (GET): (Admin endpoint) Provides basic usage statistics, such as


total conversations, escalations, feedback, and average rating.

3. Frontend Development: Chat Interface

Now, let's build the user interface for our chatbot. This will be a simple yet responsive
web page with a floating chat widget. The frontend files will be placed in the
backend/src/static/ directory, which our Flask application will serve.
3.1. Designing the User Interface ( src/static/index.html )

This HTML file will define the structure of our web application, including the main
content area and the chatbot widget.

Create or replace the content of backend/src/static/index.html with the following:


<!-- backend/src/static/index.html -->

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AI Customer Support Chatbot</title>
<link rel="stylesheet" href="styles.css">
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-
awesome/6.0.0/css/all.min.css" rel="stylesheet">
</head>
<body>
<!-- Main Application -->
<div class="app-container">
<!-- Header -->
<header class="app-header">
<div class="header-content">
<h1><i class="fas fa-robot"></i> AI Customer Support</h1>
<p>Get instant help with our AI-powered assistant</p>
</div>
</header>

<!-- Main Content -->


<main class="main-content">
<div class="content-wrapper">
<div class="welcome-section">
<h2>Welcome to Our Support Center</h2>
<p>Our AI assistant is here to help you 24/7. Click the
chat button below to get started!</p>

<div class="features-grid">
<div class="feature-card">
<i class="fas fa-clock"></i>
<h3>24/7 Support</h3>
<p>Get help anytime, day or night</p>
</div>
<div class="feature-card">
<i class="fas fa-bolt"></i>
<h3>Instant Responses</h3>
<p>Quick answers to common questions</p>
</div>
<div class="feature-card">
<i class="fas fa-user-tie"></i>
<h3>Human Escalation</h3>
<p>Connect with human agents when needed</p>
</div>
</div>
</div>
</div>
</main>

<!-- Chat Widget -->


<div class="chat-widget" id="chatWidget">
<!-- Chat Toggle Button -->
<button class="chat-toggle" id="chatToggle">
<i class="fas fa-comments"></i>
<span class="chat-badge" id="chatBadge">1</span>
</button>

<!-- Chat Window -->


<div class="chat-window" id="chatWindow">
<!-- Chat Header -->
<div class="chat-header">
<div class="chat-header-info">
<i class="fas fa-robot"></i>
<div>
<h4>AI Assistant</h4>
<span class="status online">Online</span>
</div>
</div>
<button class="chat-close" id="chatClose">
<i class="fas fa-times"></i>
</button>
</div>

<!-- Chat Messages -->


<div class="chat-messages" id="chatMessages">
<div class="message bot-message">
<div class="message-avatar">
<i class="fas fa-robot"></i>
</div>
<div class="message-content">
<p>Hello! I\'m your AI assistant. How can I help
you today?</p>
<span class="message-time">Just now</span>
</div>
</div>
</div>

<!-- Chat Input -->


<div class="chat-input-container">
<div class="chat-input-wrapper">
<input type="text" id="chatInput" placeholder="Type
your message..." maxlength="500">
<button id="sendButton" class="send-button">
<i class="fas fa-paper-plane"></i>
</button>
</div>
<div class="chat-actions">
<button class="action-button" id="escalateButton">
<i class="fas fa-user"></i> Talk to Human
</button>
<button class="action-button" id="feedbackButton">
<i class="fas fa-star"></i> Rate Chat
</button>
</div>
</div>
</div>
</div>

<!-- Feedback Modal -->


<div class="modal-overlay" id="feedbackModal">
<div class="modal-content">
<div class="modal-header">
<h3>Rate Your Experience</h3>
<button class="modal-close" id="closeFeedback">
<i class="fas fa-times"></i>
</button>
</div>
<div class="modal-body">
<div class="rating-container">
<p>How was your chat experience?</p>
<div class="star-rating" id="starRating">
<i class="fas fa-star" data-rating="1"></i>
<i class="fas fa-star" data-rating="2"></i>
<i class="fas fa-star" data-rating="3"></i>
<i class="fas fa-star" data-rating="4"></i>
<i class="fas fa-star" data-rating="5"></i>
</div>
</div>
<textarea id="feedbackComment" placeholder="Tell us more
about your experience (optional)..." rows="3"></textarea>
<button class="submit-feedback" id="submitFeedback">Submit
Feedback</button>
</div>
</div>
</div>

<!-- Loading Indicator -->


<div class="loading-indicator" id="loadingIndicator">
<div class="spinner"></div>
</div>
</div>

<script src="script.js"></script>
</body>
</html>

Explanation:

The HTML defines the main layout, including a header, a welcome section with
feature cards, and the chatbot widget itself.

The chatbot widget ( chat-widget ) is initially hidden and will be toggled by a


button.

It includes areas for messages ( chat-messages ), an input field ( chatInput ), and


action buttons ( escalateButton , feedbackButton ).

A feedbackModal is included for collecting user ratings.

styles.css and script.js are linked to provide styling and interactivity.

3.2. Styling the Chatbot ( src/static/styles.css )

This CSS file will provide the visual styling for our web application and chatbot,
ensuring a clean, modern, and responsive design.

Create or replace the content of backend/src/static/styles.css with the following:


/* backend/src/static/styles.css */

/* Reset and Base Styles */


* {
margin: 0;
padding: 0;
box-sizing: border-box;
}

body {
font-family: -apple-system, BlinkMacSystemFont, \'Segoe UI\', Roboto,
Oxygen, Ubuntu, Cantarell, sans-serif;
line-height: 1.6;
color: #333;
background-color: #f8fafc;
}

/* App Container */
.app-container {
min-height: 100vh;
display: flex;
flex-direction: column;
}

/* Header */
.app-header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 2rem 0;
text-align: center;
}

.header-content h1 {
font-size: 2.5rem;
margin-bottom: 0.5rem;
font-weight: 700;
}

.header-content p {
font-size: 1.1rem;
opacity: 0.9;
}

/* Main Content */
.main-content {
flex: 1;
padding: 3rem 1rem;
}

.content-wrapper {
max-width: 1200px;
margin: 0 auto;
}

.welcome-section {
text-align: center;
margin-bottom: 3rem;
}

.welcome-section h2 {
font-size: 2rem;
margin-bottom: 1rem;
color: #2d3748;
}

.welcome-section p {
font-size: 1.1rem;
color: #718096;
margin-bottom: 2rem;
}

/* Features Grid */
.features-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 2rem;
margin-top: 2rem;
}

.feature-card {
background: white;
padding: 2rem;
border-radius: 12px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
text-align: center;
transition: transform 0.2s, box-shadow 0.2s;
}

.feature-card:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1);
}

.feature-card i {
font-size: 2.5rem;
color: #667eea;
margin-bottom: 1rem;
}

.feature-card h3 {
font-size: 1.3rem;
margin-bottom: 0.5rem;
color: #2d3748;
}

.feature-card p {
color: #718096;
}

/* Chat Widget */
.chat-widget {
position: fixed;
bottom: 20px;
right: 20px;
z-index: 1000;
}

/* Chat Toggle Button */


.chat-toggle {
width: 60px;
height: 60px;
border-radius: 50%;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border: none;
color: white;
font-size: 1.5rem;
cursor: pointer;
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
transition: all 0.3s ease;
position: relative;
display: flex;
align-items: center;
justify-content: center;
}

.chat-toggle:hover {
transform: scale(1.1);
box-shadow: 0 6px 20px rgba(102, 126, 234, 0.6);
}

.chat-badge {
position: absolute;
top: -5px;
right: -5px;
background: #e53e3e;
color: white;
border-radius: 50%;
width: 20px;
height: 20px;
font-size: 0.7rem;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
}

/* Chat Window */
.chat-window {
position: absolute;
bottom: 80px;
right: 0;
width: 350px;
height: 500px;
background: white;
border-radius: 12px;
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.15);
display: none;
flex-direction: column;
overflow: hidden;
}

.chat-window.active {
display: flex;
}

/* Chat Header */
.chat-header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 1rem;
display: flex;
justify-content: space-between;
align-items: center;
}
.chat-header-info {
display: flex;
align-items: center;
gap: 0.75rem;
}

.chat-header-info i {
font-size: 1.5rem;
}

.chat-header-info h4 {
margin: 0;
font-size: 1rem;
}

.status {
font-size: 0.8rem;
opacity: 0.9;
}

.status.online {
color: #68d391;
}

.chat-close {
background: none;
border: none;
color: white;
font-size: 1.2rem;
cursor: pointer;
padding: 0.25rem;
border-radius: 4px;
transition: background-color 0.2s;
}

.chat-close:hover {
background-color: rgba(255, 255, 255, 0.1);
}

/* Chat Messages */
.chat-messages {
flex: 1;
padding: 1rem;
overflow-y: auto;
display: flex;
flex-direction: column;
gap: 1rem;
}

.message {
display: flex;
gap: 0.75rem;
max-width: 85%;
}

.bot-message {
align-self: flex-start;
}

.user-message {
align-self: flex-end;
flex-direction: row-reverse;
}

.message-avatar {
width: 32px;
height: 32px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.9rem;
flex-shrink: 0;
}

.bot-message .message-avatar {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}

.user-message .message-avatar {
background: #e2e8f0;
color: #4a5568;
}

.message-content {
background: #f7fafc;
padding: 0.75rem 1rem;
border-radius: 12px;
position: relative;
}

.user-message .message-content {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}

.message-content p {
margin: 0;
font-size: 0.9rem;
line-height: 1.4;
}

.message-time {
font-size: 0.7rem;
opacity: 0.7;
margin-top: 0.25rem;
display: block;
}

/* Chat Input */
.chat-input-container {
padding: 1rem;
border-top: 1px solid #e2e8f0;
background: #f8fafc;
}

.chat-input-wrapper {
display: flex;
gap: 0.5rem;
margin-bottom: 0.75rem;
}

#chatInput {
flex: 1;
padding: 0.75rem;
border: 1px solid #e2e8f0;
border-radius: 8px;
font-size: 0.9rem;
outline: none;
transition: border-color 0.2s;
}

#chatInput:focus {
border-color: #667eea;
}

.send-button {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border: none;
color: white;
padding: 0.75rem;
border-radius: 8px;
cursor: pointer;
transition: opacity 0.2s;
}

.send-button:hover {
opacity: 0.9;
}

.send-button:disabled {
opacity: 0.5;
cursor: not-allowed;
}

/* Chat Actions */
.chat-actions {
display: flex;
gap: 0.5rem;
}

.action-button {
flex: 1;
background: white;
border: 1px solid #e2e8f0;
color: #4a5568;
padding: 0.5rem;
border-radius: 6px;
font-size: 0.8rem;
cursor: pointer;
transition: all 0.2s;
}

.action-button:hover {
background: #f7fafc;
border-color: #cbd5e0;
}

/* Modal */
.modal-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
display: none;
align-items: center;
justify-content: center;
z-index: 2000;
}

.modal-overlay.active {
display: flex;
}

.modal-content {
background: white;
border-radius: 12px;
width: 90%;
max-width: 400px;
max-height: 90vh;
overflow-y: auto;
}

.modal-header {
padding: 1.5rem;
border-bottom: 1px solid #e2e8f0;
display: flex;
justify-content: space-between;
align-items: center;
}

.modal-header h3 {
margin: 0;
color: #2d3748;
}

.modal-close {
background: none;
border: none;
font-size: 1.2rem;
cursor: pointer;
color: #718096;
padding: 0.25rem;
border-radius: 4px;
transition: background-color 0.2s;
}

.modal-close:hover {
background: #f7fafc;
}

.modal-body {
padding: 1.5rem;
}

/* Star Rating */
.rating-container {
text-align: center;
margin-bottom: 1.5rem;
}

.rating-container p {
margin-bottom: 1rem;
color: #4a5568;
}
.star-rating {
display: flex;
justify-content: center;
gap: 0.25rem;
margin-bottom: 1rem;
}

.star-rating i {
font-size: 1.5rem;
color: #e2e8f0;
cursor: pointer;
transition: color 0.2s;
}

.star-rating i:hover,
.star-rating i.active {
color: #ffd700;
}

#feedbackComment {
width: 100%;
padding: 0.75rem;
border: 1px solid #e2e8f0;
border-radius: 8px;
font-family: inherit;
font-size: 0.9rem;
resize: vertical;
outline: none;
transition: border-color 0.2s;
margin-bottom: 1rem;
}

#feedbackComment:focus {
border-color: #667eea;
}

.submit-feedback {
width: 100%;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
padding: 0.75rem;
border-radius: 8px;
font-size: 0.9rem;
cursor: pointer;
transition: opacity 0.2s;
}

.submit-feedback:hover {
opacity: 0.9;
}

/* Loading Indicator */
.loading-indicator {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 3000;
display: none;
}
.loading-indicator.active {
display: block;
}

.spinner {
width: 40px;
height: 40px;
border: 4px solid #f3f3f3;
border-top: 4px solid #667eea;
border-radius: 50%;
animation: spin 1s linear infinite;
}

@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}

/* Responsive Design */
@media (max-width: 768px) {
.header-content h1 {
font-size: 2rem;
}

.main-content {
padding: 2rem 1rem;
}

.features-grid {
grid-template-columns: 1fr;
gap: 1.5rem;
}

.chat-window {
width: calc(100vw - 40px);
height: calc(100vh - 120px);
bottom: 80px;
right: 20px;
left: 20px;
}

.chat-widget {
right: 20px;
bottom: 20px;
}
}

@media (max-width: 480px) {


.header-content h1 {
font-size: 1.8rem;
}

.welcome-section h2 {
font-size: 1.5rem;
}

.feature-card {
padding: 1.5rem;
}

.chat-window {
width: calc(100vw - 20px);
height: calc(100vh - 100px);
bottom: 80px;
right: 10px;
left: 10px;
}
}

/* Scrollbar Styling */
.chat-messages::-webkit-scrollbar {
width: 6px;
}

.chat-messages::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 3px;
}

.chat-messages::-webkit-scrollbar-thumb {
background: #c1c1c1;
border-radius: 3px;
}

.chat-messages::-webkit-scrollbar-thumb:hover {
background: #a8a8a8;
}

/* Animation for new messages */


@keyframes slideInUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}

.message.new {
animation: slideInUp 0.3s ease-out;
}

/* Typing indicator */
.typing-indicator {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.75rem 1rem;
background: #f7fafc;
border-radius: 12px;
margin: 0.5rem 0;
}

.typing-dots {
display: flex;
gap: 0.25rem;
}

.typing-dots span {
width: 6px;
height: 6px;
background: #cbd5e0;
border-radius: 50%;
animation: typing 1.4s infinite ease-in-out;
}

.typing-dots span:nth-child(1) { animation-delay: -0.32s; }


.typing-dots span:nth-child(2) { animation-delay: -0.16s; }

@keyframes typing {
0%, 80%, 100% {
transform: scale(0.8);
opacity: 0.5;
}
40% {
transform: scale(1);
opacity: 1;
}
}

Explanation:

The CSS provides a clean, modern aesthetic with a purple-to-blue gradient for
headers and buttons.

It defines styles for the main application layout, feature cards, and the chatbot
widget.

Responsive design is implemented using media queries to ensure the layout


adapts to different screen sizes (desktops, tablets, and mobile phones).

Animations are included for new messages and a typing indicator to enhance the
user experience.

3.3. Adding Interactivity ( src/static/script.js )

This JavaScript file will handle all the dynamic behavior of our chatbot, including
opening/closing the chat window, sending messages, displaying responses, and
managing feedback.

Create or replace the content of backend/src/static/script.js with the following:


// backend/src/static/script.js

// Chatbot Application JavaScript

class ChatbotApp {
constructor() {
this.sessionId = this.generateSessionId();
this.isTyping = false;
this.currentRating = 0;

this.initializeElements();
this.bindEvents();
this.initializeChat();
}

generateSessionId() {
return \'session_\' + Date.now() + \'_\' +
Math.random().toString(36).substr(2, 9);
}

initializeElements() {
// Chat widget elements
this.chatToggle = document.getElementById(\'chatToggle\');
this.chatWindow = document.getElementById(\'chatWindow\');
this.chatClose = document.getElementById(\'chatClose\');
this.chatMessages = document.getElementById(\'chatMessages\');
this.chatInput = document.getElementById(\'chatInput\');
this.sendButton = document.getElementById(\'sendButton\');
this.chatBadge = document.getElementById(\'chatBadge\');

// Action buttons
this.escalateButton = document.getElementById(\'escalateButton\');
this.feedbackButton = document.getElementById(\'feedbackButton\');

// Modal elements
this.feedbackModal = document.getElementById(\'feedbackModal\');
this.closeFeedback = document.getElementById(\'closeFeedback\');
this.starRating = document.getElementById(\'starRating\');
this.feedbackComment = document.getElementById(\'feedbackComment\');
this.submitFeedback = document.getElementById(\'submitFeedback\');

// Loading indicator
this.loadingIndicator = document.getElementById(\'loadingIndicator\');
}

bindEvents() {
// Chat toggle
this.chatToggle.addEventListener(\'click\', () => this.toggleChat());
this.chatClose.addEventListener(\'click\', () => this.closeChat());

// Message sending
this.sendButton.addEventListener(\'click\', () => this.sendMessage());
this.chatInput.addEventListener(\'keypress\', (e) => {
if (e.key === \'Enter\' && !e.shiftKey) {
e.preventDefault();
this.sendMessage();
}
});

// Input validation
this.chatInput.addEventListener(\'input\', () => this.validateInput());
// Action buttons
this.escalateButton.addEventListener(\'click\', () =>
this.escalateToHuman());
this.feedbackButton.addEventListener(\'click\', () =>
this.openFeedbackModal());

// Feedback modal
this.closeFeedback.addEventListener(\'click\', () =>
this.closeFeedbackModal());
this.feedbackModal.addEventListener(\'click\', (e) => {
if (e.target === this.feedbackModal) {
this.closeFeedbackModal();
}
});

// Star rating
this.starRating.addEventListener(\'click\', (e) =>
this.handleStarRating(e));
this.starRating.addEventListener(\'mouseover\', (e) =>
this.highlightStars(e));
this.starRating.addEventListener(\'mouseout\', () =>
this.resetStarHighlight());

// Submit feedback
this.submitFeedback.addEventListener(\'click\', () =>
this.submitFeedbackForm());

// Escape key to close modals


document.addEventListener(\'keydown\', (e) => {
if (e.key === \'Escape\') {
this.closeFeedbackModal();
this.closeChat();
}
});
}

initializeChat() {
// Hide the notification badge initially
this.chatBadge.style.display = \'none\';

// Set initial input state


this.validateInput();
}

toggleChat() {
const isActive = this.chatWindow.classList.contains(\'active\');
if (isActive) {
this.closeChat();
} else {
this.openChat();
}
}

openChat() {
this.chatWindow.classList.add(\'active\');
this.chatBadge.style.display = \'none\';
this.chatInput.focus();
this.scrollToBottom();
}

closeChat() {
this.chatWindow.classList.remove(\'active\');
}

validateInput() {
const message = this.chatInput.value.trim();
this.sendButton.disabled = !message || this.isTyping;
}

async sendMessage() {
const message = this.chatInput.value.trim();
if (!message || this.isTyping) return;

// Add user message to chat


this.addMessage(message, \'user\');
this.chatInput.value = \'\';
this.validateInput();

// Show typing indicator


this.showTypingIndicator();

try {
// Send message to backend
const response = await this.callChatAPI(message);

// Remove typing indicator


this.hideTypingIndicator();

// Add bot response


this.addMessage(response.response, \'bot\');

// Handle escalation if needed


if (response.escalated) {
this.handleEscalation();
}

} catch (error) {
console.error(\'Error sending message:\', error);
this.hideTypingIndicator();
this.addMessage(\'Sorry, I encountered an error. Please try
again.\', \'bot\', true);
}
}

async callChatAPI(message) {
const response = await fetch(\'/api/chat\', {
method: \'POST\',
headers: {
\'Content-Type\': \'application/json\',
},
body: JSON.stringify({
message: message,
session_id: this.sessionId
})
});

if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}

return await response.json();


}
addMessage(text, sender, isError = false) {
const messageDiv = document.createElement(\'div\');
messageDiv.className = `message ${sender}-message new`;

const avatarDiv = document.createElement(\'div\');


avatarDiv.className = \'message-avatar\';
avatarDiv.innerHTML = sender === \'bot\' ? \'<i class="fas fa-robot">
</i>\' : \'<i class="fas fa-user"></i>\';

const contentDiv = document.createElement(\'div\');


contentDiv.className = \'message-content\';
if (isError) {
contentDiv.style.background = \'#fed7d7\';
contentDiv.style.color = \'#c53030\';
}

const textP = document.createElement(\'p\');


textP.textContent = text;

const timeSpan = document.createElement(\'span\');


timeSpan.className = \'message-time\';
timeSpan.textContent = this.formatTime(new Date());

contentDiv.appendChild(textP);
contentDiv.appendChild(timeSpan);
messageDiv.appendChild(avatarDiv);
messageDiv.appendChild(contentDiv);

this.chatMessages.appendChild(messageDiv);
this.scrollToBottom();

// Remove animation class after animation completes


setTimeout(() => {
messageDiv.classList.remove(\'new\');
}, 300);
}

showTypingIndicator() {
this.isTyping = true;
this.validateInput();

const typingDiv = document.createElement(\'div\');


typingDiv.className = \'message bot-message typing-indicator\';
typingDiv.innerHTML = `
<div class="message-avatar">
<i class="fas fa-robot"></i>
</div>
<div class="message-content">
<div class="typing-dots">
<span></span>
<span></span>
<span></span>
</div>
</div>
`;

this.chatMessages.appendChild(typingDiv);
this.scrollToBottom();
}

hideTypingIndicator() {
this.isTyping = false;
this.validateInput();

const typingIndicator = this.chatMessages.querySelector(\'.typing-


indicator\');
if (typingIndicator) {
typingIndicator.remove();
}
}

async escalateToHuman() {
try {
this.showLoading();

const response = await fetch(\'/api/escalate\', {


method: \'POST\',
headers: {
\'Content-Type\': \'application/json\',
},
body: JSON.stringify({
session_id: this.sessionId,
reason: \'User requested human agent\'
})
});

this.hideLoading();

if (response.ok) {
const data = await response.json();
this.addMessage(data.message, \'bot\');
this.addMessage(`Escalation ID: ${data.escalation_id}`,
\'bot\');
this.addMessage(`Estimated wait time: ${data.estimated_wait}`,
\'bot\');
} else {
throw new Error(\'Escalation failed\');
}

} catch (error) {
console.error(\'Error escalating:\', error);
this.hideLoading();
this.addMessage(\'Sorry, I couldn\\\'t connect you to a human agent
right now. Please try again later.\', \'bot\', true);
}
}

handleEscalation() {
// Add visual indicator that escalation is happening
this.escalateButton.innerHTML = \'<i class="fas fa-clock"></i>
Escalated\';
this.escalateButton.disabled = true;
this.escalateButton.style.background = \'#fed7d7\';
this.escalateButton.style.color = \'#c53030\';
}

openFeedbackModal() {
this.feedbackModal.classList.add(\'active\');
this.currentRating = 0;
this.resetStarHighlight();
this.feedbackComment.value = \'\';
}

closeFeedbackModal() {
this.feedbackModal.classList.remove(\'active\');
}

handleStarRating(e) {
if (e.target.tagName === \'I\' && e.target.dataset.rating) {
this.currentRating = parseInt(e.target.dataset.rating);
this.updateStarDisplay();
}
}

highlightStars(e) {
if (e.target.tagName === \'I\' && e.target.dataset.rating) {
const rating = parseInt(e.target.dataset.rating);
const stars = this.starRating.querySelectorAll(\'i\');
stars.forEach((star, index) => {
if (index < rating) {
star.style.color = \'#ffd700\';
} else {
star.style.color = \'#e2e8f0\';
}
});
}
}

resetStarHighlight() {
this.updateStarDisplay();
}

updateStarDisplay() {
const stars = this.starRating.querySelectorAll(\'i\');
stars.forEach((star, index) => {
if (index < this.currentRating) {
star.style.color = \'#ffd700\';
star.classList.add(\'active\');
} else {
star.style.color = \'#e2e8f0\';
star.classList.remove(\'active\');
}
});
}

async submitFeedbackForm() {
if (this.currentRating === 0) {
alert(\'Please select a rating before submitting.\');
return;
}

try {
this.showLoading();

const response = await fetch(\'/api/feedback\', {


method: \'POST\',
headers: {
\'Content-Type\': \'application/json\',
},
body: JSON.stringify({
session_id: this.sessionId,
rating: this.currentRating,
comment: this.feedbackComment.value.trim()
})
});
this.hideLoading();

if (response.ok) {
const data = await response.json();
this.closeFeedbackModal();
this.addMessage(\'Thank you for your feedback! It helps us
improve our service.\', \'bot\');

// Disable feedback button after submission


this.feedbackButton.innerHTML = \'<i class="fas fa-check"></i>
Submitted\';
this.feedbackButton.disabled = true;
this.feedbackButton.style.background = \'#c6f6d5\';
this.feedbackButton.style.color = \'#22543d\';
} else {
throw new Error(\'Feedback submission failed\');
}

} catch (error) {
console.error(\'Error submitting feedback:\', error);
this.hideLoading();
alert(\'Sorry, there was an error submitting your feedback. Please
try again.\');
}
}

showLoading() {
this.loadingIndicator.classList.add(\'active\');
}

hideLoading() {
this.loadingIndicator.classList.remove(\'active\');
}

scrollToBottom() {
setTimeout(() => {
this.chatMessages.scrollTop = this.chatMessages.scrollHeight;
}, 100);
}

formatTime(date) {
return date.toLocaleTimeString([], { hour: \'2-digit\', minute: \'2-
digit\' });
}
}

// Initialize the chatbot when the page loads


document.addEventListener(\'DOMContentLoaded\', () => {
window.chatbot = new ChatbotApp();
});

// Add some utility functions for potential future use


window.ChatbotUtils = {
// Function to programmatically send a message (for testing or integration)
sendMessage: (message) => {
if (window.chatbot) {
window.chatbot.chatInput.value = message;
window.chatbot.sendMessage();
}
},

// Function to open the chat programmatically


openChat: () => {
if (window.chatbot) {
window.chatbot.openChat();
}
},

// Function to get conversation history (for debugging)


getConversationHistory: () => {
if (window.chatbot) {
const messages =
window.chatbot.chatMessages.querySelectorAll(\'.message:not(.typing-
indicator)\');
return Array.from(messages).map(msg => {
const isBot = msg.classList.contains(\'bot-message\');
const text = msg.querySelector(\'p\').textContent;
const time = msg.querySelector(\'.message-time\').textContent;
return { sender: isBot ? \'bot\' : \'user\', text, time };
});
}
return [];
}
};

Explanation:

The ChatbotApp class encapsulates all the chatbot's frontend logic.

It handles UI element selection, event binding (click, keypress, input), and


managing the chat window's state.

sendMessage() sends user input to the Flask backend's /api/chat endpoint


and displays the AI's response.

escalateToHuman() sends a request to the /api/escalate endpoint.

openFeedbackModal() and submitFeedbackForm() manage the feedback


modal and send feedback to the /api/feedback endpoint.

showTypingIndicator() and hideTypingIndicator() provide visual cues


when the AI is processing a response.

addMessage() dynamically adds new messages to the chat interface.

generateSessionId() creates a unique session ID for each user, which is


important for tracking conversations.

4. Integrating Frontend and Backend

The frontend and backend are already integrated through the API calls made in
script.js to the Flask endpoints. The Flask application serves the index.html ,
styles.css , and script.js files from its static directory. When the script.js
makes fetch requests to /api/chat , /api/escalate , or /api/feedback , these
requests are routed to the corresponding Flask endpoints we defined.

4.1. Cross-Origin Resource Sharing (CORS)

CORS is a security feature implemented in web browsers that prevents a web page
from making requests to a different domain than the one that served the web page.
Since our Flask backend and frontend are served from the same origin
(localhost:5001), we don't strictly need CORS for local development. However, it's
good practice to include Flask-CORS as we did in src/main.py :

# In src/main.py
from flask_cors import CORS
...
app = Flask(__name__, static_folder=os.path.join(os.path.dirname(__file__),
\'static\'))
CORS(app) # Enable CORS for all routes

This ensures that if you were to deploy your frontend and backend on different
domains in the future, they would still be able to communicate without issues.

5. AI Logic and Database Interaction

Our AI logic is currently a placeholder based on keyword matching and an FAQ


database. This provides a functional chatbot without requiring complex external AI
model integrations initially. We will also set up a script to populate our FAQ database.

5.1. Populating the FAQ Database ( populate_sample_data.py )

To make our chatbot functional from the start, we need to populate the FAQ table in
our database with some sample questions and answers. This script will do exactly
that.

Create the file backend/populate_sample_data.py at the root of your backend


directory (next to src and requirements.txt ) with the following content:
# backend/populate_sample_data.py

#!/usr/bin/env python3
"""
Script to populate the database with sample FAQ data for the chatbot
"""

import sys
import os

# Add the project root to the Python path


sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))

from src.main import app


from src.models.user import db
from src.models.conversation import FAQ

def populate_sample_faqs():
"""Populate the database with sample FAQ data"""

sample_faqs = [
{
\'question\': \'How do I track my order?\',
\'answer\': \'You can track your order by entering your order
number on our tracking page. You can find your order number in the confirmation
email we sent you.\',
\'category\': \'Orders\',
\'keywords\': \'track,order,tracking,status,where is my order\'
},
{
\'question\': \'What is your return policy?\',
\'answer\': \'We offer a 30-day return policy for most items. Items
must be in original condition with tags attached. Please contact our support
team to initiate a return.\',
\'category\': \'Returns\',
\'keywords\': \'return,refund,exchange,policy,money back\'
},
{
\'question\': \'How long does shipping take?\',
\'answer\': \'Standard shipping takes 5-7 business days, while
express shipping takes 2-3 business days. International shipping may take 10-14
business days.\',
\'category\': \'Shipping\',
\'keywords\': \'shipping,delivery,how long,when will,arrive\'
},
{
\'question\': \'What payment methods do you accept?\',
\'answer\': \'We accept all major credit cards (Visa, MasterCard,
American Express), PayPal, Apple Pay, and Google Pay.\',
\'category\': \'Payment\',
\'keywords\': \'payment,pay,credit card,paypal,billing,checkout\'
},
{
\'question\': \'How do I reset my password?\',
\'answer\': \'Click on "Forgot Password" on the login page and
enter your email address. We\\\'ll send you a link to reset your password.\',
\'category\': \'Account\',
\'keywords\': \'password,reset,forgot,login,account,access\'
},
{
\'question\': \'Do you offer international shipping?\',
\'answer\': \'Yes, we ship to most countries worldwide. Shipping
costs and delivery times vary by destination. International orders may be
subject to customs duties.\',
\'category\': \'Shipping\',
\'keywords\': \'international,worldwide,global,shipping,countries\'
},
{
\'question\': \'How can I contact customer support?\',
\'answer\': \'You can contact our customer support team via email
at support@example.com, phone at 1-800-123-4567, or through this chat
system.\',
\'category\': \'Support\',
\'keywords\': \'contact,support,help,phone,email,customer service\'
},
{
\'question\': \'What sizes do you offer?\',
\'answer\': \'We offer sizes XS through XXL for most clothing
items. Please check the size chart on each product page for specific
measurements.\',
\'category\': \'Products\',
\'keywords\': \'size,sizing,measurements,fit,chart\'
},
{
\'question\': \'Can I cancel my order?\',
\'answer\': \'You can cancel your order within 1 hour of placing
it. After that, the order may have already been processed for shipping.\',
\'category\': \'Orders\',
\'keywords\': \'cancel,cancellation,stop,order,change\'
},
{
\'question\': \'Do you have a loyalty program?\',
\'answer\': \'Yes! Our loyalty program offers points for every
purchase, exclusive discounts, and early access to sales. Sign up for free on
your account page.\',
\'category\': \'Rewards\',
\'keywords\': \'loyalty,rewards,points,program,discounts,benefits\'
}
]

with app.app_context():
# Clear existing FAQs (optional)
print("Clearing existing FAQs...")
FAQ.query.delete()

# Add sample FAQs


print("Adding sample FAQs...")
for faq_data in sample_faqs:
faq = FAQ(
question=faq_data[\'question\'],
answer=faq_data[\'answer\'],
category=faq_data[\'category\'],
keywords=faq_data[\'keywords\']
)
db.session.add(faq)

# Commit changes
db.session.commit()
print(f"Successfully added {len(sample_faqs)} FAQs to the database!")

# Verify the data


total_faqs = FAQ.query.count()
print(f"Total FAQs in database: {total_faqs}")
# Show categories
categories = db.session.query(FAQ.category).distinct().all()
print(f"Categories: {[cat[0] for cat in categories]}")

if __name__ == \'__main__\':
populate_sample_faqs()

Explanation:

This script defines a list of sample FAQs, each with a question, answer, category,
and keywords.

It uses the Flask application context to interact with the database.

FAQ.query.delete() is included to clear any existing FAQs before adding new


ones, useful for development.

It then iterates through the sample_faqs list, creates FAQ objects, and adds
them to the database.

Finally, it commits the changes and prints a confirmation message.

To run this script and populate your database, navigate to the backend directory in
your terminal (with your virtual environment activated) and execute:

python populate_sample_data.py

6. Testing and Verification

Before we consider our project complete, it's crucial to test all components to ensure
they are working as expected. We will perform both manual testing of the web
interface and verify the API endpoints directly.

6.1. Starting the Flask Application

First, ensure your Flask application is running. Navigate to your backend directory and
start the server:

cd backend
source venv/bin/activate
python src/main.py
The server should start on http://127.0.0.1:5001 (or http://localhost:5001 ).
Keep this terminal window open.

6.2. Manual Testing of the Chatbot Interface

Open your web browser and navigate to http://localhost:5001 . You should see the
main web application page with the chatbot widget in the bottom right corner.

Perform the following tests:

1. Open/Close Chat: Click the chat bubble to open and close the chat window.

2. Send Messages: Type various messages into the input field and click the send
button or press Enter. Observe the AI's responses.
Try questions from your FAQ list (e.g., "How do I track my order?", "What is
your return policy?").

Try questions not in your FAQ list (e.g., "Tell me a joke."). This should trigger
the escalation message.

3. Escalate to Human: Click the "Talk to Human" button and observe the
escalation message.

4. Submit Feedback: Click the "Rate Chat" button, select a star rating, add a
comment, and submit. Observe the confirmation message.

6.3. Verifying API Endpoints

You can use curl (a command-line tool for making HTTP requests) or a tool like
Postman/Insomnia to directly test your API endpoints. Open a new terminal window
(keep the Flask server running in the first one) and activate your virtual environment.

Test Health Check

curl http://localhost:5001/api/health

Expected output:
{
"database": "connected",
"service": "chatbot-api",
"status": "healthy",
"timestamp": "..."
}

Test Chat Endpoint

curl -X POST http://localhost:5001/api/chat \


-H "Content-Type: application/json" \
-d \'{"message": "How do I reset my password?", "session_id":
"test_session_1"}\'

Expected output (should be an FAQ response):

{
"ai_response": "Click on \"Forgot Password\" on the login page and enter your
email address. We\'ll send you a link to reset your password.",
"conversation_id": 1,
"escalated": false,
"response": "Click on \"Forgot Password\" on the login page and enter your
email address. We\'ll send you a link to reset your password.",
"timestamp": "..."
}

Test Escalation Endpoint

curl -X POST http://localhost:5001/api/escalate \


-H "Content-Type: application/json" \
-d \'{"session_id": "test_session_2", "reason": "Complex technical issue"}\'

Expected output:

{
"escalation_id": "ESC-...",

"estimated_wait": "5-10 minutes",


"message": "Your request has been escalated to a human agent. Someone will be
with you shortly."
}
Test Feedback Endpoint

curl -X POST http://localhost:5001/api/feedback \


-H "Content-Type: application/json" \
-d \'{"session_id": "test_session_3", "rating": 5, "comment": "Very
helpful!"}\'

Expected output:

{
"feedback_id": "FB-...",
"message": "Thank you for your feedback!"
}

Test Analytics Endpoint

curl http://localhost:5001/api/analytics

Expected output (values will vary based on your testing):

{
"average_rating": 5.0,
"escalation_rate": 33.33333333333333,
"recent_conversations_24h": 3,
"total_conversations": 3,
"total_feedback": 1,
"total_escalations": 1
}

7. Deployment and Next Steps

Congratulations! You have successfully built and tested your AI-powered customer
support chatbot. This section will discuss how to prepare your application for
deployment and explore potential next steps for enhancing its capabilities.

7.1. Preparing for Deployment

For a production environment, you would typically deploy your Flask application using
a production-ready web server like Gunicorn or uWSGI, and serve it behind a reverse
proxy like Nginx or Apache. The frontend static files would also be served by the web
server directly for better performance.

Here's a simplified overview of what a production setup might look like:


1. Install Production Server: Install Gunicorn: bash pip install gunicorn

2. Run Gunicorn: From your backend directory: bash gunicorn --workers 4 --


bind 0.0.0.0:5001 src.main:app This command starts Gunicorn with 4 worker
processes, binding to all network interfaces on port 5001, and serving your Flask
application ( app object from src.main module).

3. Reverse Proxy (Nginx/Apache): Configure Nginx or Apache to proxy requests to


your Gunicorn server and serve static files directly. This provides load balancing,
SSL termination, and better security.

4. Environment Variables: Instead of hardcoding sensitive information (like


SECRET_KEY or database connection strings), use environment variables. For
example, in src/main.py : python app.config["SECRET_KEY"] =
os.environ.get("FLASK_SECRET_KEY", "default_secret_key") Then, set
FLASK_SECRET_KEY in your deployment environment.

5. Database Management: For production, consider a more robust database like


PostgreSQL or MySQL instead of SQLite, especially if you anticipate high traffic or
need advanced features. SQLAlchemy makes it relatively easy to switch
databases by changing the SQLALCHEMY_DATABASE_URI .

6. Logging: Implement proper logging for errors and application events. Flask has
built-in logging capabilities that can be configured to write to files or external
logging services.

7.2. Future Enhancements and Advanced AI Integration

This chatbot provides a solid foundation. Here are some ideas for future
enhancements:

Advanced AI Models: Integrate with more sophisticated AI models for natural


language understanding (NLU) and generation (NLG). Options include:

OpenAI GPT (e.g., GPT-3.5, GPT-4): For highly conversational and context-
aware responses. You would replace the get_ai_response_from_db
function with calls to the OpenAI API.

Google Dialogflow: For building conversational interfaces with predefined


intents and entities.
Hugging Face Transformers: For using open-source NLU/NLG models
locally or on a server.

Rasa: An open-source conversational AI framework for building custom


chatbots.

User Authentication: Implement a full user authentication system to personalize


interactions, store user-specific conversation history, and manage user profiles.

Live Agent Handoff: Enhance the human escalation feature to integrate with a
live chat platform (e.g., Zendesk, LiveChat) where human agents can seamlessly
take over conversations.

Proactive Chat: Implement logic to proactively engage users based on their


browsing behavior or time spent on a page.

Multimedia Support: Allow the chatbot to send and receive images, videos, or
other media.

Sentiment Analysis: Analyze user sentiment to prioritize urgent or negative


interactions for human intervention.

Analytics Dashboard: Build a more comprehensive analytics dashboard to


visualize chatbot performance metrics, identify common queries, and track user
satisfaction.

Multi-language Support: Extend the chatbot to support multiple languages.

Voice Interface: Integrate with speech-to-text and text-to-speech services to


enable voice interactions.

Conclusion

Building an AI-powered chatbot is an iterative process. This guide has provided you
with a robust starting point, covering the essential components of a web-based
chatbot application. By understanding each part and exploring the suggested
enhancements, you can continue to develop and refine your chatbot to meet evolving
user needs and technological advancements. The combination of a flexible Flask
backend and an interactive JavaScript frontend provides a powerful platform for
creating engaging and intelligent conversational experiences.
References

Flask Documentation: https://flask.palletsprojects.com/

Flask-SQLAlchemy Documentation: https://flask-


sqlalchemy.palletsprojects.com/

Flask-CORS Documentation: https://flask-cors.readthedocs.io/

SQLAlchemy Documentation: https://docs.sqlalchemy.org/

MDN Web Docs (HTML, CSS, JavaScript): https://developer.mozilla.org/en-


US/docs/Web

Font Awesome Icons: https://fontawesome.com/

Author: Manus AI Date: August 5, 2025

You might also like