llm_ticket3/agents/pixtral12b/agent_image_analyser.py
2025-04-14 15:34:51 +02:00

189 lines
7.6 KiB
Python

#!/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")