Développement Web

Comment créer un chatbot simple avec JavaScript

11 دقائق للقراءة

Prérequis

  • Niveau : bases JavaScript et manipulation du DOM. Notions fetch pour la version IA.
  • Outils : VS Code + Live Server. Pour la version IA : clé API OpenAI (à utiliser via un backend, jamais en front).
  • Temps estimé : 1 h 30.

Pourquoi un chatbot sur votre site ?

Un chatbot bien fait répond aux 80 % de questions répétitives (tarifs, horaires, contact) sans mobiliser votre équipe. Il qualifie aussi les leads avant de les transmettre. Un bot à règles couvre déjà beaucoup de cas ; le passage à l’IA n’est utile que si vos visiteurs posent des questions très ouvertes.

Un chatbot JavaScript en 100 lignes

Dans ce tutoriel, vous construisez un chatbot qui fonctionne directement dans le navigateur. Il reconnaît des mots-clés dans les messages de l’utilisateur et répond de manière contextuelle. Ensuite, on le connecte à l’API ChatGPT pour des réponses intelligentes. Tout en HTML, CSS et JavaScript.

L’interface du chat

<div class="chatbot">
  <div class="chat-header">
    <span class="status-dot"></span>
    <h3>Assistant ITSkillsCenter</h3>
  </div>
  <div class="chat-messages" id="messages"></div>
  <form class="chat-input" id="chatForm">
    <input type="text" id="userInput" placeholder="Tapez votre message..." autocomplete="off">
    <button type="submit">Envoyer</button>
  </form>
</div>
.chatbot {
  width: 380px;
  height: 500px;
  border-radius: 16px;
  box-shadow: 0 8px 32px rgba(0,0,0,0.15);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  font-family: -apple-system, sans-serif;
}

.chat-header {
  background: #4a90d9;
  color: white;
  padding: 16px;
  display: flex;
  align-items: center;
  gap: 10px;
}

.status-dot {
  width: 10px; height: 10px;
  background: #4caf50;
  border-radius: 50%;
}

.chat-messages {
  flex: 1;
  overflow-y: auto;
  padding: 16px;
  display: flex;
  flex-direction: column;
  gap: 12px;
  background: #f8f9fa;
}

.message {
  max-width: 80%;
  padding: 10px 14px;
  border-radius: 16px;
  font-size: 14px;
  line-height: 1.5;
}

.message.bot {
  background: white;
  align-self: flex-start;
  border-bottom-left-radius: 4px;
  box-shadow: 0 1px 2px rgba(0,0,0,0.08);
}

.message.user {
  background: #4a90d9;
  color: white;
  align-self: flex-end;
  border-bottom-right-radius: 4px;
}

.typing-indicator {
  display: flex;
  gap: 4px;
  padding: 10px 14px;
}

.typing-indicator span {
  width: 8px; height: 8px;
  background: #ccc;
  border-radius: 50%;
  animation: bounce 1.4s infinite ease-in-out both;
}

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

@keyframes bounce {
  0%, 80%, 100% { transform: scale(0); }
  40% { transform: scale(1); }
}

.chat-input {
  display: flex;
  padding: 12px;
  gap: 8px;
  background: white;
  border-top: 1px solid #eee;
}

.chat-input input {
  flex: 1;
  padding: 10px 14px;
  border: 1px solid #ddd;
  border-radius: 20px;
  font-size: 14px;
  outline: none;
}

.chat-input button {
  padding: 10px 20px;
  background: #4a90d9;
  color: white;
  border: none;
  border-radius: 20px;
  cursor: pointer;
}

Version 1 : chatbot à règles (sans IA)

const messages = document.getElementById('messages');
const chatForm = document.getElementById('chatForm');
const userInput = document.getElementById('userInput');

