Et si on intégrait LangChain dans Spring Boot ? Partie 1
Depuis l’apparition des modèles de langage de grande taille (LLM), le monde du développement logiciel vit une transformation profonde. Ces modèles — capables de comprendre et de générer du texte de manière cohérente, contextualisée, et souvent bluffante — ont ouvert la voie à une nouvelle génération d’applications dites “intelligentes”.
Dans l’écosystème Java, réputé pour sa robustesse et sa pérennité, cette révolution se traduit par l’apparition de bibliothèques dédiées à l’orchestration et à la manipulation des LLM, dont LangChain4j est aujourd’hui la référence incontournable.
Certes, Spring Boot propose déjà un starter officiel, Spring AI, qui facilite l’intégration de modèles tels qu’OpenAI, Azure OpenAI, ou encore Anthropic Claude. Mais soyons honnêtes : la majorité de ces solutions reposent sur des modèles étrangers, souvent hébergés et régulés outre-Atlantique.
Or, pour qui tient à la souveraineté technologique et à l’excellence française, il est naturel de se tourner vers une alternative nationale. C’est ici qu’entre en scène Mistral AI, jeune entreprise française devenue en quelques mois un symbole de l’innovation européenne dans le domaine de l’intelligence artificielle.
Le choix que nous faisons ici est donc volontairement chauvin : utiliser un modèle français, performant et ouvert, au sein d’un environnement Spring Boot — pilier du développement d’applications Java modernes. Et pour cela, LangChain4j nous sert de passerelle, souple et parfaitement intégrée à l’écosystème Java.
le coq gaulois
Présentation de LangChain
Pour comprendre LangChain4j, il faut d’abord remonter à son ancêtre : LangChain, une bibliothèque née dans l’univers Python en 2022, sous l’impulsion de Harrison Chase.
Son objectif initial était simple, mais ambitieux : permettre aux développeurs de structurer les interactions avec un LLM de manière modulaire et réutilisable.
[
Késaco : LangChain
LangChain, LangChain… vous entendez ce mot dans toutes les conversations sans réellement comprendre de quoi il s’agit? Pas de panique, notre Késaco est là pour vous aider !
sfeir.dev - Le média incontournable pour les passionnés de tech et d’intelligence artificielleCharlotte Ryssen (Coumont)
![]()
](https://www.sfeir.dev/ia/kesaco-langchain/)
Pour en savoir plus sur langchain c’est par ici
En effet, interagir directement avec un modèle (comme GPT, Claude ou Mistral) via une API brute est souvent limité. On envoie un texte, on obtient une réponse. Mais dès qu’on souhaite :
- enchaîner plusieurs requêtes logiquement (chaîne de prompts),
- stocker le contexte d’une conversation (mémoire),
- enrichir les réponses à partir de documents internes (RAG : Retrieval-Augmented Generation),
- ou même connecter le modèle à des outils externes (agents),
… l’approche basique ne suffit plus.
LangChain a introduit une série de concepts fondateurs :
- PromptTemplate : des modèles de texte avec des variables dynamiques.
- Chain : une séquence d’étapes connectées qui utilisent un LLM pour traiter ou enrichir des données.
- Memory : une structure permettant à un modèle de conserver le contexte d’une conversation.
- Tool / Agent : un mécanisme permettant au modèle d’appeler des fonctions ou des API extérieures.
- Retrieval : l’utilisation d’un moteur d’embeddings pour rechercher dans une base documentaire les passages pertinents avant de répondre.
Ces abstractions ont fait de LangChain une véritable boîte à outils pour concevoir des applications intelligentes et adaptables.
Rapidement, LangChain s’est imposé comme un standard de fait dans le monde Python pour bâtir des applications LLM complexes — assistants personnels, moteurs de recherche sémantiques, agents conversationnels, etc.
[
Pourquoi utiliser LangChain ?
Pourquoi LangChain est le framework incontournable pour débuter votre projet de chatbot intelligent ?
sfeir.dev - Le média incontournable pour les passionnés de tech et d’intelligence artificielleCharlotte Ryssen (Coumont)
![]()
](https://www.sfeir.dev/ia/langchain-pourquoi/)
Présentation de LangChain4j
Le succès de LangChain a naturellement inspiré un portage vers d’autres langages. C’est ainsi qu’est né LangChain4j, projet open-source qui apporte la même philosophie au monde Java.
LangChain4j transpose les grands concepts de LangChain dans un cadre familier aux développeurs Java : typage fort, configuration par annotations, injection de dépendances, compatibilité Spring Boot, etc.
Concrètement, LangChain4j fournit une série d’interfaces et de classes qui encapsulent les principales briques nécessaires pour construire des applications intelligentes :
- ChatModel : pour interagir avec un modèle de langage conversationnel (comme Mistral, OpenAI ou Ollama).
- EmbeddingModel : pour convertir du texte en vecteurs numériques et permettre la recherche sémantique.
- PromptTemplate : pour générer dynamiquement des prompts avec des variables.
- DocumentSplitter et EmbeddingStore : pour découper, indexer et rechercher dans des documents.
- ChatMemory : pour conserver le contexte des conversations et offrir des échanges cohérents dans la durée.
Mais LangChain4j ne se limite pas à la théorie.
L’un de ses grands atouts est de proposer des implémentations concrètes pour plusieurs fournisseurs de modèles : OpenAI, Anthropic, Hugging Face, Ollama, et — ce qui nous intéresse ici — Mistral AI.
Ce dernier est pris en charge grâce au module langchain4j-mistral-ai, qui permet d’accéder aux modèles de la famille Mistral Small, Mistral Medium et Mistral Embed, via leur API officielle.
[
GitHub - langchain4j/langchain4j: LangChain4j is an open-source Java library that simplifies the integration of LLMs into Java applications through a unified API, providing access to popular LLMs and vector databases. It makes implementing RAG, tool calling (including support for MCP), and agents easy. LangChain4j integrates seamlessly with various enterprise Java frameworks.
LangChain4j is an open-source Java library that simplifies the integration of LLMs into Java applications through a unified API, providing access to popular LLMs and vector databases. It makes impl…
GitHublangchain4j
](https://github.com/langchain4j/langchain4j)
⚖️ Avantages et inconvénients de LangChain4j
➕ Avantages
- API Java idiomatique : cohérente avec les conventions de Spring et facile à intégrer.
- Écosystème modulaire : prise en charge de nombreux fournisseurs de modèles et de stores d’embeddings.
- Abstractions puissantes : support natif des prompts, de la mémoire, du RAG et du streaming.
- Compatibilité Spring Boot / Quarkus : déploiement aisé dans des environnements cloud ou on-premise.
- Support Mistral : intégration directe avec les modèles Mistral, performants et souverains.
➖ Inconvénients
- Jeunesse du projet : LangChain4j évolue vite, certaines API changent d’une version à l’autre.
- Stores en mémoire : pratiques pour le prototypage, mais à remplacer par des solutions persistantes (pgvector, Elasticsearch) en production.
- Concurrence et état : attention à la gestion de la mémoire partagée entre sessions ou threads.
Exemple d’implémentation
Dépendances Maven
Pour utiliser LangChain4j avec le modèle Mistral, il faut importer deux modules principaux dans votre projet Maven :
<dependencies>
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j</artifactId>
<version>1.7.1</version>
</dependency>
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-mistral-ai</artifactId>
<version>1.7.1</version>
</dependency>
</dependencies>
les dépendances pour langchain4j et pour utiliser les modèles mistral
langchain4j
C’est le cœur de la bibliothèque.
Elle contient toutes les abstractions de base et les interfaces communes qui définissent la structure logique d’une application LangChain en Java.
En d’autres termes : c’est le moteur sans le carburant.
Elle fournit :
- Les interfaces principales :
ChatLanguageModel– pour interagir avec les modèles conversationnels.EmbeddingModel– pour générer des vecteurs numériques à partir de texte.PromptTemplate– pour formater dynamiquement les prompts.ChatMemory– pour stocker le contexte d’une conversation.RetrieveretEmbeddingStore– pour la recherche sémantique et la gestion du RAG.
- Les classes de base utilisées pour la composition de chaînes de traitement :
Chain,Prompt,Message,ToolExecutionRequest, etc.
- Les utilitaires communs :
- Convertisseurs, builders, adaptateurs, classes de support pour la sérialisation JSON, etc.
langchain4j-mistral-ai
C’est le connecteur entre LangChain4j et les modèles Mistral AI.
Il fournit les classes concrètes qui permettent d’interagir avec les API Mistral à travers les interfaces standard de LangChain4j.
En d’autres termes : c’est le carburant — le module qui rend le moteur LangChain4j capable de parler avec Mistral.
Elle fournit :
- Une implémentation du modèle de chat :
MistralAiChatModel, qui implémenteChatLanguageModel. - Une intégration directe de l’API Mistral :
gestion des clés d’API, du format de requêtes et des réponses. - La prise en charge du streaming :
vous pouvez recevoir les réponses token par token (utile pour des UI interactives). - La compatibilité avec plusieurs modèles :
mistral-smallmistral-mediummistral-largeopen-mistral-7b(modèle open source téléchargeable)
- Des options de configuration avancées :
- température, top-p, nombre maximal de tokens, etc.
Le service
Nous allons maintenant voir comment mettre en place :
- un exemple de chat classique
- un exemple avec de la mémoire
- un exemple de RAG
- un exemple de streaming
- avoir un output structuré
public ChatService(@Value("${mistral.api.key}") String KEY){
model = MistralAiChatModel.builder()
.apiKey(KEY)
.modelName(MISTRAL_SMALL_LATEST)
.logRequests(true)
.logResponses(true)
.build();
embeddingModel = MistralAiEmbeddingModel.builder()
.apiKey(KEY)
.modelName(MISTRAL_EMBED)
.logRequests(true)
.logResponses(true)
.build();
streamingChatModel = MistralAiStreamingChatModel.builder()
.apiKey(KEY)
.modelName(MISTRAL_SMALL_LATEST)
.logRequests(true)
.logResponses(true)
.build();
}
Dans le constructeur de mon service, je créer les différentes instance de modèle que j’utiliserais dans cet article, la clé d’API sera stocké dans un fichier properties et injecté dans le service (Surtout, ne la commité pas).
Voyons plus en détails les différents modèls utilisé ici :
MistralAiChatModel
- Rôle : modèle principal de génération de texte (conversation, rédaction, résumé).
- Interface implémentée :
ChatLanguageModel. - Utilisation typique : réponses textuelles à un prompt (
model.chat(...)). - Paramètres clés :
apiKey(KEY)→ clé d’accès à l’API Mistral.modelName(MISTRAL_SMALL_LATEST)→ choix du modèle.logRequests(true)/logResponses(true)→ activation des logs pour le debug. - Modèles disponibles :
MISTRAL_SMALL_LATEST→ rapide, économique, idéal pour des tests et tâches simples.MISTRAL_MEDIUM_LATEST→ équilibre entre coût et performance.MISTRAL_LARGE_LATEST→ plus précis, adapté à des tâches complexes.OPEN_MISTRAL_7B→ version open source, exécutable localement.
MistralAiEmbeddingModel
- Rôle : création d’embeddings (vecteurs représentant le sens d’un texte).
- Interface implémentée :
EmbeddingModel. - Utilisation typique : recherche sémantique et RAG (Retrieval-Augmented Generation).
-
Modèles disponible :
MISTRAL_EMBED→ modèle optimisé pour la génération d’embeddings rapides et cohérents.
MistralAiStreamingChatModel
- Rôle : génération en flux continu de texte (réponses token par token).
- Interface implémentée :
StreamingChatLanguageModel. - Utilisation typique : affichage progressif dans une interface utilisateur (chat en direct, console).
- Avantages :
- Meilleure expérience utilisateur (réponses en temps réel).
- Réduction de la latence perçue.
Passons maintenant aux exemple.
Méthode chat() — génération de texte simple
Cette méthode illustre l’utilisation la plus basique du modèle de chat Mistral dans un contexte applicatif.
Elle génère une réponse textuelle à partir d’un prompt dynamique, construit avec des variables.
public String chat(String subjet, String adjective) {
PromptTemplate promptTemplate = PromptTemplate
.from("Raconte moi une blague {{adjective}} sur {{content}}..");
Map<String, Object> variables = new HashMap<>();
variables.put("adjective", adjective);
variables.put("content", subjet);
Prompt prompt = promptTemplate.apply(variables);
return model.chat(prompt.text());
}
Fonctionnement étape par étape
- Création du template de prompt :
PromptTemplate.from(...)définit une phrase modèle contenant des variables dynamiques (``, `<h2 id="la-pandémie-sanglante-de-2005-dansworld-of-warcraft">La pandémie sanglante de 2005 dans World of Warcraft</h2>
En 2005, un événement insoupçonné traversa les frontières du jeu vidéo pour entrer dans la littérature scientifique. Ce phénomène, connu sous le nom de Corrupted Blood Incident, mit à genoux l’un des mondes virtuels les plus peuplés jamais créés.
Pour les développeurs, c’était un accident technique.
Pour les joueurs, un moment de chaos inoubliable.
Pour les chercheurs, un terrain d’observation exceptionnel.
Pour comprendre pourquoi ce phénomène reste aujourd’hui un cas d’école, il faut d’abord saisir ce qu’était World of Warcraft en 2005, ce que représentait son univers, et pourquoi une simple ligne de code imparfaite a suffi à déclencher une épidémie capable de se propager d’un continent virtuel à l’autre.
Le monde d’Azeroth : un univers vivant, peuplé et interconnecté
Azeroth à la sortie du jeu
À sa sortie en 2004, World of Warcraft (souvent abrégé WoW) devint rapidement le pilier du jeu en ligne moderne.
Mais WoW n’était pas qu’un divertissement. C’était un monde complet, appelé Azeroth, où des centaines de milliers de joueurs coexistaient en même temps.
Un monde persistant
On parle de monde persistant lorsqu’un univers virtuel continue d’exister, d’évoluer et de vivre même quand un joueur se déconnecte.
Les serveurs tournaient jour et nuit, les créatures réapparaissaient régulièrement, les villes restaient actives, les marchands poursuivaient leurs dialogues.
Pour la première fois, un univers numérique ressemblait à une petite société, avec :
- une économie,
- des zones rurales et urbaines,
- des professions,
- des hiérarchies,
- des comportements sociaux structurés.
Joueurs et PNJ : un monde mixte
Azeroth était habité par :
- des joueurs réels, contrôlant leur personnage ;
- des PNJ (Personnages Non Joueurs), entités gérées par le jeu.
Les PNJ avaient des rôles variés :
- vendeurs ;
- maîtres d’armes ;
- gardes en armure ;
- créatures pacifiques ;
- monstres hostiles.
Certains étaient fragiles et pouvaient mourir en quelques secondes.
D’autres étaient programmés pour ne jamais mourir, notamment ceux indispensables au fonctionnement général des villes.
L’organisation du monde : villages, capitales, et zones sauvages
Les capitales - Ironforge, Orgrimmar, Stormwind, Thunder Bluff… - étaient des lieux d’une densité exceptionnelle. Les joueurs s’y massaient pour :
- commercer,
- apprendre de nouvelles capacités,
- discuter,
- préparer des expéditions.
De véritables carrefours où circulaient des centaines de joueurs simultanément.
À l’inverse, les zones sauvages étaient vastes, peu habitées, et servaient de terrains d’aventure.
Cette structure allait jouer un rôle crucial dans la propagation de la future pandémie.
Les raids : des forteresses fermées réservées aux joueurs aguerris
Pour comprendre l’origine de la Pandémie sanglante, il faut expliquer ce que sont les raids.
Un raid est une instance - c’est-à-dire une zone fermée, isolée du reste du monde - où un groupe de joueurs affronte des ennemis particulièrement puissants.
Ces zones sont faites pour les joueurs les plus expérimentés, qui y affrontent des boss nécessitant coordination, stratégie et sang-froid.
En 2005, l’un de ces raids s’appelait Zul’Gurub, un ancien temple maudit au cœur de la jungle.
On y trouvait Hakkar, surnommé :
- le Dieu du sang,
- le Dévoreur d’âmes,
- le Cœur sauvage de la corruption.
Un adversaire imposant, entouré de rituels sanglants et d’effets magiques.
Hakkar
La source du désastre : un sort qui n’aurait jamais dû sortir du temple
Le combat contre Hakkar possédait une mécanique particulière : une capacité nommée Corrupted Blood - littéralement “Sang vicié”.
Ce sort infligeait :
- des dégâts continus,
- se transmettait à courte portée,
- et devait rester strictement confiné à l’intérieur du raid.
Aucun développeur n’imaginait le voir franchir les murs de l’instance.
Dans la logique du jeu, c’était impossible.
Mais il existait une faille que personne n’avait anticipée.
Les familiers : des bombes biologiques à retardement
Si l’infection avait dû s’éteindre naturellement avec le temps ou la mort des porteurs, une faille critique dans la gestion des animaux de compagnie permit de contourner cette règle. Les Chasseurs et les Démonistes possédaient la capacité de “renvoyer” leurs créatures, les faisant disparaître instantanément de l’écran.
Or, ce faisant, le jeu ne supprimait pas les effets actifs sur l’animal : il les mettait simplement en “pause”. Un familier contaminé au cœur du raid pouvait ainsi être renvoyé, voyageant en stase informatique jusqu’à une capitale surpeuplée.
Lorsque son maître le rappelait des heures plus tard au milieu d’une place commerçante, la créature réapparaissait avec le virus toujours actif. En agissant comme de véritables chevaux de Troie, ces animaux lâchaient l’épidémie au milieu d’une foule sans défense, bien loin de la jungle où le danger aurait dû rester confiné.
La propagation : quand Azeroth devint un théâtre de pandémie
Les joueurs de haut niveau : super-propagateurs involontaires
Résistants, mobiles, ils pouvaient traverser plusieurs régions en quelques minutes.
Sans le savoir, ils transportaient la maladie d’une ville à l’autre.
Les joueurs novices : des victimes par centaines
Les personnages bas niveau mouraient presque instantanément.
Le sol des quartiers commerçants se couvrait de cadavres.
Les capitales autrefois animées devinrent des paysages surréalistes.
Les PNJ immortels : les pires réservoirs possibles
Le mécanisme le plus dévastateur ne vint pas des joueurs, mais de l’infrastructure même du jeu. Pour assurer le bon fonctionnement des quêtes et du commerce, certains personnages clés (gardes, banquiers, crieurs de rue) sont programmés pour être invulnérables. C’est ici que la logique informatique s’est retournée contre ses créateurs.
Ces PNJ contractaient la maladie, mais leur immortalité empêchait le virus de tuer son hôte, ce qui était pourtant la seule façon naturelle de dissiper le sort. Ils se transformèrent alors en réservoirs viraux permanents.
Tels des “porteurs sains” indestructibles, ils continuaient de sourire et de saluer les passants, irradiant la mort sur quiconque s’approchait pour acheter une miche de pain ou réparer son armure.
Ce qui devait être une sécurité technique devint le moteur principal de l’hécatombe.
Le rôle du comportement humain
Face à l’hécatombe, la psychologie humaine se révéla dans toute sa complexité, transformant Azeroth en une véritable étude sociologique. Les réactions furent immédiates et viscérales.
Une partie de la population céda à la panique, fuyant les grandes villes pour s’isoler dans les montagnes ou les forêts reculées, imposant leur propre confinement.
À l’inverse, des joueurs altruistes, souvent des soigneurs, tentèrent héroïquement de maintenir les infectés en vie, sans réaliser qu’ils accéléraient la propagation en s’approchant des malades.
Mais le plus fascinant, et le plus sombre, fut l’apparition de comportements malveillants : certains joueurs, attirés par le chaos, utilisèrent la maladie comme une arme biologique, se téléportant volontairement au milieu des foules pour maximiser le nombre de victimes.
En quelques heures, le jeu n’était plus une question de code, mais de morale.
Les tentatives de Blizzard pour endiguer la crise
Face à un phénomène qui dépassait largement une simple anomalie technique, les équipes de Blizzard se retrouvèrent dans une situation inédite. L’épidémie de Corrupted Blood n’était pas seulement un bug : elle se comportait comme un organisme vivant, échappant aux limites que les développeurs avaient posées au reste de l’univers. Dans les premières heures, la priorité fut d’éteindre l’incendie. Dans les jours qui suivirent, il devint clair que le problème demandait des solutions plus profondes, presque chirurgicales.
Téléportation forcée des joueurs hors des zones infectées
La première réaction fut presque instinctive : éloigner les joueurs des foyers d’infection. Les maîtres du jeu intervinrent pour téléporter massivement les personnages vers des zones supposées “saines”.
Mais cette mesure ne fit que déplacer le problème sans le résoudre.
Beaucoup de joueurs retournaient immédiatement vers les capitales par simple habitude, d’autres s’y rendaient précisément par curiosité, et certains, animés d’un esprit farceur ou simplement désireux de “voir le chaos”, rapportaient involontairement le sang corrompu avec eux.
Le mal voyageait plus vite que les maîtres du jeu ne pouvaient intervenir.
Nettoyage manuel des grandes cités
Quand Forgefer, Hurlevent et Orgrimmar furent transformées en charniers numériques, les développeurs tentèrent un nettoyage plus direct. Ils réinitialisèrent les PNJ essentiels, ressuscitèrent les joueurs massés à l’entrée des villes, et purgèrent les zones les plus touchées.
Les restes de la pandémie
Hélas, la situation leur échappait pour une raison toute simple : les capitales étaient des lieux de passage obligés. En quelques minutes, elles étaient à nouveau envahies par des joueurs asymptomatiques (un animal de compagnie, un familier de chasseur…) qui ramenaient l’infection.
Chaque intervention manuelle n’offrait qu’un répit de courte durée, comme balayer une plage à marée montante.
Quarantaines improvisées : une mesure vouée à l’échec
Blizzard tenta ensuite quelque chose de plus structuré : isoler les zones d’apparition du virus. Des messages officiels conseillaient aux joueurs d’éviter certaines régions, et des maîtres du jeu tentèrent même d’instaurer des périmètres interdits.
Mais World of Warcraft n’était pas un monde où l’on pouvait “forcer” les joueurs à respecter une consigne.
Les plus disciplinés suivaient les recommandations… tandis que beaucoup d’autres prenaient ces zones “interdites” pour un défi, ou un spectacle à ne pas manquer. Certains s’y rendaient précisément pour observer l’épidémie, et finissaient par la propager en repartant.
Les quarantaines échouèrent donc pour la même raison qu’elles échouent parfois dans le monde réel : la mobilité, la curiosité, et les comportements imprévisibles.
Redémarrages de serveurs : une respiration, pas un remède
Blizzard procéda ensuite à des redémarrages massifs.
Cela avait un effet immédiat et spectaculaire : toutes les zones infectées revenaient à un état “propre”.
Mais ce répit ne durait jamais très longtemps.
Le sang corrompu pouvait réapparaître dès qu’un joueur disposant encore du débuff se connectait, ou lorsqu’un familier infecté était invoqué. Les redémarrages étaient l’équivalent numérique d’une coupure générale d’électricité : efficaces pour stopper un incendie, mais inutiles si la source de chaleur continue de brûler.
Le correctif profond : toucher au cœur même de la mécanique
Ce n’est qu’après avoir observé, analysé et tenté plusieurs approches temporaires que l’équipe finit par s’attaquer au problème à la racine.
Il fallait modifier le fonctionnement même du sort utilisé dans le raid de Zul’Gurub pour empêcher le débuff de sortir de son enceinte.
Cela signifiait revisiter des règles internes à l’intelligence artificielle des PNJ, à la gestion des familiers, et au système de propagation des effets négatifs. Un travail délicat, presque artisanal, qui demandait de ne rien casser d’autre dans un jeu déjà immense.
Cette fois, la solution fut durable : le Corrupted Blood ne put plus se propager hors du raid.
L’épidémie prit fin aussi soudainement qu’elle était apparue.
Chronologie d’une semaine en enfer (septembre 2005)
Pour saisir l’ampleur du désastre, il faut comprendre que Blizzard a perdu le contrôle de son propre jeu pendant près de sept jours.
- 13 septembre (Jour J) - Le patient zéro Déploiement de la mise à jour 1.7. Les portes de Zul’Gurub s’ouvrent. Les premières guildes d’élite affrontent Hakkar. Quelques heures plus tard, les premiers chasseurs rentrent en capitale et rappellent leurs familiers infectés. L’épidémie commence silencieusement.
- Jours 1 et 2 - L’effondrement Le sang corrompu se répand de manière exponentielle dans les capitales (Ironforge et Orgrimmar en tête). Le sol se jonche de squelettes. Les canaux de discussion sont saturés de messages de panique. Les joueurs commencent à fuir les villes pour la campagne.
- Jours 3 et 4 - L’échec du confinement Blizzard réalise que le bug ne se résout pas seul. Les Maîtres du Jeu (MJ) tentent d’établir des cordons sanitaires et des zones de quarantaine. Les joueurs, par curiosité ou défiance, brisent les quarantaines. Les redémarrages de serveurs “nettoient” le monde temporairement, mais l’infection revient via les joueurs connectés.
- Jour 5 - L’état de nature Les capitales sont désertes, abandonnées aux PNJ infectés. L’économie du jeu est à l’arrêt. Les joueurs se sont organisés en communautés isolées dans les zones de bas niveau pour survivre. Le “Griefing” (contamination volontaire) atteint son paroxysme.
- 19 septembre (jour 7) - le “Hard Reset” Devant l’impossibilité de contenir le virus par la modération, Blizzard déploie un correctif d’urgence (Hotfix). Le sort d’Hakkar est modifié pour ne plus pouvoir exister en dehors de l’instance de raid. Le monde d’Azeroth reprend son souffle.
La science s’en mêle : un laboratoire comportemental inédit
L’incident attira rapidement l’attention de plusieurs chercheurs - épidémiologistes, sociologues, mathématiciens.
Pourquoi ?
Parce qu’il avait révélé des phénomènes difficiles à modéliser dans le monde réel.
Les comportements humains en état de crise
Les modèles épidémiologiques théoriques supposent souvent :
- un comportement rationnel,
- ou au moins prévisible.
Dans Azeroth, comme dans la vraie vie, les réactions furent tout sauf rationnelles :
- fuite désorganisée,
- entraide improvisée,
- sabotage volontaire,
- résistance aux consignes,
- recherche de zones refuges.
Les asymptomatiques et réservoirs longs
Les PNJ immortels offraient un cas étonnant :
- porteurs permanents,
- non éliminables,
- contagieux de manière continue.
Un modèle idéal pour étudier la crise provoquée par un réservoir d’infection non maîtrisable.
Les super-propagateurs
Les joueurs de haut niveau, très mobiles, furent l’équivalent numérique de voyageurs internationaux.
Une diffusion exponentielle comparable aux épidémies modernes
Sans le vouloir, Blizzard avait créé :
- une population dense,
- des points d’échanges importants,
- une mobilité élevée,
- des interactions sociales constantes.
Soit exactement les ingrédients d’une diffusion réelle.
Après 2020 : un regain d’intérêt scientifique
Lorsque le monde fut confronté à une pandémie bien réelle, de nombreux chercheurs citèrent Corrupted Blood comme :
- un exemple de crise mal anticipée,
- un modèle de comportements incontrôlables,
- un outil d’étude intéressant pour simuler des réactions humaines.
Des articles universitaires revinrent sur cet épisode, montrant qu’un monde virtuel peut produire des données comportementales d’une grande richesse.
Et si la première pandémie moderne avait eu lieu dans un jeu vidéo ?
La question, posée en apparence comme une provocation, n’est pas si fantaisiste.
Cet événement a montré que :
- un monde numérique peut reproduire des dynamiques sociales complexes ;
- la mobilité humaine crée des ponts vers la catastrophe ;
- l’imprévisibilité du comportement individuel rend toute gestion de crise difficile ;
- et qu’une menace invisible - réelle ou virtuelle - provoque toujours les mêmes réflexes humains.
En somme, ce qui s’est produit en 2005 n’était pas simplement un bug.
C’était une démonstration involontaire de la fragilité de toute société interconnectée.
Conclusion
La pandémie sanglante de 2005 reste l’un des épisodes les plus étonnants de l’histoire du jeu vidéo.
Elle a révélé que, même dans un monde créé de toutes pièces :
- les comportements humains restent fidèles à leurs mécanismes ancestraux,
- les systèmes complexes peuvent s’effondrer pour un détail,
- et les interactions numériques peuvent servir à comprendre la réalité.
Azeroth n’a jamais été aussi vivante que durant cette semaine de chaos.
Et c’est précisément ce qui fait de cet incident un moment fondateur, à la frontière du jeu vidéo et de la recherche scientifique.
`).
- Injection des variables : Les valeurs réelles (ex. drôle, les développeurs Java) sont insérées dans le template via une
Map. - Application du template :
promptTemplate.apply(variables)génère un texte complet prêt à être envoyé au modèle. - Appel au modèle de chat :
model.chat(prompt.text())envoie le texte final à Mistral, qui renvoie une réponse humoristique. - Retour du résultat : La réponse textuelle est renvoyée sous forme de
Stringà la couche supérieure (ex. un contrôleur REST).
Exemple de réponse
Pour tester cette méthode je l’ai rattaché à un controller qui lui envoie les paramètre via une requête HTTP http://localhost:8080/joke?subject=ordinateur&adjective=drole
Ce qui me donne la réponse suivante :
Bien sûr ! Voici une blague informatique qui devrait te faire sourire :
**Un ordinateur entre dans un bar...**
Le barman lui dit : *"Désolé, on ne sert pas les ordinateurs ici."*
L'ordinateur répond : *"Mais je suis un *notebook* !"*
*(Blague bonus pour les geeks :)*
**Pourquoi les ordinateurs ont-ils peur du vide ?**
*Parce qu’ils pourraient *crash* !*
Tu en veux une autre ? 😄
la blague générée
l’humour étant tout relatif, je ne m’offusquerait pas si vous ne la trouvez pas drôle.
Méthode memory() — gestion de la mémoire conversationnelle
Cette méthode illustre l’utilisation d’une mémoire de conversation afin de permettre au modèle de conserver le contexte entre plusieurs échanges.
public String memory() {
var chatMemory = MessageWindowChatMemory.withMaxMessages(5);
chatMemory.add(userMessage("Bonjour je m'appelle Erwan"));
AiMessage answer = model.chat(chatMemory.messages()).aiMessage();
System.out.println(answer.text());
chatMemory.add(answer);
chatMemory.add(userMessage("Quel est mon nom ?"));
AiMessage answerWithName = model.chat(chatMemory.messages()).aiMessage();
chatMemory.add(answerWithName);
return answerWithName.text();
}
Fonctionnement étape par étape
- Initialisation de la mémoire :
MessageWindowChatMemory.withMaxMessages(5)crée une fenêtre de mémoire contenant les 5 derniers messages échangés. - Ajout du premier message utilisateur : Le modèle reçoit une première instruction : Hello, my name is Erwan.
- Réponse du modèle :
model.chat(...)renvoie une réponse, ajoutée à la mémoire pour conserver le contexte. - Nouvelle question : L’utilisateur demande ensuite : What is my name?
Comme le message précédent est encore dans la mémoire, le modèle peut répondre correctement (Your name is Erwan). - Retour de la réponse :
Le texte final est renvoyé, illustrant la persistance du contexte conversationnel.
Exemple de réponse
Comme précédemment j’ai branché mon service à un controller pour tester la réponse :
Ton nom est **Erwan** ! 😊
Si tu veux, je peux aussi t'aider à trouver des informations sur l'origine de ton prénom, des idées de surnoms ou d'autres détails. 😉
Tu cherches quelque chose en particulier ?
Méthode ragExample() — utilisation du RAG (Retrieval-Augmented Generation)
Cette méthode démontre comment utiliser les embeddings pour effectuer une recherche sémantique sur un document, avant de générer une réponse contextuelle.
public String ragExample() throws Exception {
Document document = loadDocument(
toPath(),
new TextDocumentParser()
);
DocumentSplitter splitter = DocumentSplitters.recursive(200, 0);
List<TextSegment> segments = splitter.split(document);
List<Embedding> embeddings = embeddingModel.embedAll(segments).content();
EmbeddingStore<TextSegment> embeddingStore = new InMemoryEmbeddingStore<>();
embeddingStore.addAll(embeddings, segments);
String question = "Quel est le nom scientifique des axolotl ?";
Embedding questionEmbedding = embeddingModel.embed(question).content();
EmbeddingSearchRequest embeddingSearchRequest = EmbeddingSearchRequest.builder()
.queryEmbedding(questionEmbedding)
.maxResults(3)
.minScore(0.7)
.build();
List<EmbeddingMatch<TextSegment>> relevantEmbeddings =
embeddingStore.search(embeddingSearchRequest).matches();
String information = relevantEmbeddings.stream()
.map(match -> match.embedded().text())
.collect(Collectors.joining("\n\n"));
PromptTemplate promptTemplate = PromptTemplate.from("""
Voici des informations de contexte :
------------------
{{information}}
------------------
En te basant uniquement sur ces informations et sans connaissances externes,
réponds à la question suivante :
{{question}}
Réponse :
""");
Map<String, Object> promptInputs = new HashMap<>();
promptInputs.put("question", question);
promptInputs.put("information", information);
Prompt prompt = promptTemplate.apply(promptInputs);
// 🔟 Envoi de la requête au modèle de chat
AiMessage aiMessage = model.chat(prompt.toUserMessage()).aiMessage();
return aiMessage.text();
}
static Path toPath() {
try {
URL fileUrl = ChatService.class.getResource("/" + "example-files/about-axolotl.txt");
return Paths.get(fileUrl.toURI());
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
}
Fonctionnement étape par étape
- Chargement du document : Lecture d’un fichier texte (
about-axolotl.txt) depuis les ressources. - Découpage en segments : Le document est découpé en blocs de 200 caractères pour faciliter le traitement.
- Génération des embeddings : Chaque segment est transformé en vecteur numérique via
embeddingModel. - Stockage en mémoire : Les embeddings sont stockés dans un
InMemoryEmbeddingStore. - Recherche sémantique : La question est elle aussi vectorisée, puis comparée à chaque segment via une recherche de proximité.
- Génération du prompt contextuel : Les segments les plus pertinents sont injectés dans un template, servant de base au raisonnement.
- Réponse du modèle : Mistral génère une réponse en se basant uniquement sur le contenu du document, sans connaissances externes.
ici le contenu de mon document est le premier paragraphe de wikipedia sur les axolotl.
Si vous ne connaissez pas ces petites bestioles, c’est par ici que ça se passe :
[
À la découverte de l’axolotl : cette créature étonnante capable d’auto-régénérer ses organes
L’axolotl, mascotte de SFEIR, incarne un amphibien mythique venu du Mexique. Capable de régénérer ses membres et de rester éternellement jeune, il fascine autant qu’il inquiète par son statut d’espèce menacée. À la Ferme Tropicale, Karim Daoues me révèle les secrets de cette créature extraordinaire.
sfeir.dev - Le média incontournable pour les passionnés de tech et d’intelligence artificielleCharlotte Ryssen (Coumont)
![]()
](https://www.sfeir.dev/tendances/a-la-decouverte-de-laxolotl-cette-creature-etonnante-capable-dauto-regenerer-ses-organes/)
Exemple de réponse
Voici la réponse fournit par mon modèle après avoir appelé ma méthode :
Le nom scientifique des axolotl est **Ambystoma mexicanum**.
Méthode streaming() — génération de texte en flux continu
Cette méthode illustre la capacité de Mistral à streamer les réponses en temps réel, pour un rendu fluide côté utilisateur.
public String streaming() {
String userMessage = "Écrit un poème à propos de java et des axolotl faisant 100 mots";
CompletableFuture<ChatResponse> futureResponse = new CompletableFuture<>();
streamingChatModel.chat(userMessage, new StreamingChatResponseHandler() {
@Override
public void onPartialResponse(String partialResponse) {
System.out.print(partialResponse);
}
@Override
public void onCompleteResponse(ChatResponse completeResponse) {
futureResponse.complete(completeResponse);
}
@Override
public void onError(Throwable error) {
futureResponse.completeExceptionally(error);
}
});
AiMessage response = futureResponse.join().aiMessage();
return response.text();
}
Fonctionnement étape par étape
- Envoi du message utilisateur : Le texte à générer est défini sous forme de
String. - Démarrage du flux :
streamingChatModel.chat(...)envoie la requête et ouvre un flux de réponse. - Gestion du flux :
onPartialResponse(): reçoit les fragments de texte à mesure de leur génération.onCompleteResponse(): indique la fin de la réponse.onError(): capture toute erreur éventuelle pendant le streaming.
- Récupération de la réponse complète : Une fois le flux terminé, le texte complet est récupéré via
futureResponse.join().
Exemple de réponse
**Java et les Axolotls**
Dans l’océan des bits, Java danse,
Un langage robuste, sans entrave.
Les axolotls, doux et sans âge,
Nagent en silence, sages et sages.
Le code s’écoule, fluide et pur,
Comme l’eau qui berce leur murmure.
Java compile, les axolotls rêvent,
Dans un monde où tout est possible.
Les exceptions volent, légères,
Mais les axolotls, patients, guettent.
Leur peau lisse, leur regard profond,
Reflètent l’éternité du code.
Java et les axolotls, unis,
Dans un ballet de logique et de vie.
L’un construit, l’autre renaît,
Dans l’infini des serveurs et des flots.
*(100 mots exactement)*
là comme ça on voit pas bien le mode streaming, mais je vous assure que dans la console de mon IDE, c’était bien le cas.
Méthode structuredOutput() - produire des sorties structurées
LangChain4j ne se limite pas à la génération de texte libre. Il est également capable de produire des sorties structurées conformes à un schéma JSON défini, permettant ainsi de convertir directement la réponse du modèle en objet Java.
C’est une fonctionnalité particulièrement utile pour intégrer des résultats dans une logique métier sans phase d’analyse manuelle.
public record Book(String title, String author, int year, boolean available){
}
public Book structuredOutput() throws JsonProcessingException {
ResponseFormat responseFormat = ResponseFormat.builder()
.type(JSON)
.jsonSchema(JsonSchema.builder()
.name("Book")
.rootElement(JsonObjectSchema.builder()
.addStringProperty("title")
.addStringProperty("author")
.addIntegerProperty("year")
.addBooleanProperty("available")
.required("title", "author", "year", "available")
.build())
.build())
.build();
UserMessage userMessage = UserMessage.from("""
"Les Misérables" est un roman écrit par Victor Hugo en 1862.
Cet ouvrage monumental est aujourd’hui encore disponible en librairie.
""");
ChatRequest chatRequest = ChatRequest.builder()
.responseFormat(responseFormat)
.messages(userMessage)
.build();
ChatResponse chatResponse = model.chat(chatRequest);
String output = chatResponse.aiMessage().text();
return new ObjectMapper().readValue(output, Book.class);
}
Fonctionnement étape par étape
- Définir le format attendu :
- Le format de sortie est défini comme JSON.
- Le schéma JSON décrit les propriétés attendues pour un objet
Book. - Le modèle saura ainsi structurer sa réponse selon ce schéma.
- Le message d’entrée :
- Le message fourni contient une description textuelle libre.
- LangChain4j analysera ce texte pour en extraire les éléments correspondant au schéma défini.
- Exploitation du résultat :
- Le modèle renvoie une réponse au format JSON respectant le schéma.
- Cette réponse est ensuite convertie directement en instance Java (
Book) via Jackson. - On obtient ainsi un objet parfaitement typé, prêt à être utilisé dans la logique applicative Spring.
Exemple de réponse
{
"title": "Les Misérables",
"author": "Victor Hugo",
"year": 1862,
"available": true
}
Conclusion
L’intégration de LangChain4j dans une application Spring Boot ouvre la voie à une nouvelle génération d’applications Java, capables d’interagir intelligemment avec des modèles de langage puissants comme Mistral, le fleuron de l’IA française 🇫🇷.
L’exemple présenté démontre à quel point la mise en œuvre est fluide : une configuration simple, quelques annotations, et l’on dispose déjà d’une IA conversationnelle, capable de mémoire, de raisonnement contextuel et même de traitement de texte structuré via le RAG.
LangChain4j offre un pont entre le monde Java traditionnel et l’intelligence artificielle moderne, sans renier les principes de robustesse, de modularité et de clarté qui font la force de l’écosystème Spring.
Bien que l’outil soit encore jeune, son potentiel est considérable : en s’appuyant sur des modèles ouverts et souverains comme Mistral, il ouvre la voie à une intégration éthique et maîtrisée de l’IA dans les architectures logicielles d’entreprise.



