mirror of
https://github.com/Ladebeze66/llm_ticket3.git
synced 2025-12-16 00:36:52 +01:00
347 lines
15 KiB
Python
347 lines
15 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
|
|
"""
|
|
Workflow complet d'analyse de ticket avec génération de rapport.
|
|
Ce module implémente un workflow qui combine tous les agents :
|
|
1. Analyse du ticket
|
|
2. Tri des images
|
|
3. Analyse des images pertinentes
|
|
4. Génération d'un rapport final
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
import json
|
|
import logging
|
|
import time
|
|
import argparse
|
|
from typing import Dict, Any, Optional, List
|
|
|
|
# Import des factories
|
|
from tests.common.agent_factory import (
|
|
create_ticket_analyser,
|
|
create_image_sorter,
|
|
create_image_analyser,
|
|
create_report_generator
|
|
)
|
|
from tests.common.llm_factory import create_llm
|
|
from tests.common.ticket_utils import get_ticket_json, get_ticket_images
|
|
|
|
logger = logging.getLogger("CompleteAnalysisWorkflow")
|
|
|
|
def execute_workflow(
|
|
ticket_id: str,
|
|
output_dir: str = "output",
|
|
text_model: str = "mistral_large",
|
|
vision_model: str = "pixtral_large",
|
|
generate_report: bool = True,
|
|
save_results: bool = True
|
|
) -> Dict[str, Any]:
|
|
"""
|
|
Exécute le workflow complet d'analyse de ticket et génération de rapport.
|
|
|
|
Args:
|
|
ticket_id: Identifiant du ticket
|
|
output_dir: Répertoire contenant les tickets
|
|
text_model: Nom du modèle à utiliser pour l'analyse de ticket et la génération de rapport
|
|
vision_model: Nom du modèle à utiliser pour l'analyse d'image
|
|
generate_report: Si True, génère un rapport final
|
|
save_results: Si True, sauvegarde les résultats intermédiaires
|
|
|
|
Returns:
|
|
Dict avec les résultats du workflow
|
|
"""
|
|
workflow_start = time.time()
|
|
logger.info(f"Démarrage du workflow complet pour le ticket {ticket_id}")
|
|
print(f"Démarrage du workflow complet pour le ticket {ticket_id}")
|
|
|
|
# ÉTAPE 1: Initialiser les modèles LLM
|
|
try:
|
|
print("Initialisation des modèles LLM...")
|
|
text_llm = create_llm(text_model)
|
|
vision_llm = create_llm(vision_model)
|
|
print(f"Modèles initialisés: {text_model}, {vision_model}")
|
|
except Exception as e:
|
|
logger.error(f"Erreur lors de l'initialisation des modèles: {e}")
|
|
print(f"ERREUR: Impossible d'initialiser les modèles: {e}")
|
|
return {"error": str(e), "stage": "init_llm"}
|
|
|
|
# ÉTAPE 2: Initialiser les agents
|
|
try:
|
|
print("Création des agents...")
|
|
ticket_agent = create_ticket_analyser(text_llm)
|
|
image_sorter = create_image_sorter(vision_llm)
|
|
image_analyser = create_image_analyser(vision_llm)
|
|
report_generator = create_report_generator(text_llm) if generate_report else None
|
|
print("Agents créés avec succès")
|
|
except Exception as e:
|
|
logger.error(f"Erreur lors de la création des agents: {e}")
|
|
print(f"ERREUR: Impossible de créer les agents: {e}")
|
|
return {"error": str(e), "stage": "init_agents"}
|
|
|
|
# ÉTAPE 3: Charger les données du ticket
|
|
try:
|
|
print("Chargement des données du ticket...")
|
|
json_file, ticket_data = get_ticket_json(ticket_id, output_dir)
|
|
|
|
if not json_file or not ticket_data:
|
|
error_msg = f"Impossible de charger les données du ticket {ticket_id}"
|
|
logger.error(error_msg)
|
|
print(f"ERREUR: {error_msg}")
|
|
return {"error": error_msg, "stage": "load_ticket"}
|
|
|
|
print(f"Données du ticket chargées: {json_file}")
|
|
except Exception as e:
|
|
logger.error(f"Erreur lors du chargement du ticket: {e}")
|
|
print(f"ERREUR: Impossible de charger le ticket: {e}")
|
|
return {"error": str(e), "stage": "load_ticket"}
|
|
|
|
# ÉTAPE 4: Analyser le ticket
|
|
try:
|
|
print("Analyse du ticket en cours...")
|
|
ticket_analysis = ticket_agent.executer(ticket_data)
|
|
print(f"Analyse du ticket terminée: {len(ticket_analysis) if ticket_analysis else 0} caractères")
|
|
|
|
# Sauvegarder l'analyse si demandé
|
|
if save_results:
|
|
save_dir = os.path.join("results", ticket_id)
|
|
os.makedirs(save_dir, exist_ok=True)
|
|
save_file = os.path.join(save_dir, f"ticket_analysis_{text_model}.json")
|
|
with open(save_file, "w", encoding="utf-8") as f:
|
|
json.dump({
|
|
"ticket_id": ticket_id,
|
|
"model": text_model,
|
|
"analysis": ticket_analysis,
|
|
"timestamp": time.strftime("%Y-%m-%d %H:%M:%S")
|
|
}, f, ensure_ascii=False, indent=2)
|
|
print(f"Analyse du ticket sauvegardée: {save_file}")
|
|
except Exception as e:
|
|
logger.error(f"Erreur lors de l'analyse du ticket: {e}")
|
|
print(f"ERREUR: Impossible d'analyser le ticket: {e}")
|
|
return {"error": str(e), "stage": "analyse_ticket", "ticket_data": ticket_data}
|
|
|
|
# ÉTAPE 5: Récupérer et trier les images
|
|
relevant_images = []
|
|
image_sorting_results = {}
|
|
all_images = []
|
|
|
|
try:
|
|
print("Récupération et tri des images...")
|
|
all_images = get_ticket_images(ticket_id, output_dir)
|
|
|
|
if not all_images:
|
|
print("Aucune image trouvée pour ce ticket")
|
|
else:
|
|
print(f"Images trouvées: {len(all_images)}")
|
|
|
|
# Trier les images
|
|
for img_path in all_images:
|
|
print(f"Évaluation de l'image: {os.path.basename(img_path)}")
|
|
try:
|
|
sorting_result = image_sorter.executer(img_path)
|
|
is_relevant = sorting_result.get("is_relevant", False)
|
|
reason = sorting_result.get("reason", "")
|
|
|
|
image_sorting_results[img_path] = sorting_result
|
|
|
|
if is_relevant:
|
|
print(f" => Pertinente: {reason[:50]}...")
|
|
relevant_images.append(img_path)
|
|
else:
|
|
print(f" => Non pertinente: {reason[:50]}...")
|
|
except Exception as e:
|
|
logger.error(f"Erreur lors du tri de l'image {img_path}: {e}")
|
|
print(f"ERREUR: Impossible de trier l'image {img_path}: {e}")
|
|
image_sorting_results[img_path] = {"error": str(e), "is_relevant": False}
|
|
|
|
print(f"Images pertinentes: {len(relevant_images)}/{len(all_images)}")
|
|
|
|
# Sauvegarder les résultats du tri si demandé
|
|
if save_results and image_sorting_results:
|
|
save_dir = os.path.join("results", ticket_id)
|
|
os.makedirs(save_dir, exist_ok=True)
|
|
save_file = os.path.join(save_dir, f"image_sorting_{vision_model}.json")
|
|
with open(save_file, "w", encoding="utf-8") as f:
|
|
json.dump({
|
|
"ticket_id": ticket_id,
|
|
"model": vision_model,
|
|
"images_total": len(all_images),
|
|
"images_relevant": len(relevant_images),
|
|
"results": {os.path.basename(k): v for k, v in image_sorting_results.items()},
|
|
"timestamp": time.strftime("%Y-%m-%d %H:%M:%S")
|
|
}, f, ensure_ascii=False, indent=2)
|
|
print(f"Résultats du tri d'images sauvegardés: {save_file}")
|
|
|
|
except Exception as e:
|
|
logger.error(f"Erreur lors de la récupération/tri des images: {e}")
|
|
print(f"ERREUR: Impossible de récupérer/trier les images: {e}")
|
|
if generate_report:
|
|
# On continue quand même pour générer le rapport, même sans images
|
|
print("On continue sans images...")
|
|
else:
|
|
return {
|
|
"error": str(e),
|
|
"stage": "sort_images",
|
|
"ticket_id": ticket_id,
|
|
"ticket_data": ticket_data,
|
|
"ticket_analysis": ticket_analysis
|
|
}
|
|
|
|
# ÉTAPE 6: Analyser les images pertinentes avec contexte
|
|
image_analysis_results = {}
|
|
|
|
if relevant_images:
|
|
print("Analyse des images pertinentes avec contexte...")
|
|
|
|
for img_path in relevant_images:
|
|
img_name = os.path.basename(img_path)
|
|
print(f"Analyse de l'image: {img_name}")
|
|
try:
|
|
analysis_result = image_analyser.executer(img_path, contexte=ticket_analysis)
|
|
image_analysis_results[img_path] = analysis_result
|
|
print(f" => Analyse terminée: {len(analysis_result.get('analyse', '')) if analysis_result else 0} caractères")
|
|
except Exception as e:
|
|
logger.error(f"Erreur lors de l'analyse de l'image {img_path}: {e}")
|
|
print(f"ERREUR: Impossible d'analyser l'image {img_path}: {e}")
|
|
image_analysis_results[img_path] = {"error": str(e)}
|
|
|
|
# Sauvegarder les analyses d'images si demandé
|
|
if save_results and image_analysis_results:
|
|
save_dir = os.path.join("results", ticket_id)
|
|
os.makedirs(save_dir, exist_ok=True)
|
|
save_file = os.path.join(save_dir, f"image_analysis_{vision_model}.json")
|
|
with open(save_file, "w", encoding="utf-8") as f:
|
|
json.dump({
|
|
"ticket_id": ticket_id,
|
|
"model": vision_model,
|
|
"images_analysed": len(image_analysis_results),
|
|
"results": {os.path.basename(k): v for k, v in image_analysis_results.items()},
|
|
"timestamp": time.strftime("%Y-%m-%d %H:%M:%S")
|
|
}, f, ensure_ascii=False, indent=2)
|
|
print(f"Analyses d'images sauvegardées: {save_file}")
|
|
else:
|
|
print("Aucune image pertinente à analyser")
|
|
|
|
# ÉTAPE 7: Générer le rapport final
|
|
report_file = None
|
|
report_content = None
|
|
|
|
if generate_report and report_generator is not None:
|
|
print("Génération du rapport final...")
|
|
try:
|
|
# Préparer les données pour le rapport
|
|
rapport_data = {
|
|
"ticket_id": ticket_id,
|
|
"ticket_data": ticket_data,
|
|
"ticket_analyse": ticket_analysis,
|
|
"analyse_images": {}
|
|
}
|
|
|
|
# Ajouter les résultats d'analyse d'images
|
|
for img_path, analysis in image_analysis_results.items():
|
|
sorting_result = image_sorting_results.get(img_path, {})
|
|
rapport_data["analyse_images"][img_path] = {
|
|
"sorting": sorting_result,
|
|
"analysis": analysis
|
|
}
|
|
|
|
# Définir le répertoire de destination pour le rapport
|
|
reports_dir = os.path.join("reports", ticket_id)
|
|
os.makedirs(reports_dir, exist_ok=True)
|
|
|
|
# Générer le rapport
|
|
report_result = report_generator.executer(rapport_data, reports_dir)
|
|
|
|
# Vérifier si le rapport a été généré avec succès
|
|
if report_result and isinstance(report_result, dict):
|
|
report_file = report_result.get("report_file")
|
|
report_content = report_result.get("content")
|
|
print(f"Rapport généré avec succès: {report_file}")
|
|
else:
|
|
print("Rapport généré mais le format de retour n'est pas standard")
|
|
except Exception as e:
|
|
logger.error(f"Erreur lors de la génération du rapport: {e}")
|
|
print(f"ERREUR: Impossible de générer le rapport: {e}")
|
|
elif generate_report:
|
|
logger.warning("Génération de rapport demandée mais l'agent n'a pas été initialisé correctement")
|
|
print("ATTENTION: Impossible de générer le rapport (agent non initialisé)")
|
|
|
|
# Calcul du temps total d'exécution
|
|
workflow_time = time.time() - workflow_start
|
|
print(f"Workflow complet terminé en {workflow_time:.2f} secondes")
|
|
|
|
# Retourner les résultats
|
|
return {
|
|
"ticket_id": ticket_id,
|
|
"ticket_analysis": ticket_analysis,
|
|
"images_count": len(all_images),
|
|
"relevant_images": len(relevant_images),
|
|
"images_analysed": len(image_analysis_results),
|
|
"report_file": report_file,
|
|
"execution_time": workflow_time
|
|
}
|
|
|
|
def main():
|
|
# Configuration de l'analyseur d'arguments
|
|
parser = argparse.ArgumentParser(description="Exécuter le workflow complet d'analyse de ticket")
|
|
parser.add_argument("ticket_id", help="ID du ticket à analyser (ex: T1234)")
|
|
parser.add_argument("--output_dir", default="output", help="Répertoire des tickets (défaut: output)")
|
|
parser.add_argument("--text_model", default="mistral_large", help="Modèle texte à utiliser (défaut: mistral_large)")
|
|
parser.add_argument("--vision_model", default="pixtral_large", help="Modèle vision à utiliser (défaut: pixtral_large)")
|
|
parser.add_argument("--no-report", action="store_true", help="Ne pas générer de rapport final")
|
|
parser.add_argument("--no-save", action="store_true", help="Ne pas sauvegarder les résultats intermédiaires")
|
|
parser.add_argument("--verbose", "-v", action="store_true", help="Mode verbeux")
|
|
|
|
# Analyser les arguments
|
|
args = parser.parse_args()
|
|
|
|
# Configurer le logging
|
|
log_level = logging.DEBUG if args.verbose else logging.INFO
|
|
log_file = f"complete_workflow_{args.ticket_id}.log"
|
|
|
|
handlers: List[logging.Handler] = [logging.StreamHandler()]
|
|
if log_file:
|
|
handlers.append(logging.FileHandler(log_file))
|
|
|
|
logging.basicConfig(
|
|
level=log_level,
|
|
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
|
handlers=handlers
|
|
)
|
|
|
|
# Exécuter le workflow
|
|
results = execute_workflow(
|
|
ticket_id=args.ticket_id,
|
|
output_dir=args.output_dir,
|
|
text_model=args.text_model,
|
|
vision_model=args.vision_model,
|
|
generate_report=not args.no_report,
|
|
save_results=not args.no_save
|
|
)
|
|
|
|
# Afficher un résumé des résultats
|
|
if "error" in results:
|
|
print(f"\nErreur lors de l'exécution du workflow: {results['error']}")
|
|
print(f"Étape en échec: {results.get('stage', 'inconnue')}")
|
|
return 1
|
|
|
|
print("\n=== Résumé du workflow complet ===")
|
|
print(f"Ticket: {results['ticket_id']}")
|
|
print(f"Temps d'exécution total: {results['execution_time']:.2f} secondes")
|
|
print(f"Analyse du ticket: {len(results['ticket_analysis']) if results.get('ticket_analysis') else 0} caractères")
|
|
print(f"Images trouvées: {results['images_count']}")
|
|
print(f"Images pertinentes: {results['relevant_images']}")
|
|
print(f"Images analysées: {results['images_analysed']}")
|
|
|
|
if results.get("report_file"):
|
|
print(f"Rapport généré: {results['report_file']}")
|
|
elif not args.no_report:
|
|
print("Rapport: Non généré (échec ou aucune donnée disponible)")
|
|
else:
|
|
print("Rapport: Désactivé")
|
|
|
|
return 0
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main()) |