1704-14:04

This commit is contained in:
Ladebeze66 2025-04-17 14:04:38 +02:00
parent 9885f14e3b
commit f7986bf84c
9 changed files with 658 additions and 11 deletions

View File

@ -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", {})

View File

@ -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

View File

@ -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}")

View File

@ -0,0 +1,82 @@
[
{
"prompt": "### TICKET T11143\n\n#### NOM DU TICKET\nBRGLAB - Essai inaccessible\n\n#### DESCRIPTION\n<p><br></p>\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<p><br></p>\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<p><br></p>\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<p><br></p>\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
}
}
}
]

View File

@ -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"
}
]

View File

@ -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"
}
}
]

View File

@ -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())

View File

@ -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:

View File

@ -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}")