llm_ticket3/agents/agent_filtre_images.py
2025-04-02 09:01:55 +02:00

169 lines
7.1 KiB
Python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Agent pour filtrer les images non pertinentes dans un contexte de support technique.
"""
import os
import json
import re
from typing import Dict, Any, Optional
from .agent_base import Agent
from llm import Pixtral
class AgentFiltreImages(Agent):
"""
Agent qui détermine si une image est pertinente dans un contexte de support technique.
Filtre les logos, signatures, icônes, etc.
"""
def __init__(self, api_key: Optional[str] = None):
"""
Initialise l'agent de filtrage d'images.
Args:
api_key: Clé API pour le LLM
"""
super().__init__("AgentFiltreImages")
self.llm = Pixtral(api_key=api_key)
# Configuration par défaut du LLM
default_system_prompt = """
Vous êtes un expert en analyse d'images techniques. Votre mission est de déterminer
si une image est pertinente dans un contexte de support technique ou non.
Images PERTINENTES:
- Captures d'écran montrant des problèmes, erreurs, bugs
- Photos d'équipements avec problèmes visibles
- Schémas techniques ou diagrammes
- Graphiques de données techniques
Images NON PERTINENTES:
- Logos d'entreprise
- Signatures ou avatars
- Icônes ou boutons isolés
- Bannières décoratives, séparateurs
- Images génériques sans information technique
"""
self.configurer_llm(
system_prompt=default_system_prompt,
temperature=0.2, # Basse température pour des réponses précises
max_tokens=500 # Réponses courtes pour le filtrage
)
def executer(self, image_path: str) -> Dict[str, Any]:
"""
Détermine si une image est pertinente pour l'analyse technique.
Args:
image_path: Chemin vers l'image à analyser
Returns:
Dictionnaire contenant le résultat du filtrage avec au minimum
{'pertinente': True/False}
"""
# Vérifier que l'image existe
if not os.path.exists(image_path):
erreur = f"L'image {image_path} n'existe pas"
self.ajouter_historique("filtre_image_erreur", image_path, erreur)
return {"pertinente": False, "error": erreur}
# Construire le prompt pour la classification
prompt = """
Analysez cette image et déterminez si elle est PERTINENTE ou NON PERTINENTE dans un contexte de support technique.
Répondez au format JSON suivant:
{
"pertinente": true/false,
"type_image": "capture_ecran|photo_equipement|schema|graphique|logo|icone|decorative|autre",
"description": "Brève description du contenu",
"confiance": "valeur de 0 à 100",
"justification": "Explication de votre décision"
}
"""
# Enregistrer l'action dans l'historique
self.ajouter_historique("filtre_image", image_path, "Filtrage en cours...")
# Appel au LLM
try:
# Pour les cas complexes, augmenter légèrement la température
if "complexe" in image_path or os.path.getsize(image_path) > 500000:
self.configurer_llm(temperature=0.4)
self.ajouter_historique("ajustement_temperature", "Augmentation pour image complexe", "temperature=0.4")
resultat_brut = self.llm.analyze_image(image_path, prompt)
# Essayer d'extraire le JSON de la réponse
try:
content = resultat_brut.get("content", "")
# Si le contenu est déjà au format JSON correctement formaté
if content.strip().startswith("{") and content.strip().endswith("}"):
try:
resultat_json = json.loads(content)
except json.JSONDecodeError:
# Si le contenu a la structure JSON mais ne peut pas être décodé
# nettoyer et réessayer
content_cleaned = content.replace("```json", "").replace("```", "").strip()
resultat_json = json.loads(content_cleaned)
else:
# Chercher un bloc JSON dans la réponse
json_match = re.search(r'```json\s*(.*?)\s*```', content, re.DOTALL)
if json_match:
json_str = json_match.group(1).strip()
else:
# Essayer d'extraire un objet JSON sans les blocs de code
json_match = re.search(r'(\{.*?\})', content, re.DOTALL)
if json_match:
json_str = json_match.group(1).strip()
else:
# Sinon, prendre tout le contenu comme JSON potentiel
json_str = content.strip()
# Parser le JSON
resultat_json = json.loads(json_str)
# S'assurer que le champ 'pertinente' existe
if "pertinente" not in resultat_json:
resultat_json["pertinente"] = False
resultat_json["error"] = "Format de réponse incorrect"
# Ajouter les paramètres LLM utilisés
resultat_json["parametres_llm"] = self.generer_rapport_parametres()
self.ajouter_historique("filtre_image_resultat", "Filtrage terminé",
f"Pertinente: {resultat_json.get('pertinente', False)}")
return resultat_json
except Exception as e:
# Pour les modules en mode simulation, utiliser directement la réponse
if "pertinente" in resultat_brut.get("content", ""):
try:
resultat_json = json.loads(resultat_brut.get("content", ""))
resultat_json["parametres_llm"] = self.generer_rapport_parametres()
self.ajouter_historique("filtre_image_resultat", "Filtrage terminé",
f"Pertinente: {resultat_json.get('pertinente', False)}")
return resultat_json
except:
pass
# En cas d'erreur de parsing, retourner un résultat par défaut
resultat = {
"pertinente": False,
"error": f"Erreur de parsing JSON: {str(e)}",
"response_raw": resultat_brut.get("content", "")[:200],
"parametres_llm": self.generer_rapport_parametres()
}
self.ajouter_historique("filtre_image_parsing_erreur", "Erreur de parsing", str(e))
return resultat
except Exception as e:
erreur = f"Erreur lors du filtrage: {str(e)}"
self.ajouter_historique("filtre_image_erreur", image_path, erreur)
return {
"pertinente": False,
"error": erreur,
"parametres_llm": self.generer_rapport_parametres()
}