llm_ticket3/agents/llama_vision/agent_image_analyser.py
2025-04-21 16:56:29 +02:00

261 lines
12 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
- Distingue clairement le nom complet des essais/tests/modules (par exemple, "Essai au bleu de méthylène" au lieu de simplement "essai bleu")
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
- Boutons RAZ ou réinitialisation (souvent marqués "RAZ" et non "PAZ")
- Précise si des éléments colorés font partie de l'interface standard (ex: bouton toujours rouge) ou s'ils semblent être liés au problème
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
- Vérifie spécifiquement si des messages d'erreur sont visibles en bas ou en haut de l'écran
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
- Précise le nom complet du module/essai concerné par le problème (par exemple "Essai au bleu de méthylène (MB)" et pas seulement "essai bleu")
- Identifie si l'utilisateur a accès à l'écran d'essai mais avec des erreurs, ou s'il n'y a pas d'accès du tout
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
- Tente d'extrapoler le contexte technique précis en observant l'interface (ex: "l'essai au bleu" mentionné par le client correspond clairement à "l'essai au bleu de méthylène (MB) - NF EN 933-9")
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é)
- Établis des connections explicites entre le vocabulaire utilisé par le client et ce qui est visible dans l'interface
7. Contexte technique élargi
- Identifie le contexte plus large de l'application (laboratoire, tests techniques, essais normalisés)
- Relève toutes les références à des normes ou standards (ex: NF EN 933-9)
- Mentionne tous les codes ou identifiants visibles qui pourraient être utiles (ex: numéros d'échantillons)
Règles importantes :
- Ne fais AUCUNE interprétation ni diagnostic sur les causes possibles
- Ne propose PAS de solution ou recommandation
- Reste strictement factuel et objectif, mais fais des liens explicites avec les termes utilisés par le client
- 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)
- Utilise systématiquement le nom complet et précis des modules et essais
- Vérifie la lecture correcte des boutons et menus (attention aux confusions comme PAZ/RAZ)
"""
)
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.
Tu dois être extrêmement précis dans ta lecture des interfaces et des éléments techniques.
Les clients utilisent souvent des termes abrégés (comme "essai bleu") alors que l'interface montre le terme complet ("Essai au bleu de méthylène"). Tu dois faire le lien entre ces termes.
Certains éléments dans l'interface peuvent prêter à confusion :
- Les boutons "RAZ" (réinitialisation) sont parfois difficiles à lire
- Des éléments en couleur peuvent faire partie de l'interface standard (et non du problème)
- Les messages d'erreur sont souvent en bas de l'écran et contiennent des informations cruciales
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 et pour faire le lien entre le vocabulaire du client et les éléments techniques réels.
"""
).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}
INSTRUCTIONS IMPORTANTES:
- Lis attentivement tous les textes visibles dans l'image et reporte-les avec précision.
- Veille à bien distinguer les boutons "RAZ" (réinitialisation) s'ils sont présents.
- Identifie précisément le nom complet des essais ou tests mentionnés (ex: "Essai au bleu de méthylène" plutôt que juste "essai bleu").
- Si l'interface montre un essai ou module en particulier, précise son nom complet et sa norme associée.
- Fais attention aux messages d'erreur qui peuvent apparaître en bas ou en haut de l'écran.
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)
# Effectuer des corrections sur la réponse si nécessaire
response = self._corriger_termes_courants(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 _corriger_termes_courants(self, texte: str) -> str:
"""
Corrige certains termes couramment mal interprétés par le modèle.
"""
corrections = {
"PAZ": "RAZ",
"Essai bleu": "Essai au bleu de méthylène",
"essai bleu": "essai au bleu de méthylène",
"Essai au bleu": "Essai au bleu de méthylène"
}
for terme_incorrect, terme_correct in corrections.items():
texte = texte.replace(terme_incorrect, terme_correct)
return texte
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")