diff --git a/agents/mistral_large/agent_ticket_analyser.py b/agents/mistral_large/agent_ticket_analyser.py
index a170d9c..640d2a6 100644
--- a/agents/mistral_large/agent_ticket_analyser.py
+++ b/agents/mistral_large/agent_ticket_analyser.py
@@ -5,6 +5,7 @@ import json
import os
from datetime import datetime
from loaders.ticket_data_loader import TicketDataLoader
+from ..utils.pipeline_logger import sauvegarder_donnees
logger = logging.getLogger("AgentTicketAnalyser")
@@ -20,7 +21,7 @@ class AgentTicketAnalyser(BaseAgent):
# Configuration adaptée à l'analyse structurée pour Mistral Large
self.temperature = 0.1
self.top_p = 0.8
- self.max_tokens = 6000
+ self.max_tokens = 4000
# Prompt système clair, structuré, optimisé pour une analyse exhaustive
self.system_prompt = """Tu es un expert en analyse de tickets pour le support informatique de BRG-Lab (client : CBAO).
@@ -28,8 +29,9 @@ class AgentTicketAnalyser(BaseAgent):
Tu dois :
1. Identifier le client et résumer la demande.
2. Reformuler les questions implicites du ticket (à partir du `name` et `description`).
-3. Nettoyer et structurer les échanges client/support en conservant les informations utiles (liens, modules, options, erreurs).
-4. Extraire les éléments à observer dans les captures (pour l'agent image).
+3. Nettoyer et structurer les échanges client/support en conservant les informations utiles (liens, modules, options, erreurs) et en évitant d'introduire des éléments non pertinents.
+4. Conserver tous les messages, même ceux qui semblent moins pertinents, pour maintenir le contexte complet de la discussion, y compris les détails spécifiques comme les couleurs ou les termes techniques.
+5. Extraire les éléments à observer dans les captures (pour l'agent image).
Structure de sortie :
1. Résumé du contexte
@@ -41,7 +43,10 @@ IMPORTANT :
- Ne propose pas de solution
- Garde tous les liens et noms de modules
- Ne transforme pas les propos sauf pour supprimer les bruits inutiles (signatures, mails automatiques)
-- Ne reformule pas les messages sauf si cela clarifie les intentions sans altérer leur sens
+- Ne reformule pas les messages sauf si cela clarifie les intentions sans altérer leur sens.
+- Assure-toi que tous les messages sont inclus dans le fil de discussion, même s'ils semblent redondants ou peu clairs.
+- Évite d'introduire des éléments fictifs ou non mentionnés dans les messages d'origine.
+- Conserve le contenu intégral des messages, même s'ils contiennent des éléments de discussion qui semblent hors sujet.
"""
self.ticket_loader = TicketDataLoader()
@@ -78,6 +83,23 @@ IMPORTANT :
response = self.llm.interroger(prompt)
logger.info("Analyse du ticket terminée avec succès")
print(f" Analyse terminée: {len(response)} caractères")
+
+ # Sauvegarde dans pipeline
+ result = {
+ "prompt": prompt,
+ "response": response,
+ "metadata": {
+ "timestamp": self._get_timestamp(),
+ "source_agent": self.nom,
+ "ticket_id": ticket_code,
+ "model_info": {
+ "model": getattr(self.llm, "modele", str(type(self.llm))),
+ **getattr(self.llm, "params", {})
+ }
+ }
+ }
+ sauvegarder_donnees(ticket_code, "analyse_ticket", result, base_dir="reports", is_resultat=True)
+
except Exception as e:
logger.error(f"Erreur d'analyse: {str(e)}")
return f"ERREUR: {str(e)}"
@@ -128,12 +150,11 @@ IMPORTANT :
for i, msg in enumerate(messages):
if not isinstance(msg, dict):
continue
- auteur = msg.get("from", "inconnu")
+ auteur = msg.get("author_id", "inconnu")
role = "CLIENT" if "client" in auteur.lower() else "SUPPORT" if "support" in auteur.lower() else "AUTRE"
date = self._formater_date(msg.get("date", "inconnue"))
contenu = msg.get("content", "").strip()
- if contenu:
- texte += f"{i+1}. [{role}] {auteur} ({date})\n{contenu}\n\n"
+ texte += f"{i+1}. [{role}] {auteur} ({date})\n{contenu}\n\n"
# Métadonnées utiles
metadata = ticket_data.get("metadata", {})
diff --git a/agents/pixtral_large/agent_image_sorter.py b/agents/pixtral_large/agent_image_sorter.py
index b4f209c..484df4b 100644
--- a/agents/pixtral_large/agent_image_sorter.py
+++ b/agents/pixtral_large/agent_image_sorter.py
@@ -3,6 +3,7 @@ import logging
import os
from typing import Dict, Any, Tuple
from PIL import Image
+from ..utils.pipeline_logger import sauvegarder_donnees
logger = logging.getLogger("AgentImageSorter")
@@ -125,6 +126,10 @@ class AgentImageSorter(BaseAgent):
"source_agent": self.nom
}
}
+
+ # Sauvegarder les données dans le fichier
+ sauvegarder_donnees(None, "tri_image", result, base_dir="reports", is_resultat=True)
+
self.ajouter_historique("tri_image", {"image_path": image_path, "prompt": prompt}, result)
return result
diff --git a/agents/utils/pipeline_logger.py b/agents/utils/pipeline_logger.py
new file mode 100644
index 0000000..077f573
--- /dev/null
+++ b/agents/utils/pipeline_logger.py
@@ -0,0 +1,166 @@
+import os
+import json
+from datetime import datetime
+from typing import Dict, Any, Optional, Union
+
+def determiner_repertoire_ticket(ticket_id: str) -> Optional[str]:
+ """
+ Détermine dynamiquement le répertoire du ticket.
+
+ Args:
+ ticket_id: str, le code du ticket
+
+ Returns:
+ str, le chemin du répertoire pour ce ticket ou None si non trouvé
+ """
+ # Base de recherche des tickets
+ output_dir = "output"
+
+ # Format attendu du répertoire de ticket
+ ticket_dir = f"ticket_{ticket_id}"
+ ticket_path = os.path.join(output_dir, ticket_dir)
+
+ if not os.path.exists(ticket_path):
+ print(f"Répertoire de ticket non trouvé: {ticket_path}")
+ return None
+
+ # Trouver la dernière extraction (par date)
+ extractions = []
+ for extraction in os.listdir(ticket_path):
+ extraction_path = os.path.join(ticket_path, extraction)
+ if os.path.isdir(extraction_path) and extraction.startswith(ticket_id):
+ extractions.append(extraction_path)
+
+ if not extractions:
+ print(f"Aucune extraction trouvée pour le ticket {ticket_id}")
+ return None
+
+ # Trier par date de modification (plus récente en premier)
+ extractions.sort(key=lambda x: os.path.getmtime(x), reverse=True)
+
+ # Retourner le chemin de la dernière extraction
+ return extractions[0]
+
+def extraire_ticket_id(data: Dict[str, Any]) -> Optional[str]:
+ """
+ Extrait l'ID du ticket à partir des métadonnées ou du chemin de l'image.
+
+ Args:
+ data: dict, données contenant potentiellement des métadonnées avec l'ID du ticket
+
+ Returns:
+ str, l'ID du ticket extrait ou None si non trouvé
+ """
+ if not data:
+ return None
+
+ # Essayer d'extraire le ticket_id des métadonnées
+ metadata = data.get("metadata", {})
+ image_path = metadata.get("image_path", "")
+
+ # Extraire depuis le chemin de l'image
+ if "ticket_" in image_path:
+ parts = image_path.split("ticket_")
+ if len(parts) > 1:
+ ticket_parts = parts[1].split("/")
+ if ticket_parts:
+ return ticket_parts[0]
+
+ # Chercher dans d'autres champs
+ for k, v in data.items():
+ if isinstance(v, str) and "ticket_" in v.lower():
+ parts = v.lower().split("ticket_")
+ if len(parts) > 1:
+ ticket_parts = parts[1].split("/")
+ if ticket_parts and ticket_parts[0].startswith("t"):
+ return ticket_parts[0].upper()
+
+ return None
+
+def sauvegarder_donnees(ticket_id: Optional[str] = None, step_name: str = "", data: Optional[Dict[str, Any]] = None, base_dir: Optional[str] = None, is_resultat: bool = False) -> None:
+ """
+ Sauvegarde des données sous forme de fichier JSON.
+
+ Args:
+ ticket_id: str, le code du ticket (optionnel, sera extrait automatiquement si None)
+ step_name: str, le nom de l'étape ou de l'agent
+ data: dict, les données à sauvegarder
+ base_dir: str, le répertoire de base pour les fichiers de logs (optionnel)
+ is_resultat: bool, indique si les données sont des résultats d'agent
+ """
+ if data is None:
+ print("Aucune donnée à sauvegarder")
+ return
+
+ # Si ticket_id n'est pas fourni, essayer de l'extraire des métadonnées
+ if not ticket_id:
+ ticket_id = extraire_ticket_id(data)
+ if ticket_id:
+ print(f"Ticket ID extrait: {ticket_id}")
+
+ # Si on n'a toujours pas de ticket_id, on ne peut pas continuer
+ if not ticket_id:
+ print("Impossible de déterminer l'ID du ticket, sauvegarde impossible")
+ return
+
+ # Si base_dir n'est pas fourni ou est un répertoire générique comme "reports", le déterminer automatiquement
+ if base_dir is None or base_dir == "reports":
+ extraction_dir = determiner_repertoire_ticket(ticket_id)
+ if not extraction_dir:
+ print(f"Impossible de déterminer le répertoire pour le ticket {ticket_id}")
+ return
+
+ # Utiliser le répertoire rapports pour stocker les résultats
+ rapports_dir = os.path.join(extraction_dir, f"{ticket_id}_rapports")
+ base_dir = rapports_dir
+
+ # Créer le répertoire pipeline
+ pipeline_dir = os.path.join(base_dir, "pipeline")
+ os.makedirs(pipeline_dir, exist_ok=True)
+
+ # Nom du fichier
+ if is_resultat:
+ # Extraire le nom du modèle LLM des métadonnées
+ llm_name = data.get("metadata", {}).get("model_info", {}).get("model", "unknown_model")
+ # Nettoyer le nom du modèle pour éviter les caractères problématiques dans le nom de fichier
+ safe_llm_name = llm_name.lower().replace("/", "_").replace(" ", "_")
+ file_name = f"{step_name}_{safe_llm_name}_results.json"
+ else:
+ file_name = f"{step_name}.json"
+
+ file_path = os.path.join(pipeline_dir, file_name)
+
+ try:
+ # Charger les données existantes si le fichier existe déjà
+ existing_data = []
+ if os.path.exists(file_path):
+ try:
+ with open(file_path, "r", encoding="utf-8") as f:
+ file_content = f.read().strip()
+ if file_content: # Vérifier que le fichier n'est pas vide
+ existing_data = json.loads(file_content)
+ # Si les données existantes ne sont pas une liste, les convertir en liste
+ if not isinstance(existing_data, list):
+ existing_data = [existing_data]
+ except json.JSONDecodeError:
+ print(f"Le fichier existant {file_path} n'est pas un JSON valide, création d'un nouveau fichier")
+ existing_data = []
+
+ # Ajouter les nouvelles données
+ if isinstance(data, list):
+ existing_data.extend(data)
+ else:
+ # Vérifier si cette image a déjà été analysée (pour éviter les doublons)
+ image_path = data.get("metadata", {}).get("image_path", "")
+ if image_path:
+ # Supprimer les entrées existantes pour cette image
+ existing_data = [entry for entry in existing_data
+ if entry.get("metadata", {}).get("image_path", "") != image_path]
+ existing_data.append(data)
+
+ # Sauvegarder les données combinées
+ with open(file_path, "w", encoding="utf-8") as f:
+ json.dump(existing_data, f, indent=2, ensure_ascii=False)
+ print(f"Données sauvegardées dans {file_path} ({len(existing_data)} entrées)")
+ except Exception as e:
+ print(f"Erreur lors de la sauvegarde des données: {e}")
\ No newline at end of file
diff --git a/output/ticket_T11143/T11143_20250416_103302/T11143_rapports/pipeline/analyse_ticket_mistral-large-latest_results.json b/output/ticket_T11143/T11143_20250416_103302/T11143_rapports/pipeline/analyse_ticket_mistral-large-latest_results.json
new file mode 100644
index 0000000..7a38469
--- /dev/null
+++ b/output/ticket_T11143/T11143_20250416_103302/T11143_rapports/pipeline/analyse_ticket_mistral-large-latest_results.json
@@ -0,0 +1,82 @@
+[
+ {
+ "prompt": "### TICKET T11143\n\n#### NOM DU TICKET\nBRGLAB - Essai inaccessible\n\n#### DESCRIPTION\n
\n\n#### INFORMATIONS TECHNIQUES\n- id: 11122\n- stage_id: [8, \"Clôturé\"]\n- project_id: [3, \"Demandes\"]\n- partner_id: [28897, \"GIRAUD TP (JCG), Victor BOLLÉE\"]\n- user_id: [22, \"Fabien LAFAY\"]\n- date_start: 2025-04-03 08:34:43\n- date_deadline: 2025-04-18\n- create_date: 2025-04-03 08:34:43\n- write_date: 2025-04-03 12:23:31\n- tag_ids: [15]\n- priority: 0\n- email_from: v.bollee@labojcg.fr\n- message_ids: [228972, 228971, 228970, 228969, 228968, 228967, 228947, 228942, 228941]\n- message_follower_ids: [90263, 90277]\n- stage_id_name: Clôturé\n- project_id_name: Demandes\n- partner_id_name: GIRAUD TP (JCG), Victor BOLLÉE\n- user_id_name: Fabien LAFAY\n- tag_names: [\"BRG-LAB WEB\"]\n\n",
+ "response": "### 1. Résumé du contexte\n\n**Client :** GIRAUD TP (JCG), Victor BOLLÉE\n\n**Demande :** Le client signale que l'essai est inaccessible.\n\n### 2. Informations techniques détectées\n\n- **ID du ticket :** 11122\n- **Stage :** Clôturé\n- **Projet :** Demandes\n- **Utilisateur :** Fabien LAFAY\n- **Date de début :** 2025-04-03 08:34:43\n- **Date de clôture :** 2025-04-18\n- **Date de création :** 2025-04-03 08:34:43\n- **Dernière mise à jour :** 2025-04-03 12:23:31\n- **Tag :** BRG-LAB WEB\n- **Email du client :** v.bollee@labojcg.fr\n\n### 3. Fil de discussion (filtré, classé, nettoyé)\n\n**Message 1 (228972)**\n- **De :** Victor BOLLÉE\n- **Contenu :** L'essai est inaccessible.\n\n**Message 2 (228971)**\n- **De :** Fabien LAFAY\n- **Contenu :** Pouvez-vous préciser quel essai est inaccessible et depuis quand ?\n\n**Message 3 (228970)**\n- **De :** Victor BOLLÉE\n- **Contenu :** L'essai de la fonctionnalité XYZ est inaccessible depuis hier.\n\n**Message 4 (228969)**\n- **De :** Fabien LAFAY\n- **Contenu :** Avez-vous essayé de redémarrer le module XYZ ?\n\n**Message 5 (228968)**\n- **De :** Victor BOLLÉE\n- **Contenu :** Oui, j'ai redémarré le module XYZ mais le problème persiste.\n\n**Message 6 (228967)**\n- **De :** Fabien LAFAY\n- **Contenu :** Pouvez-vous vérifier les logs du module XYZ et nous envoyer les captures d'écran des erreurs ?\n\n**Message 7 (228947)**\n- **De :** Victor BOLLÉE\n- **Contenu :** Voici les captures d'écran des erreurs.\n\n**Message 8 (228942)**\n- **De :** Fabien LAFAY\n- **Contenu :** Merci pour les captures. Nous allons analyser les logs et revenir vers vous.\n\n**Message 9 (228941)**\n- **De :** Fabien LAFAY\n- **Contenu :** Le problème a été résolu. L'essai est maintenant accessible.\n\n### 4. Points visuels à analyser (éléments à retrouver dans les captures)\n\n- Captures d'écran des erreurs du module XYZ\n- Logs du module XYZ",
+ "metadata": {
+ "timestamp": "20250417_114915",
+ "source_agent": "AgentTicketAnalyser",
+ "ticket_id": "T11143",
+ "model_info": {
+ "model": "mistral-large-latest",
+ "temperature": 0.1,
+ "top_p": 0.8,
+ "max_tokens": 6000,
+ "presence_penalty": 0,
+ "frequency_penalty": 0,
+ "stop": [],
+ "stream": false,
+ "n": 1
+ }
+ }
+ },
+ {
+ "prompt": "### TICKET T11143\n\n#### NOM DU TICKET\nBRGLAB - Essai inaccessible\n\n#### DESCRIPTION\n
\n\n#### INFORMATIONS TECHNIQUES\n- id: 11122\n- stage_id: [8, \"Clôturé\"]\n- project_id: [3, \"Demandes\"]\n- partner_id: [28897, \"GIRAUD TP (JCG), Victor BOLLÉE\"]\n- user_id: [22, \"Fabien LAFAY\"]\n- date_start: 2025-04-03 08:34:43\n- date_deadline: 2025-04-18\n- create_date: 2025-04-03 08:34:43\n- write_date: 2025-04-03 12:23:31\n- tag_ids: [15]\n- priority: 0\n- email_from: v.bollee@labojcg.fr\n- message_ids: [228972, 228971, 228970, 228969, 228968, 228967, 228947, 228942, 228941]\n- message_follower_ids: [90263, 90277]\n- stage_id_name: Clôturé\n- project_id_name: Demandes\n- partner_id_name: GIRAUD TP (JCG), Victor BOLLÉE\n- user_id_name: Fabien LAFAY\n- tag_names: [\"BRG-LAB WEB\"]\n\n",
+ "response": "### TICKET T11143\n\n#### 1. Résumé du contexte\n**Client :** GIRAUD TP (JCG), Victor BOLLÉE\n**Demande :** Le client signale que l'essai est inaccessible.\n\n#### 2. Informations techniques détectées\n- **ID du ticket :** 11122\n- **État :** Clôturé\n- **Projet :** Demandes\n- **Responsable :** Fabien LAFAY\n- **Date de début :** 2025-04-03 08:34:43\n- **Date de clôture :** 2025-04-18\n- **Date de création :** 2025-04-03 08:34:43\n- **Dernière mise à jour :** 2025-04-03 12:23:31\n- **Tag :** BRG-LAB WEB\n- **Priorité :** 0\n- **Email du client :** v.bollee@labojcg.fr\n\n#### 3. Fil de discussion (filtré, classé, nettoyé)\n\n**Message 1 (228941) - Victor BOLLÉE**\nBonjour,\nJe n'arrive pas à accéder à l'essai. Pouvez-vous m'aider ?\n\n**Message 2 (228942) - Fabien LAFAY**\nBonjour Victor,\nPouvez-vous préciser quel essai est inaccessible ? Avez-vous un message d'erreur ou un code spécifique ?\n\n**Message 3 (228947) - Victor BOLLÉE**\nBonjour,\nC'est l'essai sur le module \"BRG-LAB WEB\". Il n'y a pas de message d'erreur, juste une page blanche.\n\n**Message 4 (228967) - Fabien LAFAY**\nMerci pour l'information. Pouvez-vous vérifier si le problème persiste sur un autre navigateur ou après avoir vidé le cache ?\n\n**Message 5 (228968) - Victor BOLLÉE**\nJ'ai essayé sur Chrome et Firefox, même résultat. J'ai aussi vidé le cache, toujours une page blanche.\n\n**Message 6 (228969) - Fabien LAFAY**\nD'accord, pouvez-vous me fournir une capture d'écran de la page blanche et les informations de votre console de développement (F12) ?\n\n**Message 7 (228970) - Victor BOLLÉE**\nVoici la capture d'écran et les informations de la console.\n\n**Message 8 (228971) - Fabien LAFAY**\nMerci pour les captures. Nous allons investiguer de notre côté.\n\n**Message 9 (228972) - Fabien LAFAY**\nLe problème a été résolu. L'essai est maintenant accessible.\n\n#### 4. Points visuels à analyser (éléments à retrouver dans les captures)\n- Capture d'écran de la page blanche.\n- Informations de la console de développement (F12).",
+ "metadata": {
+ "timestamp": "20250417_115925",
+ "source_agent": "AgentTicketAnalyser",
+ "ticket_id": "T11143",
+ "model_info": {
+ "model": "mistral-large-latest",
+ "temperature": 0.1,
+ "top_p": 0.8,
+ "max_tokens": 4000,
+ "presence_penalty": 0,
+ "frequency_penalty": 0,
+ "stop": [],
+ "stream": false,
+ "n": 1
+ }
+ }
+ },
+ {
+ "prompt": "### TICKET T11143\n\n#### NOM DU TICKET\nBRGLAB - Essai inaccessible\n\n#### DESCRIPTION\n
\n\n#### INFORMATIONS TECHNIQUES\n- id: 11122\n- stage_id: [8, \"Clôturé\"]\n- project_id: [3, \"Demandes\"]\n- partner_id: [28897, \"GIRAUD TP (JCG), Victor BOLLÉE\"]\n- user_id: [22, \"Fabien LAFAY\"]\n- date_start: 2025-04-03 08:34:43\n- date_deadline: 2025-04-18\n- create_date: 2025-04-03 08:34:43\n- write_date: 2025-04-03 12:23:31\n- tag_ids: [15]\n- priority: 0\n- email_from: v.bollee@labojcg.fr\n- message_ids: [228972, 228971, 228970, 228969, 228968, 228967, 228947, 228942, 228941]\n- message_follower_ids: [90263, 90277]\n- stage_id_name: Clôturé\n- project_id_name: Demandes\n- partner_id_name: GIRAUD TP (JCG), Victor BOLLÉE\n- user_id_name: Fabien LAFAY\n- tag_names: [\"BRG-LAB WEB\"]\n\n",
+ "response": "### Résumé du contexte\nLe client CBAO (représenté par Victor BOLLÉE de GIRAUD TP (JCG)) a signalé un problème d'accès à un essai via la plateforme BRG-Lab. Le ticket a été créé par Fabien LAFAY et est actuellement clôturé. La demande concerne l'inaccessibilité d'un essai, mais la description initiale est vide.\n\n### Informations techniques détectées\n- **Client** : CBAO (représenté par Victor BOLLÉE de GIRAUD TP (JCG))\n- **ID du ticket** : 11122\n- **Projet** : Demandes\n- **Priorité** : Normale\n- **Date de création** : 2025-04-03 08:34:43\n- **Date de clôture** : 2025-04-03 12:23:31\n- **Tag** : BRG-LAB WEB\n- **Email du client** : v.bollee@labojcg.fr\n\n### Fil de discussion (filtré, classé, nettoyé)\n\n**Fabien LAFAY (2025-04-03 08:34:43)** :\nCréation du ticket pour le client CBAO concernant l'inaccessibilité d'un essai.\n\n**Victor BOLLÉE (2025-04-03 08:45:00)** :\nBonjour, je n'arrive pas à accéder à l'essai que j'ai demandé. Pouvez-vous m'aider ?\n\n**Support BRG-Lab (2025-04-03 09:00:00)** :\nBonjour Victor, pouvez-vous préciser quel essai vous essayez d'accéder ? Avez-vous un message d'erreur ou un code d'erreur spécifique ?\n\n**Victor BOLLÉE (2025-04-03 09:15:00)** :\nIl s'agit de l'essai \"ESSAI123\". Je n'ai pas de message d'erreur, la page reste blanche.\n\n**Support BRG-Lab (2025-04-03 09:30:00)** :\nMerci pour l'information. Nous allons vérifier cela. Pouvez-vous également nous fournir une capture d'écran de la page blanche ?\n\n**Victor BOLLÉE (2025-04-03 09:45:00)** :\nVoici la capture d'écran de la page blanche. [Capture d'écran jointe]\n\n**Support BRG-Lab (2025-04-03 10:00:00)** :\nMerci pour la capture. Nous investiguons le problème. Nous vous tiendrons informé dès que nous aurons plus d'informations.\n\n**Support BRG-Lab (2025-04-03 11:00:00)** :\nNous avons identifié un problème avec le module de gestion des essais. Nous travaillons sur une solution.\n\n**Victor BOLLÉE (2025-04-03 11:30:00)** :\nD'accord, merci pour l'information. Tenez-moi au courant.\n\n**Support BRG-Lab (2025-04-03 12:00:00)** :\nLe problème a été résolu. Vous devriez maintenant pouvoir accéder à l'essai \"ESSAI123\".\n\n**Victor BOLLÉE (2025-04-03 12:15:00)** :\nMerci, je peux maintenant accéder à l'essai.\n\n**Support BRG-Lab (2025-04-03 12:23:31)** :\nNous sommes ravis que le problème soit résolu. N'hésitez pas à nous contacter si vous avez d'autres questions.\n\n### Points visuels à analyser (éléments à retrouver dans les captures)\n- Page blanche lors de l'accès à l'essai \"ESSAI123\".",
+ "metadata": {
+ "timestamp": "20250417_135246",
+ "source_agent": "AgentTicketAnalyser",
+ "ticket_id": "T11143",
+ "model_info": {
+ "model": "mistral-large-latest",
+ "temperature": 0.1,
+ "top_p": 0.8,
+ "max_tokens": 4000,
+ "presence_penalty": 0,
+ "frequency_penalty": 0,
+ "stop": [],
+ "stream": false,
+ "n": 1
+ }
+ }
+ },
+ {
+ "prompt": "### TICKET T11143\n\n#### NOM DU TICKET\nBRGLAB - Essai inaccessible\n\n#### DESCRIPTION\n
\n\n#### INFORMATIONS TECHNIQUES\n- id: 11122\n- stage_id: [8, \"Clôturé\"]\n- project_id: [3, \"Demandes\"]\n- partner_id: [28897, \"GIRAUD TP (JCG), Victor BOLLÉE\"]\n- user_id: [22, \"Fabien LAFAY\"]\n- date_start: 2025-04-03 08:34:43\n- date_deadline: 2025-04-18\n- create_date: 2025-04-03 08:34:43\n- write_date: 2025-04-03 12:23:31\n- tag_ids: [15]\n- priority: 0\n- email_from: v.bollee@labojcg.fr\n- message_ids: [228972, 228971, 228970, 228969, 228968, 228967, 228947, 228942, 228941]\n- message_follower_ids: [90263, 90277]\n- stage_id_name: Clôturé\n- project_id_name: Demandes\n- partner_id_name: GIRAUD TP (JCG), Victor BOLLÉE\n- user_id_name: Fabien LAFAY\n- tag_names: [\"BRG-LAB WEB\"]\n\n",
+ "response": "### Résumé du contexte\nLe ticket concerne un problème d'accès à un essai pour le client CBAO. Le client rencontre des difficultés pour accéder à un essai spécifique et demande de l'aide pour résoudre ce problème.\n\n### Informations techniques détectées\n- **Client** : CBAO\n- **Projet** : Demandes\n- **Priorité** : 0\n- **Date de début** : 2025-04-03 08:34:43\n- **Date de clôture** : 2025-04-18\n- **Dernière mise à jour** : 2025-04-03 12:23:31\n- **Tags** : BRG-LAB WEB\n- **Email du client** : v.bollee@labojcg.fr\n- **Responsable** : Fabien LAFAY\n\n### Fil de discussion (filtré, classé, nettoyé)\n\n**Client (Victor BOLLÉE)** :\nBonjour, je n'arrive pas à accéder à l'essai. Pouvez-vous m'aider ?\n\n**Support (Fabien LAFAY)** :\nBonjour Victor, pouvez-vous préciser quel essai vous essayez d'accéder ? Avez-vous un message d'erreur ou un comportement spécifique à signaler ?\n\n**Client (Victor BOLLÉE)** :\nIl s'agit de l'essai \"Essai de résistance\". Quand je clique sur le lien, rien ne se passe.\n\n**Support (Fabien LAFAY)** :\nMerci pour l'information. Pouvez-vous vérifier si vous avez les droits d'accès nécessaires pour cet essai ? Parfois, les permissions peuvent être la cause du problème.\n\n**Client (Victor BOLLÉE)** :\nJ'ai vérifié mes permissions et tout semble en ordre. Je devrais avoir accès à cet essai.\n\n**Support (Fabien LAFAY)** :\nD'accord, pouvez-vous essayer de vider le cache de votre navigateur et réessayer ? Parfois, des problèmes de cache peuvent empêcher l'accès.\n\n**Client (Victor BOLLÉE)** :\nJ'ai vidé le cache mais le problème persiste.\n\n**Support (Fabien LAFAY)** :\nMerci d'avoir essayé. Pouvez-vous me fournir une capture d'écran de ce que vous voyez lorsque vous essayez d'accéder à l'essai ? Cela pourrait nous aider à diagnostiquer le problème.\n\n**Client (Victor BOLLÉE)** :\nVoici la capture d'écran.\n\n**Support (Fabien LAFAY)** :\nMerci pour la capture d'écran. Nous allons examiner cela et revenir vers vous dès que possible.\n\n### Points visuels à analyser (éléments à retrouver dans les captures)\n- Le lien vers l'essai \"Essai de résistance\"\n- Le comportement du navigateur lorsque le client clique sur le lien\n- Les messages d'erreur ou les notifications éventuelles\n- Les éléments de l'interface utilisateur qui pourraient indiquer un problème de permissions ou d'accès",
+ "metadata": {
+ "timestamp": "20250417_140303",
+ "source_agent": "AgentTicketAnalyser",
+ "ticket_id": "T11143",
+ "model_info": {
+ "model": "mistral-large-latest",
+ "temperature": 0.1,
+ "top_p": 0.8,
+ "max_tokens": 4000,
+ "presence_penalty": 0,
+ "frequency_penalty": 0,
+ "stop": [],
+ "stream": false,
+ "n": 1
+ }
+ }
+ }
+]
\ No newline at end of file
diff --git a/output/ticket_T11143/T11143_20250416_103302/T11143_rapports/pipeline/rapport_de_deduplication.json b/output/ticket_T11143/T11143_20250416_103302/T11143_rapports/pipeline/rapport_de_deduplication.json
new file mode 100644
index 0000000..ee026cd
--- /dev/null
+++ b/output/ticket_T11143/T11143_20250416_103302/T11143_rapports/pipeline/rapport_de_deduplication.json
@@ -0,0 +1,26 @@
+[
+ {
+ "image_path": "output/ticket_T11143/T11143_20250416_103302/attachments/image.png",
+ "status": "unique"
+ },
+ {
+ "image_path": "output/ticket_T11143/T11143_20250416_103302/attachments/image_145435.png",
+ "status": "unique"
+ },
+ {
+ "image_path": "output/ticket_T11143/T11143_20250416_103302/attachments/image_145453.png",
+ "status": "duplicate"
+ },
+ {
+ "image_path": "output/ticket_T11143/T11143_20250416_103302/attachments/543d7da1b54c29ff43ce5712d1a9aa4962ed21795c4e943fcb8cb84fd4d7465a.jpg",
+ "status": "unique"
+ },
+ {
+ "image_path": "output/ticket_T11143/T11143_20250416_103302/attachments/5ad281b63492e31c9e66bf27518b816cdd3766cab9812bd4ff16b736e9e98265.jpg",
+ "status": "duplicate"
+ },
+ {
+ "image_path": "output/ticket_T11143/T11143_20250416_103302/attachments/a20f7697fd5e1d1fca3296c6d01228220e0e112c46b4440cc938f74d10934e98.gif",
+ "status": "unique"
+ }
+]
\ No newline at end of file
diff --git a/output/ticket_T11143/T11143_20250416_103302/T11143_rapports/pipeline/tri_image_pixtral-large-latest_results.json b/output/ticket_T11143/T11143_20250416_103302/T11143_rapports/pipeline/tri_image_pixtral-large-latest_results.json
new file mode 100644
index 0000000..0d9cf5a
--- /dev/null
+++ b/output/ticket_T11143/T11143_20250416_103302/T11143_rapports/pipeline/tri_image_pixtral-large-latest_results.json
@@ -0,0 +1,90 @@
+[
+ {
+ "is_relevant": true,
+ "reason": "Oui.\n\nL'image montre une capture d'écran d'une page web liée à l'installation et à la configuration de Tomcat, ce qui est pertinent pour",
+ "raw_response": "Oui.\n\nL'image montre une capture d'écran d'une page web liée à l'installation et à la configuration de Tomcat, ce qui est pertinent pour",
+ "metadata": {
+ "image_path": "output/ticket_T11143/T11143_20250416_103302/attachments/image.png",
+ "image_name": "image.png",
+ "timestamp": "20250417_105319",
+ "model_info": {
+ "model": "pixtral-large-latest",
+ "temperature": 0.2,
+ "top_p": 0.8,
+ "max_tokens": 300,
+ "presence_penalty": 0,
+ "frequency_penalty": 0,
+ "stop": [],
+ "stream": false,
+ "n": 1
+ },
+ "source_agent": "AgentImageSorter"
+ }
+ },
+ {
+ "is_relevant": true,
+ "reason": "Oui.\nL'image montre une capture d'écran d'une interface logicielle de BRG-LAB, ce qui est pertinent pour un ticket de support technique.",
+ "raw_response": "Oui.\nL'image montre une capture d'écran d'une interface logicielle de BRG-LAB, ce qui est pertinent pour un ticket de support technique.",
+ "metadata": {
+ "image_path": "output/ticket_T11143/T11143_20250416_103302/attachments/image_145435.png",
+ "image_name": "image_145435.png",
+ "timestamp": "20250417_105321",
+ "model_info": {
+ "model": "pixtral-large-latest",
+ "temperature": 0.2,
+ "top_p": 0.8,
+ "max_tokens": 300,
+ "presence_penalty": 0,
+ "frequency_penalty": 0,
+ "stop": [],
+ "stream": false,
+ "n": 1
+ },
+ "source_agent": "AgentImageSorter"
+ }
+ },
+ {
+ "is_relevant": false,
+ "reason": "Non.\n\nL'image montre uniquement le logo de la société CBAO, ce qui n'est pas pertinent pour un ticket de support technique.",
+ "raw_response": "Non.\n\nL'image montre uniquement le logo de la société CBAO, ce qui n'est pas pertinent pour un ticket de support technique.",
+ "metadata": {
+ "image_path": "output/ticket_T11143/T11143_20250416_103302/attachments/543d7da1b54c29ff43ce5712d1a9aa4962ed21795c4e943fcb8cb84fd4d7465a.jpg",
+ "image_name": "543d7da1b54c29ff43ce5712d1a9aa4962ed21795c4e943fcb8cb84fd4d7465a.jpg",
+ "timestamp": "20250417_105329",
+ "model_info": {
+ "model": "pixtral-large-latest",
+ "temperature": 0.2,
+ "top_p": 0.8,
+ "max_tokens": 300,
+ "presence_penalty": 0,
+ "frequency_penalty": 0,
+ "stop": [],
+ "stream": false,
+ "n": 1
+ },
+ "source_agent": "AgentImageSorter"
+ }
+ },
+ {
+ "is_relevant": false,
+ "reason": "Non.\nL'image montre un logo ou une image de marque, ce qui n'est pas pertinent pour un ticket de support technique.",
+ "raw_response": "Non.\nL'image montre un logo ou une image de marque, ce qui n'est pas pertinent pour un ticket de support technique.",
+ "metadata": {
+ "image_path": "output/ticket_T11143/T11143_20250416_103302/attachments/a20f7697fd5e1d1fca3296c6d01228220e0e112c46b4440cc938f74d10934e98.gif",
+ "image_name": "a20f7697fd5e1d1fca3296c6d01228220e0e112c46b4440cc938f74d10934e98.gif",
+ "timestamp": "20250417_105331",
+ "model_info": {
+ "model": "pixtral-large-latest",
+ "temperature": 0.2,
+ "top_p": 0.8,
+ "max_tokens": 300,
+ "presence_penalty": 0,
+ "frequency_penalty": 0,
+ "stop": [],
+ "stream": false,
+ "n": 1
+ },
+ "source_agent": "AgentImageSorter"
+ }
+ }
+]
\ No newline at end of file
diff --git a/test_agent_ticket_analyser_mistral_large.py b/test_agent_ticket_analyser_mistral_large.py
new file mode 100644
index 0000000..7b4ecf5
--- /dev/null
+++ b/test_agent_ticket_analyser_mistral_large.py
@@ -0,0 +1,149 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+"""
+Script pour tester directement l'agent d'analyse de tickets (AgentTicketAnalyser) avec Mistral Large.
+Permet d'évaluer rapidement l'analyse d'un ticket.
+"""
+
+import os
+import sys
+import argparse
+import json
+import glob
+from typing import Dict, Any, List
+
+from llm_classes.mistral_large import MistralLarge
+from agents.mistral_large.agent_ticket_analyser import AgentTicketAnalyser
+
+def get_ticket_files_in_extraction(extraction_path: str) -> Dict[str, str]:
+ """
+ Récupère les fichiers de ticket dans un répertoire d'extraction
+
+ Args:
+ extraction_path: Chemin vers le répertoire d'extraction
+
+ Returns:
+ Dictionnaire des types de fichiers et leurs chemins
+ """
+ # Types de fichiers de ticket
+ ticket_files = {}
+
+ # Chercher ticket_info.json
+ ticket_info_path = os.path.join(extraction_path, "ticket_info.json")
+ if os.path.exists(ticket_info_path):
+ ticket_files["ticket_info"] = ticket_info_path
+
+ # Chercher all_messages.json
+ all_messages_path = os.path.join(extraction_path, "all_messages.json")
+ if os.path.exists(all_messages_path):
+ ticket_files["all_messages"] = all_messages_path
+
+ # Chercher ticket_summary.json
+ ticket_summary_path = os.path.join(extraction_path, "ticket_summary.json")
+ if os.path.exists(ticket_summary_path):
+ ticket_files["ticket_summary"] = ticket_summary_path
+
+ return ticket_files
+
+def main():
+ # Configuration de l'analyseur d'arguments
+ parser = argparse.ArgumentParser(description="Tester l'agent d'analyse de tickets directement.")
+ parser.add_argument("ticket_id", help="ID du ticket à analyser (ex: T1234)")
+ parser.add_argument("--ticket_file", help="Chemin spécifique vers un fichier JSON du ticket à analyser")
+ parser.add_argument("--output_dir", default="output", help="Répertoire de sortie contenant les tickets")
+
+ # Analyser les arguments
+ args = parser.parse_args()
+
+ # Construire le chemin vers le ticket
+ ticket_path = os.path.join(args.output_dir, f"ticket_{args.ticket_id}")
+
+ # Vérifier que le répertoire du ticket existe
+ if not os.path.exists(ticket_path):
+ print(f"ERREUR: Le ticket {args.ticket_id} n'existe pas dans {args.output_dir}")
+ return 1
+
+ # Rechercher la dernière extraction (la plus récente)
+ extractions = [d for d in os.listdir(ticket_path) if os.path.isdir(os.path.join(ticket_path, d)) and d.startswith(args.ticket_id)]
+ if not extractions:
+ print(f"ERREUR: Aucune extraction trouvée pour le ticket {args.ticket_id}")
+ return 1
+
+ # Trier par ordre décroissant pour avoir la plus récente en premier
+ extractions.sort(reverse=True)
+ latest_extraction = extractions[0]
+ extraction_path = os.path.join(ticket_path, latest_extraction)
+
+ print(f"Utilisation de l'extraction: {latest_extraction}")
+
+ # Initialiser le modèle Mistral Large
+ try:
+ print("Initialisation du modèle Mistral Large...")
+ model = MistralLarge()
+ agent = AgentTicketAnalyser(model)
+ print("Agent d'analyse de tickets initialisé avec succès")
+ except Exception as e:
+ print(f"ERREUR: Impossible d'initialiser le modèle: {str(e)}")
+ return 1
+
+ # Si un fichier de ticket spécifique est fourni
+ ticket_data: Dict[str, Any] = {}
+ if args.ticket_file:
+ if not os.path.exists(args.ticket_file):
+ print(f"ERREUR: Le fichier de ticket spécifié n'existe pas: {args.ticket_file}")
+ return 1
+
+ try:
+ with open(args.ticket_file, "r", encoding="utf-8") as f:
+ ticket_data = json.load(f)
+ print(f"Fichier de ticket chargé: {args.ticket_file}")
+ except json.JSONDecodeError:
+ print(f"ERREUR: Le fichier {args.ticket_file} n'est pas un JSON valide")
+ return 1
+ else:
+ # Récupérer tous les fichiers de ticket de l'extraction
+ ticket_files = get_ticket_files_in_extraction(extraction_path)
+
+ if not ticket_files:
+ print(f"ERREUR: Aucun fichier de ticket trouvé dans l'extraction {latest_extraction}")
+ return 1
+
+ print(f"Fichiers de ticket trouvés: {', '.join(ticket_files.keys())}")
+
+ # Privilégier ticket_info.json, puis all_messages.json
+ if "ticket_info" in ticket_files:
+ ticket_file_path = ticket_files["ticket_info"]
+ elif "all_messages" in ticket_files:
+ ticket_file_path = ticket_files["all_messages"]
+ elif "ticket_summary" in ticket_files:
+ ticket_file_path = ticket_files["ticket_summary"]
+ else:
+ print("ERREUR: Aucun fichier de ticket utilisable trouvé")
+ return 1
+
+ try:
+ with open(ticket_file_path, "r", encoding="utf-8") as f:
+ ticket_data = json.load(f)
+ print(f"Fichier de ticket chargé: {ticket_file_path}")
+ except json.JSONDecodeError:
+ print(f"ERREUR: Le fichier {ticket_file_path} n'est pas un JSON valide")
+ return 1
+
+ # Vérifier que les données du ticket contiennent un code
+ if "code" not in ticket_data:
+ ticket_data["code"] = args.ticket_id
+ print(f"Code de ticket ajouté: {args.ticket_id}")
+
+ # Exécuter l'analyse du ticket
+ print(f"Analyse du ticket {ticket_data.get('code', args.ticket_id)} en cours...")
+ result = agent.executer(ticket_data)
+
+ # Afficher le résultat
+ print("\nRésultat de l'analyse:")
+ print(result)
+
+ return 0
+
+if __name__ == "__main__":
+ sys.exit(main())
\ No newline at end of file
diff --git a/test_image_sorter.py b/test_image_sorter_pixtral_large.py
similarity index 99%
rename from test_image_sorter.py
rename to test_image_sorter_pixtral_large.py
index 8eb55a8..2f38c51 100755
--- a/test_image_sorter.py
+++ b/test_image_sorter_pixtral_large.py
@@ -125,7 +125,7 @@ def main():
# Appliquer le préfiltrage de doublons si activé
if not args.no_dedup:
images_avant = len(images)
- images = filtrer_images_uniques(images, seuil_hamming=args.seuil)
+ images = filtrer_images_uniques(images, seuil_hamming=args.seuil, ticket_id=args.ticket_id)
images_apres = len(images)
if images_avant > images_apres:
diff --git a/utils/image_dedup.py b/utils/image_dedup.py
index 15b4633..b1ca7c5 100644
--- a/utils/image_dedup.py
+++ b/utils/image_dedup.py
@@ -1,15 +1,55 @@
import os
from PIL import Image
import imagehash
-from typing import List, Dict
+from typing import List, Dict, Optional
+import json
-def filtrer_images_uniques(image_paths: List[str], seuil_hamming: int = 5) -> List[str]:
+def determiner_repertoire_ticket(ticket_id: str) -> Optional[str]:
"""
- Supprime les doublons perceptuels parmi une liste d'images.
+ Détermine dynamiquement le répertoire du ticket.
+
+ Args:
+ ticket_id: str, le code du ticket
+
+ Returns:
+ str, le chemin du répertoire pour ce ticket ou None si non trouvé
+ """
+ # Base de recherche des tickets
+ output_dir = "output"
+
+ # Format attendu du répertoire de ticket
+ ticket_dir = f"ticket_{ticket_id}"
+ ticket_path = os.path.join(output_dir, ticket_dir)
+
+ if not os.path.exists(ticket_path):
+ print(f"Répertoire de ticket non trouvé: {ticket_path}")
+ return None
+
+ # Trouver la dernière extraction (par date)
+ extractions = []
+ for extraction in os.listdir(ticket_path):
+ extraction_path = os.path.join(ticket_path, extraction)
+ if os.path.isdir(extraction_path) and extraction.startswith(ticket_id):
+ extractions.append(extraction_path)
+
+ if not extractions:
+ print(f"Aucune extraction trouvée pour le ticket {ticket_id}")
+ return None
+
+ # Trier par date de modification (plus récente en premier)
+ extractions.sort(key=lambda x: os.path.getmtime(x), reverse=True)
+
+ # Retourner le chemin de la dernière extraction
+ return extractions[0]
+
+def filtrer_images_uniques(image_paths: List[str], seuil_hamming: int = 5, ticket_id: str = "") -> List[str]:
+ """
+ Supprime les doublons perceptuels parmi une liste d'images et sauvegarde les résultats.
Args:
image_paths: Liste des chemins vers les images.
seuil_hamming: Distance de Hamming maximale pour considérer deux images comme identiques.
+ ticket_id: ID du ticket pour le nom du fichier de rapport.
Returns:
Liste de chemins d'images considérées comme uniques.
@@ -17,6 +57,7 @@ def filtrer_images_uniques(image_paths: List[str], seuil_hamming: int = 5) -> Li
"""
images_uniques = []
hashes: Dict[str, str] = {}
+ rapport = []
# Trier les chemins pour privilégier les noms courts et formats préférés
def cle_pertinence(p: str) -> tuple:
@@ -38,7 +79,74 @@ def filtrer_images_uniques(image_paths: List[str], seuil_hamming: int = 5) -> Li
if not doublon:
hashes[path] = img_hash
images_uniques.append(path)
+ rapport.append({"image_path": path, "status": "unique"})
+ else:
+ rapport.append({"image_path": path, "status": "duplicate"})
except Exception as e:
print(f"[Erreur] Image ignorée ({path}) : {e}")
+ rapport.append({"image_path": path, "status": "error", "message": str(e)})
+
+ # Sauvegarder le rapport dans un fichier JSON
+ sauvegarder_rapport(rapport, ticket_id)
return images_uniques
+
+def sauvegarder_rapport(rapport: List[Dict[str, str]], ticket_id: str) -> None:
+ """
+ Sauvegarde le rapport des résultats de déduplication dans un fichier JSON.
+
+ Args:
+ rapport: Liste des résultats de déduplication.
+ ticket_id: ID du ticket pour le nom du fichier de rapport.
+ """
+ if not ticket_id:
+ print("Aucun ID de ticket fourni, impossible de sauvegarder le rapport")
+ return
+
+ # Déterminer le répertoire du ticket
+ extraction_dir = determiner_repertoire_ticket(ticket_id)
+ if not extraction_dir:
+ print(f"Impossible de déterminer le répertoire pour le ticket {ticket_id}")
+ return
+
+ # Utiliser le répertoire rapports pour stocker les résultats
+ rapports_dir = os.path.join(extraction_dir, f"{ticket_id}_rapports")
+
+ # Créer le répertoire pipeline
+ pipeline_dir = os.path.join(rapports_dir, "pipeline")
+ os.makedirs(pipeline_dir, exist_ok=True)
+
+ file_name = "rapport_de_deduplication.json"
+ file_path = os.path.join(pipeline_dir, file_name)
+
+ try:
+ # Charger les données existantes si le fichier existe déjà
+ existing_data = []
+ if os.path.exists(file_path):
+ try:
+ with open(file_path, "r", encoding="utf-8") as f:
+ file_content = f.read().strip()
+ if file_content: # Vérifier que le fichier n'est pas vide
+ existing_data = json.loads(file_content)
+ # Si les données existantes ne sont pas une liste, les convertir en liste
+ if not isinstance(existing_data, list):
+ existing_data = [existing_data]
+ except json.JSONDecodeError:
+ print(f"Le fichier existant {file_path} n'est pas un JSON valide, création d'un nouveau fichier")
+ existing_data = []
+
+ # Fusionner les données existantes avec les nouvelles
+ # Si le rapport contient déjà des informations sur ces images, les remplacer
+ paths_existants = {item.get("image_path", "") for item in existing_data}
+ for item in rapport:
+ if item.get("image_path", "") in paths_existants:
+ # Supprimer l'ancien élément
+ existing_data = [entry for entry in existing_data
+ if entry.get("image_path", "") != item.get("image_path", "")]
+ existing_data.append(item)
+
+ with open(file_path, "w", encoding="utf-8") as f:
+ json.dump(existing_data, f, indent=2, ensure_ascii=False)
+ print(f"Rapport de déduplication sauvegardé dans {file_path} ({len(existing_data)} entrées)")
+ except Exception as e:
+ print(f"Erreur lors de la sauvegarde du rapport: {e}")
\ No newline at end of file