// Base de connaissances du bot
const responses = [
  { keywords: ['bonjour', 'salut', 'hello', 'bonsoir'],
    reply: 'Bonjour ! 👋 Comment puis-je vous aider ?' },
  { keywords: ['prix', 'tarif', 'combien', 'coût'],
    reply: 'Nos tarifs commencent à 150 000 FCFA pour un site vitrine. Souhaitez-vous un devis personnalisé ?' },
  { keywords: ['formation', 'cours', 'apprendre'],
    reply: 'Nous proposons des formations en développement web, marketing digital et IA. Quelle thématique vous intéresse ?' },
  { keywords: ['contact', 'joindre', 'appeler', 'téléphone'],
    reply: 'Vous pouvez nous contacter au +221 XX XXX XX XX ou par email à contact@itskillscenter.io' },
  { keywords: ['wordpress', 'site', 'web'],
    reply: 'Nous créons des sites WordPress sur mesure. Vitrine, e-commerce, blog — selon vos besoins. Quel type de site recherchez-vous ?' },
  { keywords: ['merci', 'thanks'],
    reply: 'Avec plaisir ! N\'hésitez pas si vous avez d\'autres questions. 😊' },
  { keywords: ['au revoir', 'bye', 'bonne journée'],
    reply: 'Au revoir ! Bonne journée et à bientôt sur ITSkillsCenter ! 👋' }
];

const defaultReply = 'Je ne suis pas sûr de comprendre. Pouvez-vous reformuler ? Vous pouvez me demander nos tarifs, formations ou comment nous contacter.';

function findResponse(input) {
  const lower = input.toLowerCase();
  const match = responses.find(r => 
    r.keywords.some(kw => lower.includes(kw))
  );
  return match ? match.reply : defaultReply;
}

function addMessage(text, sender) {
  const div = document.createElement('div');
  div.className = 'message ' + sender;
  div.textContent = text;
  messages.appendChild(div);
  messages.scrollTop = messages.scrollHeight;
}

function showTyping() {
  const typing = document.createElement('div');
  typing.className = 'message bot typing-indicator';
  typing.id = 'typing';
  typing.innerHTML = '<span></span><span></span><span></span>';
  messages.appendChild(typing);
  messages.scrollTop = messages.scrollHeight;
}

function hideTyping() {
  const typing = document.getElementById('typing');
  if (typing) typing.remove();
}

chatForm.addEventListener('submit', (e) => {
  e.preventDefault();
  const text = userInput.value.trim();
  if (!text) return;
  
  addMessage(text, 'user');
  userInput.value = '';
  
  showTyping();
  
  // Simuler un délai de réponse
  setTimeout(() => {
    hideTyping();
    addMessage(findResponse(text), 'bot');
  }, 800 + Math.random() * 700);
});

// Message de bienvenue
addMessage('Bonjour ! Je suis l\'assistant ITSkillsCenter. Comment puis-je vous aider ?', 'bot');

Version 2 : connecter à l’API ChatGPT

// Remplacer findResponse par un appel API
async function getAIResponse(userMessage) {
  const response = await fetch('https://api.openai.com/v1/chat/completions', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': 'Bearer VOTRE_CLE_API'  // ⚠️ Ne jamais mettre en front-end !
    },
    body: JSON.stringify({
      model: 'gpt-4o-mini',
      messages: [
        { role: 'system', content: 'Tu es l\'assistant d\'ITSkillsCenter, un centre de formation IT à Dakar. Réponds en français, de manière concise et amicale.' },
        { role: 'user', content: userMessage }
      ],
      max_tokens: 150
    })
  });
  
  const data = await response.json();
  return data.choices[0].message.content;
}

⚠️ Important : ne mettez JAMAIS votre clé API dans le code front-end — elle serait visible par tous. Créez un petit backend (Node.js, PHP, ou une fonction serverless) qui fait l’appel API côté serveur.

Erreurs fréquentes

Clé API OpenAI exposée dans le front-end

