from ..base_agent import BaseAgent import logging import os from typing import Dict, Any from PIL import Image from ..utils.pipeline_logger import sauvegarder_donnees logger = logging.getLogger("AgentImageAnalyser") class AgentImageAnalyser(BaseAgent): """ Agent pour analyser les images et extraire les informations pertinentes. """ def __init__(self, llm): super().__init__("AgentImageAnalyser", llm) # Configuration personnalisable self.params = { "temperature": 0.2, "top_p": 0.8, "max_tokens": 3000 } self.instructions_analyse = ( """ 1. Description objective Décris précisément ce que montre l'image : - Interface logicielle, menus, fenêtres, onglets - Messages d'erreur, messages système, code ou script - Nom ou titre du logiciel ou du module si visible 2. Éléments techniques clés Identifie : - Versions logicielles ou modules affichés - Codes d'erreur visibles - Paramètres configurables (champs de texte, sliders, dropdowns, cases à cocher) - Valeurs affichées ou préremplies dans les champs - Éléments désactivés, grisés ou masqués (souvent non modifiables) - Boutons actifs/inactifs 3. Éléments mis en évidence - Recherche les zones entourées, encadrées, surlignées ou fléchées - Ces éléments sont souvent importants pour le client ou le support - Mentionne explicitement leur contenu et leur style de mise en valeur 4. Relation avec le problème - Établis le lien entre les éléments visibles et le problème décrit dans le ticket - Indique si des composants semblent liés à une mauvaise configuration ou une erreur 5. Réponses potentielles - Détermine si l'image apporte des éléments de réponse à une question posée dans : - Le titre du ticket - La description du problème 6. Lien avec la discussion - Vérifie si l'image fait écho à une étape décrite dans le fil de discussion - Note les correspondances (ex: même module, même message d'erreur que précédemment mentionné) Règles importantes : - Ne fais AUCUNE interprétation ni diagnostic - Ne propose PAS de solution ou recommandation - Reste strictement factuel et objectif - Concentre-toi uniquement sur ce qui est visible dans l'image - Reproduis les textes exacts(ex : messages d'erreur, libellés de paramètres) - Prête une attention particulière aux éléments modifiables (interactifs) et non modifiables (grisés) """ ) self.system_prompt = ( """ Tu es un expert en analyse d'images pour le support technique de BRG-Lab pour la société CBAO. Ta mission est d'analyser des captures d'écran en lien avec le contexte du ticket de support. Structure ton analyse d'image de façon factuelle: {instructions} Ton analyse sera utilisée comme élément factuel pour un rapport technique plus complet. """ ).format( instructions=self.instructions_analyse ) self._appliquer_config_locale() logger.info("AgentImageAnalyser initialisé") def _appliquer_config_locale(self) -> None: """ Applique la configuration locale au modèle LLM. """ if hasattr(self.llm, "prompt_system"): self.llm.prompt_system = self.system_prompt if hasattr(self.llm, "configurer"): self.llm.configurer(**self.params) def _verifier_image(self, image_path: str) -> bool: """ Vérifie si l'image existe et est accessible """ try: if not os.path.exists(image_path) or not os.access(image_path, os.R_OK): return False with Image.open(image_path) as img: width, height = img.size return width > 0 and height > 0 except Exception as e: logger.error(f"Vérification impossible pour {image_path}: {e}") return False def _generer_prompt_analyse(self, contexte: str, prefix: str = "") -> str: """ Génère le prompt d'analyse d'image """ return f"""{prefix} CONTEXTE DU TICKET: {contexte} Fournis une analyse STRICTEMENT FACTUELLE de l'image avec les sections suivantes: {self.instructions_analyse}""" def executer(self, image_path: str, contexte: str = "") -> Dict[str, Any]: """ Analyse une image en tenant compte du contexte du ticket """ image_name = os.path.basename(image_path) print(f" AgentImageAnalyser: Analyse de {image_name}") if not self._verifier_image(image_path): return self._erreur("Erreur d'accès ou image invalide", image_path) try: prompt = self._generer_prompt_analyse(contexte, "Analyse cette image en tenant compte du contexte suivant:") if hasattr(self.llm, "interroger_avec_image"): response = self.llm.interroger_avec_image(image_path, prompt) elif hasattr(self.llm, "_encoder_image_base64"): img_base64 = self.llm._encoder_image_base64(image_path) if img_base64: prompt_base64 = self._generer_prompt_analyse(contexte, f"Analyse cette image:\n{img_base64}") response = self.llm.interroger(prompt_base64) else: return self._erreur("Impossible d'encoder l'image en base64", image_path) else: return self._erreur("Le modèle ne supporte pas l'analyse d'images", image_path) # Vérifier si la réponse contient des indications que le modèle ne peut pas analyser l'image error_phrases = [ "je ne peux pas directement visualiser", "je n'ai pas accès à l'image", "je ne peux pas voir l'image", "sans accès direct à l'image", "je n'ai pas la possibilité de voir", "je ne peux pas accéder directement", "erreur: impossible d'analyser l'image" ] if any(phrase in response.lower() for phrase in error_phrases): return self._erreur("Le modèle n'a pas pu analyser l'image correctement", image_path, raw=response) result = { "analyse": response, "raw_response": response, "metadata": { "image_path": image_path, "image_name": image_name, "timestamp": self._get_timestamp(), "model_info": { "model": getattr(self.llm, "modele", str(type(self.llm))), **getattr(self.llm, "params", {}) }, "source_agent": self.nom } } # Sauvegarder les données dans le fichier sauvegarder_donnees(None, "analyse_image", result, base_dir="reports", is_resultat=True) # Enregistrer l'analyse dans l'historique self.ajouter_historique( "analyse_image", {"image_path": image_path, "contexte": contexte, "prompt": prompt}, result ) return result except Exception as e: return self._erreur(str(e), image_path) def _erreur(self, message: str, path: str, raw: str = "") -> Dict[str, Any]: """ Crée un dictionnaire de réponse en cas d'erreur """ return { "analyse": f"ERREUR: {message}", "raw_response": raw, "error": True, "metadata": { "image_path": path, "image_name": os.path.basename(path), "timestamp": self._get_timestamp(), "error": True, "source_agent": self.nom } } def _get_timestamp(self) -> str: """Retourne un timestamp au format YYYYMMDD_HHMMSS""" from datetime import datetime return datetime.now().strftime("%Y%m%d_%H%M%S")