import json import os from .base_agent import BaseAgent from datetime import datetime from typing import Dict, Any, Tuple, Optional import logging logger = logging.getLogger("AgentReportGenerator") class AgentReportGenerator(BaseAgent): """ Agent pour générer un rapport complet à partir des analyses de ticket et d'images """ def __init__(self, llm): super().__init__("AgentReportGenerator", llm) # Configuration locale de l'agent (remplace AgentConfig) self.temperature = 0.4 # Génération de rapport factuelle mais bien structurée self.top_p = 0.9 self.max_tokens = 2500 self.system_prompt = """Tu es un expert en génération de rapports techniques pour BRG_Lab. Ta mission est de synthétiser toutes les analyses (JSON et images) en un rapport structuré et exploitable. Structure ton rapport ainsi: 1. Résumé exécutif: Synthèse du problème et des conclusions principales 2. Analyse du ticket: Détails extraits du ticket client 3. Analyse des images: Résumé des images pertinentes et leur contribution 4. Diagnostic technique: Interprétation consolidée des informations 5. Recommandations: Actions suggérées pour résoudre le problème Chaque section doit être factuelle, précise et orientée solution. Inclus tous les détails techniques pertinents (versions, configurations, messages d'erreur). Assure une traçabilité complète entre les données sources et tes conclusions.""" # Appliquer la configuration au LLM self._appliquer_config_locale() logger.info("AgentReportGenerator initialisé") def _appliquer_config_locale(self) -> None: """ Applique la configuration locale au modèle LLM. """ # Appliquer le prompt système if hasattr(self.llm, "prompt_system"): self.llm.prompt_system = self.system_prompt # Appliquer les paramètres if hasattr(self.llm, "configurer"): params = { "temperature": self.temperature, "top_p": self.top_p, "max_tokens": self.max_tokens } # Ajustements selon le type de modèle if "mistral_medium" in self.llm.__class__.__name__.lower(): params["temperature"] += 0.05 params["max_tokens"] = 1000 elif "pixtral" in self.llm.__class__.__name__.lower(): params["temperature"] -= 0.05 elif "ollama" in self.llm.__class__.__name__.lower(): params["temperature"] += 0.1 params.update({ "num_ctx": 2048, "repeat_penalty": 1.1, }) self.llm.configurer(**params) def executer(self, rapport_data: Dict, rapport_dir: str) -> Tuple[Optional[str], Optional[str]]: """ Génère un rapport à partir des analyses effectuées Args: rapport_data: Dictionnaire contenant toutes les données analysées rapport_dir: Répertoire où sauvegarder le rapport Returns: Tuple (chemin vers le rapport JSON, chemin vers le rapport Markdown) """ # Récupérer l'ID du ticket depuis les données ticket_id = rapport_data.get("ticket_id", "") if not ticket_id and "ticket_data" in rapport_data and isinstance(rapport_data["ticket_data"], dict): ticket_id = rapport_data["ticket_data"].get("code", "") if not ticket_id: ticket_id = os.path.basename(os.path.dirname(rapport_dir)) if not ticket_id.startswith("T"): # Dernier recours, utiliser le dernier segment du chemin ticket_id = os.path.basename(rapport_dir) logger.info(f"Génération du rapport pour le ticket: {ticket_id}") print(f"AgentReportGenerator: Génération du rapport pour {ticket_id}") # S'assurer que le répertoire existe if not os.path.exists(rapport_dir): os.makedirs(rapport_dir) try: # Préparer les données formatées pour l'analyse ticket_analyse = rapport_data.get("ticket_analyse", "") if not ticket_analyse and "analyse_json" in rapport_data: ticket_analyse = rapport_data.get("analyse_json", "Aucune analyse de ticket disponible") # Préparer les données d'analyse d'images images_analyses = [] analyse_images_data = rapport_data.get("analyse_images", {}) # Collecter des informations sur les agents et LLM utilisés agents_info = self._collecter_info_agents(rapport_data) # Transformer les analyses d'images en liste structurée pour le prompt for image_path, analyse_data in analyse_images_data.items(): image_name = os.path.basename(image_path) # Récupérer l'analyse détaillée si elle existe analyse_detail = None if "analysis" in analyse_data and analyse_data["analysis"]: if isinstance(analyse_data["analysis"], dict) and "analyse" in analyse_data["analysis"]: analyse_detail = analyse_data["analysis"]["analyse"] elif isinstance(analyse_data["analysis"], dict): analyse_detail = str(analyse_data["analysis"]) # Si l'analyse n'a pas été trouvée mais que le tri indique que l'image est pertinente if not analyse_detail and "sorting" in analyse_data and analyse_data["sorting"].get("is_relevant", False): analyse_detail = f"Image marquée comme pertinente. Raison: {analyse_data['sorting'].get('reason', 'Non spécifiée')}" # Ajouter l'analyse à la liste si elle existe if analyse_detail: images_analyses.append({ "image_name": image_name, "analyse": analyse_detail }) num_images = len(images_analyses) # Créer un prompt détaillé prompt = f"""Génère un rapport technique complet pour le ticket #{ticket_id}, en te basant sur les analyses suivantes. ## ANALYSE DU TICKET {ticket_analyse} ## ANALYSES DES IMAGES ({num_images} images) """ # Ajouter l'analyse de chaque image for i, img_analyse in enumerate(images_analyses, 1): image_name = img_analyse.get("image_name", f"Image {i}") analyse = img_analyse.get("analyse", "Analyse non disponible") prompt += f"\n### IMAGE {i}: {image_name}\n{analyse}\n" prompt += f""" Ton rapport doit être structuré avec les sections suivantes: 1. Résumé exécutif 2. Analyse détaillée du ticket 3. Analyse des images pertinentes 4. Diagnostic technique 5. Recommandations Fournir le rapport en format Markdown, avec des titres clairs et une structure cohérente. Assure-toi d'inclure toutes les informations techniques importantes et les liens entre les différentes analyses. """ # Appeler le LLM pour générer le rapport logger.info("Interrogation du LLM pour la génération du rapport") rapport_contenu = self.llm.interroger(prompt) # Créer les noms de fichiers pour la sauvegarde timestamp = self._get_timestamp() base_filename = f"{ticket_id}_{timestamp}" json_path = os.path.join(rapport_dir, f"{base_filename}.json") md_path = os.path.join(rapport_dir, f"{base_filename}.md") # Collecter les métadonnées du rapport avec détails sur les agents et LLM utilisés metadata = { "timestamp": timestamp, "model": getattr(self.llm, "modele", str(type(self.llm))), "temperature": self.temperature, "top_p": self.top_p, "max_tokens": self.max_tokens, "system_prompt": self.system_prompt, "agents_info": agents_info } # Sauvegarder le rapport au format JSON (données brutes + rapport généré) rapport_data_complet = rapport_data.copy() rapport_data_complet["rapport_genere"] = rapport_contenu rapport_data_complet["metadata"] = metadata with open(json_path, "w", encoding="utf-8") as f: json.dump(rapport_data_complet, f, ensure_ascii=False, indent=2) # Générer et sauvegarder le rapport au format Markdown basé directement sur le JSON markdown_content = self._generer_markdown_depuis_json(rapport_data_complet) with open(md_path, "w", encoding="utf-8") as f: f.write(markdown_content) logger.info(f"Rapport sauvegardé: {json_path} et {md_path}") except Exception as e: error_message = f"Erreur lors de la génération du rapport: {str(e)}" logger.error(error_message) print(f" ERREUR: {error_message}") return None, None # Enregistrer l'historique self.ajouter_historique("generation_rapport", { "rapport_dir": rapport_dir, "rapport_data": rapport_data, "prompt": prompt, "temperature": self.temperature, "top_p": self.top_p, "max_tokens": self.max_tokens }, { "json_path": json_path, "md_path": md_path, "rapport_contenu": rapport_contenu[:300] + ("..." if len(rapport_contenu) > 300 else "") }) message = f"Rapports générés dans: {rapport_dir}" print(f" {message}") return json_path, md_path def _collecter_info_agents(self, rapport_data: Dict) -> Dict: """ Collecte des informations sur les agents utilisés dans l'analyse Args: rapport_data: Données du rapport Returns: Dictionnaire contenant les informations sur les agents """ agents_info = {} # Informations sur l'agent JSON Analyser if "analyse_json" in rapport_data: json_analysis = rapport_data["analyse_json"] # Vérifier si l'analyse JSON contient des métadonnées if isinstance(json_analysis, dict) and "metadata" in json_analysis: agents_info["json_analyser"] = json_analysis["metadata"] # Informations sur les agents d'image if "analyse_images" in rapport_data and rapport_data["analyse_images"]: # Image Sorter sorter_info = {} analyser_info = {} for img_path, img_data in rapport_data["analyse_images"].items(): # Collecter info du sorter if "sorting" in img_data and isinstance(img_data["sorting"], dict) and "metadata" in img_data["sorting"]: if "model_info" in img_data["sorting"]["metadata"]: sorter_info = img_data["sorting"]["metadata"]["model_info"] # Collecter info de l'analyser if "analysis" in img_data and img_data["analysis"] and isinstance(img_data["analysis"], dict) and "metadata" in img_data["analysis"]: if "model_info" in img_data["analysis"]["metadata"]: analyser_info = img_data["analysis"]["metadata"]["model_info"] # Une fois qu'on a trouvé les deux, on peut sortir if sorter_info and analyser_info: break if sorter_info: agents_info["image_sorter"] = sorter_info if analyser_info: agents_info["image_analyser"] = analyser_info # Ajouter les informations de l'agent report generator agents_info["report_generator"] = { "model": getattr(self.llm, "modele", str(type(self.llm))), "temperature": self.temperature, "top_p": self.top_p, "max_tokens": self.max_tokens } return agents_info def _generer_markdown_depuis_json(self, rapport_data: Dict) -> str: """ Génère un rapport Markdown directement à partir des données JSON Args: rapport_data: Données JSON complètes du rapport Returns: Contenu Markdown du rapport """ ticket_id = rapport_data.get("ticket_id", "") timestamp = rapport_data.get("metadata", {}).get("timestamp", self._get_timestamp()) # Contenu de base du rapport (partie générée par le LLM) rapport_contenu = rapport_data.get("rapport_genere", "") # Entête du document markdown = f"# Rapport d'analyse du ticket #{ticket_id}\n\n" markdown += f"*Généré le: {timestamp}*\n\n" # Ajouter le rapport principal markdown += rapport_contenu + "\n\n" # Ajouter les informations techniques (agents, LLM, paramètres) markdown += "## Informations techniques\n\n" # Ajouter les informations sur les agents utilisés agents_info = rapport_data.get("metadata", {}).get("agents_info", {}) if agents_info: markdown += "### Agents et modèles utilisés\n\n" # Agent JSON Analyser if "json_analyser" in agents_info: info = agents_info["json_analyser"] markdown += "#### Agent d'analyse de texte\n" markdown += f"- **Modèle**: {info.get('model', 'Non spécifié')}\n" markdown += f"- **Température**: {info.get('temperature', 'Non spécifiée')}\n" markdown += f"- **Top-p**: {info.get('top_p', 'Non spécifié')}\n" markdown += f"- **Max tokens**: {info.get('max_tokens', 'Non spécifié')}\n\n" # Agent Image Sorter if "image_sorter" in agents_info: info = agents_info["image_sorter"] markdown += "#### Agent de tri d'images\n" markdown += f"- **Modèle**: {info.get('model', 'Non spécifié')}\n" markdown += f"- **Température**: {info.get('temperature', 'Non spécifiée')}\n" markdown += f"- **Top-p**: {info.get('top_p', 'Non spécifié')}\n" markdown += f"- **Max tokens**: {info.get('max_tokens', 'Non spécifié')}\n\n" # Agent Image Analyser if "image_analyser" in agents_info: info = agents_info["image_analyser"] markdown += "#### Agent d'analyse d'images\n" markdown += f"- **Modèle**: {info.get('model', 'Non spécifié')}\n" markdown += f"- **Température**: {info.get('temperature', 'Non spécifiée')}\n" markdown += f"- **Top-p**: {info.get('top_p', 'Non spécifié')}\n" markdown += f"- **Max tokens**: {info.get('max_tokens', 'Non spécifié')}\n\n" # Agent Report Generator if "report_generator" in agents_info: info = agents_info["report_generator"] markdown += "#### Agent de génération de rapport\n" markdown += f"- **Modèle**: {info.get('model', 'Non spécifié')}\n" markdown += f"- **Température**: {info.get('temperature', 'Non spécifiée')}\n" markdown += f"- **Top-p**: {info.get('top_p', 'Non spécifié')}\n" markdown += f"- **Max tokens**: {info.get('max_tokens', 'Non spécifié')}\n\n" # Statistiques d'analyse markdown += "### Statistiques\n\n" if "metadata" in rapport_data: metadata = rapport_data["metadata"] if "images_analysees" in metadata: markdown += f"- **Images analysées**: {metadata['images_analysees']}\n" if "images_pertinentes" in metadata: markdown += f"- **Images pertinentes**: {metadata['images_pertinentes']}\n" return markdown def _get_timestamp(self) -> str: """Retourne un timestamp au format YYYYMMDD_HHMMSS""" return datetime.now().strftime("%Y%m%d_%H%M%S")