7 minute read

perroquet multi couleurs avec un robot @wildagsx

Je vous propose la suite de l’article prĂ©cĂ©dent nous ayant permis la dĂ©couverte de LangChain4j au travers de Quarkus.

â„č Je vous laisse donc y jeter un oeil pour toute la phase d’installation, de prĂ©requis nĂ©cessaires Ă  la bonne comprĂ©hension de cet article.

Lors de ce premier article, nous avons vu ensemble comment dĂ©velopper notre premier chat bot. Celui-ci Ă©tait trĂšs simple et il fallait attendre que le modĂšle distant fabrique l’ensemble de la rĂ©ponse avant de l’avoir en retour. Pas trĂšs pratique et convivial.

Cette fois-ci, je vous propose d’ajouter quelques fonctionnalitĂ©s rendant notre chat bot plus “intelligent”.
On va donc le faire se comporter comme un chat bot normal : streamer sa rĂ©ponse. On va aussi lui ajouter un peu de contexte afin qu’il connaisse plus de choses essentielles 😉 !

â„č L’ensemble du code source se trouve dans le repository GitHub discover-langchain4j

🌊 Activation du mode streaming

C’est la premiĂšre fonctionnalitĂ© que nous allons rajouter : cela permet de rendre le bot plus convivial et de ne pas avoir Ă  attendre sans trop savoir quand il va nous rĂ©pondre 😅.
Pour activer cette fonctionnalitĂ©, c’est assez simple : nous allons utiliser Mutiny. Mais c’est quoi me direz-vous đŸ€š ?
En deux mots : cela vous permet d’ajouter une notion d’asynchronisme dans votre dĂ©veloppement et de basculer dans ce que l’on appelle la programmation reactive.

L’objectif ? Permettre Ă  notre IA d’envoyer son dĂ©but de rĂ©ponse avant mĂȘme d’avoir envoyĂ© l’ensemble de la rĂ©ponse.

🔀 Ajout de l’asynchronisme

Quarkus et son extension quarkus-langchain4j vont encore grandement nous aider.

â„č Nous repartons du code de l’article prĂ©cĂ©dent, si vous souhaitez plus de dĂ©tails n’hĂ©sitez pas Ă  vous reporter Ă  l’article 😉.

Nous allons donc modifier notre service OllamaService.java pour qu’il supporte le mode streaming :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package fr.wilda.quarkus;

import dev.langchain4j.service.SystemMessage;
import dev.langchain4j.service.UserMessage;
import io.quarkiverse.langchain4j.RegisterAiService;
import io.smallrye.mutiny.Multi;

// AI service bean registration
@RegisterAiService
public interface OllamaAIService {
  
  // Context message
  @SystemMessage("You are an AI assistant.")  
  // Prompt customisation
  @UserMessage("Answer as best possible to the following question: {question}. The answer must be in a style of a virtual assistant and use emoji.")
  String askAQuestion(String question);

  // Context message
  @SystemMessage("You are an AI assistant.")  
  // Prompt customisation
  @UserMessage("Answer as best possible to the following question: {question}. The answer must be in a style of a virtual assistant and use emoji.")
  // Multi use is enough to activate streaming mode
  Multi<String> askAQuestionStreamingMode(String question);

}

⚠ Notez-bien ici l’utilisation de l’interface io.smallrye.mutiny.Multi qui permet “d’activer” le mode streaming. L’extension se charge de l’activer lors de ses requĂȘtes au modĂšle 😉.
A noter que seul le type String est, pour l’instant, supportĂ© pour le mode streaming mais des Ă©tudes d’évolutions sont en cours ⚠

Maintenant, nous allons faire Ă©voluer notre partie API pour qu’elle puisse profiter de cette arrivĂ©e d’informations au fil de l’eau.

đŸ§© Modification de l’API Rest

