La semaine dernière, j'ai ajouté un chatbot IA à ce site. Pas un widget de support avec des réponses préfabriquées, mais un véritable agent conversationnel qui connaît mon CV, mes articles de blog, mes opinions tech et mes hobbies — et peut en discuter dans n'importe quelle langue. De l'idée à la production, ça a pris environ une heure. Voici exactement comment je l'ai construit, quelles décisions j'ai prises et ce que je ferais différemment.
Le chatbot que vous voyez en bas à droite de cette page est propulsé par Claude Haiku, le modèle le plus rapide et le moins cher d'Anthropic. Il fonctionne via une seule API route Next.js, n'a pas de base de données, pas de dépendance SDK, pas de vector store et pas de LangChain. C'est environ 140 lignes de code serveur et 230 lignes de code client. C'est tout.
L'architecture : délibérément simple
L'ensemble du backend est un seul fichier : une API route à /api/chat. Elle reçoit l'historique de conversation du client, préfixe un system prompt avec tout ce que le chatbot doit savoir, et le transmet à l'API Messages de Claude via un appel fetch brut. Pas de SDK, pas de bibliothèque wrapper, pas de couche d'abstraction.
Pourquoi pas de SDK ? Parce que l'API Messages d'Anthropic est un seul endpoint POST avec un contrat JSON propre. Ajouter @anthropic-ai/sdk apporterait des dépendances dont je n'ai pas besoin pour ce qui est essentiellement un appel fetch avec trois headers. C'est la Règle de Moindre Puissance en pratique — utiliser la technologie la plus simple qui résout le problème.
Le client est un composant React avec useState pour les messages et un formulaire qui poste vers l'API route. Les messages sont stockés dans le state du composant — pas de Redux, pas de context provider, pas de bibliothèque de gestion d'état. Quand l'utilisateur ferme le chat, la conversation disparaît. C'est acceptable pour un chatbot de portfolio.
Le system prompt : votre chatbot n'est aussi bon que son briefing
C'est là que la plupart des tutoriels de chatbot IA s'arrêtent trop tôt. Ils vous montrent comment appeler l'API et afficher la réponse. Mais le system prompt est le vrai produit. C'est ce qui rend votre chatbot utile plutôt que générique.
Mon system prompt fait environ 3 000 tokens. Il contient : mon historique de carrière complet (9 postes, chronologique), ma formation (MAS Software Engineering, CAS Frontend Engineering), mes compétences techniques, mes théories de management (fenêtre de Johari, 9 Box Grid, PAC), des résumés de mes 10 articles de blog avec leurs arguments clés, mes hobbies et des règles de confidentialité strictes.
Les règles de confidentialité sont critiques. Le system prompt dit explicitement : ne jamais révéler mon adresse, mon numéro de téléphone, ma date de naissance ou mon état civil. Si on demande, rediriger vers l'email ou LinkedIn. J'ai testé ça de manière approfondie — Claude respecte ces limites de manière fiable. Mais vous devriez tester vos propres garde-fous. Les LLMs peuvent être créatifs pour laisser fuiter des informations si le prompt n'est pas assez explicite.
Rate limiting : protéger votre portefeuille
Un chatbot IA sans rate limiting est une carte de crédit attachée à un endpoint public. Chaque message coûte de l'argent — pas beaucoup avec Haiku, mais ça s'accumule si quelqu'un décide de scripter 10 000 requêtes.
J'ai implémenté un simple rate limiter en mémoire : 800 messages par adresse IP par fenêtre de 12 heures. C'est une Map qui stocke les compteurs et les timestamps de réinitialisation. Quand une requête arrive, elle vérifie la map, incrémente le compteur et renvoie 429 si la limite est dépassée.
Le rate limiting en mémoire est-il parfait ? Non. Il se réinitialise quand la fonction serverless démarre à froid, et il ne partage pas l'état entre les instances. Pour un site portfolio, c'est suffisant. Si j'avais besoin d'un rate limiting de niveau production, j'utiliserais Vercel KV ou Upstash Redis. Mais ça ajouterait une dépendance et une connexion de service pour un problème que je n'ai pas encore. YAGNI.
Historique de conversation : rendre le chat vraiment conversationnel
La première version du chatbot était sans état — chaque message était envoyé à Claude de manière isolée, sans mémoire des messages précédents. Ça fonctionnait, mais ça semblait cassé. « Qu'est-ce que tu voulais dire par là ? » obtenait une réponse confuse parce que « là » n'avait pas de référent.
La solution était simple : envoyer l'historique complet de la conversation avec chaque requête. Le client maintient un tableau de messages, et à chaque envoi, il envoie le tableau complet à l'API route, qui le transmet à Claude. La fenêtre de contexte de Claude fait le reste.
J'ai plafonné l'historique à 50 messages par requête pour prévenir les abus. En pratique, personne n'a une conversation de 50 messages avec un chatbot de portfolio, mais la limite empêche quelqu'un d'envoyer un payload avec des milliers de messages fabriqués.
Rendu markdown : blocs de code, coloration syntaxique, bouton copier
Quand les gens posent des questions techniques au chatbot, Claude répond avec des blocs de code. Le rendu en texte brut les rendait illisibles — juste un mur de texte monospace sans structure visuelle.
J'ai construit un renderer markdown léger directement dans le composant — pas de react-markdown, pas de remark, pas de rehype. Il gère les blocs de code fenced avec détection de langage, le code inline et le texte gras. Pour la coloration syntaxique, j'ai écrit un tokenizer en un seul passage qui identifie les mots-clés, les chaînes, les commentaires et les nombres pour JavaScript/TypeScript, CSS et HTML.
La leçon clé ici : n'utilisez pas de remplacements regex chaînés pour la coloration syntaxique. Ma première tentative utilisait des appels .replace() séquentiels, et les regex ultérieurs matchaient les tags <span class="..."> générés par les précédents. La solution était une approche en un seul passage où chaque match regex est traité exactement une fois.
J'ai aussi ajouté un bouton copier à chaque bloc de code. Il utilise l'API Clipboard (navigator.clipboard.writeText) avec un état de confirmation « Copié ». Petit détail, grande amélioration d'utilisabilité.
L'interface : la faire ressembler à une personne
La première version utilisait une bulle de chat violette générique avec une icône de bulle de discussion. Ça fonctionnait, mais ça ressemblait à tous les autres widgets de support SaaS. Je voulais que ça donne l'impression de vraiment parler avec moi.
La version actuelle utilise ma photo comme bulle de chat avec un overlay violet IA et un badge « AI » — rendant immédiatement clair que c'est une version IA de moi, pas un bot caché prétendant être humain. Le header du chat affiche mon nom avec « AI Agent » et un indicateur vert en ligne. Les messages de l'assistant montrent un petit avatar à côté. L'écran d'accueil a une photo plus grande avec « Hey, I'm Luca ! »
J'ai aussi ajouté un mode plein écran. Le petit widget 360×480 fonctionne pour les questions rapides, mais pour les conversations riches en code, il faut plus d'espace. Un bouton d'expansion dans le header bascule vers un layout en plein écran avec un contenu centré plafonné à max-w-3xl.
Ce que ça a coûté
Claude Haiku est remarquablement bon marché. Avec mon system prompt (~3 000 tokens) et des longueurs de conversation typiques, chaque message coûte environ $0.001-0.003. Même avec une utilisation généreuse, le coût mensuel est de quelques dollars. Le rate limiter est là pour la prévention des abus, pas la gestion des coûts.
Le coût de développement était essentiellement nul en termes de dépendances. Aucun nouveau package npm n'a été ajouté. L'ensemble de la fonctionnalité est construite avec les API routes Next.js, l'API Fetch, le state React et Tailwind CSS — des outils déjà dans le projet.
Ce que je ferais différemment
Les réponses en streaming. Actuellement, l'utilisateur voit des points « ... » jusqu'à ce que la réponse complète arrive. Avec l'API streaming de Claude, je pourrais rendre les tokens au fur et à mesure qu'ils arrivent, rendant le chatbot beaucoup plus réactif.
Les conversations persistantes. En ce moment, fermer le chat perd la conversation. LocalStorage résoudrait ça trivialement. Je ne l'ai pas ajouté parce que je ne suis pas sûr que les gens veulent que leurs conversations de chatbot soient persistées — mais ce serait une meilleure UX pour les visiteurs récurrents.
Une meilleure gestion des erreurs pour les cas limites. L'implémentation actuelle gère les erreurs réseau et le rate limiting, mais ne gère pas élégamment les cas comme le filtrage de contenu de Claude ou les réponses très longues qui atteignent la limite de tokens en plein milieu d'une phrase.
L'essentiel à retenir
Construire un chatbot IA en 2026 n'est pas un projet d'infrastructure complexe. Avec une bonne API de modèle, une route côté serveur et des compétences frontend de base, vous pouvez passer de rien à un chatbot en production en un après-midi. La partie difficile n'est pas la technologie — c'est de créer un system prompt qui rend le chatbot véritablement utile plutôt que génériquement bavard.
L'implémentation totale : ~140 lignes de code serveur, ~230 lignes de code client, zéro nouvelle dépendance, une variable d'environnement. Si vous avez un site portfolio et voulez que les visiteurs puissent poser des questions sur votre travail, c'est probablement la fonctionnalité à plus fort impact que vous puissiez ajouter.

