llm_ticket3/.specstory/history/2025-04-10_08-33-analyse-et-simplification-de-agent-report-generator-py.md
2025-04-10 10:47:32 +02:00

18 KiB

Analyse et simplification de agent_report_generator.py (2025-04-10 10:33:39)

User

je voudrai que tu analyses le fonctionnement de mon agent_report_generator.py je voudrai le simplifier. Nous avons déjà essagyé avec report_utils.py et report_formatter.py je te rappelle que je voudrai garder mes rapports tels qu'il les génère actuellment nous pouvons éventuellement créér un autre agent pour séparer clairement les rôles si c'est possible de gérer par des scripts, il faudra alors tenir compte de ces modifications dans le reste de mon projet


Assistant

Je vais analyser le fonctionnement de votre agent_report_generator.py et proposer des simplifications.


Listed directory agents • **7** results
Name
📄 agent_ticket_analyser.py
📁 __pycache__
📄 agent_image_analyser.py
📄 agent_report_generator.py
📁 utils
📄 agent_image_sorter.py
📄 base_agent.py

Listed directory agents/utils • **3** results
Name
📁 __pycache__
📄 report_utils.py
📄 __init__.py

Read file: agents/utils/report_utils.py


Read file: agents/utils/init.py


Searched codebase "report_formatter" • **2** results
File
undefined
undefined

Read file: formatters/report_formatter.py


Read file: agents/base_agent.py


Après avoir analysé votre code, je peux proposer une simplification de agent_report_generator.py. Voici mon analyse et mes recommandations:

Analyse du code actuel

  1. Problème principal: Le fichier agent_report_generator.py est très volumineux (1043 lignes) et contient plusieurs responsabilités:

    • Configuration et interaction avec le LLM
    • Formatage du prompt
    • Extraction des données du rapport
    • Génération du rapport JSON
    • Génération du rapport Markdown
  2. Travail déjà effectué:

    • report_utils.py contient des fonctions extraites comme extraire_et_traiter_json
    • report_formatter.py existe déjà pour la génération des rapports en différents formats
  3. Opportunités de simplification:

    • Séparer la génération de contenu (via LLM) de la mise en forme du rapport
    • Extraire plus de fonctions utilitaires
    • Créer une structure modulaire

Proposition de refactorisation

Je propose la structure suivante:

  1. Garder AgentReportGenerator centré sur son rôle principal: Interroger le LLM et générer le contenu brut du rapport
  2. Créer une classe ReportBuilder: Pour construire le rapport JSON final
  3. Utiliser formatters/report_formatter.py: Pour la génération des formats spécifiques (MD, HTML)

Plan de mise en œuvre


Listed current directory • **36** results
Name
📄 orchestrator.log
📁 .git
📁 __pycache__
📄 test_orchestrator_qwen.py
📄 test_orchestrator_large.py
📄 test_orchestrator.py
📄 orchestrator.py
📁 output
📄 retrieve_ticket.log
📄 retrieve_tickets_batch.log
📄 retrieve_ticket.py
📁 utils
📁 reports
📁 docs
📁 odoo
📁 tests
📁 llm_interface
📁 loaders
📁 formatters
📁 core
📁 agents
📁 utils_old
📄 test_tableau_qr.py
📄 test_import.py
📄 test_agents_with_models.py
📄 .gitignore
📄 test_multiple_analyses.py
📄 README_TICKET_ANALYSER.md
📁 llm_classes
📄 requirements.txt
📄 test_models.py
📁 venv
📁 .specstory
📄 config.json
📄 README.md
📄 config.template.json

Voici ma proposition de restructuration de agent_report_generator.py :

# agents/report_builder.py
import json
import os
from datetime import datetime
from typing import Dict, Any, Tuple, Optional, List
import logging
import traceback

logger = logging.getLogger("ReportBuilder")

