# Information générale ## Description du PoC Ce projet se concentre sur la mise en place d'un PoC visant à déclencher des actions concrètes à partir d'un NLP/LLM, afin d'exécuter des tâches spécifiques en fonction de l'intention par déclenchement (scripts python). Une deuxième partie du PoC se concentre sur la présentation d'un modèle pour analyser des données non structurées source par un LLM et les convertir en un format JSON pour du parsing. ## Objectif du projet (première partie) L'objectif est de communiquer avec un chatbot sous l'environnement Rasa, permettant : - La communication avec le LLM Llama3 (installé en local au travers de Ollama) lors d'une communication générale. - Le déclenchement d'actions Python (scripts) lorsque les intentions suivantes sont reconnues : - **Intention de connaître les informations d'une ville suisse** depuis le NPA, déclenchant l'accès à une API afin de récupérer ces informations. - **Intention de connaître la signification d'un prénom**, interrogeant les connaissances du LLM llama3 avec une demande de prompt personnalisé afin de donner la signification ainsi que trois personnes historiquement célèbre ayant porté ce nom. - **Intention de générer un profil d'une personne aléatoirement**, en interrogeant une API. En fonction des intentions détectées par le NLU, les différentes actions sont routées et jouées. ## Réalisation - Installer et configurer Ollama - Créer et configurer l'environnement Rasa - Créer les intentions, les stories, les règles et leur liaison aux actions. - Créer les actions déclenchées en Python. - Entraîner le modèle sur les intentions dans Rasa. - Monter l'infrastructure. - Interagir avec le chatbot et tester la bonne exécution des actions. # Réalisation du PoC ## Prérequis Les technologies utilisées sont accessibles librement ou gratuitement (API, technologies). Les programmes suivants doivent être installés : - Python 3.10 doit être installé sur la machine. ## Installation d'Ollama Ollama est une plateforme conçue pour faciliter l'utilisation et le déploiement de modèles de langage de type LLM (Large Language Models) localement sur des machines. Ollama propose une interface simple pour interagir avec différents modèles de langage. L'installation d'Ollama va nous permettre d'utiliser localement un LLM. Ce concept étant fort intéressant pour la création d'un privacy LLM. - Installer Ollama depuis cet [https://ollama.com/](https://ollama.com/). - Téléchargez le modèle Llama 3.2 (par défaut, le modèle contenant 3,2 milliards de paramètres est téléchargé). ```sh ollama pull llama3.2 ``` - Essayer d'exécuter et de converser avec le modèle. ```sh ollama run llama3.2 >>> Hello, comment ça va ? 'Bonjour ! Je vais bien, merci pour ta question. C est un plaisir de discuter avec toi en français ! Comment puis-je t aider aujourd hui ?' ``` > Ollama offre l'accès à sont API sur http://localhost:11434/api/generate ## Créer le projet rasa Rasa est une plateforme open source de développement de chatbots et d'assistants virtuels qui utilise le traitement du langage naturel (NLP) pour comprendre les intentions des utilisateurs et gérer les dialogues. Elle permet de créer des modèles personnalisés et d'exécuter des actions en réponse aux demandes des utilisateurs, tout en offrant des fonctionnalités avancées comme la gestion des dialogues contextuels et l'intégration avec diverses API. ### Créer un environnement virtuel python ```sh python3.10 -m venv ./venv source ./venv/bin/activate ``` > Dans cet exemple, j'utilise la version 3.10 de Python, car c'est celle qui est le mieux supportée. ### Installer les dépendances ```sh pip install rasa ``` ### Création d'un nouveau projet rasa ```sh mkdir rasa && cd rasa rasa init . ├── actions │ ├── __init__.py │ ├── __pycache__ │ │ ├── __init__.cpython-38.pyc │ │ └── actions.cpython-38.pyc │ └── actions.py ├── config.yml ├── credentials.yml ├── data │ ├── nlu.yml │ ├── rules.yml │ └── stories.yml ├── domain.yml ├── endpoints.yml ├── models │ └── 20241008-123922-metal-event.tar.gz └── tests └── test_stories.yml ``` ## Construire le projet ### nlu.yml Le fichier `nlu.yml`permet de spécifier les intentions qui seront entraîner par le modèle NLU (Natural Language Processing) : Nous avons donc 4 intentions : - Générer un profil utilisateur par défaut (API) - Discuter de manière générale avec le LLM - Demander au LLM la signification d'un prénom - Récupérer les informations d'une ville Suisse depuis un NPA (API) ```yaml version: "3.1" nlu: - intent: intent_randomuser examples: | - Génère moi un profil utilisateur - Crée un profil d'utilisateur - Peux-tu me donner un profil utilisateur aléatoire ? - J'ai besoin d'un profil utilisateur - Donne-moi un exemple de profil utilisateur - Fais-moi un profil utilisateur - Génère un profil utilisateur au hasard - Crée un utilisateur aléatoire pour moi - Est-ce que tu peux créer un profil utilisateur ? - intent: intent_ask_llama examples: | - Qu'est-ce que tu sais sur les modèles de langage ? - Comment fonctionne un ordinateur ? - Quelle est la capitale de la France ? - intent: intent_firstname_signification_by_llama examples: | - Que signifie mon prénom [Guillaume](firstname) ? - Peux-tu me dire ce que signifie le prénom [Juliette](firstname) ? - Quelle est la signification de [Emma](firstname) ? - Explique-moi la signification du prénom [Lucas](firstname). - Que veut dire le prénom [Sophie](firstname) ? - Quelle est l'origine du prénom [Maxime](firstname) ? - Dis-moi la signification de [Chloé](firstname). - Que représente le prénom [Thomas](firstname) ? - Je voudrais savoir ce que signifie le prénom [Camille](firstname). - Que signifie le prénom [Alice](firstname) ? - intent: intent_swiss_zipcode examples: | - Quel est le code postal en Suisse de ce numéro [1977](zipcode) - Donne-moi le code postal de [1977](zipcode) - Quel est le code postal pour [1977](zipcode) ? ``` > À noter que dans des expressions telle que ```[1977](zipcode)```, le modèle NLU sera capable de reconnaître que 1977 représente une valeur variable, ce qui lui permettra d'attribuer les valeurs de code postal fournies à la variable `zipcode`. ### stories.yml Le fichier `stories.yml` permet de définir les scénarios d'interaction en spécifiant les séquences d'actions et d'intentions que le modèle doit apprendre pour simuler un dialogue. Nous sommes face à des stories très simple, soit : - Reconnaître l'intention - Exécuter l'action associée ```yaml version: "3.1" stories: - story: request_randomuser steps: - intent: intent_randomuser - action: action_randomuser - story: request_ask_llama steps: - intent: intent_ask_llama - action: action_ask_llama - story: request_firstname_signification_by_llama steps: - intent: intent_firstname_signification_by_llama - action: action_firstname_signification_by_llama - story: request_swiss_zipcode steps: - intent: intent_swiss_zipcode - action: action_swiss_zipcode ``` > Il est tout à fait possible de complexifier les stories comme : > - Rajouter des réponses dans le scénario de conversation ### rules.yml Le fichier `rules.yml` permet de spécifier les règles qui guideront le comportement du modèle, en définissant des séquences d'actions et de conditions à suivre lors des interactions avec les utilisateurs.: Nous avons donc une séquence, qui dans l'ordre va repérer les intentions pour : 1. Générer un profil utilisateur par défaut (API) 2. Discuter de manière générale avec le LLM 3. Demander au LLM la signification d'un prénom 4. Récupérer les informations d'une ville Suisse depuis un NPA (API) ```yaml version: "3.1" rules: - rule: rule__randomuser steps: - intent: intent_randomuser - action: action_randomuser - rule: rule_swiss_zipcode steps: - intent: intent_swiss_zipcode - action: action_swiss_zipcode - rule: rule_firstname_signification_by_llama steps: - intent: intent_firstname_signification_by_llama - action: action_firstname_signification_by_llama - rule: General question redirect to LLM steps: - intent: intent_ask_llama - action: action_ask_llama ``` > Il est tout à fait possible de complexifier les rules comme : > - Créer des boucles (remplissage de formulaires, input > - Rajouter des conditions ### domain.yml Le fichier `domain.ym` définit les éléments essentiels de l'application Rasa, y compris les intentions, les entités, les actions, les réponses et les configurations de dialogue, permettant ainsi au modèle de gérer les interactions avec les utilisateurs. Il faut renseigner les éléments dans le fichier `domain.ym`. ```yaml version: "3.1" intents: - intent_randomuser - intent_ask_llama - intent_swiss_zipcode - intent_firstname_signification_by_llama actions: - action_randomuser - action_ask_llama - action_swiss_zipcode - action_firstname_signification_by_llama ``` ### actions.py Le fichier `actions.py` contient les actions personnalisées que le modèle Rasa peut exécuter en réponse à des requêtes utilisateur. Ce fichier permet de définir des fonctions qui réalisent des tâches spécifiques, comme l'appel à des API, le traitement de données ou l'interaction avec des bases de données. On retrouve nos fonctions : - L'appel à `action_ask_llama`pour les questions générales ```python # Action to ask to the Llama model class ActionAskLlama(Action): def name(self): return "action_ask_llama" def run(self, dispatcher, tracker, domain): user_input = tracker.latest_message.get('text') # Call API (Streaming) url = "http://localhost:11434/api/generate" payload = { "model": "llama3.1", "prompt": user_input } try: response = requests.post(url, json=payload, stream=True) if response.status_code == 200: llm_response = "" # Read the response line by line for line in response.iter_lines(): if line: json_line = line.decode('utf-8') json_data = json.loads(json_line) # Done flag for defining the end of the response if json_data.get("done"): break llm_response += json_data.get("response", "") dispatcher.utter_message(text=llm_response.strip()) else: dispatcher.utter_message(text=f"Erreur lors de l'appel à Ollama: {response.status_code} - {response.text}") except Exception as e: dispatcher.utter_message(text=f"Une exception est survenue : {str(e)}") return [] ``` - L'appel à `action_randomuser`pour générer un profil aléatoire (API) ```python class ActionRandomuser(Action): def name(self): return "action_randomuser" ... ``` - L'appel à `action_swiss_zipcode`pour récupérer les information d'une ville Suisse depuis un NPA ```python class ActionGetZipcode(Action): def name(self) -> str: return "action_swiss_zipcode" def run(self, dispatcher, tracker, domain): zipcode = next(tracker.get_latest_entity_values("zipcode"), None) with open('../logs.txt', 'a') as f: f.write(zipcode + '\n') if zipcode: url = f"https://api.zippopotam.us/ch/{zipcode}" response = requests.get(url) if response.status_code == 200: data = response.json() places = data.get("places", []) if places: place_info = places[0] city = place_info.get("place name") state = place_info.get("state") message = f"Le code postal {zipcode} correspond à {city}, {state}." else: message = f"Aucune information trouvée pour le code postal {zipcode}." else: message = f"Erreur lors de l'appel à l'API : {response.status_code}." else: message = "Je n'ai pas pu extraire le code postal." dispatcher.utter_message(text=message) return [] ``` - L'appel à `action_firstname_signification_by_llama`pour connaître la signification d'un prénom. ```python class ActionFirstnameSignificationLlama(Action): def name(self): return "action_firstname_signification_by_llama" ... ``` ## Entraîner le modèle rasa Pour entraîner le modèle Rasa, utilisez la commande suivante : ```sh rasa train ``` ## Tester le chatbot ### Lancer le chatbot Pour teser le projet, exécuter la commande suivantes : ```sh rasa run actions & rasa shell ``` Cette commande : - Démarre le serveur des actions - Exécuter un shell interractif ### Tester le chabot ![chatbot_test](./doc_assets/chatbot_test.png) > À noter qu'une des fonctionnalités d'un NLU est l'automatisation et la transparence de la langue. C'est pourquoi la question en anglais est comprise, même si l'intention est en français. ## Objectif du projet (deuxième partie) Cette deuxième partie se concentre sur un PoC de parsing de données non structurées en JSON à l'aide du LLM. Les informations sur les cours, présentées sous forme de texte naturel, doivent être comprises et mises en relation avec un modèle JSON fourni en prompt, accompagné d'un guide expliquant ce que doit faire le LLM. Le LLM doit alors générer uniquement un JSON en sortie. ### Code Python ```python import openai # API OpenAI client = openai.Client(api_key='XXXXXXXXX') # Texte à convertir en JSON text = 'Le cours "Python ML Avancé" est un programme dédié à l\'apprentissage du Machine Learning et de la régression en Python, proposé chaque lundi. Il est recommandé d\'avoir une bonne maîtrise des bases du Python et du Machine Learning débutant. Le tarif du cours est de 1000 euros.' # Modèle JSON model = ''' { "name": "Data Science avec Python", "description": "Cours introductif sur l'analyse de données et les bases du machine learning avec Python", "day": "Mercredi", "prerequest": "Aucun prérequis nécessaire", "price": 800 } ''' # Appel de l'API OpenAI response = client.chat.completions.create( model="gpt-3.5-turbo", messages=[ { "role": "system", "content": f"Convert this text {text} according to this JSON model: {model}", }, { "role": "system", "content": "Give only the output without text or explanation, I will use it in a JSON parser.", } ], max_tokens=2000, ) # Affichage de la réponse print(response.choices[0].message.content) ``` Pour teser le projet, exécuter la commande suivantes : ```sh python parsingDemo.py ``` Après exécution par le LLM, la sortie générle est la suivante : ```sh { "name": "Python ML Avancé", "description": "Cours dédié à l'apprentissage du Machine Learning et de la régression en Python", "day": "Lundi", "prerequest": "Bonne maîtrise des bases du Python et du Machine Learning débutant recommandée", "price": 1000 } ``` > Il est crucial de bien guider le prompt afin d'éviter toute interférence lors du parsing.