L’idĂ©e est de proposer une ressource qui va ĂȘtre streamĂ©e au fur et Ă  mesure. On met donc Ă  jour la classe AIAssistant qui expose le endpoint hal9000.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package fr.wilda.quarkus;

import org.jboss.resteasy.reactive.RestQuery;
import io.smallrye.mutiny.Multi;
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;

// Endpoint root path
@Path("hal9000")
public class AIAssistant {

  // AI Service injection to use it later
  @Inject
  OllamaAIService ollamaAIService;

  // ask resource exposition with POST method
  @Path("ask")
  @POST
  public String ask(String question) {
    // Call the Mistral AI chat model
    return ollamaAIService.askAQuestion(question);
  }

  // Stream response
  @Path("streaming")
  @GET
  public Multi<String> streaming(@RestQuery("question") String question) {
    // Call the Mistral AI chat model
    return ollamaAIService.askAQuestionStreamingMode(question);
  }
}

On a rajouté une ressource streaming qui retourne Multi<String>, Quarkus fera le reste.
Ensuite il suffit d’aller sur l’URL http://localhost:8080/hal9000/streaming?question="What is the answer to the Ultimate Question of Life,the Universe, and Everything?"

👌 Ajoutons un peu de contexte

L’intelligence des diffĂ©rents modĂšles que l’on utilise dĂ©pend grandement des donnĂ©es utilisĂ©es mais aussi de quand date la derniĂšre indexation de ces donnĂ©es (oui je sais que ce n’est pas le terme standard mais ça permet de se comprendre 😉).

Essayons ce prompt, voulez-vous : Can you tell me more about Stéphane Philippart?

Voilà la réponse donnée:

1
2
3
4
👋 Hello! StĂ©phane Philippart is a renowned figure in the tech industry, particularly in Belgium.
🇧đŸ‡ȘđŸ’» He's known for his expertise in Information Technology (IT) and Digital Transformation. StĂ©phane has spent over two decades in the tech sector, holding various key positions in leading companies.
🕒✹ His achievements include co-founding several successful startups and contributing significantly to their growth. 
He's also a sought-after speaker at industry events, sharing his insights on digital transformation trends._

Flatteur mais trĂšs loin de la rĂ©alitĂ© non đŸ€š ?

đŸ—ƒïž Le RAG Ă  la rescousse

Rien d’étonnant dans cette rĂ©ponse :

  • je ne suis pas trĂšs connu et donc pas Ă©tonnant de pas avoir beaucoup de donnĂ©es sur moi
  • mon prĂ©nom, assez commun, fait que l’hallucination avec d’autres StĂ©phane n’est pas Ă©tonnante
  • la plupart des Ă©lĂ©ments publiques datent de 2-3 ans et souvent les modĂšles ont Ă©tĂ© entraĂźnĂ©s sur des donnĂ©es plus anciennes

En quoi le RAG va nous aider ?
Le RAG ou encore Retrieval Augmented Generation va vous permettre d’ajouter des donnĂ©es non connues de votre modĂšle pour lui donner un contexte qui correspond Ă  votre domaine fonctionnel. Pour ajouter ce contexte on va prendre une source de donnĂ©es, par exemple des fichiers, puis les transformer dans un format permettant une recherche par similitudes, dans ce cas une base de donnĂ©es vectorielle.

Une fois ce contenu ajoutĂ©, lors de requĂȘtes envoyĂ©es au modĂšle, celui-ci va pouvoir se baser sur ces donnĂ©es supplĂ©mentaires pour contextualiser sa rĂ©ponse.

Le RAG se diffĂ©rencie d’une autre technique, le transfert learning, par le fait que l’on utilise le modĂšle tel quel en lui ajoutant des donnĂ©es / du contexte.
Le transfert learning va consister Ă  rĂ©-entrainer un modĂšle pour un use case diffĂ©rent de celui d’origine, cela demande donc plus de calculs et une phase d’entraĂźnement lĂ  oĂč le RAG se fait au runtime.