class ReportBuilder:
    """
    Classe utilitaire pour construire le rapport JSON final.
    
    Cette classe est responsable de l'extraction des données et de la construction
    du rapport JSON qui sera utilisé par le formateur de rapport.
    """
    
    def __init__(self):
        """Initialise le ReportBuilder"""
        pass
    
    def extraire_ticket_id(self, rapport_data: Dict, rapport_dir: str) -> str:
        """Extrait l'ID du ticket des données ou du chemin"""
        # Code extrait de _extraire_ticket_id dans AgentReportGenerator
        # [code existant]
    
    def extraire_analyse_ticket(self, rapport_data: Dict) -> str:
        """Extrait l'analyse du ticket des données"""
        # Code extrait de _extraire_analyse_ticket dans AgentReportGenerator
        # [code existant]
    
    def extraire_analyses_images(self, rapport_data: Dict) -> List[Dict]:
        """Extrait et formate les analyses d'images pertinentes"""
        # Code extrait de _extraire_analyses_images dans AgentReportGenerator
        # [code existant]
    
    def extraire_analyse_image(self, analyse_data: Dict) -> Optional[str]:
        """Extrait l'analyse d'une image depuis les données"""
        # Code extrait de _extraire_analyse_image dans AgentReportGenerator
        # [code existant]
    
    def extraire_sections_texte(self, rapport_genere: str) -> Tuple[str, str, str]:
        """Extrait le résumé, l'analyse des images et le diagnostic du rapport généré"""
        # Code extrait de _extraire_sections_texte dans AgentReportGenerator
        # [code existant]
    
    def collecter_info_agents(self, rapport_data: Dict) -> Dict:
        """Collecte des informations sur les agents utilisés dans l'analyse"""
        # Code extrait de _collecter_info_agents dans AgentReportGenerator
        # [code existant]
    
    def collecter_prompts_agents(self) -> Dict[str, str]:
        """Collecte les prompts système de tous les agents impliqués dans l'analyse"""
        # Code extrait de _collecter_prompts_agents dans AgentReportGenerator
        # [code existant]
    
    def construire_rapport_json(self, 
                               rapport_genere: str, 
                               rapport_data: Dict, 
                               ticket_id: str, 
                               ticket_analyse: str, 
                               images_analyses: List[Dict], 
                               generation_time: float,
                               agent_metadata: Dict) -> Dict:
        """
        Construit le rapport JSON final à partir des données générées
        
        Args:
            rapport_genere: Texte du rapport généré par le LLM
            rapport_data: Données brutes du rapport
            ticket_id: ID du ticket
            ticket_analyse: Analyse du ticket
            images_analyses: Liste des analyses d'images
            generation_time: Temps de génération du rapport en secondes
            agent_metadata: Métadonnées de l'agent (modèle, paramètres, etc.)
            
        Returns:
            Dictionnaire du rapport JSON complet
        """
        # Code extrait et adapté de la partie construction du rapport JSON de AgentReportGenerator
        # [code existant]
# agents/agent_report_generator.py (version simplifiée)
import json
import os
from .base_agent import BaseAgent
from datetime import datetime
from typing import Dict, Any, Tuple, Optional, List
import logging
import traceback
import re
import sys
from .utils.report_utils import extraire_et_traiter_json
from formatters.report_formatter import generate_markdown_report
from .report_builder import ReportBuilder

logger = logging.getLogger("AgentReportGenerator")

