Créer un chatbot "intelligent" pour un site internet paraît simple sur le papier. En pratique, quand on veut un assistant rapide, peu coûteux, capable de tourner sur une machine modeste, et surtout vraiment utile pour répondre à de vraies questions clients, les choses deviennent beaucoup plus intéressantes.
Dans mon cas, je ne voulais pas simplement intégrer une API externe ou brancher un modèle générique. Mon objectif était plus ambitieux : concevoir un chatbot spécialisé sur mon activité, capable de répondre de manière pertinente à partir du contenu de mon site, avec un comportement suffisamment robuste pour être déployé sur une infrastructure légère.
J'ai donc construit toute une chaîne de travail :
- génération du dataset,
- enrichissement et augmentation des données,
- fine-tuning de plusieurs modèles,
- évaluation expérimentale,
- sélection du meilleur compromis,
- conversion vers un format exploitable en production,
- intégration dans une interface web en Svelte,
- et déploiement via Ollama sur un serveur sans GPU.
Ce projet m'a demandé beaucoup plus qu'un simple entraînement de modèle. Il a mobilisé des compétences en :
- data generation,
- évaluation de modèles,
- entrainement et déploiement de modèle d'IA,
- optimisation,
- et développement front-end / UX.
Le besoin initial : un chatbot réellement utile pour mon site
Au départ, mon besoin était simple : je voulais un chatbot capable de répondre aux visiteurs de mon site sur des sujets comme :
- mes offres,
- mes compétences,
- la facturation,
- le déroulement des prestations,
- les délais,
- les demandes de contact,
- et aussi les questions hors sujet ou ambiguës.
Je ne voulais pas un assistant "généraliste" qui improvise. Je voulais un modèle qui comprenne mon activité, mon vocabulaire, ma manière de présenter mes services, et qui sache répondre de façon cohérente dans un cadre bien défini.
Le problème, c'est que pour obtenir cela avec un petit modèle local, il faut un dataset de qualité. Et c'est là que le vrai travail a commencé.
Première étape : construire un dataset métier à partir du contenu de mon site
La première difficulté a été la création du dataset. Je voulais disposer de plus de 2 000 exemples pour entraîner correctement un modèle conversationnel spécialisé, mais je n'avais pas au départ une base de questions suffisamment large.
J'ai donc commencé par utiliser de gros modèles d'IA pour m'aider à structurer le problème.
Mon idée a été la suivante :
- donner à un modèle le contenu de mon site comme contexte ;
- lui demander d'identifier plusieurs grandes catégories de questions qu'un visiteur pourrait poser ;
- puis générer, catégorie par catégorie, des exemples de questions réalistes.
Cette approche m'a permis d'obtenir une première liste, avec par exemple des catégories du type :
- facturation,
- compétences,
- offres,
- déroulement de mission,
- aspects techniques,
- prise de contact.
Ensuite, plutôt que de demander d'un coup une énorme liste de questions, j'ai demandé environ 300 exemples par catégorie. L'idée était de réduire la dérive du modèle en le gardant focalisé sur un sous-sujet précis.
Sur le principe, c'était logique. En pratique, les résultats n'étaient pas satisfaisants.
Les problèmes étaient assez classiques :
- les questions finissaient par se ressembler,
- certaines formulations étaient artificielles,
- la qualité baissait au fil de la génération,
- et le modèle "oubliait" une partie du contexte à mesure que la conversation avançait.
Autrement dit : j'obtenais du volume, mais pas encore assez de qualité.
Changer de stratégie : passer sur un très gros contexte en local
À ce stade, j'ai compris qu'il me fallait une autre méthode de génération. J'ai donc décidé d'utiliser un modèle exécuté en local, avec une très grande fenêtre de contexte, afin de pouvoir lui fournir davantage d'informations sur mon activité sans trop perdre en cohérence.
J'ai utilisé Qwen 3.5 35B A3B en local, notamment parce que sa fenêtre de contexte annoncée était extrêmement large. Cela m'a permis de charger plus facilement de gros blocs d'informations sur mon activité et de produire des générations plus consistantes.
Avec un script Node.js, j'ai mis en place un pipeline de génération question/réponse par sujet. Cette fois, au lieu de demander 300 exemples par catégorie, j'ai réduit le volume à environ 200 exemples par sujet, avec une logique plus contrôlée.
Le changement a été important : au lieu de chercher uniquement la quantité, j'ai commencé à viser un meilleur ratio qualité / diversité / cohérence métier.
Le dataset n'a pas été seulement généré : il a aussi été conçu à la main
Une erreur fréquente, lorsqu'on construit un dataset de fine-tuning, consiste à croire qu'il suffit de générer des milliers d'exemples automatiquement.
En réalité, les exemples les plus importants sont souvent les plus difficiles à générer automatiquement :
- les cas limites,
- les formulations ambiguës,
- les fautes d'orthographe réalistes,
- les demandes mal exprimées,
- les questions volontairement vagues,
- les hors sujets,
- ou les formulations que de vrais clients utilisent sans adopter un vocabulaire "propre".
J'ai donc rédigé plus de 300 questions/réponses manuellement pour compléter le dataset. C'était une étape essentielle.
J'y ai ajouté notamment :
- des questions très spécifiques liées à mon activité ;
- des formulations imparfaites ou incomplètes ;
- des questions hors périmètre ;
- des cas où le modèle devait recadrer la conversation ;
- des variantes proches pour mieux apprendre la généralisation.
C'est probablement l'une des parties les plus importantes du projet, car c'est là que le dataset a commencé à devenir de meilleure qualité.
Augmentation des données : fautes, synonymes, reformulations
Une fois la première base construite, j'ai utilisé à nouveau le modèle local pour faire de l'augmentation de données.
Je lui ai demandé de reprendre mes couples question/réponse et de produire des variantes en :
- introduisant des fautes d'orthographe réalistes,
- remplaçant certains mots par des synonymes,
- reformulant les phrases,
- modifiant légèrement le style de questionnement,
- tout en conservant l'intention initiale.
L'objectif était clair : éviter que le futur modèle apprenne uniquement des formulations trop "propres" ou trop figées.
Dans un vrai contexte utilisateur, les gens ne posent pas toujours des questions parfaitement structurées. Ils écrivent vite, parfois avec des fautes, des raccourcis, des imprécisions. Si l'on veut un chatbot crédible, il faut l'entraîner à ça.
J'ai testé plusieurs prompts système pour cette étape, en comparant les résultats manuellement. Je n'ai pas simplement lancé une génération "en aveugle" : j'ai fait plusieurs itérations, observé les sorties, modifié les consignes, puis recommencé.
Cette logique expérimentale a été présente pendant tout le projet.
Vérification manuelle : indispensable, même à grande échelle
Après la génération et l'augmentation, j'ai effectué une vérification manuelle du dataset.
Je n'ai pas pu relire chaque exemple de manière exhaustive, car le volume total devenait très important, mais j'ai procédé par survol ciblé et contrôle de cohérence :
- vérification des formulations absurdes,
- détection de répétitions trop visibles,
- identification de réponses incohérentes,
- contrôle du ton général,
- et validation de l'alignement avec mon activité réelle.
Ce type de contrôle est fondamental. Même quand on automatise beaucoup, un dataset supervisé de qualité ne se construit pas sans regard humain.
Première phase d'entraînement : Qwen 2.5 1.5B avec LoRA
Pour l'entraînement, j'ai choisi LLaMA Factory, un outil que j'avais déjà utilisé auparavant pour quelques expérimentations. C'est une solution très complète pour faire du fine-tuning, notamment en SFT et avec différentes stratégies comme LoRA.
Au départ, j'ai commencé avec l'interface graphique, surtout pour aller vite et lancer des tests exploratoires. J'ai fait plusieurs entraînements en faisant varier les paramètres, parfois de manière assez empirique.
Mon premier vrai candidat a été Qwen 2.5 1.5B Instruct.
L'idée était logique :
- modèle léger,
- entraînement plus accessible,
- inférence plus facile,
- et meilleure compatibilité avec une future exécution sur serveur CPU.
J'ai utilisé LoRA pour fine-tuner le modèle, ce qui m'a permis de limiter les coûts de calcul tout en itérant rapidement.
Les premiers résultats étaient encourageants, mais assez irréguliers. J'ai dû lancer environ une quinzaine de runs avant d'obtenir quelque chose de vraiment exploitable.
À ce moment-là, j'ai compris deux choses :
- le dataset avait du potentiel ;
- mais pour progresser sérieusement, il fallait structurer beaucoup plus proprement mes expérimentations.
Passage au CLI : plus de rigueur, plus de reproductibilité
Pour gagner en efficacité et mieux suivre mes essais, j'ai abandonné progressivement l'interface graphique au profit du CLI de LLaMA Factory.
Cela m'a permis de :
- versionner mes essais proprement,
- comparer les paramètres plus facilement,
- documenter les runs,
- reproduire les entraînements,
- et garder une trace claire de ce qui fonctionnait réellement.
J'ai créé plusieurs scripts .bat nommés par versions (v1, v2, v3, etc.) afin d'industrialiser un minimum mes tests.
Voici par exemple la configuration du meilleur run obtenu à cette étape sur le modèle 1.5B :
llamafactory-cli train ^
--stage sft ^
--do_train True ^
--model_name_or_path Qwen/Qwen2.5-1.5B-Instruct ^
--preprocessing_num_workers 16 ^
--finetuning_type lora ^
--template qwen ^
--flash_attn auto ^
--dataset_dir data ^
--dataset chat_augmented ^
--num_train_epochs 4.0 ^
--packing False ^
--enable_thinking False ^
--report_to none ^
--per_device_train_batch_size 4 ^
--per_device_eval_batch_size 6 ^
--gradient_accumulation_steps 4 ^
--lr_scheduler_type cosine ^
--bf16 True ^
--plot_loss True ^
--max_grad_norm 0.5 ^
--cutoff_len 1024 ^
--learning_rate 2e-04 ^
--logging_steps 5 ^
--eval_steps 20 ^
--save_steps 20 ^
--warmup_steps 130 ^
--val_size 0.1 ^
--output_dir saves\qwen2.5-1.5B-Instruct\lora\chatbot-augmented-v4 ^
--include_num_input_tokens_seen False ^
--optim adamw_torch ^
--weight_decay 0.01 ^
--eval_strategy steps ^
--lora_rank 16 ^
--lora_alpha 32 ^
--lora_dropout 0.10 ^
--lora_target q_proj,k_proj,v_proj,o_proj,up_proj,down_proj ^
--load_best_model_at_end True ^
--metric_for_best_model eval_loss ^
--greater_is_better FalsePourquoi cette configuration était intéressante
Cette configuration représentait un compromis raisonnable entre :
- vitesse d'entraînement,
- stabilité,
- capacité d'adaptation,
- et coût de calcul.
Quelques points importants :
- LoRA rank 16 : suffisamment expressif sans être excessif ;
- learning rate à ( 2 \times 10^{-4} ) : agressif mais encore viable sur ce type de SFT ;
- dropout LoRA à 0.10 : utile pour limiter un peu l'overfitting ;
- warmup + cosine scheduler : pour lisser l'apprentissage ;
- validation à 10 % : indispensable pour comparer objectivement les runs.
Le modèle 1.5B fonctionnait… mais pas assez bien pour la production
Une fois le fine-tuning terminé, j'ai testé l'inférence avec les poids LoRA appliqués au modèle de base. Les résultats étaient acceptables sur une partie des questions du dataset.
Ensuite, pour préparer la mise en production, j'ai suivi le workflow classique :
- merge des poids LoRA avec le modèle principal ;
- conversion du modèle au format GGUF ;
- exécution locale pour tester un scénario proche de la production.
Cette étape était cruciale, car j'avais dès le départ une contrainte forte d'infrastructure :
- pas de GPU sur le serveur cible ;
- seulement 16 Go de RAM ;
- et un processeur "moyen".
Je savais donc que chaque choix de modèle allait avoir des conséquences directes sur :
- la latence,
- la qualité des réponses,
- la consommation mémoire,
- et l'expérience utilisateur.
Le problème, c'est qu'une fois converti et exécuté dans des conditions proches du réel, le modèle 1.5B a montré ses limites.
Il connaissait une partie de mes données. On sentait qu'il avait appris le dataset. Mais les réponses restaient parfois :
- mal tournées,
- peu naturelles,
- fragiles dès que la question était légèrement différente,
- et insuffisamment robustes en généralisation.
En d'autres termes : il mémorisait partiellement, mais ne "comprenait" pas assez bien.
Pour un prototype, cela pouvait passer. Pour un chatbot réellement déployé sur un site professionnel, ce n'était pas suffisant.
Le constat : 1.5B, c'est souvent trop juste pour une spécialisation solide
Après analyse, le diagnostic était assez clair.
Un modèle 1.5B peut apprendre un corpus spécialisé, surtout si le dataset est bien structuré. En revanche, dès qu'on lui demande de gérer :
- des formulations imprécises,
- des questions longues,
- des variantes non vues,
- des fautes,
- des intentions proches mais pas identiques,
ses limites apparaissent beaucoup plus vite.
Il devient bon pour "réciter" certaines structures du dataset, mais plus fragile lorsqu'il faut réellement généraliser.
J'ai donc décidé de changer d'échelle et de passer à un modèle 3B.
L'idée n'était pas de prendre un gros modèle par confort, mais de viser un meilleur compromis :
- suffisamment petit pour rester déployable,
- suffisamment grand pour mieux absorber la variabilité des questions.
Deuxième phase : amélioration du dataset et passage à Qwen 2.5 3B
Avant de relancer l'entraînement, j'ai amélioré le dataset une nouvelle fois.
Cette amélioration n'a pas consisté à ajouter du volume "au hasard", mais à corriger précisément les faiblesses observées sur le 1.5B :
- questions auxquelles le modèle répondait mal,
- formulations où il se trompait de sujet,
- cas où il manquait de nuance,
- situations où la réponse était trop littérale,
- exemples nécessitant une meilleure robustesse.
J'ai donc enrichi le dataset à la main avec de nouveaux cas ciblés. C'était une manière beaucoup plus efficace de progresser que d'ajouter des centaines d'exemples génériques.
Ensuite, j'ai repris le fine-tuning sur un modèle Qwen 2.5 3B, avec une approche cette fois beaucoup plus structurée.
Une démarche plus professionnelle : suivi des runs avec Weights & Biases
Pour cette seconde phase, je voulais sortir d'une logique d'essais dispersés et mettre en place un vrai protocole d'expérimentation.
J'ai donc utilisé Weights & Biases (W&B) pour suivre les runs de fine-tuning.
L'intérêt a été immédiat :
- historisation des expériences,
- comparaison visuelle des courbes,
- suivi des hyperparamètres,
- centralisation des métriques,
- et meilleure capacité à prendre des décisions rationnelles.
J'ai préparé plusieurs runs avec des paramètres volontairement distincts pour explorer différentes zones de l'espace de recherche :
- learning rate,
- nombre d'epochs,
- batch size effectif,
- régularisation,
- paramètres LoRA,
- etc.
J'ai d'abord lancé 5 runs très différents, puis sélectionné la configuration la plus prometteuse. À partir de cette base, j'ai décliné 4 runs supplémentaires avec des ajustements plus fins.
L'objectif n'était pas seulement d'obtenir la plus petite eval_loss, mais de trouver un modèle qui généralise bien.
C'est un point important : une run avec une loss très basse n'est pas forcément le meilleur en production.
Le choix du "meilleur" modèle : pas seulement une question de loss
Au final, j'ai obtenu une run avec environ :
- train loss : 0.71
- eval loss : 0.84
J'ai eu certaines runs avec une eval_loss encore plus basse, autour de 0.81, mais avec une train_loss extrêmement basse également, ce qui suggérait un surapprentissage plus marqué.
En parallèle, j'ai mis en place un petit protocole d'évaluation automatisée en Node.js, basé sur des métriques de similarité textuelle comme :
- BLEU,
- ROUGE,
- BERTScore.
Je faisais inférer le modèle sur le dataset d'entraînement afin d'obtenir des scores quantitatifs comparables entre runs.
Techniquement, je savais que ce n'était pas le protocole idéal. Le mieux aurait été d'évaluer sur un ensemble complètement inédit. Mais dans le contexte du projet, avec le volume de données disponible et mon besoin d'itération rapide, cette approche me donnait malgré tout un signal utile pour comparer les modèles entre eux.
Le point le plus important, c'est que je n'ai pas choisi mon modèle uniquement sur des chiffres.
J'ai croisé :
- les losses,
- les scores automatiques,
- et surtout des tests qualitatifs réels sur des questions plus naturelles.
C'est d'ailleurs là que certains modèles apparemment "meilleurs" se sont révélés moins bons en pratique : dès qu'une question devenait un peu longue, un peu maladroite, ou s'éloignait légèrement du dataset, ils répondaient à côté.
J'ai donc préféré sélectionner le modèle qui offrait le meilleur compromis entre :
- stabilité,
- généralisation,
- pertinence métier,
- et qualité perçue.
C'est, à mon sens, une décision beaucoup plus sérieuse qu'une simple optimisation aveugle d'une métrique.
Déploiement : transformer un modèle fine-tuné en chatbot utilisable sur un vrai site
Une fois le bon modèle sélectionné, il restait toute la partie "produit" : faire en sorte qu'il fonctionne réellement sur mon site web.
Et c'est là qu'un projet IA devient intéressant : entraîner un modèle, c'est une chose ; l'intégrer proprement dans une application web réelle, c'en est une autre.
Mon objectif de déploiement était clair :
- exécution locale sur serveur,
- pas de dépendance à une API externe,
- réponses en streaming,
- interface fluide,
- et maîtrise complète de l'infrastructure.
J'ai choisi Ollama comme runtime d'inférence pour faire tourner le modèle sur le serveur.
Pourquoi ce choix ?
- installation simple,
- gestion pratique des modèles,
- intégration facile côté backend,
- support du streaming,
- et compatibilité intéressante pour un déploiement léger.
Pourquoi le format GGUF était indispensable
Comme mon serveur ne dispose pas de GPU et possède des ressources limitées, je ne pouvais pas me permettre de déployer un modèle dans un format trop lourd.
J'ai donc utilisé le workflow classique :
- fusion des poids LoRA avec le modèle de base ;
- conversion vers GGUF ;
- quantification adaptée au contexte CPU ;
- import du modèle dans Ollama via un
Modelfile.
Le format GGUF est particulièrement adapté dans ce type de contexte, car il facilite l'exécution de modèles quantifiés sur CPU, avec un bon compromis entre mémoire, vitesse et qualité.
Dans mon cas, cette étape était essentielle pour rendre le projet viable économiquement et techniquement.
tokenizer = AutoTokenizer.from_pretrained(args.model)
base_model = AutoModelForCausalLM.from_pretrained(
args.model,
torch_dtype=torch.float16,
device_map="auto"
)
model = PeftModel.from_pretrained(base_model, args.lora)
model = model.merge_and_unload()
model.save_pretrained(args.output)
tokenizer.save_pretrained(args.output)python ".\llama.cpp\convert_hf_to_gguf.py" ".\qwen_merged" --outfile ".\qwen_3b_f16.gguf" --outtype f16
".\llama.bin\llama-quantize.exe" ".\qwen_3b_f16.gguf" ".\qwen_3b_q4_k.gguf" Q4_K_MFROM ./chat-bot-q4-k.gguf
PARAMETER top_p 0.90
PARAMETER temperature 0.05
PARAMETER repeat_penalty 1.05
PARAMETER num_ctx 512
PARAMETER num_batch 1024
TEMPLATE """<|im_start|>system
{{ .System }}<|im_end|>
<|im_start|>user
{{ .Prompt }}<|im_end|>
<|im_start|>assistant
"""
PARAMETER stop "<|im_start|>"
PARAMETER stop "<|im_end|>"
"""Développement du chatbot : Svelte, streaming et UX temps réel
Pour l'interface, j'ai développé le chatbot directement en Svelte afin de l'intégrer proprement à mon site.
Je voulais une expérience utilisateur proche de ce qu'on retrouve dans les assistants modernes :
- affichage conversationnel,
- réponse progressive,
- sensation de fluidité,
- interface légère,
- et intégration naturelle dans le site existant.
Pour obtenir un affichage en direct de la réponse du modèle, j'ai mis en place un système de streaming basé sur :
TransformStream- et
ReadableStream
Cela permet de recevoir les tokens générés progressivement et de les afficher au fur et à mesure dans l'interface.
C'est un détail technique qui change énormément l'expérience perçue. Même quand le modèle prend quelques secondes à répondre, le fait de voir la réponse apparaître en temps réel améliore nettement le ressenti utilisateur.
async function processNext(): Promise<void> {
if (isProcessing || queue.length === 0) return;
isProcessing = true;
const { userMessage, resolve, reject } = queue.shift()!;
try {
const response = await fetch(`${OLLAMA_BASE_URL}/api/chat`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
model: OLLAMA_MODEL,
messages: [{ role: 'user', content: userMessage }],
stream: true
})
});
if (!response.ok || !response.body) {
const err = await response.text();
reject(new Error(`Ollama error ${response.status}: ${err}`));
isProcessing = false;
processNext();
return;
}
const { readable, writable } = new TransformStream < Uint8Array, Uint8Array> ();
response.body.pipeTo(writable).finally(() => {
isProcessing = false;
processNext();
});
resolve(readable);
}
catch (err) {
reject(err instanceof Error ? err : new Error(String(err)));
isProcessing = false;
processNext();
}
}const res = await fetch('/api/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message: msg })
});
if (!res.ok || !res.body) {
const errData = await res.json().catch(() => ({}));
const errMsg = res.status === 429 ? (errData.message ?? 'Veuillez réessayer dans quelques secondes.') : res.status === 403 ? 'Session expirée. Rechargez la page.' : 'Désolé, une erreur est survenue. Veuillez réessayer.';
throw new Error(errMsg);
}
const reader = res.body.getReader();
const decoder = new TextDecoder();
let buffer = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split('\n');
buffer = lines.pop() ?? '';
for (const line of lines) {
if (!line.trim()) continue;
try {
const chunk = JSON.parse(line);
const token = chunk?.message?.content ?? '';
if (token) {
isLoading = false;
if (!botMessageStarted) {
botMessageStarted = true;
messages = [...messages, { role: 'bot', text: token }];
}
else {
const last = messages[messages.length - 1];
messages = [...messages.slice(0, -1), { ...last, text: last.text + token }];
}
await scrollToBottom();
}
} catch { }
}
}Gérer la charge : file d'attente côté serveur
Comme l'inférence se fait sur CPU, j'ai aussi réfléchi à un problème souvent sous-estimé : la concurrence.
Si plusieurs utilisateurs sollicitent le modèle en même temps, un serveur modeste peut rapidement saturer. Il ne suffit donc pas que le chatbot fonctionne pour un utilisateur isolé ; il faut aussi penser au comportement global du système.
J'ai donc prévu un mécanisme de queue côté serveur pour éviter qu'un trop grand nombre de requêtes simultanées ne submerge le CPU.
Cette partie est importante, car elle montre qu'un projet IA déployé sérieusement ne se limite pas au modèle. Il faut penser :
- à l'ordonnancement,
- à la robustesse,
- à la latence,
- à l'expérience utilisateur,
- et au dimensionnement de l'infrastructure.
Résultat : un chatbot local, rapide, spécialisé et crédible
Aujourd'hui, le chatbot fonctionne correctement sur mon site.
Il ne répond pas parfaitement à tout — et je pense qu'il faut être honnête sur ce point. Mais compte tenu des contraintes :
- modèle de petite taille,
- exécution locale,
- serveur sans GPU,
- mémoire limitée,
- besoin de rapidité,
le résultat est très satisfaisant.
Le modèle répond vite, comprend bien mon domaine, et reste suffisamment cohérent pour un usage réel. Surtout, il repose sur une architecture que je maîtrise entièrement, de la donnée jusqu'au déploiement.
C'est précisément ce que je recherchais :
non pas une démonstration théorique, mais un système fonctionnel, optimisé, et réellement intégrable dans un environnement de production simple.
Ce que ce projet montre concrètement
Au-delà du chatbot lui-même, ce projet illustre ma manière de travailler sur un sujet technique complexe.
Je ne me suis pas contenté de "brancher un modèle". J'ai :
- conçu un dataset métier,
- mis en place une stratégie de génération et d'augmentation de données,
- effectué du contrôle qualité,
- expérimenté plusieurs modèles et configurations,
- comparé les runs avec méthode,
- arbitré entre performance théorique et comportement réel,
- optimisé le modèle pour un environnement contraint,
- développé une interface web complète,
- et pensé le déploiement comme un vrai système, pas comme une simple démo.
Autrement dit, ce projet mobilise à la fois :
Compétences IA / machine learning
- fine-tuning supervisé,
- LoRA,
- construction de dataset,
- augmentation de données,
- évaluation de modèles,
- sélection d'hyperparamètres.
Compétences backend / infra
- scripts d'automatisation,
- intégration d'Ollama,
- gestion de file d'attente,
- préparation d'un déploiement CPU,
- conversion et optimisation de modèles.
Compétences front-end / produit
- développement Svelte,
- UX conversationnelle,
- streaming temps réel,
- intégration à un site existant.
C'est précisément le type de projet que j'aime : un problème concret, avec de vraies contraintes, qui demande de combiner réflexion, expérimentation et exécution.
Ce que j'améliorerai ensuite
Le projet fonctionne déjà bien, mais il ouvre naturellement plusieurs pistes d'amélioration :
- construire un vrai jeu de test indépendant pour l'évaluation ;
- mesurer plus rigoureusement la robustesse sur des formulations hors distribution ;
- tester la montée en charge avec plusieurs utilisateurs simultanés ;
- comparer plusieurs niveaux de quantification ;
- ajouter éventuellement une couche hybride, par exemple avec RAG sur certaines pages du site ;
- enrichir encore les comportements métier du chatbot ;
- affiner le prompt système selon les cas d'usage réels observés.
C'est aussi ce que j'apprécie dans ce type de travail : il y a une première version utile, puis une trajectoire claire d'amélioration continue.
Conclusion
Créer un chatbot spécialisé pour un site web avec un petit modèle local n'est pas un simple exercice technique. C'est un travail de conception complet, à la croisée de la donnée, du machine learning, de l'ingénierie logicielle et du déploiement.
Dans ce projet, j'ai dû :
- comprendre les limites des approches naïves,
- construire et enrichir un dataset pertinent,
- tester, comparer, itérer,
- accepter d'abandonner une première piste insuffisante,
- améliorer la méthode,
- puis transformer un modèle entraîné en un vrai produit utilisable.
C'est précisément ce type de démarche qui m'intéresse : partir d'un besoin concret, explorer plusieurs solutions, mesurer, corriger, et aller jusqu'à une implémentation opérationnelle.
je ne me limite pas à une idée ou à un outil, je construis des solutions complètes, pragmatiques et adaptées aux contraintes réelles.