📃 Les donnĂ©es Ă  rajouter

Je vous l’ai dit, on peut choisir plusieurs types de donnĂ©es. Dans mon cas, je vais choisir un fichier texte.

1
2
Stéphane Philippart is a world-renowned developer advocate in the field of cloud computing. He is also the CTO of Tours' biggest meetup.
Tours is well known for its famous rillettes.

đŸ§© Activation du RAG

Pour utiliser le RAG il va falloir deux choses :

  • un moyen de transformer nos donnĂ©es en vecteurs
  • un moyen d’ajouter ces donnĂ©es dans la chaĂźne d’interrogation de notre modĂšle

LĂ  encore, vous vous en doutez, Quarkus et LangChain4j vont nous ĂȘtre d’un grand secours !

1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- To add RAG capabilities -->
<dependency>
    <groupId>io.quarkiverse.langchain4j</groupId>
    <artifactId>quarkus-langchain4j-easy-rag</artifactId>
    <version>0.13.0</version>
</dependency>  

<!-- Inner process embedding model -->
<dependency>
    <groupId>dev.langchain4j</groupId>
    <artifactId>langchain4j-embeddings-all-minilm-l6-v2-q</artifactId>
    <version>0.30.0</version>
</dependency>

Merci Ă  l’extension Quarkus qui, par le simple ajout de la dĂ©pendance quarkus-langchain4j-easy-rag, active le mode Easy RAG.

Notez l’ajout de la dĂ©pendance dev.langchain4j:langchain4j-embeddings-all-minilm-l6-v2-q qui me permet d’avoir un embedding model in process. J’ai choisi cela plutĂŽt que d’utiliser le endpoint d’embedding de Mistral car en mode Ollama il n’est pas accessible avec LangChain4j.

Au chargement de l’application on voit que le mode RAG est activĂ© avec les bonnes donnĂ©es :

1
2024-05-05 20:12:23,987 INFO  [io.qua.lan.eas.run.EasyRagRecorder] (Quarkus Main Thread) Ingesting documents from path: ./src/main/resources/rag/, path matcher = glob:**, recursive = true

RĂ©essayons de demander au modĂšle s’il connaĂźt des choses sur StĂ©phane Philippart !
L’appel Ă  l’URL http://localhost:8080/hal9000/streaming?question="Can you tell me more about StĂ©phane Philippart?" donne cette fois :

1
2
3
🌐 Hey there! StĂ©phane Philippart,đŸ‘šâ€đŸ’» is a globally recognized developer advocate in the cloud computing domain! *claps* 
His expertise is highly sought after, making him a key figure in this innovative field.💡Moreover, StĂ©phane holds an impressive title as the CTO of Tours' biggest meetup!
🏱 This city in France, famously known for its scrumptious rillettes,đŸ„“đŸ˜‹ is where he makes a significant impact`

Pas forcĂ©ment plus vrai mais mieux quand mĂȘme non ? 😉

⚠ Ce petit exemple doit vous faire allumer quelques alertes dans votre cerveau : on ne peut dĂ©finitivement pas faire confiance aux rĂ©sultats d’une IA que l’on peut si facilement biaiser en quelques lignes de codes ⚠

En conclusion

J’espĂšre que vous avez pu, simplement, vous rendre compte comme il est assez facile avec Quarkus et LangChain4j d’ajouter des capacitĂ©s Ă  notre application de chat bot. Je vais continuer Ă  ajouter quelques autres choses plus ou moins utiles dans les articles suivants 😉.

Si vous ĂȘtes arrivĂ©s jusque lĂ  merci de m’avoir lu et si il y a des coquilles n’hĂ©sitez pas Ă  me faire une issue ou PR 😊.

Merci à ma relectrice, Fanny, qui vous permet de lire cet article sans avoir trop les yeux qui saignent 😘.

L’ensemble des sources des exemples est disponible dans le repository GitHub langchain4j-discovery.

Comments