Cause : on met 'Authorization': 'Bearer sk-...' dans le JS livré au navigateur — visible par tous, facturable à l’infini.
Solution : créez un endpoint backend (Node.js, PHP, Cloudflare Workers, Vercel Function) qui ajoute la clé côté serveur. Votre front appelle votre backend.

Erreur 400 « Invalid request » avec OpenAI

Cause : le payload utilise rôle avec accent au lieu de role. L’API attend strictement les clés en anglais : role, content, messages, model.
Solution : utilisez exclusivement { role: 'user', content: '...' }.

Bot qui ne reconnaît pas les variantes de mots

Cause : détection par includes sensible aux accents et à la casse.
Solution : normalisez l’entrée avec input.toLowerCase().normalize('NFD').replace(/\p{Diacritic}/gu, '') avant la comparaison.

Conversation perdue au rechargement de page

Cause : rien n’est sauvegardé.
Solution : sérialisez l’historique dans localStorage à chaque message, et restaurez-le au chargement.

Exercice

  1. Ajoutez des boutons de réponse rapide (« Tarifs », « Formations », « Contact ») sous les messages du bot
  2. Ajoutez un indicateur « En ligne » / « Hors ligne » basé sur l’heure
  3. Sauvegardez l’historique de conversation dans localStorage
  4. Ajoutez un bouton flottant en bas à droite pour ouvrir/fermer le chat

Pour étoffer le tableau

Choisir entre OpenAI, Anthropic et un modèle local pour son chatbot

Le choix du moteur conversationnel dépend de trois critères : qualité des réponses, coût opérationnel, contraintes de confidentialité. OpenAI GPT-4o reste rapide et compétitif, Claude Sonnet 4.6 excelle sur le raisonnement long et le français nuancé. Pour des données très sensibles (santé, juridique), un modèle local comme Llama 3.3 70B sur un serveur dédié évite l’envoi vers un tiers.

Pour une PME basée à Plateau qui anime un chatbot service client, la matrice simple : trafic faible (moins de 1 000 conversations/mois) → API publique (OpenAI ou Anthropic). Trafic élevé et budget serré → quantization Llama sur VPS dédié. Données réglementées → modèle local mandatory. La plupart des sites peuvent démarrer avec l’API et basculer plus tard si nécessaire.

// Connexion API Anthropic minimale
const response = await fetch('https://api.anthropic.com/v1/messages', {
  method: 'POST',
  headers: {
    'x-api-key': process.env.ANTHROPIC_API_KEY,
    'anthropic-version': '2023-06-01',
    'content-type': 'application/json'
  },
  body: JSON.stringify({
    model: 'claude-sonnet-4-6',
    max_tokens: 512,
    messages: [{role: 'user', content: userMessage}]
  })
});

Coût indicatif Claude Sonnet : 3 USD/M tokens entrée, 15 USD/M tokens sortie. Sur 1 000 conversations de 500 tokens entrée + 200 tokens sortie, vous payez environ 4,50 USD (≈ 2 700 FCFA). Pour une boutique de Sicap Liberté, c’est un coût ridicule pour la valeur créée.

Mémoriser le contexte sur plusieurs tours sans saturer le quota

Un chatbot qui oublie ce que vous avez dit il y a 30 secondes frustre instantanément. Mais retransmettre tout l’historique à chaque tour explose le coût en tokens. La technique 2026 : maintenir un buffer rolling de 6-10 derniers messages côté client, et résumer les échanges anciens en un seul paragraphe injecté en début de prompt.

const historique = [];
function ajouterMessage(role, content) {
  historique.push({role, content});
  if (historique.length > 10) {
    // Garder les 6 derniers + un résumé des plus anciens
    historique.splice(0, historique.length - 6);
  }
}

Pour les conversations vraiment longues (support technique sur plusieurs jours), stockez l’intégralité côté serveur dans une base PostgreSQL avec embeddings. Au début de chaque session, faites un retrieval des 5 messages les plus pertinents par similarité sémantique. Ce pattern de chat avec mémoire longue est devenu standard sur les grandes plateformes en 2025-2026.