class AgentReportGenerator(BaseAgent):
    """
    Agent pour générer un rapport synthétique à partir des analyses de ticket et d'images.
    
    L'agent récupère:
    1. L'analyse du ticket effectuée par AgentTicketAnalyser
    2. Les analyses des images pertinentes effectuées par AgentImageAnalyser
    
    Il génère:
    - Un rapport JSON structuré (format principal)
    - Un rapport Markdown pour la présentation
    """
    def __init__(self, llm):
        super().__init__("AgentReportGenerator", llm)
        
        # Configuration locale de l'agent
        self.temperature = 0.2
        self.top_p = 0.9
        self.max_tokens = 2500
        
        # Prompt système pour la génération de rapport
        self.system_prompt = """Tu es un expert en génération de rapports techniques pour BRG-Lab pour la société CBAO.
Ta mission est de synthétiser les analyses (ticket et images) en un rapport structuré.

EXIGENCE ABSOLUE - Ton rapport DOIT inclure:
1. Un résumé du problème initial (nom de la demande + description)
2. Une reconstitution du fil de discussion client/support - tu peux synthétiser si trop long mais GARDE les éléments déterminants (références, normes, éléments techniques importants)
3. Un tableau des informations essentielles avec cette structure:
```json
{
  "chronologie_echanges": [
    {"date": "date exacte", "emetteur": "CLIENT ou SUPPORT", "type": "Question ou Réponse ou Information technique", "contenu": "contenu synthétisé fidèlement"}
  ]
}
  1. Une analyse des images pertinentes en lien avec le problème (OBLIGATOIRE)
  2. Un diagnostic technique des causes probables

IMPORTANT POUR LE TABLEAU:

  • COMMENCE par inclure toute question identifiée dans le NOM DE LA DEMANDE ou la DESCRIPTION initiale
  • Il doit contenir d'un côté les questions et de l'autre les réponses
  • Si aucune réponse n'a été fournie, indique "Il ne ressort pas de réponse de l'analyse"
  • AJOUTE des éléments de l'analyse d'image si cela constitue une réponse plausible à une question
  • Identifie clairement chaque intervenant (CLIENT ou SUPPORT)
  • Pour les questions issues du NOM ou de la DESCRIPTION, utilise l'émetteur "CLIENT" et la date d'ouverture du ticket

IMPORTANT POUR LA STRUCTURE:

  • Le rapport doit être clairement divisé en sections avec des titres (## Résumé, ## Fil de discussion, ## Tableau questions/réponses, ## Analyse des images, ## Diagnostic)

  • Pour l'analyse des images, décris précisément comment chaque image illustre le problème ou la solution

  • Si aucune image n'est fournie, tu DOIS l'indiquer explicitement dans la section "Analyse des images"

  • Reste factuel et précis dans ton analyse"""

      # Version du prompt pour la traçabilité
      self.prompt_version = "v2.2"
    
      # Initialiser le ReportBuilder
      self.report_builder = ReportBuilder()
    
      # Appliquer la configuration au LLM
      self._appliquer_config_locale()
    
      logger.info("AgentReportGenerator initialisé")
    

    def _appliquer_config_locale(self) -> None: """ Applique la configuration locale au modèle LLM. """ # Appliquer le prompt système if hasattr(self.llm, "prompt_system"): self.llm.prompt_system = self.system_prompt

      # Appliquer les paramètres
      if hasattr(self.llm, "configurer"):
          params = {
              "temperature": self.temperature,
              "top_p": self.top_p,
              "max_tokens": self.max_tokens
          }
          self.llm.configurer(**params)
          logger.info(f"Configuration appliquée au modèle: {str(params)}")
    

    def _formater_prompt_pour_rapport(self, ticket_analyse: str, images_analyses: List[Dict]) -> str: """ Formate le prompt pour la génération du rapport

      Args:
          ticket_analyse: Analyse du ticket
          images_analyses: Liste des analyses d'images
    
      Returns:
          Prompt formaté pour le LLM
      """
      # Conservé en l'état car c'est une fonction centrale à la génération
      # [code existant]
    

    def executer(self, rapport_data: Dict, rapport_dir: str) -> Tuple[Optional[str], Optional[str]]: """ Génère un rapport à partir des analyses effectuées

      Args:
          rapport_data: Dictionnaire contenant toutes les données analysées
          rapport_dir: Répertoire où sauvegarder le rapport
    
      Returns:
          Tuple (chemin JSON, chemin Markdown) - Peut contenir None si une génération échoue
      """
      try:
          # 1. PRÉPARATION
          ticket_id = self.report_builder.extraire_ticket_id(rapport_data, rapport_dir)
          logger.info(f"Génération du rapport pour le ticket: {ticket_id}")
          print(f"AgentReportGenerator: Génération du rapport pour {ticket_id}")
    
          # Créer le répertoire de sortie si nécessaire
          os.makedirs(rapport_dir, exist_ok=True)
    
          # 2. EXTRACTION DES DONNÉES
          ticket_analyse = self.report_builder.extraire_analyse_ticket(rapport_data)
          images_analyses = self.report_builder.extraire_analyses_images(rapport_data)
    
          # 3. GÉNÉRATION DU RAPPORT VIA LLM
          prompt = self._formater_prompt_pour_rapport(ticket_analyse, images_analyses)
    
          logger.info("Génération du rapport avec le LLM")
          print(f"  Génération du rapport avec le LLM...")
    
          # Mesurer le temps d'exécution
          start_time = datetime.now()
          rapport_genere = self.llm.interroger(prompt)
          generation_time = (datetime.now() - start_time).total_seconds()
    
          logger.info(f"Rapport généré: {len(rapport_genere)} caractères")
          print(f"  Rapport généré: {len(rapport_genere)} caractères")
    
          # 4. TRAITEMENT DU RAPPORT GÉNÉRÉ
          # Utiliser l'utilitaire de report_utils.py pour extraire les données JSON
          rapport_traite, echanges_json, _ = extraire_et_traiter_json(rapport_genere)
    
          # Extraire les sections textuelles
          resume, analyse_images, diagnostic = self.report_builder.extraire_sections_texte(rapport_genere)
    
          # 5. COLLECTE DES MÉTADONNÉES
          agent_metadata = {
              "model": getattr(self.llm, "modele", str(type(self.llm))),
              "model_version": getattr(self.llm, "version", "non spécifiée"),
              "temperature": self.temperature,
              "top_p": self.top_p,
              "max_tokens": self.max_tokens,
              "generation_time": generation_time,
              "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
              "agents": self.report_builder.collecter_info_agents(rapport_data)
          }
    
          # 6. CONSTRUCTION DU RAPPORT JSON
          rapport_json = self.report_builder.construire_rapport_json(
              rapport_genere=rapport_genere,
              rapport_data=rapport_data,
              ticket_id=ticket_id,
              ticket_analyse=ticket_analyse,
              images_analyses=images_analyses,
              generation_time=generation_time,
              agent_metadata=agent_metadata
          )
    
          # 7. SAUVEGARDE DU RAPPORT JSON
          json_path = os.path.join(rapport_dir, f"{ticket_id}_rapport_final.json")
          with open(json_path, "w", encoding="utf-8") as f:
              json.dump(rapport_json, f, ensure_ascii=False, indent=2)
    
          logger.info(f"Rapport JSON sauvegardé: {json_path}")
          print(f"  Rapport JSON sauvegardé: {json_path}")
    
          # 8. GÉNÉRATION DU RAPPORT MARKDOWN VIA FORMATTER
          success, md_path = generate_markdown_report(json_path)
          if not success:
              logger.error(f"Erreur lors de la génération du rapport Markdown: {md_path}")
              md_path = None
          else:
              logger.info(f"Rapport Markdown généré: {md_path}")
              print(f"  Rapport Markdown généré: {md_path}")
    
          return json_path, md_path
    
      except Exception as e:
          error_message = f"Erreur lors de la génération du rapport: {str(e)}"
          logger.error(error_message)
          logger.error(traceback.format_exc())
          print(f"  ERREUR: {error_message}")
          return None, None
    

