from ..base_agent import BaseAgent from typing import Dict, Any import logging 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") class AgentTicketAnalyser(BaseAgent): """ Agent pour analyser les tickets (JSON ou Markdown) et en extraire les informations importantes. Utilisé pour contextualiser les tickets avant l'analyse des captures d'écran. """ def __init__(self, llm): super().__init__("AgentTicketAnalyser", llm) # Configuration adaptée à l'analyse structurée pour Mistral Large self.temperature = 0.05 self.top_p = 0.8 self.max_tokens = 7000 # 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). Je vais te donner un ticket de support avec plusieurs messages, et tu dois REPRODUIRE EXACTEMENT le contenu technique de chaque message, sans inventer ni reformuler. STRUCTURE DE SORTIE ATTENDUE : 1. Résumé du contexte (bref, factuel) 2. Informations techniques détectées 3. Fil de discussion (REPRODUCTION EXACTE, SANS HALLUCINATION) 4. Points visuels à analyser (éléments à retrouver dans les captures) RÈGLES ABSOLUES : - Le NOM (titre) et la DESCRIPTION du ticket sont le PREMIER MESSAGE du client et contiennent souvent l'information cruciale - REPRODUIS MOT POUR MOT le contenu des messages, notamment les termes techniques comme "essai au bleu" - N'INVENTE JAMAIS de messages qui n'existent pas dans le ticket original - N'INTRODUIS JAMAIS de termes comme "XYZ" ou autres références qui n'existent pas dans le texte original - NE DÉDUIS PAS d'information qui n'est pas explicitement mentionnée - NE REFORMULE PAS les messages, copie-les exactement - GARDE TOUS les liens, noms d'essais et détails techniques tels quels - Ne supprime que les signatures, formules de politesse et parties non pertinentes ATTENTION AUX DÉTAILS : - Un "essai au bleu" doit rester "essai au bleu", pas "essai" ni "essai XYZ" - Les auteurs des messages doivent être correctement attribués - L'ordre chronologique des messages doit être respecté - Les URLs et références techniques doivent être préservées telles quelles ASTUCE : Si tu n'es pas sûr d'un détail, reproduis-le tel quel plutôt que de l'interpréter. """ self.ticket_loader = TicketDataLoader() self._appliquer_config_locale() logger.info("AgentTicketAnalyser initialisé") def _appliquer_config_locale(self) -> None: """Applique les paramètres et le prompt système au modèle""" if hasattr(self.llm, "prompt_system"): self.llm.prompt_system = self.system_prompt if hasattr(self.llm, "configurer"): self.llm.configurer( temperature=self.temperature, top_p=self.top_p, max_tokens=self.max_tokens ) def executer(self, ticket_data: Dict[str, Any]) -> str: """Analyse le ticket donné sous forme de dict""" if isinstance(ticket_data, str) and os.path.exists(ticket_data): ticket_data = self.ticket_loader.charger(ticket_data) if not isinstance(ticket_data, dict): logger.error("Les données du ticket ne sont pas valides") return "ERREUR: Format de ticket invalide" ticket_code = ticket_data.get("code", "Inconnu") logger.info(f"Analyse du ticket {ticket_code}") print(f"AgentTicketAnalyser: analyse du ticket {ticket_code}") prompt = self._generer_prompt(ticket_data) try: 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)}" self.ajouter_historique( "analyse_ticket", { "ticket_id": ticket_code, "prompt": prompt, "timestamp": self._get_timestamp() }, response ) return response def _generer_prompt(self, ticket_data: Dict[str, Any]) -> str: """ Prépare un prompt texte structuré à partir des données brutes du ticket. """ ticket_code = ticket_data.get("code", "Inconnu") name = ticket_data.get("name", "").strip() description = ticket_data.get("description", "").strip() create_date = ticket_data.get("create_date", "") partner_id_name = ticket_data.get("partner_id_name", "").strip() email_from = ticket_data.get("email_from", "").strip() # En-tête avec instructions spécifiques texte = f"### TICKET {ticket_code}\n\n" texte += "IMPORTANT: Tu dois reproduire EXACTEMENT le contenu des messages sans reformulation ni invention.\n" texte += "ATTENTION: Tu dois conserver les termes techniques EXACTS comme 'essai au bleu' sans les modifier.\n" texte += "CRITIQUE: Le NOM et la DESCRIPTION du ticket sont le PREMIER MESSAGE du client et doivent être inclus au début du fil.\n" texte += "INTERDIT: N'invente JAMAIS de messages qui n'existent pas dans le ticket.\n\n" if name: texte += f"#### NOM DU TICKET\n{name}\n\n" if description: texte += f"#### DESCRIPTION\n{description}\n\n" # Informations techniques utiles (hors champs exclus) champs_exclus = {"code", "name", "description", "messages", "metadata"} info_techniques = [ f"- {k}: {json.dumps(v, ensure_ascii=False)}" if isinstance(v, (dict, list)) else f"- {k}: {v}" for k, v in ticket_data.items() if k not in champs_exclus and v ] if info_techniques: texte += "#### INFORMATIONS TECHNIQUES\n" + "\n".join(info_techniques) + "\n\n" # Fil des échanges client/support - format amélioré texte += "#### ÉCHANGES CLIENT / SUPPORT (À REPRODUIRE TEXTUELLEMENT)\n" # Ajouter le titre et la description comme premier message si pertinent if name or description: texte += "--- MESSAGE INITIAL (Ouverture du ticket) ---\n" texte += f"ID: ticket_creation\n" texte += f"Auteur: {partner_id_name if partner_id_name else email_from if email_from else 'Client'}\n" texte += f"Date: {create_date}\n" texte += f"Type: Création du ticket\n" texte += f"Sujet: {name}\n" texte += "Contenu (à reproduire EXACTEMENT):\n" if description and description != "