Modérer les réponses pour éviter les dérapages

Un chatbot livré sans modération est un chatbot qui finira par insulter un client ou produire un avis juridique inapproprié. Anthropic et OpenAI proposent des modes de modération automatique, mais une couche supplémentaire côté serveur protège mieux. Le pattern : avant chaque réponse renvoyée à l’utilisateur, vérifier qu’elle ne contient pas de PII non sollicitées, pas de promesses non autorisées (remboursement automatique, garantie de résultat), pas de langage inapproprié.

async function moderer(reponse) {
  // Test 1 : numéros de carte bancaire
  if (/\b\d{13,19}\b/.test(reponse)) return false;
  // Test 2 : promesses interdites
  const interdits = ['je vous rembourse', 'je vous garantis', 'votre commande sera livrée demain'];
  if (interdits.some(p => reponse.toLowerCase().includes(p))) return false;
  return true;
}

Si la modération bloque une réponse, le chatbot affiche un fallback poli (« Je transmets votre demande à un opérateur humain ») et déclenche une alerte WhatsApp Business à l’équipe. Cette discipline préserve la marque sans tuer l’expérience.

Mesurer la performance du chatbot en KPI métier

Un chatbot ne se mesure pas en nombre de conversations mais en taux de résolution sans escalade humaine. Pour un freelance à Cocody qui automatise son SAV, l’objectif réaliste est 60-75 % de résolution sans intervention. Au-dessus de 75 %, vous frôlez l’overpromising et risquez la frustration client. En dessous de 50 %, le chatbot ne sert à rien et coûte plus cher en tokens qu’il n’économise en temps humain.

Tenez un dashboard simple : conversations totales, taux de résolution, satisfaction post-conversation (étoiles de 1 à 5), temps moyen par conversation, coût mensuel API. Quand le coût dépasse 10 % du salaire d’un agent humain, basculez les questions complexes vers l’humain et limitez le bot aux FAQ simples.

Préparer le chatbot à intégrer WhatsApp Business à terme

Le navigateur reste un canal mineur pour les PME ouest-africaines. WhatsApp Business est le vrai canal commercial. Concevez votre chatbot avec une couche d’abstraction qui permet de basculer du widget web vers WhatsApp Cloud API sans réécrire la logique métier.

class CanalAdapter {
  async envoyerMessage(destinataire, contenu) { throw 'override'; }
}
class CanalWeb extends CanalAdapter { /* WebSocket */ }
class CanalWhatsApp extends CanalAdapter { /* Cloud API */ }

Cette séparation permet de réutiliser 80-90 % du code chatbot quand vous lancez la version WhatsApp. À lire ensuite, voir notre tutoriel WhatsApp Cloud API et chatbot WhatsApp Claude pour PME.

Tester le chatbot avant le lancement public

Un chatbot livré en production sans tests devient un terrain de jeu pour les utilisateurs qui le poussent dans ses retranchements. Le minimum viable : 30 conversations de test couvrant les 10 cas d’usage légitimes, 10 cas limites (questions hors périmètre) et 10 tentatives d’attaque (prompt injection, jailbreak, abuse).

Ces tests ne sont pas un harnais automatisé sophistiqué. C’est simplement un fichier Markdown avec un tableau : intention utilisateur, réponse attendue, comportement constaté. Vous le complétez à la main pendant 2 heures avant le lancement, puis vous corrigez les écarts. Cette discipline économise des semaines de fix en post-launch sur des bugs qu’un utilisateur réel découvrirait en 30 secondes.

Et un dernier rappel pratique : versionnez les prompts système dans Git comme du code. Une régression de prompt qui dégrade la qualité des réponses devient ainsi traçable et réversible en 30 secondes via git revert.

Cette pratique professionnalise votre opération conversationnelle et vous prépare à grandir sans douleur.

مشاركة