#!/usr/bin/env python3 # -*- coding: utf-8 -*- import os import json import logging from typing import Dict, Any, Optional from datetime import datetime # Importer BaseAgent depuis le répertoire utils from agents.utils.base_agent import BaseAgent logger = logging.getLogger("AgentImageAnalyser_Pixtral12b") class AgentImageAnalyser(BaseAgent): """ Agent pour analyser en détail les images des tickets. Utilise un LLM avec capacités de vision pour décrire et analyser le contenu des images. """ def __init__(self, llm): super().__init__(llm) # Configuration locale de l'agent self.system_prompt = """Tu es un expert en analyse d'images techniques. Ta mission est d'analyser en détail des captures d'écran et images techniques pour le support informatique. Tu dois: 1. Décrire précisément le contenu visible de l'image 2. Identifier tout texte, message d'erreur ou information technique visible 3. Repérer les problèmes potentiels ou anomalies visibles 4. Fournir un contexte sur ce que l'image montre dans le cadre d'un problème informatique Sois factuel, précis et exhaustif dans ton analyse. Concentre-toi sur les aspects techniques. Format ta réponse de manière structurée pour faciliter la compréhension. """ def executer(self, image_path: str, contexte: Optional[str] = None) -> Dict[str, Any]: """ Analyse une image en détail pour en extraire les informations pertinentes. Args: image_path: Chemin vers l'image à analyser contexte: Contexte optionnel sur le ticket pour aider à l'analyse Returns: Dictionnaire contenant l'analyse détaillée de l'image """ image_name = os.path.basename(image_path) logger.info(f"Analyse détaillée de l'image: {image_name}") # Vérifier que l'image existe if not os.path.exists(image_path): logger.error(f"L'image n'existe pas: {image_path}") return { "error": True, "message": f"L'image n'existe pas: {image_name}", "image_path": image_path } # Préparer le prompt avec le contexte si disponible prompt_base = "Analyse cette image en détail et réponds au format JSON." if contexte: prompt_base = f"Analyse cette image en détail dans le contexte suivant:\n{contexte}\n\nRéponds au format JSON." prompt = f"""{prompt_base} Analyse chaque élément important visible et fournit une description détaillée. Format JSON attendu: {{ "description_generale": "Description générale de ce que montre l'image", "elements_techniques": ["Liste des éléments techniques visibles"], "texte_visible": "Tout texte important visible dans l'image", "messages_erreur": ["Liste des messages d'erreur si présents"], "problemes_identifies": ["Liste des problèmes potentiels identifiés"], "contexte_technique": "Explication du contexte technique de cette image", "recommandations": ["Suggestions basées sur ce qui est visible"] }}""" # Effectuer l'analyse via le LLM try: # Utiliser la méthode d'interrogation avec image resultat_brut = self.llm.interroger_avec_image(image_path, prompt) # Tenter d'extraire le JSON de la réponse json_str = self._extraire_json(resultat_brut) if json_str: try: # Charger le JSON analyse = json.loads(json_str) # Ajouter des métadonnées analyse["image_path"] = image_path analyse["image_name"] = image_name analyse["timestamp"] = self._get_timestamp() analyse["source"] = "agent_image_analyser" # Ajouter la réponse brute pour référence analyse["raw_response"] = resultat_brut logger.info(f"Analyse complétée avec succès pour {image_name}") return analyse except json.JSONDecodeError as e: logger.error(f"Erreur de décodage JSON pour {image_name}: {e}") # Fournir une analyse de secours plus simple return self._analyse_fallback(resultat_brut, image_path) else: logger.warning(f"Format de réponse non-JSON pour {image_name}") # Fournir une analyse de secours plus simple return self._analyse_fallback(resultat_brut, image_path) except Exception as e: logger.error(f"Erreur lors de l'analyse de l'image {image_name}: {str(e)}") return { "error": True, "message": f"Erreur d'analyse: {str(e)}", "image_path": image_path, "image_name": image_name, "timestamp": self._get_timestamp() } def _extraire_json(self, texte: str) -> Optional[str]: """ Extrait le contenu JSON d'une chaîne de texte. Args: texte: Texte contenant potentiellement du JSON Returns: Chaîne JSON extraite ou None si aucun JSON n'est trouvé """ # Chercher des accolades ouvrantes et fermantes debut = texte.find('{') fin = texte.rfind('}') if debut != -1 and fin != -1 and fin > debut: return texte[debut:fin+1] return None def _analyse_fallback(self, texte: str, image_path: str) -> Dict[str, Any]: """ Crée une analyse de secours quand le format JSON n'est pas utilisable. Args: texte: Texte de l'analyse brute image_path: Chemin de l'image Returns: Dictionnaire avec l'analyse simplifiée """ image_name = os.path.basename(image_path) # Diviser le texte en paragraphes paragraphes = [p.strip() for p in texte.split('\n\n') if p.strip()] # Extraire ce qui pourrait être une description générale (premier paragraphe) description = paragraphes[0] if paragraphes else "Analyse non disponible au format JSON" # Chercher des éléments techniques ou messages d'erreur elements_techniques = [] messages_erreur = [] for p in paragraphes[1:]: if "erreur" in p.lower() or "error" in p.lower() or "exception" in p.lower(): messages_erreur.append(p) elif any(terme in p.lower() for terme in ["technique", "système", "logiciel", "interface", "configuration"]): elements_techniques.append(p) # Construire un dictionnaire simplifié analyse = { "description_generale": description, "elements_techniques": elements_techniques, "messages_erreur": messages_erreur, "texte_visible": "Extraction de texte non disponible", "problemes_identifies": [], "contexte_technique": "Contexte non disponible au format JSON", "recommandations": [], "image_path": image_path, "image_name": image_name, "timestamp": self._get_timestamp(), "source": "agent_image_analyser", "format_fallback": True, "raw_response": texte } return analyse def _get_timestamp(self) -> str: """Retourne un timestamp au format YYYYMMDD_HHMMSS""" return datetime.now().strftime("%Y%m%d_%H%M%S")