llm_ticket3/agents/pixtral_large/agent_image_analyser.py
2025-04-21 16:04:53 +02:00

212 lines
8.6 KiB
Python

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")