TP 3 - Application REST avec LangChain4j

Retour TPs

Ce TP vous donnera une illustration pratique en Java (avec Jakarta EE) de l'utilisation d'une API IA. Vous allez utiliser l'API de Gemini.

N'oubliez pas d'utiliser Git et GitHub.

Support de cours

Description de l'application

Vous allez écrire une application REST avec un endpoint d'URL "api/guide_touristique/ville_ou_pays". La réponse à une requête REST GET contiendra un document JSON qui donnera des informations touristiques sur une ville ou un pays.

Par exemple, pour avoir des informations touristiques sur le Maroc, l'URL sera "api/guide_touristique/ville_ou_pays/Maroc" ; pour la ville de Paris, ce sera "api/guide_touristique/ville_ou_pays/Paris".

Création de l'application REST

Nouveau projet Jakarta EE de type REST (pas Web).

File > New > Project
Generators : Jakarta EE

  • Name : xxx (n'oubliez pas d'y inclure votre nom)
  • Location : xxxx (choisissez un répertoire dans lequel viendra le répertoire de l'application)
  • Laisser coché « Create Git repository »
  • Template : REST service
  • Application server : Payara 6.xxxx

Commit Git pour l'état initial du projet, et push sur GitHub...

Lisez cette page pour une rapide introduction à JAX-RS, la spécification de Jakarta EE pour écrire une application REST, pour comprendre le code généré par l'IDE et pour le tester.

Code de l'application

Avec JAX-RS, les endpoints sont créés dans des classes de ressources, du modèle de la classe de ressource générée par l'IDE.

Vous allez créer une nouvelle classe de ressource GuideTouristiquResource annotée @Path("/guide")

Cette classe a une méthode annotée @GET @Path("lieu/{ville_ou_pays}")qui retourne un document JSON qui contient une liste JSON des 2 (pour limiter le nombre de tokens de la réponse, et donc le coût des tests) principaux endroits à visiter dans un pays. Cette méthode a un paramètre de type String qui contient le nom du lieu sur lequel on veut des informations (une ville ou un pays).

Pour cela, prenez exemple sur l'introduction à JAX-RS que vous venez de faire.

Testez en retournant la valeur du paramètre pour la méthode.

Commit et push.

Utiliser l'API du LLM

Maintenant vous allez demander au LLM de faire le guide touristique. C'est lui qui va retourner des informations sur la ville ou le pays passé en paramètre.

Voici ce qu'il faut faire :

  1. Modifiez pom.xml pour pouvoir utiliser l'API de Gemini (revoyez le TP 2).
  2. Créer une classe de configuration REST pour indiquer en particulier avec quels URLs cette application sera concernée. Vous l'avez déjà écrite (HelloApplication). Il serait sans doute préférable de la renommer mais vous pouvez ne rien changer.
  3. La ressource est aussi déjà créée (GuideTouristiquResource). Il reste juste à modifier le code de la méthode villeOuPays pour utiliser l'API du LLM.
  4. Vous n'allez pas écrire le code qui fait l'interface avec l'API du LLM dans la classe GuideTouristiquResource. Comme ce que vous avez fait dans le TP 2 avec le backing bean, il vaut mieux écrire cette classe à part et vous l'injecterez dans GuideTouristiquResource. En fait, vous avez déjà écrit cette classe dans le TP 2, à quelques modifications près.
  5. Prévoyez une classe d'exception pour le cas où il y aurait un problème dans les échanges avec le LLM.
  6. Comme pour le TP 2 vous allez créer une interface qui sera implémentée par LangChain4j, et qui contiendra la méthode qui sera utilisée pour envoyer des requêtes au LLM, et recevoir ses réponses.

La différence avec le TP 2 est qu'il n'y a qu'un seul rôle système, celui de guide touristique. Pour le cas où on voudrait ajouter d'autres façons de dialoguer avec le LLM, on va ajouter ce rôle directement dans l'interface (point 6 ci-dessus). Aidez-vous du support de cours sur LangChain4j pour savoir comment ajouter un rôle système (un message système) dans une telle interface avec @SystemMessage. Dans ce message système vous demandez au LLM de vous indiquer les 2 principaux endroits à visiter dans le lieu, ainsi que le prix moyen d'un repas dans la devise du pays. Vous lui dites aussi que vous voulez une réponse au format JSON, avec exactement ce format :

{
  "ville_ou_pays": "nom de la ville ou du pays",
  "endroits_a_visiter": ["endroit 1", "endroit 2"],
  "prix_moyen_repas": "<prix> <devise du pays>"
}

Vous pouvez aussi ajouter un exemple de ce que vous voulez comme réponse, mais le LLM devrait s'en sortir sans.

Utilisez la réponse de l'API dans la méthode REST de la classe de ressource. Inspirez-vous du code généré par l'IDE et de cette page.

Tester l'application

Pour tester, vous pouvez tout simplement taper l'URL de la ressource dans votre navigateur en remplaçant le <context-path> ci-dessous pour que l'URL lance votre application) : http://localhost:8080/<context-path>/api/guide/lieu/France

Explications pour cet URL :

  • Pour avoir le contexte, il suffit de lancer l'application et d'examiner l'URL de lancement ; le contexte est la partie qui est comprise entre "localhost:8080/" et "/index.xhtml", par exemple "xxxxxxxxx-1.0-SNAPSHOT" si l'URL de lancement est "http://localhost:8080/xxxxxxxxx-1.0-SNAPSHOT/index.xhtml".
  • "api" vient de la classe de configuration REST générée par l'IDE.
  • "lieu" vient de l'annotation de la méthode associée à la requête GET (dans la classe de ressource).
  • Evidemment vous pouvez choisir d'autres valeurs en modifiant le code ou le nom de l'application.

Voici le type d'affichage que vous devriez avoir dans le navigateur (le format exact dépend des noms que vous avez choisis pour l'exemple que vous avez donné dans le message système du "Guide touristique") :
{ "ville_ou_pays": "France", "endroits_a_visiter": ["Eiffel Tower", "Louvre Museum"], "prix_moyen_repas": 30 }

Dans l'email que vous enverrez pour signaler que vous avez fini le TP, écrivez l'URL et la réponse du LLM.

Test avec page HTML (optionnel)

Si vous avez un peu de temps (cette partie est optionnelle) vous pouvez aussi utiliser cette page HTML. ; créez un fichier HTML, copiez le code cette page et modifier le <context-path> dans l'URL de la requête GET pour qu'il corresponde à celui de votre application. Puisque cette page est locale vous devrez aussi éviter le problème de CORS en ajoutant un header à la réponse du serveur (un avantage d'utiliser Response plutôt que String comme type retour d'une méthode de ressource) :

Response.ResponseBuilder responseBuilder = Response.ok(reponseJson);
responseBuilder.header("Cache-Control", "no-store, no-cache, must-revalidate, max-age=0");
responseBuilder.header("Pragma", "no-cache");
responseBuilder.header("Expires", "0");
responseBuilder.header("Access-Control-Allow-Origin", "*");
return responseBuilder.build();

J'ai ajouté d'autres headers (pour éviter le cache des pages) pour vous montrer comment faire.

Correction

Classe de ressource
Classe de configuration REST
Interface GuideTouristique.java
LlmClientForGuideTouristique.java

Retour TPs