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
-
Problème principal: Le fichier
agent_report_generator.pyest 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
-
Travail déjà effectué:
report_utils.pycontient des fonctions extraites commeextraire_et_traiter_jsonreport_formatter.pyexiste déjà pour la génération des rapports en différents formats
-
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:
- Garder
AgentReportGeneratorcentré sur son rôle principal: Interroger le LLM et générer le contenu brut du rapport - Créer une classe
ReportBuilder: Pour construire le rapport JSON final - 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"}
]
}
- Une analyse des images pertinentes en lien avec le problème (OBLIGATOIRE)
- 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?
---
---