mirror of
https://github.com/Ladebeze66/llm_ticket3.git
synced 2025-12-13 10:46:51 +01:00
261 lines
12 KiB
Python
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") |