## Avantages de cette refactorisation

1. **Réduction de la taille** de `agent_report_generator.py` (de 1043 lignes à environ 180 lignes)
2. **Séparation des responsabilités**:
   - `AgentReportGenerator`: Gestion de l'agent LLM et génération du rapport
   - `ReportBuilder`: Construction des données du rapport JSON
   - `report_formatter.py`: Mise en forme pour différents formats de sortie
3. **Meilleure maintenabilité** car les modifications peuvent être isolées à des composants spécifiques
4. **Préservation du comportement actuel** car nous n'avons pas modifié la logique existante
5. **Extension facile** pour ajouter de nouveaux formats de sortie ou fonctionnalités

## Étapes pour l'implémentation

1. Créer le fichier `agents/report_builder.py`
2. Déplacer les fonctions auxiliaires dans `report_builder.py`
3. Simplifier `agent_report_generator.py` en utilisant `ReportBuilder`
4. Assurer que `formatters/report_formatter.py` est correctement importé
5. Mettre à jour les tests pour refléter les nouvelles classes

Cette architecture permettra des évolutions futures comme l'ajout de nouveaux formats de rapport ou la personnalisation des rapports sans toucher au cœur de l'agent LLM.

Souhaitez-vous que je détaille davantage certains aspects spécifiques de cette refactorisation?

---



---