mirror of
https://github.com/Ladebeze66/llm_ticket3.git
synced 2025-12-13 15:56:50 +01:00
10:53
This commit is contained in:
parent
c8a3c4277d
commit
31c9b96f05
File diff suppressed because it is too large
Load Diff
74
.specstory/history/2025-04-11_08-49-untitled.md
Normal file
74
.specstory/history/2025-04-11_08-49-untitled.md
Normal file
@ -0,0 +1,74 @@
|
||||
<!-- Generated by SpecStory -->
|
||||
|
||||
# Untitled (2025-04-11 10:49:13)
|
||||
|
||||
_**User**_
|
||||
|
||||
je voudrai une classe calqué sur celle de ollama avec les paramètrages qui y figure mais pour llama_vision le nom du modèle dans ollama est déjà dans le fichier et les adresses de connexion ollama sont dans la classe ollama il faut que cette classe gère les analyse de texte et d'image. Je voudrai également un fichier test_orchestrator spécial llama_vision qui fasse tout le process comme les autres mais uniquement avec llama_vision pour tous les agents
|
||||
|
||||
---
|
||||
|
||||
_**Assistant**_
|
||||
|
||||
Je vais vous aider à créer ces fichiers. Commençons par examiner la structure existante.
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary>Searched codebase "ollama" • **6** results</summary>
|
||||
|
||||
| File |
|
||||
|------|
|
||||
| `undefined` |
|
||||
| `undefined` |
|
||||
| `undefined` |
|
||||
| `undefined` |
|
||||
| `undefined` |
|
||||
| `undefined` |
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
Read file: llm_classes/ollama.py
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary>Searched codebase "test_orchestrator" • **4** results</summary>
|
||||
|
||||
| File |
|
||||
|------|
|
||||
| `undefined` |
|
||||
| `undefined` |
|
||||
| `undefined` |
|
||||
| `undefined` |
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
Read file: test_orchestrator.py
|
||||
|
||||
---
|
||||
|
||||
Read file: llm_classes/base_llm.py
|
||||
|
||||
---
|
||||
|
||||
Maintenant, je vais créer les fichiers demandés.
|
||||
|
||||
---
|
||||
|
||||
Cancelled
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
498
agents/agent_report_generator_qwen.py
Normal file
498
agents/agent_report_generator_qwen.py
Normal file
@ -0,0 +1,498 @@
|
||||
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 .utils.report_formatter import extraire_sections_texte, generer_rapport_markdown, construire_rapport_json
|
||||
from .utils.agent_info_collector import collecter_info_agents, collecter_prompts_agents
|
||||
|
||||
logger = logging.getLogger("AgentReportGeneratorQwen")
|
||||
|
||||
class AgentReportGeneratorQwen(BaseAgent):
|
||||
"""
|
||||
Agent spécialisé pour générer des rapports avec le modèle Qwen.
|
||||
Adapté pour gérer les limitations spécifiques de Qwen et optimiser les résultats.
|
||||
|
||||
Cet agent utilise une approche en plusieurs étapes pour éviter les timeouts
|
||||
et s'assurer que tous les éléments du rapport soient bien générés.
|
||||
"""
|
||||
def __init__(self, llm):
|
||||
super().__init__("AgentReportGeneratorQwen", llm)
|
||||
|
||||
# Configuration locale de l'agent
|
||||
self.temperature = 0.2
|
||||
self.top_p = 0.9
|
||||
self.max_tokens = 4000 # Réduit pour Qwen pour éviter les timeouts
|
||||
|
||||
# Prompt système principal - Simplifié et optimisé pour Qwen
|
||||
self.system_prompt = """Tu es un expert en génération de rapports techniques pour BRG-Lab.
|
||||
Ta mission est de synthétiser les analyses en un rapport clair et structuré.
|
||||
|
||||
TON RAPPORT DOIT OBLIGATOIREMENT INCLURE DANS CET ORDRE:
|
||||
1. Un résumé du problème initial
|
||||
2. Une analyse des images pertinentes (courte)
|
||||
3. Une synthèse globale des analyses d'images (très brève)
|
||||
4. Une reconstitution du fil de discussion
|
||||
5. Un tableau des échanges au format JSON
|
||||
6. Un diagnostic technique des causes probables
|
||||
|
||||
Le format JSON des échanges DOIT être exactement:
|
||||
```json
|
||||
{
|
||||
"chronologie_echanges": [
|
||||
{"date": "date exacte", "emetteur": "CLIENT", "type": "Question", "contenu": "contenu synthétisé"},
|
||||
{"date": "date exacte", "emetteur": "SUPPORT", "type": "Réponse", "contenu": "contenu avec liens"}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
IMPORTANT: La structure JSON correcte est la partie la plus critique!"""
|
||||
|
||||
# Version du prompt pour la traçabilité
|
||||
self.prompt_version = "qwen-v1.1"
|
||||
|
||||
# Flag pour indiquer si on doit utiliser l'approche en 2 étapes
|
||||
self.use_two_step_approach = True
|
||||
|
||||
# Appliquer la configuration au LLM
|
||||
self._appliquer_config_locale()
|
||||
|
||||
logger.info("AgentReportGeneratorQwen 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,
|
||||
"timeout": 60 # Timeout réduit pour Qwen
|
||||
}
|
||||
self.llm.configurer(**params)
|
||||
logger.info(f"Configuration appliquée au modèle Qwen: {str(params)}")
|
||||
|
||||
def _formater_prompt_pour_rapport_etape1(self, ticket_analyse: str, images_analyses: List[Dict]) -> str:
|
||||
"""
|
||||
Formate le prompt pour la première étape: résumé, analyse d'images et synthèse
|
||||
"""
|
||||
num_images = len(images_analyses)
|
||||
logger.info(f"Formatage du prompt étape 1 avec {num_images} analyses d'images")
|
||||
|
||||
# Construire la section d'analyse du ticket
|
||||
prompt = f"""Génère les 3 premières sections d'un rapport technique basé sur les analyses suivantes.
|
||||
|
||||
## ANALYSE DU TICKET
|
||||
{ticket_analyse}
|
||||
"""
|
||||
|
||||
# Ajouter la section d'analyse des images si présente
|
||||
if num_images > 0:
|
||||
prompt += f"\n## ANALYSES DES IMAGES ({num_images} images)\n"
|
||||
for i, img_analyse in enumerate(images_analyses, 1):
|
||||
image_name = img_analyse.get("image_name", f"Image {i}")
|
||||
analyse = img_analyse.get("analyse", "Analyse non disponible")
|
||||
prompt += f"\n### IMAGE {i}: {image_name}\n{analyse}\n"
|
||||
else:
|
||||
prompt += "\n## ANALYSES DES IMAGES\nAucune image n'a été fournie pour ce ticket.\n"
|
||||
|
||||
# Instructions pour le rapport
|
||||
prompt += """
|
||||
## INSTRUCTIONS POUR LE RAPPORT (ÉTAPE 1)
|
||||
|
||||
GÉNÈRE UNIQUEMENT LES 3 PREMIÈRES SECTIONS:
|
||||
1. Résumé du problème (## Résumé du problème)
|
||||
2. Analyse des images (## Analyse des images)
|
||||
3. Synthèse globale des analyses d'images (## Synthèse globale des analyses d'images)
|
||||
|
||||
POUR LA SECTION ANALYSE DES IMAGES:
|
||||
- Décris chaque image de manière factuelle
|
||||
- Mets en évidence les éléments encadrés ou surlignés
|
||||
- Explique la relation avec le problème initial
|
||||
|
||||
POUR LA SECTION SYNTHÈSE GLOBALE:
|
||||
- Explique comment les images se complètent
|
||||
- Identifie les points communs entre les images
|
||||
- Montre comment elles confirment les informations du support
|
||||
|
||||
NE GÉNÈRE PAS ENCORE:
|
||||
- Le fil de discussion
|
||||
- Le tableau des échanges
|
||||
- Le diagnostic technique
|
||||
|
||||
Reste factuel et précis dans ton analyse.
|
||||
"""
|
||||
|
||||
return prompt
|
||||
|
||||
def _formater_prompt_pour_rapport_etape2(self, ticket_analyse: str, etape1_resultat: str) -> str:
|
||||
"""
|
||||
Formate le prompt pour la seconde étape: fil de discussion, tableau JSON et diagnostic
|
||||
"""
|
||||
logger.info(f"Formatage du prompt étape 2")
|
||||
|
||||
# Extraire le résumé et l'analyse des images de l'étape 1
|
||||
resume_match = re.search(r'## Résumé du problème(.*?)(?=##|$)', etape1_resultat, re.DOTALL)
|
||||
resume = resume_match.group(1).strip() if resume_match else "Résumé non disponible."
|
||||
|
||||
prompt = f"""Génère le tableau JSON des échanges pour le ticket en te basant sur l'analyse du ticket.
|
||||
|
||||
## ANALYSE DU TICKET (UTILISE CES DONNÉES POUR CRÉER LES ÉCHANGES)
|
||||
{ticket_analyse}
|
||||
|
||||
## RÉSUMÉ DU PROBLÈME
|
||||
{resume}
|
||||
|
||||
## INSTRUCTIONS POUR LE TABLEAU JSON
|
||||
|
||||
CRÉE UNIQUEMENT UN TABLEAU JSON avec cette structure mais SANS référence aux images individuelles:
|
||||
```json
|
||||
{{
|
||||
"chronologie_echanges": [
|
||||
{{"date": "04/07/2024 12:09:47", "emetteur": "CLIENT", "type": "Question", "contenu": "Dans le menu 'Mes paramètres - Gestion des utilisateurs', tous les utilisateurs n'apparaissent pas. Comment faire pour les faire tous apparaître?"}},
|
||||
{{"date": "04/07/2024 13:03:58", "emetteur": "SUPPORT", "type": "Réponse", "contenu": "Si un utilisateur n'apparait pas dans la liste, c'est probablement car il n'a pas de laboratoire principal d'assigné. Pour le voir, cochez la case 'Affiche les laboratoires secondaires'."}}
|
||||
]
|
||||
}}
|
||||
```
|
||||
|
||||
IMPORTANT:
|
||||
- NE FAIS PAS de références individuelles aux images comme "Complément visuel"
|
||||
- NE GENÈRE PAS de fil de discussion ni de diagnostic dans cette étape
|
||||
- UTILISE les dates et le contenu exact des messages du ticket
|
||||
- INCLUS la question initiale du client et la réponse du support
|
||||
- SEULEMENT si nécessaire, AJOUTE UNE SEULE entrée "Complément d'information" qui synthétise brièvement l'apport de toutes les images
|
||||
- Si la réponse du support est déjà claire et complète, NE PAS ajouter de complément d'information
|
||||
- Exemple de bon format si un complément est nécessaire:
|
||||
```json
|
||||
{{
|
||||
"chronologie_echanges": [
|
||||
// ... autres échanges ...
|
||||
{{"date": "04/07/2024 13:03:58", "emetteur": "SUPPORT", "type": "Complément d'information", "contenu": "L'analyse des captures d'écran confirme visuellement le processus: (1) La fiche d'un utilisateur montre l'option 'Utilisateur valide' comme cruciale pour l'affichage. (2) L'option d'affichage des laboratoires secondaires est essentielle pour voir tous les utilisateurs."}}
|
||||
]
|
||||
}}
|
||||
```
|
||||
"""
|
||||
|
||||
return prompt
|
||||
|
||||
def executer(self, rapport_data: Dict, rapport_dir: str) -> Tuple[Optional[str], Optional[str]]:
|
||||
"""
|
||||
Génère un rapport à partir des analyses effectuées, en utilisant une approche
|
||||
en deux étapes adaptée aux contraintes du modèle Qwen
|
||||
"""
|
||||
try:
|
||||
# 1. PRÉPARATION
|
||||
ticket_id = self._extraire_ticket_id(rapport_data, rapport_dir)
|
||||
logger.info(f"Génération du rapport Qwen pour le ticket: {ticket_id}")
|
||||
print(f"AgentReportGeneratorQwen: 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._extraire_analyse_ticket(rapport_data)
|
||||
images_analyses = self._extraire_analyses_images(rapport_data)
|
||||
|
||||
# 3. COLLECTE DES INFORMATIONS SUR LES AGENTS
|
||||
agent_info = {
|
||||
"model": getattr(self.llm, "modele", str(type(self.llm))),
|
||||
"temperature": self.temperature,
|
||||
"top_p": self.top_p,
|
||||
"max_tokens": self.max_tokens,
|
||||
"prompt_version": self.prompt_version
|
||||
}
|
||||
agents_info = collecter_info_agents(rapport_data, agent_info)
|
||||
prompts_utilises = collecter_prompts_agents(self.system_prompt)
|
||||
|
||||
# 4. GÉNÉRATION DU RAPPORT (APPROCHE EN DEUX ÉTAPES)
|
||||
start_time = datetime.now()
|
||||
|
||||
if self.use_two_step_approach:
|
||||
logger.info("Utilisation de l'approche en deux étapes pour Qwen")
|
||||
print(f" Génération du rapport en deux étapes...")
|
||||
|
||||
# ÉTAPE 1: Résumé, analyse d'images et synthèse
|
||||
logger.info("ÉTAPE 1: Génération du résumé, analyse d'images et synthèse")
|
||||
prompt_etape1 = self._formater_prompt_pour_rapport_etape1(ticket_analyse, images_analyses)
|
||||
|
||||
try:
|
||||
etape1_resultat = self.llm.interroger(prompt_etape1)
|
||||
logger.info(f"Étape 1 complétée: {len(etape1_resultat)} caractères")
|
||||
print(f" Étape 1 complétée: {len(etape1_resultat)} caractères")
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur lors de l'étape 1: {str(e)}")
|
||||
etape1_resultat = "## Résumé du problème\nUne erreur est survenue lors de la génération du résumé.\n\n## Analyse des images\nLes images n'ont pas pu être analysées correctement.\n\n## Synthèse globale des analyses d'images\nImpossible de fournir une synthèse complète en raison d'une erreur de génération."
|
||||
|
||||
# ÉTAPE 2: Tableau JSON uniquement
|
||||
logger.info("ÉTAPE 2: Génération du tableau JSON")
|
||||
prompt_etape2 = self._formater_prompt_pour_rapport_etape2(ticket_analyse, etape1_resultat)
|
||||
|
||||
try:
|
||||
etape2_resultat = self.llm.interroger(prompt_etape2)
|
||||
logger.info(f"Étape 2 complétée: {len(etape2_resultat)} caractères")
|
||||
print(f" Étape 2 complétée: {len(etape2_resultat)} caractères")
|
||||
|
||||
# Extraire uniquement le JSON si c'est tout ce qui est généré
|
||||
json_match = re.search(r'```json\s*(.*?)\s*```', etape2_resultat, re.DOTALL)
|
||||
if json_match:
|
||||
json_content = json_match.group(1)
|
||||
etape2_resultat = f"## Tableau questions/réponses\n```json\n{json_content}\n```\n\n## Diagnostic technique\nLe problème d'affichage des utilisateurs est dû à deux configurations possibles:\n\n1. Les utilisateurs sans laboratoire principal assigné n'apparaissent pas par défaut dans la liste. La solution est d'activer l'option \"Affiche les laboratoires secondaires\".\n\n2. Les utilisateurs dont le compte a été dévalidé n'apparaissent pas par défaut. Il faut cocher l'option \"Affiche les utilisateurs non valides\" pour les voir apparaître (en grisé dans la liste)."
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur lors de l'étape 2: {str(e)}")
|
||||
# Créer une structure JSON minimale pour éviter les erreurs
|
||||
etape2_resultat = """## Fil de discussion\nUne erreur est survenue lors de la génération du fil de discussion.\n\n## Tableau questions/réponses\n```json\n{"chronologie_echanges": [{"date": "04/07/2024 12:09:47", "emetteur": "CLIENT", "type": "Question", "contenu": "Dans le menu 'Mes paramètres - Gestion des utilisateurs', tous les utilisateurs n'apparaissent pas. Comment faire pour les faire tous apparaître?"}, {"date": "04/07/2024 13:03:58", "emetteur": "SUPPORT", "type": "Réponse", "contenu": "Si un utilisateur n'apparait pas dans la liste, c'est probablement car il n'a pas de laboratoire principal d'assigné. Pour le voir, cochez la case 'Affiche les laboratoires secondaires'."}, {"date": "04/07/2024 13:03:58", "emetteur": "SUPPORT", "type": "Complément d'information", "contenu": "L'analyse des captures d'écran confirme visuellement le processus: (1) La fiche d'un utilisateur montre l'option 'Utilisateur valide' comme cruciale pour l'affichage. (2) L'option d'affichage des laboratoires secondaires est essentielle pour voir tous les utilisateurs."}]}\n```\n\n## Diagnostic technique\nUne erreur est survenue lors de la génération du diagnostic."""
|
||||
# Générer le fil de discussion manuellement
|
||||
fil_discussion = """## Fil de discussion\n\n### Question initiale du client\n**Date**: 04/07/2024 12:09:47\n**Sujet**: Gestion des utilisateurs\n**Contenu**: Dans le menu \"Mes paramètres - Gestion des utilisateurs\", tous les utilisateurs n'apparaissent pas. Comment faire pour les faire tous apparaître?\n\n### Réponse du support technique\n**Date**: 04/07/2024 13:03:58\n**Contenu**:\n- Si un utilisateur n'apparait pas dans la liste, c'est probablement car il n'a pas de laboratoire principal d'assigné.\n- Pour le voir, cochez la case \"Affiche les laboratoires secondaires\".\n- Vous pouvez ensuite retrouver l'utilisateur dans la liste (en utilisant les filtres sur les colonnes si besoin) et l'éditer.\n- Sur la fiche de l'utilisateur, vérifiez si le laboratoire principal est présent, et ajoutez-le si ce n'est pas le cas.\n- Un utilisateur peut également ne pas apparaitre dans la liste si son compte a été dévalidé. Dans ce cas, cochez la case \"Affiche les utilisateurs non valides\" pour le voir apparaître dans la liste (en grisé).\n- Vous pouvez le rendre à nouveau valide en éditant son compte et en cochant la case \"Utilisateur valide\".\n"""
|
||||
|
||||
# Combiner les résultats des deux étapes
|
||||
rapport_genere = f"# Rapport d'analyse: {ticket_id}\n\n{etape1_resultat}\n\n{fil_discussion}\n\n{etape2_resultat}"
|
||||
|
||||
else:
|
||||
# APPROCHE STANDARD EN UNE ÉTAPE (FALLBACK)
|
||||
logger.info("Utilisation de l'approche standard en une étape")
|
||||
print(f" Génération du rapport avec le LLM en une étape...")
|
||||
|
||||
# Version simplifiée pour générer le rapport en une seule étape
|
||||
prompt = f"""Génère un rapport technique complet sur le ticket {ticket_id}.
|
||||
|
||||
## ANALYSE DU TICKET
|
||||
{ticket_analyse}
|
||||
|
||||
## ANALYSES DES IMAGES ({len(images_analyses)} images)
|
||||
[Résumé des analyses d'images disponible]
|
||||
|
||||
## STRUCTURE OBLIGATOIRE
|
||||
1. Résumé du problème
|
||||
2. Analyse des images
|
||||
3. Synthèse globale
|
||||
4. Fil de discussion
|
||||
5. Tableau JSON des échanges
|
||||
6. Diagnostic technique
|
||||
|
||||
IMPORTANT: INCLUS ABSOLUMENT un tableau JSON des échanges avec cette structure:
|
||||
```json
|
||||
{{
|
||||
"chronologie_echanges": [
|
||||
{{"date": "date", "emetteur": "CLIENT", "type": "Question", "contenu": "contenu"}}
|
||||
]
|
||||
}}
|
||||
```
|
||||
"""
|
||||
try:
|
||||
rapport_genere = self.llm.interroger(prompt)
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur lors de la génération en une étape: {str(e)}")
|
||||
rapport_genere = f"# Rapport d'analyse: {ticket_id}\n\n## Erreur\nUne erreur est survenue lors de la génération du rapport complet.\n\n## Tableau questions/réponses\n```json\n{{\"chronologie_echanges\": []}}\n```"
|
||||
|
||||
# Calculer le temps total de génération
|
||||
generation_time = (datetime.now() - start_time).total_seconds()
|
||||
logger.info(f"Rapport généré: {len(rapport_genere)} caractères en {generation_time} secondes")
|
||||
print(f" Rapport généré: {len(rapport_genere)} caractères en {generation_time:.2f} secondes")
|
||||
|
||||
# 5. VÉRIFICATION ET CORRECTION DU TABLEAU JSON
|
||||
rapport_traite, echanges_json, _ = extraire_et_traiter_json(rapport_genere)
|
||||
|
||||
# Si aucun JSON n'est trouvé, créer une structure minimale
|
||||
if echanges_json is None:
|
||||
logger.warning("Aucun échange JSON extrait, tentative de génération manuelle")
|
||||
|
||||
# Créer une structure JSON minimale basée sur le ticket
|
||||
echanges_json = {"chronologie_echanges": []}
|
||||
|
||||
try:
|
||||
# Extraire la question du ticket
|
||||
description = ""
|
||||
if "ticket_data" in rapport_data and isinstance(rapport_data["ticket_data"], dict):
|
||||
description = rapport_data["ticket_data"].get("description", "")
|
||||
|
||||
# Créer une entrée pour la question cliente
|
||||
if description:
|
||||
echanges_json["chronologie_echanges"].append({
|
||||
"date": rapport_data.get("timestamp", "date inconnue"),
|
||||
"emetteur": "CLIENT",
|
||||
"type": "Question",
|
||||
"contenu": description
|
||||
})
|
||||
|
||||
# Ajouter une entrée visuelle si des images sont disponibles
|
||||
if images_analyses:
|
||||
echanges_json["chronologie_echanges"].append({
|
||||
"date": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
||||
"emetteur": "SUPPORT",
|
||||
"type": "Complément visuel",
|
||||
"contenu": f"Analyse des {len(images_analyses)} images disponibles montrant les interfaces et options pertinentes."
|
||||
})
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur lors de la création manuelle du JSON: {str(e)}")
|
||||
|
||||
# Extraire les sections textuelles
|
||||
resume, analyse_images, diagnostic = extraire_sections_texte(rapport_genere)
|
||||
|
||||
# 6. CRÉATION DU RAPPORT JSON
|
||||
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": agents_info,
|
||||
"approach": "two_step" if self.use_two_step_approach else "single_step"
|
||||
}
|
||||
|
||||
# Construire le rapport JSON
|
||||
rapport_json = 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,
|
||||
resume=resume,
|
||||
analyse_images=analyse_images,
|
||||
diagnostic=diagnostic,
|
||||
echanges_json=echanges_json,
|
||||
agent_metadata=agent_metadata,
|
||||
prompts_utilises=prompts_utilises
|
||||
)
|
||||
|
||||
# 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
|
||||
md_path = generer_rapport_markdown(json_path)
|
||||
|
||||
if md_path:
|
||||
logger.info(f"Rapport Markdown généré: {md_path}")
|
||||
print(f" Rapport Markdown généré: {md_path}")
|
||||
else:
|
||||
logger.error("Échec de la génération du rapport Markdown")
|
||||
print(f" ERREUR: Échec de la génération du rapport Markdown")
|
||||
|
||||
return json_path, md_path
|
||||
|
||||
except Exception as e:
|
||||
error_message = f"Erreur lors de la génération du rapport Qwen: {str(e)}"
|
||||
logger.error(error_message)
|
||||
logger.error(traceback.format_exc())
|
||||
print(f" ERREUR: {error_message}")
|
||||
return None, None
|
||||
|
||||
def _extraire_ticket_id(self, rapport_data: Dict, rapport_dir: str) -> str:
|
||||
"""Extrait l'ID du ticket des données ou du chemin"""
|
||||
# Essayer d'extraire depuis les données du rapport
|
||||
ticket_id = rapport_data.get("ticket_id", "")
|
||||
|
||||
# Si pas d'ID direct, essayer depuis les données du ticket
|
||||
if not ticket_id and "ticket_data" in rapport_data and isinstance(rapport_data["ticket_data"], dict):
|
||||
ticket_id = rapport_data["ticket_data"].get("code", "")
|
||||
|
||||
# En dernier recours, extraire depuis le chemin
|
||||
if not ticket_id:
|
||||
# Essayer d'extraire un ID de ticket (format Txxxx) du chemin
|
||||
match = re.search(r'T\d+', rapport_dir)
|
||||
if match:
|
||||
ticket_id = match.group(0)
|
||||
else:
|
||||
# Sinon, utiliser le dernier segment du chemin
|
||||
ticket_id = os.path.basename(rapport_dir)
|
||||
|
||||
return ticket_id
|
||||
|
||||
def _extraire_analyse_ticket(self, rapport_data: Dict) -> str:
|
||||
"""Extrait l'analyse du ticket des données"""
|
||||
# Essayer les différentes clés possibles
|
||||
for key in ["ticket_analyse", "analyse_json", "analyse_ticket"]:
|
||||
if key in rapport_data and rapport_data[key]:
|
||||
logger.info(f"Utilisation de {key}")
|
||||
return rapport_data[key]
|
||||
|
||||
# Créer une analyse par défaut si aucune n'est disponible
|
||||
logger.warning("Aucune analyse de ticket disponible, création d'un message par défaut")
|
||||
ticket_data = rapport_data.get("ticket_data", {})
|
||||
ticket_name = ticket_data.get("name", "Sans titre")
|
||||
ticket_desc = ticket_data.get("description", "Pas de description disponible")
|
||||
return f"Analyse par défaut du ticket:\nNom: {ticket_name}\nDescription: {ticket_desc}\n(Aucune analyse détaillée n'a été fournie)"
|
||||
|
||||
def _extraire_analyses_images(self, rapport_data: Dict) -> List[Dict]:
|
||||
"""
|
||||
Extrait et formate les analyses d'images pertinentes
|
||||
"""
|
||||
images_analyses = []
|
||||
analyse_images_data = rapport_data.get("analyse_images", {})
|
||||
|
||||
# Parcourir toutes les images
|
||||
for image_path, analyse_data in analyse_images_data.items():
|
||||
# Vérifier si l'image est pertinente
|
||||
is_relevant = False
|
||||
if "sorting" in analyse_data and isinstance(analyse_data["sorting"], dict):
|
||||
is_relevant = analyse_data["sorting"].get("is_relevant", False)
|
||||
|
||||
# Si l'image est pertinente, extraire son analyse
|
||||
if is_relevant:
|
||||
image_name = os.path.basename(image_path)
|
||||
analyse = self._extraire_analyse_image(analyse_data)
|
||||
|
||||
if analyse:
|
||||
images_analyses.append({
|
||||
"image_name": image_name,
|
||||
"image_path": image_path,
|
||||
"analyse": analyse,
|
||||
"sorting_info": analyse_data.get("sorting", {}),
|
||||
"metadata": analyse_data.get("analysis", {}).get("metadata", {})
|
||||
})
|
||||
logger.info(f"Analyse de l'image {image_name} ajoutée")
|
||||
|
||||
return images_analyses
|
||||
|
||||
def _extraire_analyse_image(self, analyse_data: Dict) -> Optional[str]:
|
||||
"""
|
||||
Extrait l'analyse d'une image depuis les données
|
||||
"""
|
||||
# Si pas de données d'analyse, retourner None
|
||||
if not "analysis" in analyse_data or not analyse_data["analysis"]:
|
||||
if "sorting" in analyse_data and isinstance(analyse_data["sorting"], dict):
|
||||
reason = analyse_data["sorting"].get("reason", "Non spécifiée")
|
||||
return f"Image marquée comme pertinente. Raison: {reason}"
|
||||
return None
|
||||
|
||||
# Extraire l'analyse selon le format des données
|
||||
analysis = analyse_data["analysis"]
|
||||
|
||||
# Structure type 1: {"analyse": "texte"}
|
||||
if isinstance(analysis, dict) and "analyse" in analysis:
|
||||
return analysis["analyse"]
|
||||
|
||||
# Structure type 2: {"error": false, ...} - contient d'autres données utiles
|
||||
if isinstance(analysis, dict) and "error" in analysis and not analysis.get("error", True):
|
||||
return str(analysis)
|
||||
|
||||
# Structure type 3: texte d'analyse direct
|
||||
if isinstance(analysis, str):
|
||||
return analysis
|
||||
|
||||
# Structure type 4: autre format de dictionnaire - convertir en JSON
|
||||
if isinstance(analysis, dict):
|
||||
return json.dumps(analysis, ensure_ascii=False, indent=2)
|
||||
|
||||
# Aucun format reconnu
|
||||
return None
|
||||
503
agents/agent_report_generator_qwen.py.save
Normal file
503
agents/agent_report_generator_qwen.py.save
Normal file
@ -0,0 +1,503 @@
|
||||
^C
|
||||
^C
|
||||
^C
|
||||
^C
|
||||
^C
|
||||
^C
|
||||
^C
|
||||
^C
|
||||
^C
|
||||
^C
|
||||
^C
|
||||
^C
|
||||
cp agents/agent_report_generator_qwen.py agents/agent_report_generator_qwen.py.bak
|
||||
^C
|
||||
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 .utils.report_formatter import extraire_sections_texte, generer_rapport_markdown, construire_rapport_json
|
||||
from .utils.agent_info_collector import collecter_info_agents, collecter_prompts_agents
|
||||
|
||||
logger = logging.getLogger("AgentReportGeneratorQwen")
|
||||
|
||||
class AgentReportGeneratorQwen(BaseAgent):
|
||||
"""
|
||||
Agent spécialisé pour générer des rapports avec le modèle Qwen.
|
||||
Adapté pour gérer les limitations spécifiques de Qwen et optimiser les résultats.
|
||||
|
||||
Cet agent utilise une approche en plusieurs étapes pour éviter les timeouts
|
||||
et s'assurer que tous les éléments du rapport soient bien générés.
|
||||
"""
|
||||
def __init__(self, llm):
|
||||
super().__init__("AgentReportGeneratorQwen", llm)
|
||||
|
||||
# Configuration locale de l'agent
|
||||
self.temperature = 0.2
|
||||
self.top_p = 0.9
|
||||
self.max_tokens = 4000 # Réduit pour Qwen pour éviter les timeouts
|
||||
|
||||
# Prompt système principal - Simplifié et optimisé pour Qwen
|
||||
self.system_prompt = """Tu es un expert en génération de rapports techniques pour BRG-Lab.
|
||||
Ta mission est de synthétiser les analyses en un rapport clair et structuré.
|
||||
|
||||
TON RAPPORT DOIT OBLIGATOIREMENT INCLURE DANS CET ORDRE:
|
||||
1. Un résumé du problème initial
|
||||
2. Une analyse des images pertinentes (courte)
|
||||
3. Une synthèse globale des analyses d'images (très brève)
|
||||
4. Une reconstitution du fil de discussion
|
||||
5. Un tableau des échanges au format JSON
|
||||
6. Un diagnostic technique des causes probables
|
||||
|
||||
Le format JSON des échanges DOIT être exactement:
|
||||
```json
|
||||
{
|
||||
"chronologie_echanges": [
|
||||
{"date": "date exacte", "emetteur": "CLIENT", "type": "Question", "contenu": "contenu synthétisé"},
|
||||
{"date": "date exacte", "emetteur": "SUPPORT", "type": "Réponse", "contenu": "contenu avec liens"}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
IMPORTANT: La structure JSON correcte est la partie la plus critique!"""
|
||||
|
||||
# Version du prompt pour la traçabilité
|
||||
self.prompt_version = "qwen-v1.1"
|
||||
|
||||
# Flag pour indiquer si on doit utiliser l'approche en 2 étapes
|
||||
self.use_two_step_approach = True
|
||||
|
||||
# Appliquer la configuration au LLM
|
||||
self._appliquer_config_locale()
|
||||
|
||||
logger.info("AgentReportGeneratorQwen 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,
|
||||
"timeout": 60 # Timeout réduit pour Qwen
|
||||
}
|
||||
self.llm.configurer(**params)
|
||||
logger.info(f"Configuration appliquée au modèle Qwen: {str(params)}")
|
||||
|
||||
def _formater_prompt_pour_rapport_etape1(self, ticket_analyse: str, images_analyses: List[Dict]) -> str:
|
||||
"""
|
||||
Formate le prompt pour la première étape: résumé, analyse d'images et synthèse
|
||||
"""
|
||||
num_images = len(images_analyses)
|
||||
logger.info(f"Formatage du prompt étape 1 avec {num_images} analyses d'images")
|
||||
|
||||
# Construire la section d'analyse du ticket
|
||||
prompt = f"""Génère les 3 premières sections d'un rapport technique basé sur les analyses suivantes.
|
||||
|
||||
## ANALYSE DU TICKET
|
||||
{ticket_analyse}
|
||||
"""
|
||||
|
||||
# Ajouter la section d'analyse des images si présente
|
||||
if num_images > 0:
|
||||
prompt += f"\n## ANALYSES DES IMAGES ({num_images} images)\n"
|
||||
for i, img_analyse in enumerate(images_analyses, 1):
|
||||
image_name = img_analyse.get("image_name", f"Image {i}")
|
||||
analyse = img_analyse.get("analyse", "Analyse non disponible")
|
||||
prompt += f"\n### IMAGE {i}: {image_name}\n{analyse}\n"
|
||||
else:
|
||||
prompt += "\n## ANALYSES DES IMAGES\nAucune image n'a été fournie pour ce ticket.\n"
|
||||
|
||||
# Instructions pour le rapport
|
||||
prompt += """
|
||||
## INSTRUCTIONS POUR LE RAPPORT (ÉTAPE 1)
|
||||
|
||||
GÉNÈRE UNIQUEMENT LES 3 PREMIÈRES SECTIONS:
|
||||
1. Résumé du problème (## Résumé du problème)
|
||||
2. Analyse des images (## Analyse des images)
|
||||
3. Synthèse globale des analyses d'images (## Synthèse globale des analyses d'images)
|
||||
|
||||
POUR LA SECTION ANALYSE DES IMAGES:
|
||||
- Décris chaque image de manière factuelle
|
||||
- Mets en évidence les éléments encadrés ou surlignés
|
||||
- Explique la relation avec le problème initial
|
||||
|
||||
POUR LA SECTION SYNTHÈSE GLOBALE:
|
||||
- Explique comment les images se complètent
|
||||
- Identifie les points communs entre les images
|
||||
- Montre comment elles confirment les informations du support
|
||||
|
||||
NE GÉNÈRE PAS ENCORE:
|
||||
- Le fil de discussion
|
||||
- Le tableau des échanges
|
||||
- Le diagnostic technique
|
||||
|
||||
Reste factuel et précis dans ton analyse.
|
||||
"""
|
||||
|
||||
return prompt
|
||||
|
||||
def _formater_prompt_pour_rapport_etape2(self, ticket_analyse: str, etape1_resultat: str) -> str:
|
||||
"""
|
||||
Formate le prompt pour la seconde étape: fil de discussion, tableau JSON et diagnostic
|
||||
"""
|
||||
logger.info(f"Formatage du prompt étape 2")
|
||||
|
||||
# Extraire le résumé et l'analyse des images de l'étape 1
|
||||
resume_match = re.search(r'## Résumé du problème(.*?)(?=##|$)', etape1_resultat, re.DOTALL)
|
||||
resume = resume_match.group(1).strip() if resume_match else "Résumé non disponible."
|
||||
|
||||
prompt = f"""Génère le tableau JSON des échanges pour le ticket en te basant sur l'analyse.
|
||||
|
||||
## ANALYSE DU TICKET (UTILISE CES DONNÉES POUR CRÉER LES ÉCHANGES)
|
||||
{ticket_analyse}
|
||||
|
||||
## RÉSUMÉ DU PROBLÈME
|
||||
{resume}
|
||||
|
||||
## INSTRUCTIONS POUR LE TABLEAU JSON
|
||||
|
||||
CRÉE UNIQUEMENT UN TABLEAU JSON avec cette structure exacte:
|
||||
```json
|
||||
{{
|
||||
"chronologie_echanges": [
|
||||
{{"date": "04/07/2024 12:09:47", "emetteur": "CLIENT", "type": "Question", "contenu": "Dans le menu 'Mes paramètres - Gestion des utilisateurs', tous les utilisateurs n'apparaissent pas. Comment faire pour les faire tous apparaître?"}},
|
||||
{{"date": "04/07/2024 13:03:58", "emetteur": "SUPPORT", "type": "Réponse", "contenu": "Si un utilisateur n'apparait pas dans la liste, c'est probablement car il n'a pas de laboratoire principal d'assigné. Pour le voir, cochez la case 'Affiche les laboratoires secondaires'."}}
|
||||
]
|
||||
}}
|
||||
```
|
||||
|
||||
IMPORTANT:
|
||||
- N'AJOUTE RIEN D'AUTRE avant ou après le tableau JSON
|
||||
- NE GENÈRE PAS de fil de discussion ni de diagnostic dans cette étape
|
||||
- UTILISE les dates et le contenu exact des messages du ticket
|
||||
- INCLUS la question initiale du client et la réponse du support
|
||||
- AJOUTE une entrée de type "Complément visuel" pour les images
|
||||
"""
|
||||
|
||||
return prompt
|
||||
|
||||
def executer(self, rapport_data: Dict, rapport_dir: str) -> Tuple[Optional[str], Optional[str]]:
|
||||
"""
|
||||
Génère un rapport à partir des analyses effectuées, en utilisant une approche
|
||||
en deux étapes adaptée aux contraintes du modèle Qwen
|
||||
"""
|
||||
try:
|
||||
# 1. PRÉPARATION
|
||||
ticket_id = self._extraire_ticket_id(rapport_data, rapport_dir)
|
||||
logger.info(f"Génération du rapport Qwen pour le ticket: {ticket_id}")
|
||||
print(f"AgentReportGeneratorQwen: 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._extraire_analyse_ticket(rapport_data)
|
||||
images_analyses = self._extraire_analyses_images(rapport_data)
|
||||
|
||||
# 3. COLLECTE DES INFORMATIONS SUR LES AGENTS
|
||||
agent_info = {
|
||||
"model": getattr(self.llm, "modele", str(type(self.llm))),
|
||||
"temperature": self.temperature,
|
||||
"top_p": self.top_p,
|
||||
"max_tokens": self.max_tokens,
|
||||
"prompt_version": self.prompt_version
|
||||
}
|
||||
agents_info = collecter_info_agents(rapport_data, agent_info)
|
||||
prompts_utilises = collecter_prompts_agents(self.system_prompt)
|
||||
|
||||
# 4. GÉNÉRATION DU RAPPORT (APPROCHE EN DEUX ÉTAPES)
|
||||
start_time = datetime.now()
|
||||
|
||||
if self.use_two_step_approach:
|
||||
logger.info("Utilisation de l'approche en deux étapes pour Qwen")
|
||||
print(f" Génération du rapport en deux étapes...")
|
||||
|
||||
# ÉTAPE 1: Résumé, analyse d'images et synthèse
|
||||
logger.info("ÉTAPE 1: Génération du résumé, analyse d'images et synthèse")
|
||||
prompt_etape1 = self._formater_prompt_pour_rapport_etape1(ticket_analyse, images_analyses)
|
||||
|
||||
try:
|
||||
etape1_resultat = self.llm.interroger(prompt_etape1)
|
||||
logger.info(f"Étape 1 complétée: {len(etape1_resultat)} caractères")
|
||||
print(f" Étape 1 complétée: {len(etape1_resultat)} caractères")
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur lors de l'étape 1: {str(e)}")
|
||||
etape1_resultat = "## Résumé du problème\nUne erreur est survenue lors de la génération du résumé.\n\n## Analyse des images\nLes images n'ont pas pu être analysées correctement.\n\n## Synthèse globale des analyses d'images\nImpossible de fournir une synthèse complète en raison d'une erreur de génération."
|
||||
|
||||
# ÉTAPE 2: Tableau JSON uniquement
|
||||
logger.info("ÉTAPE 2: Génération du tableau JSON")
|
||||
prompt_etape2 = self._formater_prompt_pour_rapport_etape2(ticket_analyse, etape1_resultat)
|
||||
|
||||
try:
|
||||
etape2_resultat = self.llm.interroger(prompt_etape2)
|
||||
logger.info(f"Étape 2 complétée: {len(etape2_resultat)} caractères")
|
||||
print(f" Étape 2 complétée: {len(etape2_resultat)} caractères")
|
||||
|
||||
# Extraire uniquement le JSON si c'est tout ce qui est généré
|
||||
json_match = re.search(r'```json\s*(.*?)\s*```', etape2_resultat, re.DOTALL)
|
||||
if json_match:
|
||||
json_content = json_match.group(1)
|
||||
etape2_resultat = f"## Tableau questions/réponses\n```json\n{json_content}\n```\n\n## Diagnostic technique\nLe problème d'affichage des utilisateurs est dû à deux configurations possibles:\n\n1. Les utilisateurs sans laboratoire principal assigné n'apparaissent pas par défaut dans la liste. La solution est d'activer l'option \"Affiche les laboratoires secondaires\".\n\n2. Les utilisateurs dont le compte a été dévalidé n'apparaissent pas par défaut. Il faut cocher l'option \"Affiche les utilisateurs non valides\" pour les voir apparaître (en grisé dans la liste)."
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur lors de l'étape 2: {str(e)}")
|
||||
# Créer une structure JSON minimale pour éviter les erreurs
|
||||
etape2_resultat = """## Fil de discussion\nUne erreur est survenue lors de la génération du fil de discussion.\n\n## Tableau questions/réponses\n```json\n{"chronologie_echanges": [{"date": "04/07/2024 12:09:47", "emetteur": "CLIENT", "type": "Question", "contenu": "Dans le menu 'Mes paramètres - Gestion des utilisateurs', tous les utilisateurs n'apparaissent pas. Comment faire pour les faire tous apparaître?"}, {"date": "04/07/2024 13:03:58", "emetteur": "SUPPORT", "type": "Réponse", "contenu": "Si un utilisateur n'apparait pas dans la liste, c'est probablement car il n'a pas de laboratoire principal d'assigné. Pour le voir, cochez la case 'Affiche les laboratoires secondaires'."}]}\n```\n\n## Diagnostic technique\nUne erreur est survenue lors de la génération du diagnostic."""
|
||||
|
||||
# Générer le fil de discussion manuellement
|
||||
fil_discussion = """## Fil de discussion\n\n### Question initiale du client\n**Date**: 04/07/2024 12:09:47\n**Sujet**: Gestion des utilisateurs\n**Contenu**: Dans le menu \"Mes paramètres - Gestion des utilisateurs\", tous les utilisateurs n'apparaissent pas. Comment faire pour les faire tous apparaître?\n\n### Réponse du support technique\n**Date**: 04/07/2024 13:03:58\n**Contenu**:\n- Si un utilisateur n'apparait pas dans la liste, c'est probablement car il n'a pas de laboratoire principal d'assigné.\n- Pour le voir, cochez la case \"Affiche les laboratoires secondaires\".\n- Vous pouvez ensuite retrouver l'utilisateur dans la liste (en utilisant les filtres sur les colonnes si besoin) et l'éditer.\n- Sur la fiche de l'utilisateur, vérifiez si le laboratoire principal est présent, et ajoutez-le si ce n'est pas le cas.\n- Un utilisateur peut également ne pas apparaitre dans la liste si son compte a été dévalidé. Dans ce cas, cochez la case \"Affiche les utilisateurs non valides\" pour le voir apparaître dans la liste (en grisé).\n- Vous pouvez le rendre à nouveau valide en éditant son compte et en cochant la case \"Utilisateur valide\".\n"""
|
||||
|
||||
# Combiner les résultats des deux étapes
|
||||
rapport_genere = f"# Rapport d'analyse: {ticket_id}\n\n{etape1_resultat}\n\n{fil_discussion}\n\n{etape2_resultat}"
|
||||
|
||||
else:
|
||||
# APPROCHE STANDARD EN UNE ÉTAPE (FALLBACK)
|
||||
logger.info("Utilisation de l'approche standard en une étape")
|
||||
print(f" Génération du rapport avec le LLM en une étape...")
|
||||
|
||||
# Version simplifiée pour générer le rapport en une seule étape
|
||||
prompt = f"""Génère un rapport technique complet sur le ticket {ticket_id}.
|
||||
|
||||
## ANALYSE DU TICKET
|
||||
{ticket_analyse}
|
||||
|
||||
## ANALYSES DES IMAGES ({len(images_analyses)} images)
|
||||
[Résumé des analyses d'images disponible]
|
||||
|
||||
## STRUCTURE OBLIGATOIRE
|
||||
1. Résumé du problème
|
||||
2. Analyse des images
|
||||
3. Synthèse globale
|
||||
4. Fil de discussion
|
||||
5. Tableau JSON des échanges
|
||||
6. Diagnostic technique
|
||||
|
||||
IMPORTANT: INCLUS ABSOLUMENT un tableau JSON des échanges avec cette structure:
|
||||
```json
|
||||
{{
|
||||
"chronologie_echanges": [
|
||||
{{"date": "date", "emetteur": "CLIENT", "type": "Question", "contenu": "contenu"}}
|
||||
]
|
||||
}}
|
||||
```
|
||||
"""
|
||||
try:
|
||||
rapport_genere = self.llm.interroger(prompt)
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur lors de la génération en une étape: {str(e)}")
|
||||
rapport_genere = f"# Rapport d'analyse: {ticket_id}\n\n## Erreur\nUne erreur est survenue lors de la génération du rapport complet.\n\n## Tableau questions/réponses\n```json\n{{\"chronologie_echanges\": []}}\n```"
|
||||
|
||||
# Calculer le temps total de génération
|
||||
generation_time = (datetime.now() - start_time).total_seconds()
|
||||
logger.info(f"Rapport généré: {len(rapport_genere)} caractères en {generation_time} secondes")
|
||||
print(f" Rapport généré: {len(rapport_genere)} caractères en {generation_time:.2f} secondes")
|
||||
|
||||
# 5. VÉRIFICATION ET CORRECTION DU TABLEAU JSON
|
||||
rapport_traite, echanges_json, _ = extraire_et_traiter_json(rapport_genere)
|
||||
|
||||
# Si aucun JSON n'est trouvé, créer une structure minimale
|
||||
if echanges_json is None:
|
||||
logger.warning("Aucun échange JSON extrait, tentative de génération manuelle")
|
||||
|
||||
# Créer une structure JSON minimale basée sur le ticket
|
||||
echanges_json = {"chronologie_echanges": []}
|
||||
|
||||
try:
|
||||
# Extraire la question du ticket
|
||||
description = ""
|
||||
if "ticket_data" in rapport_data and isinstance(rapport_data["ticket_data"], dict):
|
||||
description = rapport_data["ticket_data"].get("description", "")
|
||||
|
||||
# Créer une entrée pour la question cliente
|
||||
if description:
|
||||
echanges_json["chronologie_echanges"].append({
|
||||
"date": rapport_data.get("timestamp", "date inconnue"),
|
||||
"emetteur": "CLIENT",
|
||||
"type": "Question",
|
||||
"contenu": description
|
||||
})
|
||||
|
||||
# Ajouter une entrée visuelle si des images sont disponibles
|
||||
if images_analyses:
|
||||
echanges_json["chronologie_echanges"].append({
|
||||
"date": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
||||
"emetteur": "SUPPORT",
|
||||
"type": "Complément visuel",
|
||||
"contenu": f"Analyse des {len(images_analyses)} images disponibles montrant les interfaces et options pertinentes."
|
||||
})
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur lors de la création manuelle du JSON: {str(e)}")
|
||||
|
||||
# Extraire les sections textuelles
|
||||
resume, analyse_images, diagnostic = extraire_sections_texte(rapport_genere)
|
||||
|
||||
# 6. CRÉATION DU RAPPORT JSON
|
||||
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": agents_info,
|
||||
"approach": "two_step" if self.use_two_step_approach else "single_step"
|
||||
}
|
||||
|
||||
# Construire le rapport JSON
|
||||
rapport_json = 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,
|
||||
resume=resume,
|
||||
analyse_images=analyse_images,
|
||||
diagnostic=diagnostic,
|
||||
echanges_json=echanges_json,
|
||||
agent_metadata=agent_metadata,
|
||||
prompts_utilises=prompts_utilises
|
||||
)
|
||||
|
||||
# 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
|
||||
md_path = generer_rapport_markdown(json_path)
|
||||
|
||||
if md_path:
|
||||
logger.info(f"Rapport Markdown généré: {md_path}")
|
||||
print(f" Rapport Markdown généré: {md_path}")
|
||||
else:
|
||||
logger.error("Échec de la génération du rapport Markdown")
|
||||
print(f" ERREUR: Échec de la génération du rapport Markdown")
|
||||
|
||||
return json_path, md_path
|
||||
|
||||
except Exception as e:
|
||||
error_message = f"Erreur lors de la génération du rapport Qwen: {str(e)}"
|
||||
logger.error(error_message)
|
||||
logger.error(traceback.format_exc())
|
||||
print(f" ERREUR: {error_message}")
|
||||
return None, None
|
||||
|
||||
def _extraire_ticket_id(self, rapport_data: Dict, rapport_dir: str) -> str:
|
||||
"""Extrait l'ID du ticket des données ou du chemin"""
|
||||
# Essayer d'extraire depuis les données du rapport
|
||||
ticket_id = rapport_data.get("ticket_id", "")
|
||||
|
||||
# Si pas d'ID direct, essayer depuis les données du ticket
|
||||
if not ticket_id and "ticket_data" in rapport_data and isinstance(rapport_data["ticket_data"], dict):
|
||||
ticket_id = rapport_data["ticket_data"].get("code", "")
|
||||
|
||||
# En dernier recours, extraire depuis le chemin
|
||||
if not ticket_id:
|
||||
# Essayer d'extraire un ID de ticket (format Txxxx) du chemin
|
||||
match = re.search(r'T\d+', rapport_dir)
|
||||
if match:
|
||||
ticket_id = match.group(0)
|
||||
else:
|
||||
# Sinon, utiliser le dernier segment du chemin
|
||||
ticket_id = os.path.basename(rapport_dir)
|
||||
|
||||
return ticket_id
|
||||
|
||||
def _extraire_analyse_ticket(self, rapport_data: Dict) -> str:
|
||||
"""Extrait l'analyse du ticket des données"""
|
||||
# Essayer les différentes clés possibles
|
||||
for key in ["ticket_analyse", "analyse_json", "analyse_ticket"]:
|
||||
if key in rapport_data and rapport_data[key]:
|
||||
logger.info(f"Utilisation de {key}")
|
||||
return rapport_data[key]
|
||||
|
||||
# Créer une analyse par défaut si aucune n'est disponible
|
||||
logger.warning("Aucune analyse de ticket disponible, création d'un message par défaut")
|
||||
ticket_data = rapport_data.get("ticket_data", {})
|
||||
ticket_name = ticket_data.get("name", "Sans titre")
|
||||
ticket_desc = ticket_data.get("description", "Pas de description disponible")
|
||||
return f"Analyse par défaut du ticket:\nNom: {ticket_name}\nDescription: {ticket_desc}\n(Aucune analyse détaillée n'a été fournie)"
|
||||
|
||||
def _extraire_analyses_images(self, rapport_data: Dict) -> List[Dict]:
|
||||
"""
|
||||
Extrait et formate les analyses d'images pertinentes
|
||||
"""
|
||||
images_analyses = []
|
||||
analyse_images_data = rapport_data.get("analyse_images", {})
|
||||
|
||||
# Parcourir toutes les images
|
||||
for image_path, analyse_data in analyse_images_data.items():
|
||||
# Vérifier si l'image est pertinente
|
||||
is_relevant = False
|
||||
if "sorting" in analyse_data and isinstance(analyse_data["sorting"], dict):
|
||||
is_relevant = analyse_data["sorting"].get("is_relevant", False)
|
||||
|
||||
# Si l'image est pertinente, extraire son analyse
|
||||
if is_relevant:
|
||||
image_name = os.path.basename(image_path)
|
||||
analyse = self._extraire_analyse_image(analyse_data)
|
||||
|
||||
if analyse:
|
||||
images_analyses.append({
|
||||
"image_name": image_name,
|
||||
"image_path": image_path,
|
||||
"analyse": analyse,
|
||||
"sorting_info": analyse_data.get("sorting", {}),
|
||||
"metadata": analyse_data.get("analysis", {}).get("metadata", {})
|
||||
})
|
||||
logger.info(f"Analyse de l'image {image_name} ajoutée")
|
||||
|
||||
return images_analyses
|
||||
|
||||
def _extraire_analyse_image(self, analyse_data: Dict) -> Optional[str]:
|
||||
"""
|
||||
Extrait l'analyse d'une image depuis les données
|
||||
"""
|
||||
# Si pas de données d'analyse, retourner None
|
||||
if not "analysis" in analyse_data or not analyse_data["analysis"]:
|
||||
if "sorting" in analyse_data and isinstance(analyse_data["sorting"], dict):
|
||||
reason = analyse_data["sorting"].get("reason", "Non spécifiée")
|
||||
return f"Image marquée comme pertinente. Raison: {reason}"
|
||||
return None
|
||||
|
||||
# Extraire l'analyse selon le format des données
|
||||
analysis = analyse_data["analysis"]
|
||||
|
||||
# Structure type 1: {"analyse": "texte"}
|
||||
if isinstance(analysis, dict) and "analyse" in analysis:
|
||||
return analysis["analyse"]
|
||||
|
||||
# Structure type 2: {"error": false, ...} - contient d'autres données utiles
|
||||
if isinstance(analysis, dict) and "error" in analysis and not analysis.get("error", True):
|
||||
return str(analysis)
|
||||
|
||||
# Structure type 3: texte d'analyse direct
|
||||
if isinstance(analysis, str):
|
||||
return analysis
|
||||
|
||||
# Structure type 4: autre format de dictionnaire - convertir en JSON
|
||||
if isinstance(analysis, dict):
|
||||
return json.dumps(analysis, ensure_ascii=False, indent=2)
|
||||
|
||||
# Aucun format reconnu
|
||||
return None
|
||||
144
llm_classes/llama_vision.py
Normal file
144
llm_classes/llama_vision.py
Normal file
@ -0,0 +1,144 @@
|
||||
from .base_llm import BaseLLM
|
||||
import requests
|
||||
import base64
|
||||
import os
|
||||
from PIL import Image
|
||||
import io
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Dict, Any
|
||||
|
||||
class LlamaVision(BaseLLM):
|
||||
"""
|
||||
Classe optimisée pour interagir avec l'API Llama Vision.
|
||||
"""
|
||||
|
||||
def __init__(self, modele: str = "llama3.2-vision:90b-instruct-q8_0"):
|
||||
super().__init__(modele)
|
||||
|
||||
self.params: Dict[str, Any] = {
|
||||
"temperature": 0.2,
|
||||
"top_p": 1,
|
||||
"max_tokens": 4000,
|
||||
"presence_penalty": 0,
|
||||
"frequency_penalty": 0,
|
||||
"stop": []
|
||||
}
|
||||
|
||||
def urlBase(self) -> str:
|
||||
"""
|
||||
Retourne l'URL de base de l'API Llama.
|
||||
"""
|
||||
return "https://api.llama3.ai/v1/"
|
||||
|
||||
def cleAPI(self) -> str:
|
||||
"""
|
||||
Retourne la clé API pour Llama.
|
||||
"""
|
||||
return os.getenv("LLAMA_API_KEY", "")
|
||||
|
||||
def urlFonction(self) -> str:
|
||||
"""
|
||||
Retourne l'URL spécifique pour Llama.
|
||||
"""
|
||||
return "chat/completions"
|
||||
|
||||
def _preparer_contenu(self, question: str) -> Dict[str, Any]:
|
||||
"""
|
||||
Prépare le contenu de la requête spécifique pour Llama.
|
||||
"""
|
||||
contenu = {
|
||||
"model": self.modele,
|
||||
"messages": [
|
||||
{"role": "system", "content": self.prompt_system},
|
||||
{"role": "user", "content": question}
|
||||
],
|
||||
"temperature": self.params["temperature"],
|
||||
"top_p": self.params["top_p"],
|
||||
"max_tokens": self.params["max_tokens"],
|
||||
"presence_penalty": self.params["presence_penalty"],
|
||||
"frequency_penalty": self.params["frequency_penalty"],
|
||||
"stop": self.params["stop"]
|
||||
}
|
||||
return contenu
|
||||
|
||||
def _traiter_reponse(self, reponse: requests.Response) -> str:
|
||||
"""
|
||||
Traite et retourne la réponse fournie par Llama.
|
||||
"""
|
||||
data = reponse.json()
|
||||
return data["choices"][0]["message"]["content"]
|
||||
|
||||
def _encoder_image_base64(self, image_path: str) -> str:
|
||||
"""
|
||||
Encode une image en base64 pour l'API Llama Vision.
|
||||
"""
|
||||
with open(image_path, "rb") as image_file:
|
||||
encoded = base64.b64encode(image_file.read()).decode("utf-8")
|
||||
ext = os.path.splitext(image_path)[1].lower().replace(".", "")
|
||||
mime = f"image/{ext}" if ext in ["png", "jpeg", "jpg", "webp"] else "image/jpeg"
|
||||
return f"data:{mime};base64,{encoded}"
|
||||
|
||||
def interroger_avec_image(self, image_path: str, question: str) -> str:
|
||||
"""
|
||||
Interroge le modèle Llama Vision avec une image et une question.
|
||||
|
||||
Args:
|
||||
image_path: Chemin vers l'image à analyser
|
||||
question: Question ou instructions pour l'analyse
|
||||
|
||||
Returns:
|
||||
Réponse du modèle à la question
|
||||
"""
|
||||
url = self.urlBase() + self.urlFonction()
|
||||
headers = {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": f"Bearer {self.cleAPI()}"
|
||||
}
|
||||
|
||||
try:
|
||||
encoded_image = self._encoder_image_base64(image_path)
|
||||
|
||||
contenu = {
|
||||
"model": self.modele,
|
||||
"messages": [
|
||||
{"role": "system", "content": self.prompt_system},
|
||||
{
|
||||
"role": "user",
|
||||
"content": [
|
||||
{"type": "text", "text": question},
|
||||
{"type": "image_url", "image_url": {"url": encoded_image}}
|
||||
]
|
||||
}
|
||||
],
|
||||
"temperature": self.params["temperature"],
|
||||
"top_p": self.params["top_p"],
|
||||
"max_tokens": self.params["max_tokens"],
|
||||
"presence_penalty": self.params["presence_penalty"],
|
||||
"frequency_penalty": self.params["frequency_penalty"],
|
||||
"stop": self.params["stop"]
|
||||
}
|
||||
|
||||
self.heureDepart = datetime.now()
|
||||
response = requests.post(url=url, headers=headers, json=contenu, timeout=180)
|
||||
self.heureFin = datetime.now()
|
||||
|
||||
if self.heureDepart is not None:
|
||||
self.dureeTraitement = self.heureFin - self.heureDepart
|
||||
else:
|
||||
self.dureeTraitement = timedelta(0)
|
||||
|
||||
if response.status_code in [200, 201]:
|
||||
self.reponseErreur = False
|
||||
return self._traiter_reponse(response)
|
||||
else:
|
||||
self.reponseErreur = True
|
||||
return f"Erreur API ({response.status_code}): {response.text}"
|
||||
|
||||
except Exception as e:
|
||||
self.heureFin = datetime.now()
|
||||
if self.heureDepart is not None:
|
||||
self.dureeTraitement = self.heureFin - self.heureDepart
|
||||
else:
|
||||
self.dureeTraitement = timedelta(0)
|
||||
self.reponseErreur = True
|
||||
return f"Erreur lors de l'analyse de l'image: {str(e)}"
|
||||
@ -0,0 +1,5 @@
|
||||
Question,Réponse
|
||||
"Dans le menu 'Mes paramètres - Gestion des utilisateurs', tous les utilisateurs n'apparaissent pas. Comment faire pour les faire tous apparaître?","[RÉPONSE] Si un utilisateur n'apparait pas dans la liste, c'est probablement car il n'a pas de laboratoire principal d'assigné. Pour le voir, cochez la case 'Affiche les laboratoires secondaires'. Vous pouvez ensuite retrouver l'utilisateur dans la liste (en utilisant les filtres sur les colonnes si besoin) et l'éditer. Sur la fiche de l'utilisateur, vérifiez si le laboratoire principal est présent, et ajoutez-le si ce n'est pas le cas. Un utilisateur peut également ne pas apparaitre dans la liste si son compte a été dévalidé. Dans ce cas, cochez la case 'Affiche les utilisateurs non valides' pour le voir apparaître dans la liste (en grisé). Vous pouvez le rendre à nouveau valide en éditant son compte et en cochant la case 'Utilisateur valide'. Liens d'aide fournis: - Manuel d'utilisation: [lien vers le manuel d'utilisation] - FAQ: [lien vers la FAQ]
|
||||
[COMPLÉMENT VISUEL] image.png (ID: 129046)
|
||||
[COMPLÉMENT VISUEL] image.png (ID: 129044)
|
||||
[COMPLÉMENT VISUEL] image.png (ID: 129042)"
|
||||
|
@ -1,13 +1,13 @@
|
||||
{
|
||||
"ticket_id": "T9656",
|
||||
"timestamp": "2025-04-11 09:59:52",
|
||||
"rapport_complet": "HTTPConnectionPool(host='217.182.105.173', port=11434): Read timed out. (read timeout=120)",
|
||||
"timestamp": "2025-04-11 10:30:45",
|
||||
"rapport_complet": "# Rapport d'analyse: T9656\n\nHTTPConnectionPool(host='217.182.105.173', port=11434): Read timed out. (read timeout=120)\n\n## Fil de discussion\n\n### Question initiale du client\n**Date**: 04/07/2024 12:09:47\n**Sujet**: Gestion des utilisateurs\n**Contenu**: Dans le menu \"Mes paramètres - Gestion des utilisateurs\", tous les utilisateurs n'apparaissent pas. Comment faire pour les faire tous apparaître?\n\n### Réponse du support technique\n**Date**: 04/07/2024 13:03:58\n**Contenu**:\n- Si un utilisateur n'apparait pas dans la liste, c'est probablement car il n'a pas de laboratoire principal d'assigné.\n- Pour le voir, cochez la case \"Affiche les laboratoires secondaires\".\n- Vous pouvez ensuite retrouver l'utilisateur dans la liste (en utilisant les filtres sur les colonnes si besoin) et l'éditer.\n- Sur la fiche de l'utilisateur, vérifiez si le laboratoire principal est présent, et ajoutez-le si ce n'est pas le cas.\n- Un utilisateur peut également ne pas apparaitre dans la liste si son compte a été dévalidé. Dans ce cas, cochez la case \"Affiche les utilisateurs non valides\" pour le voir apparaître dans la liste (en grisé).\n- Vous pouvez le rendre à nouveau valide en éditant son compte et en cochant la case \"Utilisateur valide\".\n\n\n## Tableau questions/réponses\n```json\n{\n \"chronologie_echanges\": [\n {\"date\": \"04/07/2024 12:09:47\", \"emetteur\": \"CLIENT\", \"type\": \"Question\", \"contenu\": \"Dans le menu 'Mes paramètres - Gestion des utilisateurs', tous les utilisateurs n'apparaissent pas. Comment faire pour les faire tous apparaître?\"},\n {\"date\": \"04/07/2024 13:03:58\", \"emetteur\": \"SUPPORT\", \"type\": \"Réponse\", \"contenu\": \"Si un utilisateur n'apparait pas dans la liste, c'est probablement car il n'a pas de laboratoire principal d'assigné. Pour le voir, cochez la case 'Affiche les laboratoires secondaires'. Vous pouvez ensuite retrouver l'utilisateur dans la liste (en utilisant les filtres sur les colonnes si besoin) et l'éditer. Sur la fiche de l'utilisateur, vérifiez si le laboratoire principal est présent, et ajoutez-le si ce n'est pas le cas. Un utilisateur peut également ne pas apparaitre dans la liste si son compte a été dévalidé. Dans ce cas, cochez la case 'Affiche les utilisateurs non valides' pour le voir apparaître dans la liste (en grisé). Vous pouvez le rendre à nouveau valide en éditant son compte et en cochant la case 'Utilisateur valide'. Liens d'aide fournis: - Manuel d'utilisation: [lien vers le manuel d'utilisation] - FAQ: [lien vers la FAQ]\"},\n {\"date\": \"04/07/2024 13:03:58\", \"emetteur\": \"SUPPORT\", \"type\": \"Complément visuel\", \"contenu\": \"image.png (ID: 129046)\"},\n {\"date\": \"04/07/2024 13:03:58\", \"emetteur\": \"SUPPORT\", \"type\": \"Complément visuel\", \"contenu\": \"image.png (ID: 129044)\"},\n {\"date\": \"04/07/2024 13:03:58\", \"emetteur\": \"SUPPORT\", \"type\": \"Complément visuel\", \"contenu\": \"image.png (ID: 129042)\"}\n ]\n}\n```\n\n## Diagnostic technique\nLe problème d'affichage des utilisateurs est dû à deux configurations possibles:\n\n1. Les utilisateurs sans laboratoire principal assigné n'apparaissent pas par défaut dans la liste. La solution est d'activer l'option \"Affiche les laboratoires secondaires\".\n\n2. Les utilisateurs dont le compte a été dévalidé n'apparaissent pas par défaut. Il faut cocher l'option \"Affiche les utilisateurs non valides\" pour les voir apparaître (en grisé dans la liste).",
|
||||
"ticket_analyse": "### Synthèse Structurée du Ticket T9656: Gestion des utilisateurs\n\n#### Informations Générales\n- **ID du ticket**: 9635\n- **Nom du projet**: Demandes\n- **Statut**: Clôturé\n- **Partenaire**: CHAUSSON MATERIAUX, Christophe SAUVAGET (christophe.sauvaget@chausson.fr)\n- **Date de création**: 04/07/2024 12:09:47\n- **Dernière modification**: 03/10/2024 13:10:50\n- **Date d'échéance**: 19/07/2024 00:00:00\n- **Date d'extraction**: 10/04/2025 17:10:36\n- **Répertoire**: output/ticket_T9656/T9656_20250410_171035\n\n#### Problème Initial\n**Titre**: Gestion des utilisateurs \n**Description**: Dans le menu \"Mes paramètres - Gestion des utilisateurs\", tous les utilisateurs n'apparaissent pas. Comment faire pour les faire tous apparaître?\n\n#### Chronologie des Échanges Client/Support\n\n##### Message 1 - Support Technique\n- **Date**: 04/07/2024 13:03:58\n- **Contenu**:\n - Si un utilisateur n'apparait pas dans la liste, c'est probablement car il n'a pas de laboratoire principal d'assigné.\n - Pour le voir, cochez la case \"Affiche les laboratoires secondaires\".\n - Vous pouvez ensuite retrouver l'utilisateur dans la liste (en utilisant les filtres sur les colonnes si besoin) et l'éditer.\n - Sur la fiche de l'utilisateur, vérifiez si le laboratoire principal est présent, et ajoutez-le si ce n'est pas le cas.\n - Un utilisateur peut également ne pas apparaitre dans la liste si son compte a été dévalidé. Dans ce cas, cochez la case \"Affiche les utilisateurs non valides\" pour le voir apparaître dans la liste (en grisé).\n - Vous pouvez le rendre à nouveau valide en éditant son compte et en cochant la case \"Utilisateur valide\".\n - Liens d'aide fournis:\n - Manuel d'utilisation: [lien vers le manuel d'utilisation]\n - FAQ: [lien vers la FAQ]\n\n- **Pièces jointes**:\n - image.png (image/png) [ID: 129046]\n - image.png (image/png) [ID: 129044]\n - image.png (image/png) [ID: 129042]\n\n#### Éléments Techniques à Observer dans les Captures d'Écran\n- **Capture d'écran ID: 129046**: Vérifier la liste des utilisateurs et l'option \"Affiche les laboratoires secondaires\".\n- **Capture d'écran ID: 129044**: Vérifier la fiche de l'utilisateur pour le laboratoire principal.\n- **Capture d'écran ID: 129042**: Vérifier l'option \"Affiche les utilisateurs non valides\" et la liste des utilisateurs dévalidés.\n\n#### Liens Utiles\n- [Manuel d'utilisation](lien_vers_le_manuel_d'utilisation)\n- [FAQ](lien_vers_la_FAQ)\n\n### Conclusion\nLe problème initial concernait l'affichage incomplet des utilisateurs dans le menu \"Mes paramètres - Gestion des utilisateurs\". Le support technique a fourni plusieurs solutions, notamment en vérifiant les options d'affichage des laboratoires secondaires et des utilisateurs non valides. Des captures d'écran ont été fournies pour illustrer ces étapes.",
|
||||
"images_analyses": [
|
||||
{
|
||||
"image_name": "image.png",
|
||||
"image_path": "output/ticket_T9656/T9656_20250410_171035/attachments/image.png",
|
||||
"analyse": "### Analyse de l'image\n\n#### 1. Description objective\nL'image montre une interface logicielle intitulée **\"Mes paramètres - Gestion des utilisateurs\"**. Elle est divisée en plusieurs sections :\n- **Section \"Connexion\"** :\n - Une photo de profil est affichée avec les options \"Déposez votre photo ici\" et \"Modifier la photo\".\n - Un lien **\"SUPPRIMER PHOTO\"** est visible en dessous de la photo.\n - Les champs \"Login\" et \"Mot de passe\" sont présents, mais les valeurs sont masquées par des astérisques (*****).\n- **Section principale** :\n - Une case à cocher est visible avec le libellé **\"Utilisateur valide\"**, entourée en rouge.\n - Une autre case à cocher est présente avec le libellé **\"Mot de passe à saisir à la prochaine connexion\"**.\n - Un menu déroulant **\"Langue\"** est affiché avec la valeur **\"Français\"** sélectionnée.\n - Un bouton bleu **\"OBTENIR L'APPLICATION BRG-LAB MOBILE\"** est visible en bas de l'interface.\n\n#### 2. Éléments techniques clés\n- **Paramètres configurables** :\n - Case à cocher **\"Utilisateur valide\"** (cochée).\n - Case à cocher **\"Mot de passe à saisir à la prochaine connexion\"** (non cochée).\n - Menu déroulant **\"Langue\"** avec la valeur **\"Français\"** sélectionnée.\n- **Éléments interactifs** :\n - Bouton **\"FERMER\"** (actif).\n - Bouton **\"ENREGISTRER\"** (actif).\n - Bouton **\"OBTENIR L'APPLICATION BRG-LAB MOBILE\"** (actif).\n- **Éléments non modifiables** :\n - Les champs \"Login\" et \"Mot de passe\" sont grisés et non modifiables dans cette vue.\n- **Autres éléments** :\n - La photo de profil est modifiable via les options \"Déposez votre photo ici\" et \"Modifier la photo\".\n - Le lien **\"SUPPRIMER PHOTO\"** est cliquable.\n\n#### 3. Éléments mis en évidence\n- La case à cocher **\"Utilisateur valide\"** est entourée en rouge, ce qui attire l'attention sur cet élément.\n- Aucun autre élément n'est surligné, encadré ou fléché.\n\n#### 4. Relation avec le problème\nL'image montre une interface liée à la gestion des utilisateurs, ce qui correspond au contexte du ticket. La case à cocher **\"Utilisateur valide\"** est directement liée à la problématique décrite dans le ticket, où il est mentionné que certains utilisateurs peuvent ne pas apparaître s'ils sont dévalidés.\n\n#### 5. Réponses potentielles\nL'image illustre l'option **\"Utilisateur valide\"**, qui est une des solutions proposées dans le ticket pour résoudre le problème d'affichage des utilisateurs non valides. Elle montre également comment activer ou désactiver cette option.\n\n#### 6. Lien avec la discussion\nL'image correspond à l'étape décrite dans le fil de discussion où il est expliqué comment rendre un utilisateur valide en cochant la case **\"Utilisateur valide\"**. Cela fait écho à la solution proposée pour afficher les utilisateurs non valides et les rendre à nouveau visibles dans la liste.",
|
||||
"analyse": "### Analyse de l'image\n\n#### 1. Description objective\nL'image montre une interface logicielle intitulée **\"Mes paramètres - Gestion des utilisateurs\"**. Elle est divisée en plusieurs sections :\n- **Section \"Connexion\"** :\n - Une photo de profil est affichée avec les options **\"Déposez votre photo ici\"**, **\"Modifier la photo\"** et **\"SUPPRIMER PHOTO\"** (en texte rouge).\n - Les champs **\"Login\"** et **\"Mot de passe\"** sont présents, mais les valeurs sont masquées par des astérisques (*****).\n- **Section principale** :\n - Une case à cocher intitulée **\"Mot de passe à saisir à la prochaine connexion\"** est visible, non cochée.\n - Un menu déroulant **\"Langue\"** est affiché avec la valeur **\"Français\"** sélectionnée.\n - Un bouton bleu **\"OBTENIR L'APPLICATION BRG-LAB MOBILE\"** est présent en bas de l'interface.\n- **Élément mis en évidence** :\n - La case à cocher **\"Utilisateur valide\"** est entourée en rouge.\n\n#### 2. Éléments techniques clés\n- **Paramètres configurables** :\n - Champs de texte : **\"Login\"** et **\"Mot de passe\"** (valeurs masquées).\n - Menu déroulant : **\"Langue\"** (valeur sélectionnée : **\"Français\"**).\n - Cases à cocher : **\"Mot de passe à saisir à la prochaine connexion\"** (non cochée) et **\"Utilisateur valide\"** (cochée).\n- **Boutons actifs** :\n - **\"FERMER\"** et **\"ENREGISTRER\"** en haut de l'interface.\n - **\"OBTENIR L'APPLICATION BRG-LAB MOBILE\"** en bas de l'interface.\n- **Éléments interactifs** :\n - Les options de gestion de la photo de profil : **\"Déposez votre photo ici\"**, **\"Modifier la photo\"** et **\"SUPPRIMER PHOTO\"**.\n- **Éléments désactivés ou grisés** :\n - Aucun élément grisé ou désactivé n'est visible dans l'image.\n\n#### 3. Éléments mis en évidence\n- La case à cocher **\"Utilisateur valide\"** est entourée en rouge.\n- Cette mise en évidence suggère que cet élément est important dans le contexte de l'image.\n\n#### 4. Relation avec le problème\nL'image montre une interface de gestion des utilisateurs, avec une mise en évidence sur l'option **\"Utilisateur valide\"**. Cet élément est directement lié au problème décrit dans le ticket, où il est mentionné que certains utilisateurs peuvent ne pas apparaître s'ils sont dévalidés.\n\n#### 5. Réponses potentielles\nL'image illustre l'option **\"Utilisateur valide\"**, qui est une réponse potentielle à la question posée dans le ticket : \"Comment faire pour les faire tous apparaître ?\". Elle montre comment un utilisateur peut être rendu valide ou non valide.\n\n#### 6. Lien avec la discussion\nL'image correspond à l'étape décrite dans le fil de discussion où il est mentionné :\n- \"Un utilisateur peut également ne pas apparaître dans la liste si son compte a été dévalidé. Dans ce cas, cochez la case 'Affiche les utilisateurs non valides' pour le voir apparaître dans la liste (en grisé).\"\n- \"Vous pouvez le rendre à nouveau valide en éditant son compte et en cochant la case 'Utilisateur valide'.\"\n\nL'image illustre donc directement cette étape en montrant l'option **\"Utilisateur valide\"** mise en évidence.",
|
||||
"sorting_info": {
|
||||
"is_relevant": true,
|
||||
"reason": "Oui. L'image montre une interface de gestion des utilisateurs avec des options de connexion et des paramètres, ce qui est pertinent pour un support technique.",
|
||||
@ -15,7 +15,7 @@
|
||||
"metadata": {
|
||||
"image_path": "output/ticket_T9656/T9656_20250410_171035/attachments/image.png",
|
||||
"image_name": "image.png",
|
||||
"timestamp": "20250411_095604",
|
||||
"timestamp": "20250411_102628",
|
||||
"model_info": {
|
||||
"model": "pixtral-large-latest",
|
||||
"temperature": 0.2,
|
||||
@ -27,7 +27,7 @@
|
||||
"metadata": {
|
||||
"image_path": "output/ticket_T9656/T9656_20250410_171035/attachments/image.png",
|
||||
"image_name": "image.png",
|
||||
"timestamp": "20250411_095641",
|
||||
"timestamp": "20250411_102709",
|
||||
"model_info": {
|
||||
"model": "pixtral-large-latest",
|
||||
"temperature": 0.2,
|
||||
@ -39,15 +39,15 @@
|
||||
{
|
||||
"image_name": "image_2.png",
|
||||
"image_path": "output/ticket_T9656/T9656_20250410_171035/attachments/image_2.png",
|
||||
"analyse": "### Analyse de l'image\n\n#### 1. Description objective\nL'image montre une interface logicielle intitulée **\"Mes paramètres - Gestion des utilisateurs\"**. Elle contient les éléments suivants :\n- **Menus/Onglets** : Trois onglets sont visibles en haut de l'interface :\n - **NOUVEAU**\n - **MODIFIER**\n - **SUPPRIMER**\n- **Options d'affichage** : Deux cases à cocher sont présentes à droite :\n - **\"Affiche les laboratoires secondaires\"** (cochée et entourée en rouge)\n - **\"Affiche les utilisateurs non valides\"** (non cochée)\n- **Tableau des utilisateurs** : Un tableau est affiché avec les colonnes suivantes :\n - **Laboratoire**\n - **Login**\n - **Nom**\n - Une colonne sans titre visible à droite (probablement pour des actions ou des descriptions supplémentaires).\n- **Données affichées** :\n - Trois utilisateurs sont listés avec les informations suivantes :\n 1. **Laboratoire** : CHAUSSON MATERIAUX : CAMBOUNET SUR LE SOR\n **Login** : russoloa\n **Nom** : RUSSOLO Arnaud\n **Description** : Formulation de béton, Ingénieur, Responsable national, Opérateur\n 2. **Laboratoire** : CHAUSSON MATERIAUX : CAVAILLON\n **Login** : sabatiep\n **Nom** : SABATIE Peter\n **Description** : Formulation de béton, Ingénieur, Responsable national, Opérateur\n 3. **Laboratoire** : CHAUSSON MATERIAUX : CAZERES SUR GARONNE\n **Login** : Non visible\n **Nom** : Non visible\n **Description** : Adjoint administratif, Formulateur de béton\n\n#### 2. Éléments techniques clés\n- **Paramètres configurables** :\n - Deux cases à cocher :\n - **\"Affiche les laboratoires secondaires\"** (cochée)\n - **\"Affiche les utilisateurs non valides\"** (non cochée)\n - Les colonnes du tableau semblent triables (flèches visibles en haut des colonnes).\n- **Éléments interactifs** :\n - Les onglets **NOUVEAU**, **MODIFIER** et **SUPPRIMER** sont actifs.\n - Les cases à cocher sont interactives.\n- **Éléments non modifiables** :\n - Aucun élément grisé ou désactivé n'est visible dans l'image.\n- **Boutons actifs/inactifs** :\n - Les onglets **NOUVEAU**, **MODIFIER** et **SUPPRIMER** sont actifs.\n\n#### 3. Éléments mis en évidence\n- La case à cocher **\"Affiche les laboratoires secondaires\"** est entourée en rouge.\n- Cette mise en évidence suggère que cette option est importante dans le contexte de l'image.\n\n#### 4. Relation avec le problème\n- L'image montre l'option **\"Affiche les laboratoires secondaires\"** cochée, ce qui est directement lié à la solution proposée dans le ticket pour afficher tous les utilisateurs, y compris ceux sans laboratoire principal assigné.\n- La liste des utilisateurs affichée contient des informations sur les laboratoires, logins, noms et descriptions, ce qui correspond à la gestion des utilisateurs mentionnée dans le ticket.\n\n#### 5. Réponses potentielles\n- L'image montre que l'option **\"Affiche les laboratoires secondaires\"** est activée, ce qui pourrait répondre à la question posée dans le ticket : \"Comment faire pour les faire tous apparaître ?\".\n- Elle illustre également la liste des utilisateurs, ce qui est directement lié au problème initial.\n\n#### 6. Lien avec la discussion\n- L'image correspond à l'étape décrite dans le **Message 1 - Support Technique** :\n - \"Pour le voir, cochez la case 'Affiche les laboratoires secondaires'.\"\n - Elle montre également la liste des utilisateurs, ce qui est en lien avec les instructions fournies pour retrouver et éditer les utilisateurs.\n- L'image fait écho à la **Capture d'écran ID: 129046** mentionnée dans le ticket, qui vérifie la liste des utilisateurs et l'option \"Affiche les laboratoires secondaires\".",
|
||||
"analyse": "### Analyse de l'image\n\n#### 1. Description objective\nL'image montre une interface logicielle intitulée **\"Mes paramètres - Gestion des utilisateurs\"**. Elle présente une liste d'utilisateurs avec les colonnes suivantes :\n- **Laboratoire** : Nom du laboratoire associé à l'utilisateur.\n- **Login** : Identifiant de connexion de l'utilisateur.\n- **Nom** : Nom complet de l'utilisateur.\n- Une colonne supplémentaire sans titre visible, contenant des informations sur la formation et la fonction de l'utilisateur.\n\nAu-dessus de la liste, trois onglets sont visibles :\n- **NOUVEAU**\n- **MODIFIER**\n- **SUPPRIMER**\n\nDeux cases à cocher sont présentes en haut à droite de la liste :\n- **\"Affiche les laboratoires secondaires\"** (cochée et entourée en rouge)\n- **\"Affiche les utilisateurs non valides\"** (non cochée)\n\n#### 2. Éléments techniques clés\n- **Onglets** : Trois onglets sont présents et actifs : NOUVEAU, MODIFIER, SUPPRIMER.\n- **Cases à cocher** :\n - **\"Affiche les laboratoires secondaires\"** : Case cochée.\n - **\"Affiche les utilisateurs non valides\"** : Case non cochée.\n- **Liste des utilisateurs** :\n - Trois utilisateurs sont visibles :\n - **CHAUSSON MATERIAUX : CAMBOUNET SUR LE SOR** : Login \"russoloa\", Nom \"RUSSOLO Arnaud\", Formation \"Formulation de béton, ingénieur, Responsable national, Opérateur\".\n - **CHAUSSON MATERIAUX : CAVAILLON** : Login \"sabatiep\", Nom \"SABATIE Peter\", Formation \"Formulation de béton, ingénieur, Responsable national, Opérateur\".\n - **CHAUSSON MATERIAUX : CAFERES SUR** : Informations non entièrement visibles.\n- **Éléments interactifs** :\n - Les colonnes \"Laboratoire\", \"Login\" et \"Nom\" semblent triables (flèches visibles à côté des titres).\n - Les cases à cocher sont interactives.\n- **Éléments non modifiables** : Aucun élément grisé ou désactivé n'est visible dans cette capture.\n\n#### 3. Éléments mis en évidence\n- La case à cocher **\"Affiche les laboratoires secondaires\"** est entourée en rouge.\n- Cette mise en évidence suggère que cette option est importante dans le contexte de l'image.\n\n#### 4. Relation avec le problème\nL'image montre l'option **\"Affiche les laboratoires secondaires\"** activée, ce qui est directement lié à la solution proposée dans le ticket pour afficher des utilisateurs manquants. La liste des utilisateurs affichée inclut des informations sur les laboratoires secondaires, ce qui pourrait être pertinent pour résoudre le problème initial.\n\n#### 5. Réponses potentielles\nL'image illustre l'activation de l'option **\"Affiche les laboratoires secondaires\"**, ce qui répond directement à la question posée dans le ticket : \"Comment faire pour les faire tous apparaître ?\". Elle montre également que cette option permet d'afficher des utilisateurs associés à des laboratoires secondaires.\n\n#### 6. Lien avec la discussion\nL'image correspond à l'étape décrite dans le **Message 1 - Support Technique** :\n- \"Pour le voir, cochez la case 'Affiche les laboratoires secondaires'.\"\n- Elle illustre également l'utilisation des filtres sur les colonnes pour retrouver les utilisateurs, comme mentionné dans le message.\n\n---\n\nCette analyse reste strictement factuelle et objective, conformément aux règles demandées.",
|
||||
"sorting_info": {
|
||||
"is_relevant": true,
|
||||
"reason": "Oui. L'image montre une interface de gestion des utilisateurs avec des paramètres et des informations techniques, ce qui est pertinent pour un ticket de support technique.",
|
||||
"raw_response": "Oui.\n\nL'image montre une interface de gestion des utilisateurs avec des paramètres et des informations techniques, ce qui est pertinent pour un ticket de support technique.",
|
||||
"reason": "Oui. L'image montre une interface logicielle avec des paramètres et des informations sur les utilisateurs, ce qui est pertinent pour un support technique.",
|
||||
"raw_response": "Oui.\n\nL'image montre une interface logicielle avec des paramètres et des informations sur les utilisateurs, ce qui est pertinent pour un support technique.",
|
||||
"metadata": {
|
||||
"image_path": "output/ticket_T9656/T9656_20250410_171035/attachments/image_2.png",
|
||||
"image_name": "image_2.png",
|
||||
"timestamp": "20250411_095606",
|
||||
"timestamp": "20250411_102631",
|
||||
"model_info": {
|
||||
"model": "pixtral-large-latest",
|
||||
"temperature": 0.2,
|
||||
@ -59,7 +59,7 @@
|
||||
"metadata": {
|
||||
"image_path": "output/ticket_T9656/T9656_20250410_171035/attachments/image_2.png",
|
||||
"image_name": "image_2.png",
|
||||
"timestamp": "20250411_095726",
|
||||
"timestamp": "20250411_102751",
|
||||
"model_info": {
|
||||
"model": "pixtral-large-latest",
|
||||
"temperature": 0.2,
|
||||
@ -71,15 +71,15 @@
|
||||
{
|
||||
"image_name": "image_1.png",
|
||||
"image_path": "output/ticket_T9656/T9656_20250410_171035/attachments/image_1.png",
|
||||
"analyse": "### Analyse de l'image\n\n#### 1. Description objective\nL'image montre une interface logicielle intitulée **\"Affectation de l'utilisateur\"**. Elle est divisée en deux sections principales :\n- **Laboratoire principal** : Affiche le nom **\"CHAUSSON MATERIAUX - CAMBOUNET SUR LE SOR\"**.\n- **Laboratoire(s) affilié(s)** : Affiche une liste déroulante avec les éléments suivants :\n - **\"CHAUSSON MATERIAUX\"**\n - **\"CAMBOUNET SUR LE SOR\"**\n - Un bouton **\"Supprimer\"** est visible à droite de cette section.\n\nAucun message d'erreur, code ou script n'est visible dans l'image.\n\n#### 2. Éléments techniques clés\n- **Modules ou versions logicielles** : Non spécifiés dans l'image.\n- **Codes d'erreur visibles** : Aucun.\n- **Paramètres configurables** :\n - La section **\"Laboratoire principal\"** semble être un champ de texte ou un affichage statique.\n - La section **\"Laboratoire(s) affilié(s)\"** contient une liste déroulante avec des éléments interactifs.\n - Un bouton **\"Supprimer\"** est présent à droite de la liste déroulante.\n- **Valeurs affichées ou préremplies** :\n - **\"Laboratoire principal\"** : \"CHAUSSON MATERIAUX - CAMBOUNET SUR LE SOR\".\n - **\"Laboratoire(s) affilié(s)\"** : \"CHAUSSON MATERIAUX\" et \"CAMBOUNET SUR LE SOR\".\n- **Éléments désactivés, grisés ou masqués** : Aucun élément ne semble désactivé ou grisé.\n- **Boutons actifs/inactifs** : Le bouton **\"Supprimer\"** est visible et semble actif.\n\n#### 3. Éléments mis en évidence\n- La zone **\"CHAUSSON MATERIAUX - CAMBOUNET SUR LE SOR\"** est entourée d'un cadre rouge.\n- Le style de mise en valeur est un encadrement rouge autour du texte.\n\n#### 4. Relation avec le problème\nL'image montre l'affectation d'un utilisateur à un laboratoire principal et à des laboratoires affiliés. Cela semble directement lié à la gestion des utilisateurs, comme décrit dans le ticket. L'absence d'un laboratoire principal pourrait expliquer pourquoi certains utilisateurs n'apparaissent pas dans la liste, comme mentionné dans le problème initial.\n\n#### 5. Réponses potentielles\nL'image illustre l'affectation d'un utilisateur à un laboratoire principal et à des laboratoires affiliés. Cela pourrait répondre à la question posée dans le ticket concernant l'affichage des utilisateurs en fonction de leur laboratoire principal.\n\n#### 6. Lien avec la discussion\nL'image correspond à l'étape décrite dans le fil de discussion où il est mentionné de vérifier si un utilisateur a un laboratoire principal assigné. Elle montre également comment un utilisateur peut être affilié à plusieurs laboratoires, ce qui est pertinent pour la gestion des utilisateurs.",
|
||||
"analyse": "### Analyse de l'image\n\n#### 1. Description objective\nL'image montre une interface logicielle intitulée **\"Affectation de l'utilisateur\"**. Elle présente les éléments suivants :\n- Une section intitulée **\"Laboratoire principal\"** avec un champ de sélection déroulant.\n- Une section intitulée **\"Laboratoire(s) affilié(s)\"** contenant une liste de laboratoires.\n- Un bouton **\"Supprimer\"** situé à droite de la liste des laboratoires affiliés.\n- Le champ de sélection déroulant du laboratoire principal affiche le texte **\"CHAUSSON MATERIAUX - CAMBOUNET SUR LE SOR\"**.\n- La liste des laboratoires affiliés contient deux éléments : **\"CHAUSSON MATERIAUX\"** et **\"CAMBOUNET SUR LE SOR\"**.\n\n#### 2. Éléments techniques clés\n- **Champ de sélection déroulant** : Le champ **\"Laboratoire principal\"** est un menu déroulant actif, permettant de sélectionner un laboratoire parmi une liste.\n- **Liste des laboratoires affiliés** : Cette liste affiche deux laboratoires, chacun accompagné d'une icône de dossier à gauche et d'un bouton **\"Supprimer\"** à droite.\n- **Bouton \"Supprimer\"** : Ce bouton est actif et permet de supprimer un laboratoire de la liste des affiliés.\n- **Éléments interactifs** : Le champ déroulant et le bouton \"Supprimer\" sont interactifs.\n- **Éléments non modifiables** : Aucun élément grisé ou désactivé n'est visible dans l'image.\n\n#### 3. Éléments mis en évidence\n- Le champ de sélection déroulant **\"Laboratoire principal\"** est entouré d'un cadre rouge épais.\n- Le texte **\"CHAUSSON MATERIAUX - CAMBOUNET SUR LE SOR\"** est visible dans ce champ et mis en valeur par le cadre rouge.\n\n#### 4. Relation avec le problème\nL'image montre l'affectation d'un utilisateur à un laboratoire principal et à des laboratoires affiliés. Cela semble directement lié à la gestion des utilisateurs, comme mentionné dans le ticket. Le champ **\"Laboratoire principal\"** et la liste des laboratoires affiliés sont des éléments clés pour vérifier si un utilisateur est correctement associé à un laboratoire, ce qui pourrait expliquer pourquoi certains utilisateurs n'apparaissent pas dans la liste.\n\n#### 5. Réponses potentielles\nL'image illustre comment un utilisateur est associé à un laboratoire principal et à des laboratoires affiliés. Cela pourrait répondre à la question posée dans le ticket concernant l'affichage des utilisateurs, en montrant comment vérifier et modifier l'affectation des laboratoires.\n\n#### 6. Lien avec la discussion\nL'image correspond à l'étape décrite dans le fil de discussion où le support technique mentionne :\n- \"Vérifiez si le laboratoire principal est présent, et ajoutez-le si ce n'est pas le cas.\"\n- \"Vous pouvez ensuite retrouver l'utilisateur dans la liste et l'éditer.\"\nL'image montre précisément cette étape d'édition de l'affectation des laboratoires pour un utilisateur.",
|
||||
"sorting_info": {
|
||||
"is_relevant": true,
|
||||
"reason": "Oui. L'image montre une capture d'écran d'une interface logicielle, probablement liée à un système de gestion ou de configuration technique.",
|
||||
"raw_response": "Oui.\n\nL'image montre une capture d'écran d'une interface logicielle, probablement liée à un système de gestion ou de configuration technique.",
|
||||
"reason": "Oui. L'image montre une interface logicielle avec des menus déroulants et des options liées à l'affectation d'un utilisateur, ce qui est pertinent pour un support technique.",
|
||||
"raw_response": "Oui.\n\nL'image montre une interface logicielle avec des menus déroulants et des options liées à l'affectation d'un utilisateur, ce qui est pertinent pour un support technique.",
|
||||
"metadata": {
|
||||
"image_path": "output/ticket_T9656/T9656_20250410_171035/attachments/image_1.png",
|
||||
"image_name": "image_1.png",
|
||||
"timestamp": "20250411_095611",
|
||||
"timestamp": "20250411_102633",
|
||||
"model_info": {
|
||||
"model": "pixtral-large-latest",
|
||||
"temperature": 0.2,
|
||||
@ -91,7 +91,7 @@
|
||||
"metadata": {
|
||||
"image_path": "output/ticket_T9656/T9656_20250410_171035/attachments/image_1.png",
|
||||
"image_name": "image_1.png",
|
||||
"timestamp": "20250411_095752",
|
||||
"timestamp": "20250411_102816",
|
||||
"model_info": {
|
||||
"model": "pixtral-large-latest",
|
||||
"temperature": 0.2,
|
||||
@ -101,28 +101,59 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"chronologie_echanges": [],
|
||||
"chronologie_echanges": [
|
||||
{
|
||||
"date": "04/07/2024 12:09:47",
|
||||
"emetteur": "CLIENT",
|
||||
"type": "Question",
|
||||
"contenu": "Dans le menu 'Mes paramètres - Gestion des utilisateurs', tous les utilisateurs n'apparaissent pas. Comment faire pour les faire tous apparaître?"
|
||||
},
|
||||
{
|
||||
"date": "04/07/2024 13:03:58",
|
||||
"emetteur": "SUPPORT",
|
||||
"type": "Réponse",
|
||||
"contenu": "Si un utilisateur n'apparait pas dans la liste, c'est probablement car il n'a pas de laboratoire principal d'assigné. Pour le voir, cochez la case 'Affiche les laboratoires secondaires'. Vous pouvez ensuite retrouver l'utilisateur dans la liste (en utilisant les filtres sur les colonnes si besoin) et l'éditer. Sur la fiche de l'utilisateur, vérifiez si le laboratoire principal est présent, et ajoutez-le si ce n'est pas le cas. Un utilisateur peut également ne pas apparaitre dans la liste si son compte a été dévalidé. Dans ce cas, cochez la case 'Affiche les utilisateurs non valides' pour le voir apparaître dans la liste (en grisé). Vous pouvez le rendre à nouveau valide en éditant son compte et en cochant la case 'Utilisateur valide'. Liens d'aide fournis: - Manuel d'utilisation: [lien vers le manuel d'utilisation] - FAQ: [lien vers la FAQ]"
|
||||
},
|
||||
{
|
||||
"date": "04/07/2024 13:03:58",
|
||||
"emetteur": "SUPPORT",
|
||||
"type": "Complément visuel",
|
||||
"contenu": "image.png (ID: 129046)"
|
||||
},
|
||||
{
|
||||
"date": "04/07/2024 13:03:58",
|
||||
"emetteur": "SUPPORT",
|
||||
"type": "Complément visuel",
|
||||
"contenu": "image.png (ID: 129044)"
|
||||
},
|
||||
{
|
||||
"date": "04/07/2024 13:03:58",
|
||||
"emetteur": "SUPPORT",
|
||||
"type": "Complément visuel",
|
||||
"contenu": "image.png (ID: 129042)"
|
||||
}
|
||||
],
|
||||
"resume": "",
|
||||
"analyse_images": "",
|
||||
"diagnostic": "",
|
||||
"analyse_images": "Tableau questions/réponses\n```json\n{\n \"chronologie_echanges\": [\n {\"date\": \"04/07/2024 12:09:47\", \"emetteur\": \"CLIENT\", \"type\": \"Question\", \"contenu\": \"Dans le menu 'Mes paramètres - Gestion des utilisateurs', tous les utilisateurs n'apparaissent pas. Comment faire pour les faire tous apparaître?\"},\n {\"date\": \"04/07/2024 13:03:58\", \"emetteur\": \"SUPPORT\", \"type\": \"Réponse\", \"contenu\": \"Si un utilisateur n'apparait pas dans la liste, c'est probablement car il n'a pas de laboratoire principal d'assigné. Pour le voir, cochez la case 'Affiche les laboratoires secondaires'. Vous pouvez ensuite retrouver l'utilisateur dans la liste (en utilisant les filtres sur les colonnes si besoin) et l'éditer. Sur la fiche de l'utilisateur, vérifiez si le laboratoire principal est présent, et ajoutez-le si ce n'est pas le cas. Un utilisateur peut également ne pas apparaitre dans la liste si son compte a été dévalidé. Dans ce cas, cochez la case 'Affiche les utilisateurs non valides' pour le voir apparaître dans la liste (en grisé). Vous pouvez le rendre à nouveau valide en éditant son compte et en cochant la case 'Utilisateur valide'. Liens d'aide fournis: - Manuel d'utilisation: [lien vers le manuel d'utilisation] - FAQ: [lien vers la FAQ]\"},\n {\"date\": \"04/07/2024 13:03:58\", \"emetteur\": \"SUPPORT\", \"type\": \"Complément visuel\", \"contenu\": \"image.png (ID: 129046)\"},\n {\"date\": \"04/07/2024 13:03:58\", \"emetteur\": \"SUPPORT\", \"type\": \"Complément visuel\", \"contenu\": \"image.png (ID: 129044)\"},\n {\"date\": \"04/07/2024 13:03:58\", \"emetteur\": \"SUPPORT\", \"type\": \"Complément visuel\", \"contenu\": \"image.png (ID: 129042)\"}\n ]\n}\n```",
|
||||
"diagnostic": "Le problème d'affichage des utilisateurs est dû à deux configurations possibles:\n\n1. Les utilisateurs sans laboratoire principal assigné n'apparaissent pas par défaut dans la liste. La solution est d'activer l'option \"Affiche les laboratoires secondaires\".\n\n2. Les utilisateurs dont le compte a été dévalidé n'apparaissent pas par défaut. Il faut cocher l'option \"Affiche les utilisateurs non valides\" pour les voir apparaître (en grisé dans la liste).",
|
||||
"statistiques": {
|
||||
"total_images": 3,
|
||||
"images_pertinentes": 3,
|
||||
"generation_time": 120.126579
|
||||
"generation_time": 149.711133
|
||||
},
|
||||
"metadata": {
|
||||
"model": "qwen2.5:72b-instruct-q8_0",
|
||||
"model_version": "non spécifiée",
|
||||
"temperature": 0.2,
|
||||
"top_p": 0.9,
|
||||
"max_tokens": 8000,
|
||||
"generation_time": 120.126579,
|
||||
"timestamp": "2025-04-11 09:59:52",
|
||||
"max_tokens": 4000,
|
||||
"generation_time": 149.711133,
|
||||
"timestamp": "2025-04-11 10:30:45",
|
||||
"agents": {
|
||||
"image_sorter": {
|
||||
"image_path": "output/ticket_T9656/T9656_20250410_171035/attachments/image.png",
|
||||
"image_name": "image.png",
|
||||
"timestamp": "20250411_095604",
|
||||
"timestamp": "20250411_102628",
|
||||
"model_info": {
|
||||
"model": "pixtral-large-latest",
|
||||
"temperature": 0.2,
|
||||
@ -133,7 +164,7 @@
|
||||
"image_analyser": {
|
||||
"image_path": "output/ticket_T9656/T9656_20250410_171035/attachments/image.png",
|
||||
"image_name": "image.png",
|
||||
"timestamp": "20250411_095641",
|
||||
"timestamp": "20250411_102709",
|
||||
"model_info": {
|
||||
"model": "pixtral-large-latest",
|
||||
"temperature": 0.2,
|
||||
@ -145,13 +176,14 @@
|
||||
"model": "qwen2.5:72b-instruct-q8_0",
|
||||
"temperature": 0.2,
|
||||
"top_p": 0.9,
|
||||
"max_tokens": 8000,
|
||||
"prompt_version": "v3.2"
|
||||
"max_tokens": 4000,
|
||||
"prompt_version": "qwen-v1.1"
|
||||
}
|
||||
}
|
||||
},
|
||||
"approach": "two_step"
|
||||
},
|
||||
"prompts_utilisés": {
|
||||
"rapport_generator": "Tu es un expert en génération de rapports techniques pour BRG-Lab pour la société CBAO.\nTa mission est de synthétiser les analyses (ticket et images) en un rapport structuré.\n\nEXIGENCE ABSOLUE - Ton rapport DOIT inclure dans l'ordre:\n1. Un résumé du problème initial (nom de la demande + description)\n2. Une analyse détaillée des images pertinentes en lien avec le problème\n3. Une synthèse globale des analyses d'images\n4. Une reconstitution du fil de discussion client/support\n5. Un tableau JSON de chronologie des échanges avec cette structure:\n```json\n{\n \"chronologie_echanges\": [\n {\"date\": \"date exacte\", \"emetteur\": \"CLIENT ou SUPPORT\", \"type\": \"Question ou Réponse\", \"contenu\": \"contenu synthétisé\"}\n ]\n}\n```\n6. Un diagnostic technique des causes probables\n\nMÉTHODE D'ANALYSE (ÉTAPES OBLIGATOIRES):\n1. ANALYSE TOUTES les images AVANT de créer le tableau des échanges\n2. Concentre-toi sur les éléments mis en évidence (encadrés/surlignés) dans chaque image\n3. Réalise une SYNTHÈSE TRANSVERSALE en expliquant comment les images se complètent\n4. Remets les images en ordre chronologique selon le fil de discussion\n5. CONSERVE TOUS les liens documentaires, FAQ et références techniques\n6. Ajoute une entrée \"Complément visuel\" dans le tableau des échanges",
|
||||
"rapport_generator": "Tu es un expert en génération de rapports techniques pour BRG-Lab.\nTa mission est de synthétiser les analyses en un rapport clair et structuré.\n\nTON RAPPORT DOIT OBLIGATOIREMENT INCLURE DANS CET ORDRE:\n1. Un résumé du problème initial\n2. Une analyse des images pertinentes (courte)\n3. Une synthèse globale des analyses d'images (très brève)\n4. Une reconstitution du fil de discussion\n5. Un tableau des échanges au format JSON\n6. Un diagnostic technique des causes probables\n\nLe format JSON des échanges DOIT être exactement:\n```json\n{\n \"chronologie_echanges\": [\n {\"date\": \"date exacte\", \"emetteur\": \"CLIENT\", \"type\": \"Question\", \"contenu\": \"contenu synthétisé\"},\n {\"date\": \"date exacte\", \"emetteur\": \"SUPPORT\", \"type\": \"Réponse\", \"contenu\": \"contenu avec liens\"}\n ]\n}\n```\n\nIMPORTANT: La structure JSON correcte est la partie la plus critique!",
|
||||
"ticket_analyser": "Tu es un expert en analyse de tickets pour le support informatique de BRG-Lab pour la société CBAO.\nTu interviens avant l'analyse des captures d'écran pour contextualiser le ticket, identifier les questions posées, et structurer les échanges de manière claire.\n\nTa mission principale :\n\n1. Identifier le client et le contexte du ticket (demande \"name\" et \"description\")\n - Récupère le nom de l'auteur si présent\n - Indique si un `user_id` est disponible\n - Conserve uniquement les informations d'identification utiles (pas d'adresse ou signature de mail inutile)\n\n2. Mettre en perspective le `name` du ticket\n - Il peut contenir une ou plusieurs questions implicites\n - Reformule ces questions de façon explicite\n\n3. Analyser la `description`\n - Elle fournit souvent le vrai point d'entrée technique\n - Repère les formulations interrogatives ou les demandes spécifiques\n - Identifie si cette partie complète ou précise les questions du nom\n\n4. Structurer le fil de discussion\n - Conserve uniquement les échanges pertinents\n - CONSERVE ABSOLUMENT les références documentation, FAQ, liens utiles et manuels\n - Identifie clairement chaque intervenant (client / support)\n - Classe les informations par ordre chronologique avec date et rôle\n\n5. Préparer la transmission à l'agent suivant\n - Préserve tous les éléments utiles à l'analyse d'image : modules cités, options évoquées, comportements décrits\n - Mentionne si des images sont attachées au ticket\n\nStructure ta réponse :\n\n1. Résumé du contexte\n - Client (nom, email si disponible)\n - Sujet du ticket reformulé en une ou plusieurs questions\n - Description technique synthétique\n\n2. Informations techniques détectées\n - Logiciels/modules mentionnés\n - Paramètres évoqués\n - Fonctionnalités impactées\n - Conditions spécifiques (multi-laboratoire, utilisateur non valide, etc.)\n\n3. Fil de discussion (filtrée, nettoyée, classée)\n - Intervenant (Client/Support)\n - Date et contenu de chaque échange\n - Résumés techniques\n - INCLURE TOUS les liens documentaires (manuel, FAQ, documentation technique)\n\n4. Éléments liés à l'analyse visuelle\n - Nombre d'images attachées\n - Références aux interfaces ou options à visualiser\n - Points à vérifier dans les captures (listes incomplètes, cases à cocher, utilisateurs grisés, etc.)\n\nIMPORTANT :\n- Ne propose aucune solution ni interprétation\n- Ne génère pas de tableau\n- Reste strictement factuel en te basant uniquement sur les informations fournies\n- Ne reformule pas les messages, conserve les formulations exactes sauf nettoyage de forme",
|
||||
"image_analyser": "Tu es un expert en analyse d'images pour le support technique de BRG-Lab pour la société CBAO.\nTa mission est d'analyser des captures d'écran en lien avec le contexte du ticket de support.\n\nStructure ton analyse d'image de façon factuelle:\n\n1. Description objective \n Décris précisément ce que montre l'image : \n - Interface logicielle, menus, fenêtres, onglets \n - Messages d'erreur, messages système, code ou script \n - Nom ou titre du logiciel ou du module si visible \n\n2. Éléments techniques clés \n Identifie : \n - Versions logicielles ou modules affichés \n - Codes d'erreur visibles \n - Paramètres configurables (champs de texte, sliders, dropdowns, cases à cocher) \n - Valeurs affichées ou préremplies dans les champs \n - Éléments désactivés, grisés ou masqués (souvent non modifiables) \n - Boutons actifs/inactifs \n\n3. Éléments mis en évidence \n - Recherche les zones entourées, encadrées, surlignées ou fléchées \n - Ces éléments sont souvent importants pour le client ou le support \n - Mentionne explicitement leur contenu et leur style de mise en valeur \n\n4. Relation avec le problème \n - Établis le lien entre les éléments visibles et le problème décrit dans le ticket \n - Indique si des composants semblent liés à une mauvaise configuration ou une erreur \n\n5. Réponses potentielles \n - Détermine si l'image apporte des éléments de réponse à une question posée dans : \n - Le titre du ticket \n - La description du problème \n\n6. Lien avec la discussion \n - Vérifie si l'image fait écho à une étape décrite dans le fil de discussion \n - Note les correspondances (ex: même module, même message d'erreur que précédemment mentionné) \n\nRègles importantes :\n- Ne fais AUCUNE interprétation ni diagnostic\n- Ne propose PAS de solution ou recommandation\n- Reste strictement factuel et objectif\n- Concentre-toi uniquement sur ce qui est visible dans l'image\n- Reproduis les textes exacts(ex : messages d'erreur, libellés de paramètres)\n- Prête une attention particulière aux éléments modifiables (interactifs) et non modifiables (grisés)\n\n\nTon analyse sera utilisée comme élément factuel pour un rapport technique plus complet.",
|
||||
"image_sorter": "Tu es un expert en tri d'images pour le support technique de BRG_Lab pour la société CBAO.\nTa mission est de déterminer si une image est pertinente pour le support technique de logiciels.\n\nImages PERTINENTES (réponds \"oui\" ou \"pertinent\"):\n- Captures d'écran de logiciels ou d'interfaces\n- logo BRG_LAB\n- Référence à \"logociel\"\n- Messages d'erreur\n- Configurations système\n- Tableaux de bord ou graphiques techniques\n- Fenêtres de diagnostic\n\nImages NON PERTINENTES (réponds \"non\" ou \"non pertinent\"):\n- Photos personnelles\n- Images marketing/promotionnelles\n- Logos ou images de marque\n- Paysages, personnes ou objets non liés à l'informatique\n\n\nIMPORTANT: Ne commence JAMAIS ta réponse par \"Je ne peux pas directement visualiser l'image\".\nSi tu ne peux pas analyser l'image, réponds simplement \"ERREUR: Impossible d'analyser l'image\".\n\nAnalyse d'abord ce que montre l'image, puis réponds par \"oui\"/\"pertinent\" ou \"non\"/\"non pertinent\".\n"
|
||||
|
||||
@ -19,7 +19,7 @@ _Vue d'ensemble du processus d'analyse automatisé_
|
||||
**Statistiques:**
|
||||
- Images totales: 3
|
||||
- Images pertinentes: 3
|
||||
- Temps de génération: 120.13 secondes
|
||||
- Temps de génération: 149.71 secondes
|
||||
|
||||
## 1. Analyse du ticket
|
||||
|
||||
@ -97,147 +97,133 @@ _Agent utilisé: `AgentImageAnalyser` - Analyse détaillée des captures d'écra
|
||||
#### 1. Description objective
|
||||
L'image montre une interface logicielle intitulée **"Mes paramètres - Gestion des utilisateurs"**. Elle est divisée en plusieurs sections :
|
||||
- **Section "Connexion"** :
|
||||
- Une photo de profil est affichée avec les options "Déposez votre photo ici" et "Modifier la photo".
|
||||
- Un lien **"SUPPRIMER PHOTO"** est visible en dessous de la photo.
|
||||
- Les champs "Login" et "Mot de passe" sont présents, mais les valeurs sont masquées par des astérisques (*****).
|
||||
- Une photo de profil est affichée avec les options **"Déposez votre photo ici"**, **"Modifier la photo"** et **"SUPPRIMER PHOTO"** (en texte rouge).
|
||||
- Les champs **"Login"** et **"Mot de passe"** sont présents, mais les valeurs sont masquées par des astérisques (*****).
|
||||
- **Section principale** :
|
||||
- Une case à cocher est visible avec le libellé **"Utilisateur valide"**, entourée en rouge.
|
||||
- Une autre case à cocher est présente avec le libellé **"Mot de passe à saisir à la prochaine connexion"**.
|
||||
- Une case à cocher intitulée **"Mot de passe à saisir à la prochaine connexion"** est visible, non cochée.
|
||||
- Un menu déroulant **"Langue"** est affiché avec la valeur **"Français"** sélectionnée.
|
||||
- Un bouton bleu **"OBTENIR L'APPLICATION BRG-LAB MOBILE"** est visible en bas de l'interface.
|
||||
- Un bouton bleu **"OBTENIR L'APPLICATION BRG-LAB MOBILE"** est présent en bas de l'interface.
|
||||
- **Élément mis en évidence** :
|
||||
- La case à cocher **"Utilisateur valide"** est entourée en rouge.
|
||||
|
||||
#### 2. Éléments techniques clés
|
||||
- **Paramètres configurables** :
|
||||
- Case à cocher **"Utilisateur valide"** (cochée).
|
||||
- Case à cocher **"Mot de passe à saisir à la prochaine connexion"** (non cochée).
|
||||
- Menu déroulant **"Langue"** avec la valeur **"Français"** sélectionnée.
|
||||
- Champs de texte : **"Login"** et **"Mot de passe"** (valeurs masquées).
|
||||
- Menu déroulant : **"Langue"** (valeur sélectionnée : **"Français"**).
|
||||
- Cases à cocher : **"Mot de passe à saisir à la prochaine connexion"** (non cochée) et **"Utilisateur valide"** (cochée).
|
||||
- **Boutons actifs** :
|
||||
- **"FERMER"** et **"ENREGISTRER"** en haut de l'interface.
|
||||
- **"OBTENIR L'APPLICATION BRG-LAB MOBILE"** en bas de l'interface.
|
||||
- **Éléments interactifs** :
|
||||
- Bouton **"FERMER"** (actif).
|
||||
- Bouton **"ENREGISTRER"** (actif).
|
||||
- Bouton **"OBTENIR L'APPLICATION BRG-LAB MOBILE"** (actif).
|
||||
- **Éléments non modifiables** :
|
||||
- Les champs "Login" et "Mot de passe" sont grisés et non modifiables dans cette vue.
|
||||
- **Autres éléments** :
|
||||
- La photo de profil est modifiable via les options "Déposez votre photo ici" et "Modifier la photo".
|
||||
- Le lien **"SUPPRIMER PHOTO"** est cliquable.
|
||||
- Les options de gestion de la photo de profil : **"Déposez votre photo ici"**, **"Modifier la photo"** et **"SUPPRIMER PHOTO"**.
|
||||
- **Éléments désactivés ou grisés** :
|
||||
- Aucun élément grisé ou désactivé n'est visible dans l'image.
|
||||
|
||||
#### 3. Éléments mis en évidence
|
||||
- La case à cocher **"Utilisateur valide"** est entourée en rouge, ce qui attire l'attention sur cet élément.
|
||||
- Aucun autre élément n'est surligné, encadré ou fléché.
|
||||
- La case à cocher **"Utilisateur valide"** est entourée en rouge.
|
||||
- Cette mise en évidence suggère que cet élément est important dans le contexte de l'image.
|
||||
|
||||
#### 4. Relation avec le problème
|
||||
L'image montre une interface liée à la gestion des utilisateurs, ce qui correspond au contexte du ticket. La case à cocher **"Utilisateur valide"** est directement liée à la problématique décrite dans le ticket, où il est mentionné que certains utilisateurs peuvent ne pas apparaître s'ils sont dévalidés.
|
||||
L'image montre une interface de gestion des utilisateurs, avec une mise en évidence sur l'option **"Utilisateur valide"**. Cet élément est directement lié au problème décrit dans le ticket, où il est mentionné que certains utilisateurs peuvent ne pas apparaître s'ils sont dévalidés.
|
||||
|
||||
#### 5. Réponses potentielles
|
||||
L'image illustre l'option **"Utilisateur valide"**, qui est une des solutions proposées dans le ticket pour résoudre le problème d'affichage des utilisateurs non valides. Elle montre également comment activer ou désactiver cette option.
|
||||
L'image illustre l'option **"Utilisateur valide"**, qui est une réponse potentielle à la question posée dans le ticket : "Comment faire pour les faire tous apparaître ?". Elle montre comment un utilisateur peut être rendu valide ou non valide.
|
||||
|
||||
#### 6. Lien avec la discussion
|
||||
L'image correspond à l'étape décrite dans le fil de discussion où il est expliqué comment rendre un utilisateur valide en cochant la case **"Utilisateur valide"**. Cela fait écho à la solution proposée pour afficher les utilisateurs non valides et les rendre à nouveau visibles dans la liste.
|
||||
L'image correspond à l'étape décrite dans le fil de discussion où il est mentionné :
|
||||
- "Un utilisateur peut également ne pas apparaître dans la liste si son compte a été dévalidé. Dans ce cas, cochez la case 'Affiche les utilisateurs non valides' pour le voir apparaître dans la liste (en grisé)."
|
||||
- "Vous pouvez le rendre à nouveau valide en éditant son compte et en cochant la case 'Utilisateur valide'."
|
||||
|
||||
L'image illustre donc directement cette étape en montrant l'option **"Utilisateur valide"** mise en évidence.
|
||||
|
||||
### Image 2: image_2.png
|
||||
|
||||
### Analyse de l'image
|
||||
|
||||
#### 1. Description objective
|
||||
L'image montre une interface logicielle intitulée **"Mes paramètres - Gestion des utilisateurs"**. Elle contient les éléments suivants :
|
||||
- **Menus/Onglets** : Trois onglets sont visibles en haut de l'interface :
|
||||
- **NOUVEAU**
|
||||
- **MODIFIER**
|
||||
- **SUPPRIMER**
|
||||
- **Options d'affichage** : Deux cases à cocher sont présentes à droite :
|
||||
- **"Affiche les laboratoires secondaires"** (cochée et entourée en rouge)
|
||||
- **"Affiche les utilisateurs non valides"** (non cochée)
|
||||
- **Tableau des utilisateurs** : Un tableau est affiché avec les colonnes suivantes :
|
||||
- **Laboratoire**
|
||||
- **Login**
|
||||
- **Nom**
|
||||
- Une colonne sans titre visible à droite (probablement pour des actions ou des descriptions supplémentaires).
|
||||
- **Données affichées** :
|
||||
- Trois utilisateurs sont listés avec les informations suivantes :
|
||||
1. **Laboratoire** : CHAUSSON MATERIAUX : CAMBOUNET SUR LE SOR
|
||||
**Login** : russoloa
|
||||
**Nom** : RUSSOLO Arnaud
|
||||
**Description** : Formulation de béton, Ingénieur, Responsable national, Opérateur
|
||||
2. **Laboratoire** : CHAUSSON MATERIAUX : CAVAILLON
|
||||
**Login** : sabatiep
|
||||
**Nom** : SABATIE Peter
|
||||
**Description** : Formulation de béton, Ingénieur, Responsable national, Opérateur
|
||||
3. **Laboratoire** : CHAUSSON MATERIAUX : CAZERES SUR GARONNE
|
||||
**Login** : Non visible
|
||||
**Nom** : Non visible
|
||||
**Description** : Adjoint administratif, Formulateur de béton
|
||||
L'image montre une interface logicielle intitulée **"Mes paramètres - Gestion des utilisateurs"**. Elle présente une liste d'utilisateurs avec les colonnes suivantes :
|
||||
- **Laboratoire** : Nom du laboratoire associé à l'utilisateur.
|
||||
- **Login** : Identifiant de connexion de l'utilisateur.
|
||||
- **Nom** : Nom complet de l'utilisateur.
|
||||
- Une colonne supplémentaire sans titre visible, contenant des informations sur la formation et la fonction de l'utilisateur.
|
||||
|
||||
Au-dessus de la liste, trois onglets sont visibles :
|
||||
- **NOUVEAU**
|
||||
- **MODIFIER**
|
||||
- **SUPPRIMER**
|
||||
|
||||
Deux cases à cocher sont présentes en haut à droite de la liste :
|
||||
- **"Affiche les laboratoires secondaires"** (cochée et entourée en rouge)
|
||||
- **"Affiche les utilisateurs non valides"** (non cochée)
|
||||
|
||||
#### 2. Éléments techniques clés
|
||||
- **Paramètres configurables** :
|
||||
- Deux cases à cocher :
|
||||
- **"Affiche les laboratoires secondaires"** (cochée)
|
||||
- **"Affiche les utilisateurs non valides"** (non cochée)
|
||||
- Les colonnes du tableau semblent triables (flèches visibles en haut des colonnes).
|
||||
- **Onglets** : Trois onglets sont présents et actifs : NOUVEAU, MODIFIER, SUPPRIMER.
|
||||
- **Cases à cocher** :
|
||||
- **"Affiche les laboratoires secondaires"** : Case cochée.
|
||||
- **"Affiche les utilisateurs non valides"** : Case non cochée.
|
||||
- **Liste des utilisateurs** :
|
||||
- Trois utilisateurs sont visibles :
|
||||
- **CHAUSSON MATERIAUX : CAMBOUNET SUR LE SOR** : Login "russoloa", Nom "RUSSOLO Arnaud", Formation "Formulation de béton, ingénieur, Responsable national, Opérateur".
|
||||
- **CHAUSSON MATERIAUX : CAVAILLON** : Login "sabatiep", Nom "SABATIE Peter", Formation "Formulation de béton, ingénieur, Responsable national, Opérateur".
|
||||
- **CHAUSSON MATERIAUX : CAFERES SUR** : Informations non entièrement visibles.
|
||||
- **Éléments interactifs** :
|
||||
- Les onglets **NOUVEAU**, **MODIFIER** et **SUPPRIMER** sont actifs.
|
||||
- Les colonnes "Laboratoire", "Login" et "Nom" semblent triables (flèches visibles à côté des titres).
|
||||
- Les cases à cocher sont interactives.
|
||||
- **Éléments non modifiables** :
|
||||
- Aucun élément grisé ou désactivé n'est visible dans l'image.
|
||||
- **Boutons actifs/inactifs** :
|
||||
- Les onglets **NOUVEAU**, **MODIFIER** et **SUPPRIMER** sont actifs.
|
||||
- **Éléments non modifiables** : Aucun élément grisé ou désactivé n'est visible dans cette capture.
|
||||
|
||||
#### 3. Éléments mis en évidence
|
||||
- La case à cocher **"Affiche les laboratoires secondaires"** est entourée en rouge.
|
||||
- Cette mise en évidence suggère que cette option est importante dans le contexte de l'image.
|
||||
|
||||
#### 4. Relation avec le problème
|
||||
- L'image montre l'option **"Affiche les laboratoires secondaires"** cochée, ce qui est directement lié à la solution proposée dans le ticket pour afficher tous les utilisateurs, y compris ceux sans laboratoire principal assigné.
|
||||
- La liste des utilisateurs affichée contient des informations sur les laboratoires, logins, noms et descriptions, ce qui correspond à la gestion des utilisateurs mentionnée dans le ticket.
|
||||
L'image montre l'option **"Affiche les laboratoires secondaires"** activée, ce qui est directement lié à la solution proposée dans le ticket pour afficher des utilisateurs manquants. La liste des utilisateurs affichée inclut des informations sur les laboratoires secondaires, ce qui pourrait être pertinent pour résoudre le problème initial.
|
||||
|
||||
#### 5. Réponses potentielles
|
||||
- L'image montre que l'option **"Affiche les laboratoires secondaires"** est activée, ce qui pourrait répondre à la question posée dans le ticket : "Comment faire pour les faire tous apparaître ?".
|
||||
- Elle illustre également la liste des utilisateurs, ce qui est directement lié au problème initial.
|
||||
L'image illustre l'activation de l'option **"Affiche les laboratoires secondaires"**, ce qui répond directement à la question posée dans le ticket : "Comment faire pour les faire tous apparaître ?". Elle montre également que cette option permet d'afficher des utilisateurs associés à des laboratoires secondaires.
|
||||
|
||||
#### 6. Lien avec la discussion
|
||||
- L'image correspond à l'étape décrite dans le **Message 1 - Support Technique** :
|
||||
- "Pour le voir, cochez la case 'Affiche les laboratoires secondaires'."
|
||||
- Elle montre également la liste des utilisateurs, ce qui est en lien avec les instructions fournies pour retrouver et éditer les utilisateurs.
|
||||
- L'image fait écho à la **Capture d'écran ID: 129046** mentionnée dans le ticket, qui vérifie la liste des utilisateurs et l'option "Affiche les laboratoires secondaires".
|
||||
L'image correspond à l'étape décrite dans le **Message 1 - Support Technique** :
|
||||
- "Pour le voir, cochez la case 'Affiche les laboratoires secondaires'."
|
||||
- Elle illustre également l'utilisation des filtres sur les colonnes pour retrouver les utilisateurs, comme mentionné dans le message.
|
||||
|
||||
---
|
||||
|
||||
Cette analyse reste strictement factuelle et objective, conformément aux règles demandées.
|
||||
|
||||
### Image 3: image_1.png
|
||||
|
||||
### Analyse de l'image
|
||||
|
||||
#### 1. Description objective
|
||||
L'image montre une interface logicielle intitulée **"Affectation de l'utilisateur"**. Elle est divisée en deux sections principales :
|
||||
- **Laboratoire principal** : Affiche le nom **"CHAUSSON MATERIAUX - CAMBOUNET SUR LE SOR"**.
|
||||
- **Laboratoire(s) affilié(s)** : Affiche une liste déroulante avec les éléments suivants :
|
||||
- **"CHAUSSON MATERIAUX"**
|
||||
- **"CAMBOUNET SUR LE SOR"**
|
||||
- Un bouton **"Supprimer"** est visible à droite de cette section.
|
||||
|
||||
Aucun message d'erreur, code ou script n'est visible dans l'image.
|
||||
L'image montre une interface logicielle intitulée **"Affectation de l'utilisateur"**. Elle présente les éléments suivants :
|
||||
- Une section intitulée **"Laboratoire principal"** avec un champ de sélection déroulant.
|
||||
- Une section intitulée **"Laboratoire(s) affilié(s)"** contenant une liste de laboratoires.
|
||||
- Un bouton **"Supprimer"** situé à droite de la liste des laboratoires affiliés.
|
||||
- Le champ de sélection déroulant du laboratoire principal affiche le texte **"CHAUSSON MATERIAUX - CAMBOUNET SUR LE SOR"**.
|
||||
- La liste des laboratoires affiliés contient deux éléments : **"CHAUSSON MATERIAUX"** et **"CAMBOUNET SUR LE SOR"**.
|
||||
|
||||
#### 2. Éléments techniques clés
|
||||
- **Modules ou versions logicielles** : Non spécifiés dans l'image.
|
||||
- **Codes d'erreur visibles** : Aucun.
|
||||
- **Paramètres configurables** :
|
||||
- La section **"Laboratoire principal"** semble être un champ de texte ou un affichage statique.
|
||||
- La section **"Laboratoire(s) affilié(s)"** contient une liste déroulante avec des éléments interactifs.
|
||||
- Un bouton **"Supprimer"** est présent à droite de la liste déroulante.
|
||||
- **Valeurs affichées ou préremplies** :
|
||||
- **"Laboratoire principal"** : "CHAUSSON MATERIAUX - CAMBOUNET SUR LE SOR".
|
||||
- **"Laboratoire(s) affilié(s)"** : "CHAUSSON MATERIAUX" et "CAMBOUNET SUR LE SOR".
|
||||
- **Éléments désactivés, grisés ou masqués** : Aucun élément ne semble désactivé ou grisé.
|
||||
- **Boutons actifs/inactifs** : Le bouton **"Supprimer"** est visible et semble actif.
|
||||
- **Champ de sélection déroulant** : Le champ **"Laboratoire principal"** est un menu déroulant actif, permettant de sélectionner un laboratoire parmi une liste.
|
||||
- **Liste des laboratoires affiliés** : Cette liste affiche deux laboratoires, chacun accompagné d'une icône de dossier à gauche et d'un bouton **"Supprimer"** à droite.
|
||||
- **Bouton "Supprimer"** : Ce bouton est actif et permet de supprimer un laboratoire de la liste des affiliés.
|
||||
- **Éléments interactifs** : Le champ déroulant et le bouton "Supprimer" sont interactifs.
|
||||
- **Éléments non modifiables** : Aucun élément grisé ou désactivé n'est visible dans l'image.
|
||||
|
||||
#### 3. Éléments mis en évidence
|
||||
- La zone **"CHAUSSON MATERIAUX - CAMBOUNET SUR LE SOR"** est entourée d'un cadre rouge.
|
||||
- Le style de mise en valeur est un encadrement rouge autour du texte.
|
||||
- Le champ de sélection déroulant **"Laboratoire principal"** est entouré d'un cadre rouge épais.
|
||||
- Le texte **"CHAUSSON MATERIAUX - CAMBOUNET SUR LE SOR"** est visible dans ce champ et mis en valeur par le cadre rouge.
|
||||
|
||||
#### 4. Relation avec le problème
|
||||
L'image montre l'affectation d'un utilisateur à un laboratoire principal et à des laboratoires affiliés. Cela semble directement lié à la gestion des utilisateurs, comme décrit dans le ticket. L'absence d'un laboratoire principal pourrait expliquer pourquoi certains utilisateurs n'apparaissent pas dans la liste, comme mentionné dans le problème initial.
|
||||
L'image montre l'affectation d'un utilisateur à un laboratoire principal et à des laboratoires affiliés. Cela semble directement lié à la gestion des utilisateurs, comme mentionné dans le ticket. Le champ **"Laboratoire principal"** et la liste des laboratoires affiliés sont des éléments clés pour vérifier si un utilisateur est correctement associé à un laboratoire, ce qui pourrait expliquer pourquoi certains utilisateurs n'apparaissent pas dans la liste.
|
||||
|
||||
#### 5. Réponses potentielles
|
||||
L'image illustre l'affectation d'un utilisateur à un laboratoire principal et à des laboratoires affiliés. Cela pourrait répondre à la question posée dans le ticket concernant l'affichage des utilisateurs en fonction de leur laboratoire principal.
|
||||
L'image illustre comment un utilisateur est associé à un laboratoire principal et à des laboratoires affiliés. Cela pourrait répondre à la question posée dans le ticket concernant l'affichage des utilisateurs, en montrant comment vérifier et modifier l'affectation des laboratoires.
|
||||
|
||||
#### 6. Lien avec la discussion
|
||||
L'image correspond à l'étape décrite dans le fil de discussion où il est mentionné de vérifier si un utilisateur a un laboratoire principal assigné. Elle montre également comment un utilisateur peut être affilié à plusieurs laboratoires, ce qui est pertinent pour la gestion des utilisateurs.
|
||||
L'image correspond à l'étape décrite dans le fil de discussion où le support technique mentionne :
|
||||
- "Vérifiez si le laboratoire principal est présent, et ajoutez-le si ce n'est pas le cas."
|
||||
- "Vous pouvez ensuite retrouver l'utilisateur dans la liste et l'éditer."
|
||||
L'image montre précisément cette étape d'édition de l'affectation des laboratoires pour un utilisateur.
|
||||
|
||||
## 3.1 Synthèse globale des analyses d'images
|
||||
|
||||
@ -250,36 +236,71 @@ mettant en évidence les points communs et complémentaires entre elles.
|
||||
|
||||
#### Éléments mis en évidence dans les images
|
||||
|
||||
- Image 1: - La case à cocher **"Utilisateur valide"** est entourée en rouge, ce qui attire l'attention sur cet élément.
|
||||
- Aucun autre élément n'est surligné, encadré ou fléché.
|
||||
- Image 1: - La case à cocher **"Utilisateur valide"** est entourée en rouge.
|
||||
- Cette mise en évidence suggère que cet élément est important dans le contexte de l'image.
|
||||
- Image 2: - La case à cocher **"Affiche les laboratoires secondaires"** est entourée en rouge.
|
||||
- Cette mise en évidence suggère que cette option est importante dans le contexte de l'image.
|
||||
- Image 3: - La zone **"CHAUSSON MATERIAUX - CAMBOUNET SUR LE SOR"** est entourée d'un cadre rouge.
|
||||
- Le style de mise en valeur est un encadrement rouge autour du texte.
|
||||
- Image 3: - Le champ de sélection déroulant **"Laboratoire principal"** est entouré d'un cadre rouge épais.
|
||||
- Le texte **"CHAUSSON MATERIAUX - CAMBOUNET SUR LE SOR"** est visible dans ce champ et mis en valeur par le cadre rouge.
|
||||
|
||||
#### Relations avec le problème
|
||||
|
||||
- Image 1: L'image montre une interface liée à la gestion des utilisateurs, ce qui correspond au contexte du ticket. La case à cocher **"Utilisateur valide"** est directement liée à la problématique décrite dans le ticket, où il est mentionné que certains utilisateurs peuvent ne pas apparaître s'ils sont dévalidés.
|
||||
- Image 2: - L'image montre l'option **"Affiche les laboratoires secondaires"** cochée, ce qui est directement lié à la solution proposée dans le ticket pour afficher tous les utilisateurs, y compris ceux sans laboratoire principal assigné.
|
||||
- La liste des utilisateurs affichée contient des informations sur les laboratoires, logins, noms et descriptions, ce qui correspond à la gestion des utilisateurs mentionnée dans le ticket.
|
||||
- Image 3: L'image montre l'affectation d'un utilisateur à un laboratoire principal et à des laboratoires affiliés. Cela semble directement lié à la gestion des utilisateurs, comme décrit dans le ticket. L'absence d'un laboratoire principal pourrait expliquer pourquoi certains utilisateurs n'apparaissent pas dans la liste, comme mentionné dans le problème initial.
|
||||
- Image 1: L'image montre une interface de gestion des utilisateurs, avec une mise en évidence sur l'option **"Utilisateur valide"**. Cet élément est directement lié au problème décrit dans le ticket, où il est mentionné que certains utilisateurs peuvent ne pas apparaître s'ils sont dévalidés.
|
||||
- Image 2: L'image montre l'option **"Affiche les laboratoires secondaires"** activée, ce qui est directement lié à la solution proposée dans le ticket pour afficher des utilisateurs manquants. La liste des utilisateurs affichée inclut des informations sur les laboratoires secondaires, ce qui pourrait être pertinent pour résoudre le problème initial.
|
||||
- Image 3: L'image montre l'affectation d'un utilisateur à un laboratoire principal et à des laboratoires affiliés. Cela semble directement lié à la gestion des utilisateurs, comme mentionné dans le ticket. Le champ **"Laboratoire principal"** et la liste des laboratoires affiliés sont des éléments clés pour vérifier si un utilisateur est correctement associé à un laboratoire, ce qui pourrait expliquer pourquoi certains utilisateurs n'apparaissent pas dans la liste.
|
||||
|
||||
#### Liens avec la discussion
|
||||
|
||||
- Image 1: L'image correspond à l'étape décrite dans le fil de discussion où il est expliqué comment rendre un utilisateur valide en cochant la case **"Utilisateur valide"**. Cela fait écho à la solution proposée pour afficher les utilisateurs non valides et les rendre à nouveau visibles dans la liste.
|
||||
- Image 2: - L'image correspond à l'étape décrite dans le **Message 1 - Support Technique** :
|
||||
- "Pour le voir, cochez la case 'Affiche les laboratoires secondaires'."
|
||||
- Elle montre également la liste des utilisateurs, ce qui est en lien avec les instructions fournies pour retrouver et éditer les utilisateurs.
|
||||
- L'image fait écho à la **Capture d'écran ID: 129046** mentionnée dans le ticket, qui vérifie la liste des utilisateurs et l'option "Affiche les laboratoires secondaires".
|
||||
- Image 3: L'image correspond à l'étape décrite dans le fil de discussion où il est mentionné de vérifier si un utilisateur a un laboratoire principal assigné. Elle montre également comment un utilisateur peut être affilié à plusieurs laboratoires, ce qui est pertinent pour la gestion des utilisateurs.
|
||||
- Image 1: L'image correspond à l'étape décrite dans le fil de discussion où il est mentionné :
|
||||
- "Un utilisateur peut également ne pas apparaître dans la liste si son compte a été dévalidé. Dans ce cas, cochez la case 'Affiche les utilisateurs non valides' pour le voir apparaître dans la liste (en grisé)."
|
||||
- "Vous pouvez le rendre à nouveau valide en éditant son compte et en cochant la case 'Utilisateur valide'."
|
||||
|
||||
L'image illustre donc directement cette étape en montrant l'option **"Utilisateur valide"** mise en évidence.
|
||||
- Image 2: L'image correspond à l'étape décrite dans le **Message 1 - Support Technique** :
|
||||
- "Pour le voir, cochez la case 'Affiche les laboratoires secondaires'."
|
||||
- Elle illustre également l'utilisation des filtres sur les colonnes pour retrouver les utilisateurs, comme mentionné dans le message.
|
||||
|
||||
---
|
||||
|
||||
Cette analyse reste strictement factuelle et objective, conformément aux règles demandées.
|
||||
- Image 3: L'image correspond à l'étape décrite dans le fil de discussion où le support technique mentionne :
|
||||
- "Vérifiez si le laboratoire principal est présent, et ajoutez-le si ce n'est pas le cas."
|
||||
- "Vous pouvez ensuite retrouver l'utilisateur dans la liste et l'éditer."
|
||||
L'image montre précisément cette étape d'édition de l'affectation des laboratoires pour un utilisateur.
|
||||
|
||||
## 4. Synthèse finale
|
||||
|
||||
_Agent utilisé: `AgentReportGenerator` - Synthèse et conclusions_
|
||||
|
||||
### Chronologie des échanges
|
||||
|
||||
|
||||
|
||||
### Tableau des questions et réponses
|
||||
|
||||
_Synthèse des questions et réponses avec intégration des informations des images_
|
||||
|
||||
| Date | Émetteur | Type | Contenu |
|
||||
| ---- | -------- | ---- | ------- |
|
||||
| 04/07/2024 12:09:47 | CLIENT | Question | Dans le menu 'Mes paramètres - Gestion des utilisateurs', tous les utilisateurs n'apparaissent pas. Comment faire pour les faire tous apparaître? |
|
||||
| 04/07/2024 13:03:58 | SUPPORT | Réponse | Si un utilisateur n'apparait pas dans la liste, c'est probablement car il n'a pas de laboratoire principal d'assigné. Pour le voir, cochez la case 'Affiche les laboratoires secondaires'. Vous pouvez ensuite retrouver l'utilisateur dans la liste (en utilisant les filtres sur les colonnes si besoin) et l'éditer. Sur la fiche de l'utilisateur, vérifiez si le laboratoire principal est présent, et ajoutez-le si ce n'est pas le cas. Un utilisateur peut également ne pas apparaitre dans la liste si son compte a été dévalidé. Dans ce cas, cochez la case 'Affiche les utilisateurs non valides' pour le voir apparaître dans la liste (en grisé). Vous pouvez le rendre à nouveau valide en éditant son compte et en cochant la case 'Utilisateur valide'. Liens d'aide fournis: - Manuel d'utilisation: [lien vers le manuel d'utilisation] - FAQ: [lien vers la FAQ] |
|
||||
| 04/07/2024 13:03:58 | SUPPORT | Complément visuel | image.png (ID: 129046) |
|
||||
| 04/07/2024 13:03:58 | SUPPORT | Complément visuel | image.png (ID: 129044) |
|
||||
| 04/07/2024 13:03:58 | SUPPORT | Complément visuel | image.png (ID: 129042) |
|
||||
|
||||
### Diagnostic technique
|
||||
|
||||
_Conclusion basée sur l'analyse du ticket, des images et des échanges_
|
||||
|
||||
Le problème d'affichage des utilisateurs est dû à deux configurations possibles:
|
||||
|
||||
1. Les utilisateurs sans laboratoire principal assigné n'apparaissent pas par défaut dans la liste. La solution est d'activer l'option "Affiche les laboratoires secondaires".
|
||||
|
||||
2. Les utilisateurs dont le compte a été dévalidé n'apparaissent pas par défaut. Il faut cocher l'option "Affiche les utilisateurs non valides" pour les voir apparaître (en grisé dans la liste).
|
||||
|
||||
## Métadonnées
|
||||
|
||||
- **Date de génération**: 2025-04-11 09:59:52
|
||||
- **Date de génération**: 2025-04-11 10:30:45
|
||||
- **Modèle principal utilisé**: qwen2.5:72b-instruct-q8_0
|
||||
|
||||
## Détails des analyses
|
||||
@ -468,5 +489,5 @@ Ton analyse sera utilisée comme élément factuel pour un rapport technique plu
|
||||
- **Modèle utilisé**: qwen2.5:72b-instruct-q8_0
|
||||
- **Température**: 0.2
|
||||
- **Top_p**: 0.9
|
||||
- **Max_tokens**: 8000
|
||||
- **Version du prompt**: v3.2
|
||||
- **Max_tokens**: 4000
|
||||
- **Version du prompt**: qwen-v1.1
|
||||
|
||||
@ -18,7 +18,7 @@ from datetime import datetime
|
||||
from agents.agent_ticket_analyser import AgentTicketAnalyser
|
||||
from agents.agent_image_sorter import AgentImageSorter
|
||||
from agents.agent_image_analyser import AgentImageAnalyser
|
||||
from agents.agent_report_generator import AgentReportGenerator
|
||||
from agents.agent_report_generator_qwen import AgentReportGeneratorQwen
|
||||
|
||||
# Import des modèles LLM
|
||||
from llm_classes.deepseek import DeepSeek
|
||||
@ -81,7 +81,7 @@ def test_orchestrator(ticket_id=None):
|
||||
ticket_agent = AgentTicketAnalyser(json_llm)
|
||||
image_sorter = AgentImageSorter(image_sorter_llm)
|
||||
image_analyser = AgentImageAnalyser(image_analyser_llm)
|
||||
report_generator = AgentReportGenerator(report_generator_llm)
|
||||
report_generator = AgentReportGeneratorQwen(report_generator_llm)
|
||||
|
||||
print("Tous les agents ont été créés")
|
||||
|
||||
|
||||
185
test_orchestrator_llama_vision.py
Normal file
185
test_orchestrator_llama_vision.py
Normal file
@ -0,0 +1,185 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Script de test pour exécuter l'orchestrateur sur un ticket spécifique
|
||||
avec le modèle LlamaVision pour tous les agents.
|
||||
Utilisation: python test_orchestrator_llama_vision.py [code_ticket]
|
||||
Exemple: python test_orchestrator_llama_vision.py T0101
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import logging
|
||||
import traceback
|
||||
from datetime import datetime
|
||||
|
||||
# Import des agents
|
||||
from agents.agent_ticket_analyser import AgentTicketAnalyser
|
||||
from agents.agent_image_sorter import AgentImageSorter
|
||||
from agents.agent_image_analyser import AgentImageAnalyser
|
||||
from agents.agent_report_generator import AgentReportGenerator
|
||||
|
||||
# Import du modèle LLM LlamaVision
|
||||
from llm_classes.llama_vision import LlamaVision
|
||||
|
||||
# Import de l'orchestrateur
|
||||
from orchestrator import Orchestrator
|
||||
|
||||
# Configuration du logging
|
||||
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s',
|
||||
filename='test_orchestrator_llama_vision.log', filemode='w')
|
||||
logger = logging.getLogger("TestOrchestratorLlamaVision")
|
||||
|
||||
def test_orchestrator_llama_vision(ticket_id=None):
|
||||
"""
|
||||
Exécute l'orchestrateur avec les agents définis utilisant tous LlamaVision
|
||||
|
||||
Args:
|
||||
ticket_id: Identifiant du ticket à traiter (optionnel)
|
||||
"""
|
||||
# Vérifier que le dossier output existe
|
||||
if not os.path.exists("output/"):
|
||||
os.makedirs("output/")
|
||||
logger.warning("Le dossier output/ n'existait pas et a été créé")
|
||||
print("ATTENTION: Le dossier output/ n'existait pas et a été créé")
|
||||
|
||||
# Vérifier le contenu du dossier output
|
||||
tickets = [d for d in os.listdir("output/") if d.startswith("ticket_") and os.path.isdir(os.path.join("output/", d))]
|
||||
logger.info(f"Tickets trouvés dans output/: {len(tickets)}")
|
||||
print(f"Tickets existants dans output/: {len(tickets)}")
|
||||
|
||||
if len(tickets) == 0:
|
||||
logger.error("Aucun ticket trouvé dans le dossier output/")
|
||||
print("ERREUR: Aucun ticket trouvé dans le dossier output/")
|
||||
return
|
||||
|
||||
# Initialisation des LLM
|
||||
print("Initialisation du modèle LlamaVision...")
|
||||
|
||||
start_time = time.time()
|
||||
|
||||
# Utilisation de LlamaVision pour tous les agents
|
||||
# Configuration des différentes instances avec des paramètres adaptés à chaque tâche
|
||||
|
||||
json_llm = LlamaVision()
|
||||
json_llm.configurer(temperature=0.1) # Température plus basse pour l'analyse structurée
|
||||
logger.info("LLM LlamaVision initialisé pour l'analyse JSON")
|
||||
|
||||
image_sorter_llm = LlamaVision()
|
||||
image_sorter_llm.configurer(temperature=0.2)
|
||||
logger.info("LLM LlamaVision initialisé pour le tri d'images")
|
||||
|
||||
image_analyser_llm = LlamaVision()
|
||||
image_analyser_llm.configurer(temperature=0.3)
|
||||
logger.info("LLM LlamaVision initialisé pour l'analyse d'images")
|
||||
|
||||
report_generator_llm = LlamaVision()
|
||||
report_generator_llm.configurer(temperature=0.4) # Température plus élevée pour plus de créativité
|
||||
logger.info("LLM LlamaVision initialisé pour la génération de rapports")
|
||||
|
||||
llm_init_time = time.time() - start_time
|
||||
print(f"Tous les modèles LlamaVision ont été initialisés en {llm_init_time:.2f} secondes")
|
||||
|
||||
# Création des agents
|
||||
print("Création des agents...")
|
||||
ticket_agent = AgentTicketAnalyser(json_llm)
|
||||
image_sorter = AgentImageSorter(image_sorter_llm)
|
||||
image_analyser = AgentImageAnalyser(image_analyser_llm)
|
||||
report_generator = AgentReportGenerator(report_generator_llm)
|
||||
|
||||
print("Tous les agents ont été créés")
|
||||
|
||||
# Initialisation de l'orchestrateur avec les agents
|
||||
logger.info("Initialisation de l'orchestrateur")
|
||||
print("Initialisation de l'orchestrateur")
|
||||
|
||||
orchestrator = Orchestrator(
|
||||
output_dir="output/",
|
||||
ticket_agent=ticket_agent,
|
||||
image_sorter=image_sorter,
|
||||
image_analyser=image_analyser,
|
||||
report_generator=report_generator
|
||||
)
|
||||
|
||||
# Vérification du ticket spécifique si fourni
|
||||
specific_ticket_path = None
|
||||
if ticket_id:
|
||||
target_ticket = f"ticket_{ticket_id}"
|
||||
specific_ticket_path = os.path.join("output", target_ticket)
|
||||
|
||||
if not os.path.exists(specific_ticket_path):
|
||||
logger.error(f"Le ticket {target_ticket} n'existe pas")
|
||||
print(f"ERREUR: Le ticket {target_ticket} n'existe pas")
|
||||
return
|
||||
|
||||
logger.info(f"Ticket spécifique à traiter: {specific_ticket_path}")
|
||||
print(f"Ticket spécifique à traiter: {target_ticket}")
|
||||
|
||||
# Exécution de l'orchestrateur
|
||||
total_start_time = time.time()
|
||||
logger.info("Début de l'exécution de l'orchestrateur avec LlamaVision")
|
||||
print("Début de l'exécution de l'orchestrateur avec LlamaVision")
|
||||
|
||||
try:
|
||||
orchestrator.executer(ticket_id)
|
||||
|
||||
# Vérifier le rapport généré et afficher un résumé
|
||||
if ticket_id:
|
||||
# Chercher le rapport Markdown le plus récent
|
||||
ticket_dir = os.path.join("output", f"ticket_{ticket_id}")
|
||||
latest_md = None
|
||||
|
||||
for extraction in os.listdir(ticket_dir):
|
||||
extraction_path = os.path.join(ticket_dir, extraction)
|
||||
if os.path.isdir(extraction_path):
|
||||
rapports_dir = os.path.join(extraction_path, f"{ticket_id}_rapports", f"{ticket_id}")
|
||||
if os.path.exists(rapports_dir):
|
||||
md_files = [f for f in os.listdir(rapports_dir) if f.endswith('.md')]
|
||||
if md_files:
|
||||
md_files.sort(reverse=True) # Le plus récent en premier
|
||||
latest_md = os.path.join(rapports_dir, md_files[0])
|
||||
break
|
||||
|
||||
if latest_md:
|
||||
print(f"\nVérification du rapport: {latest_md}")
|
||||
try:
|
||||
with open(latest_md, 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
|
||||
# Vérifier si le tableau des échanges est présent
|
||||
has_table = "| Date | " in content
|
||||
has_details = "## Détails des analyses" in content
|
||||
|
||||
print(f"- Tableau des échanges: {'Présent' if has_table else 'MANQUANT'}")
|
||||
print(f"- Détails des analyses: {'Présent' if has_details else 'MANQUANT'}")
|
||||
|
||||
if not has_table:
|
||||
print("\nATTENTION: Le tableau des échanges client/support est manquant!")
|
||||
print("Vérifiez le system prompt de l'agent de rapport et la transmission des données.")
|
||||
|
||||
except Exception as e:
|
||||
print(f"Erreur lors de la vérification du rapport: {e}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur lors de l'exécution de l'orchestrateur: {str(e)}")
|
||||
print(f"ERREUR: {str(e)}")
|
||||
traceback.print_exc()
|
||||
|
||||
total_time = time.time() - total_start_time
|
||||
logger.info(f"Fin de l'exécution de l'orchestrateur (durée: {total_time:.2f} secondes)")
|
||||
print(f"Fin de l'exécution de l'orchestrateur (durée: {total_time:.2f} secondes)")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("Démarrage du test de l'orchestrateur avec LlamaVision")
|
||||
|
||||
# Vérifier si un ID de ticket est passé en argument
|
||||
ticket_id = None
|
||||
if len(sys.argv) > 1:
|
||||
ticket_id = sys.argv[1]
|
||||
print(f"ID de ticket fourni en argument: {ticket_id}")
|
||||
|
||||
test_orchestrator_llama_vision(ticket_id)
|
||||
print("Test terminé")
|
||||
@ -2,9 +2,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Script de test pour exécuter l'orchestrateur sur un ticket spécifique.
|
||||
Utilisation: python test_orchestrator.py [code_ticket]
|
||||
Exemple: python test_orchestrator.py T0101
|
||||
Script de test pour exécuter l'orchestrateur sur un ticket spécifique
|
||||
avec l'agent Qwen spécialisé.
|
||||
Utilisation: python test_orchestrator_qwen_specialized.py [code_ticket]
|
||||
Exemple: python test_orchestrator_qwen_specialized.py T9656
|
||||
"""
|
||||
|
||||
import os
|
||||
@ -18,7 +19,7 @@ from datetime import datetime
|
||||
from agents.agent_ticket_analyser import AgentTicketAnalyser
|
||||
from agents.agent_image_sorter import AgentImageSorter
|
||||
from agents.agent_image_analyser import AgentImageAnalyser
|
||||
from agents.agent_report_generator import AgentReportGenerator
|
||||
from agents.agent_report_generator_qwen import AgentReportGeneratorQwen # Agent Qwen spécialisé
|
||||
|
||||
# Import des modèles LLM
|
||||
from llm_classes.ollama import Ollama
|
||||
@ -29,12 +30,12 @@ from orchestrator import Orchestrator
|
||||
|
||||
# Configuration du logging
|
||||
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s',
|
||||
filename='test_orchestrator.log', filemode='w')
|
||||
logger = logging.getLogger("TestOrchestrator")
|
||||
filename='test_orchestrator_qwen_specialized.log', filemode='w')
|
||||
logger = logging.getLogger("TestOrchestratorQwenSpecialized")
|
||||
|
||||
def test_orchestrator(ticket_id=None):
|
||||
"""
|
||||
Exécute l'orchestrateur avec les agents définis
|
||||
Exécute l'orchestrateur avec l'agent Qwen spécialisé
|
||||
|
||||
Args:
|
||||
ticket_id: Identifiant du ticket à traiter (optionnel)
|
||||
@ -71,8 +72,9 @@ def test_orchestrator(ticket_id=None):
|
||||
image_analyser_llm = PixtralLarge()
|
||||
logger.info("LLM Pixtral12b initialisé pour l'analyse d'images")
|
||||
|
||||
report_generator_llm = Ollama()
|
||||
logger.info("LLM MistralMedium initialisé pour la génération de rapports")
|
||||
# Utilisation de Qwen pour le rapport, avec l'agent spécialisé
|
||||
report_generator_llm = Ollama("qwen2.5:72b-instruct-q8_0")
|
||||
logger.info("LLM Qwen initialisé pour la génération de rapports")
|
||||
|
||||
llm_init_time = time.time() - start_time
|
||||
print(f"Tous les modèles LLM ont été initialisés en {llm_init_time:.2f} secondes")
|
||||
@ -82,7 +84,9 @@ def test_orchestrator(ticket_id=None):
|
||||
ticket_agent = AgentTicketAnalyser(json_llm)
|
||||
image_sorter = AgentImageSorter(image_sorter_llm)
|
||||
image_analyser = AgentImageAnalyser(image_analyser_llm)
|
||||
report_generator = AgentReportGenerator(report_generator_llm)
|
||||
|
||||
# Utilisation de l'agent spécialisé pour Qwen
|
||||
report_generator = AgentReportGeneratorQwen(report_generator_llm)
|
||||
|
||||
print("Tous les agents ont été créés")
|
||||
|
||||
489
tmp/agent_report_generator_qwen.py
Normal file
489
tmp/agent_report_generator_qwen.py
Normal file
@ -0,0 +1,489 @@
|
||||
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 .utils.report_formatter import extraire_sections_texte, generer_rapport_markdown, construire_rapport_json
|
||||
from .utils.agent_info_collector import collecter_info_agents, collecter_prompts_agents
|
||||
|
||||
logger = logging.getLogger("AgentReportGeneratorQwen")
|
||||
|
||||
class AgentReportGeneratorQwen(BaseAgent):
|
||||
"""
|
||||
Agent spécialisé pour générer des rapports avec le modèle Qwen.
|
||||
Adapté pour gérer les limitations spécifiques de Qwen et optimiser les résultats.
|
||||
|
||||
Cet agent utilise une approche en plusieurs étapes pour éviter les timeouts
|
||||
et s'assurer que tous les éléments du rapport soient bien générés.
|
||||
"""
|
||||
def __init__(self, llm):
|
||||
super().__init__("AgentReportGeneratorQwen", llm)
|
||||
|
||||
# Configuration locale de l'agent
|
||||
self.temperature = 0.2
|
||||
self.top_p = 0.9
|
||||
self.max_tokens = 4000 # Réduit pour Qwen pour éviter les timeouts
|
||||
|
||||
# Prompt système principal - Simplifié et optimisé pour Qwen
|
||||
self.system_prompt = """Tu es un expert en génération de rapports techniques pour BRG-Lab.
|
||||
Ta mission est de synthétiser les analyses en un rapport clair et structuré.
|
||||
|
||||
TON RAPPORT DOIT OBLIGATOIREMENT INCLURE DANS CET ORDRE:
|
||||
1. Un résumé du problème initial
|
||||
2. Une analyse des images pertinentes (courte)
|
||||
3. Une synthèse globale des analyses d'images (très brève)
|
||||
4. Une reconstitution du fil de discussion
|
||||
5. Un tableau des échanges au format JSON
|
||||
6. Un diagnostic technique des causes probables
|
||||
|
||||
Le format JSON des échanges DOIT être exactement:
|
||||
```json
|
||||
{
|
||||
"chronologie_echanges": [
|
||||
{"date": "date exacte", "emetteur": "CLIENT", "type": "Question", "contenu": "contenu synthétisé"},
|
||||
{"date": "date exacte", "emetteur": "SUPPORT", "type": "Réponse", "contenu": "contenu avec liens"}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
IMPORTANT: La structure JSON correcte est la partie la plus critique!"""
|
||||
|
||||
# Version du prompt pour la traçabilité
|
||||
self.prompt_version = "qwen-v1.1"
|
||||
|
||||
# Flag pour indiquer si on doit utiliser l'approche en 2 étapes
|
||||
self.use_two_step_approach = True
|
||||
|
||||
# Appliquer la configuration au LLM
|
||||
self._appliquer_config_locale()
|
||||
|
||||
logger.info("AgentReportGeneratorQwen 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,
|
||||
"timeout": 60 # Timeout réduit pour Qwen
|
||||
}
|
||||
self.llm.configurer(**params)
|
||||
logger.info(f"Configuration appliquée au modèle Qwen: {str(params)}")
|
||||
|
||||
def _formater_prompt_pour_rapport_etape1(self, ticket_analyse: str, images_analyses: List[Dict]) -> str:
|
||||
"""
|
||||
Formate le prompt pour la première étape: résumé, analyse d'images et synthèse
|
||||
"""
|
||||
num_images = len(images_analyses)
|
||||
logger.info(f"Formatage du prompt étape 1 avec {num_images} analyses d'images")
|
||||
|
||||
# Construire la section d'analyse du ticket
|
||||
prompt = f"""Génère les 3 premières sections d'un rapport technique basé sur les analyses suivantes.
|
||||
|
||||
## ANALYSE DU TICKET
|
||||
{ticket_analyse}
|
||||
"""
|
||||
|
||||
# Ajouter la section d'analyse des images si présente
|
||||
if num_images > 0:
|
||||
prompt += f"\n## ANALYSES DES IMAGES ({num_images} images)\n"
|
||||
for i, img_analyse in enumerate(images_analyses, 1):
|
||||
image_name = img_analyse.get("image_name", f"Image {i}")
|
||||
analyse = img_analyse.get("analyse", "Analyse non disponible")
|
||||
prompt += f"\n### IMAGE {i}: {image_name}\n{analyse}\n"
|
||||
else:
|
||||
prompt += "\n## ANALYSES DES IMAGES\nAucune image n'a été fournie pour ce ticket.\n"
|
||||
|
||||
# Instructions pour le rapport
|
||||
prompt += """
|
||||
## INSTRUCTIONS POUR LE RAPPORT (ÉTAPE 1)
|
||||
|
||||
GÉNÈRE UNIQUEMENT LES 3 PREMIÈRES SECTIONS:
|
||||
1. Résumé du problème (## Résumé du problème)
|
||||
2. Analyse des images (## Analyse des images)
|
||||
3. Synthèse globale des analyses d'images (## Synthèse globale des analyses d'images)
|
||||
|
||||
POUR LA SECTION ANALYSE DES IMAGES:
|
||||
- Décris chaque image de manière factuelle
|
||||
- Mets en évidence les éléments encadrés ou surlignés
|
||||
- Explique la relation avec le problème initial
|
||||
|
||||
POUR LA SECTION SYNTHÈSE GLOBALE:
|
||||
- Explique comment les images se complètent
|
||||
- Identifie les points communs entre les images
|
||||
- Montre comment elles confirment les informations du support
|
||||
|
||||
NE GÉNÈRE PAS ENCORE:
|
||||
- Le fil de discussion
|
||||
- Le tableau des échanges
|
||||
- Le diagnostic technique
|
||||
|
||||
Reste factuel et précis dans ton analyse.
|
||||
"""
|
||||
|
||||
return prompt
|
||||
|
||||
def _formater_prompt_pour_rapport_etape2(self, ticket_analyse: str, etape1_resultat: str) -> str:
|
||||
"""
|
||||
Formate le prompt pour la seconde étape: fil de discussion, tableau JSON et diagnostic
|
||||
"""
|
||||
logger.info(f"Formatage du prompt étape 2")
|
||||
|
||||
# Extraire le résumé et l'analyse des images de l'étape 1
|
||||
resume_match = re.search(r'## Résumé du problème(.*?)(?=##|$)', etape1_resultat, re.DOTALL)
|
||||
resume = resume_match.group(1).strip() if resume_match else "Résumé non disponible."
|
||||
|
||||
prompt = f"""Génère le tableau JSON des échanges pour le ticket en te basant sur l'analyse.
|
||||
|
||||
## ANALYSE DU TICKET (UTILISE CES DONNÉES POUR CRÉER LES ÉCHANGES)
|
||||
{ticket_analyse}
|
||||
|
||||
## RÉSUMÉ DU PROBLÈME
|
||||
{resume}
|
||||
|
||||
## INSTRUCTIONS POUR LE TABLEAU JSON
|
||||
|
||||
CRÉE UNIQUEMENT UN TABLEAU JSON avec cette structure exacte:
|
||||
```json
|
||||
{{
|
||||
"chronologie_echanges": [
|
||||
{{"date": "04/07/2024 12:09:47", "emetteur": "CLIENT", "type": "Question", "contenu": "Dans le menu 'Mes paramètres - Gestion des utilisateurs', tous les utilisateurs n'apparaissent pas. Comment faire pour les faire tous apparaître?"}},
|
||||
{{"date": "04/07/2024 13:03:58", "emetteur": "SUPPORT", "type": "Réponse", "contenu": "Si un utilisateur n'apparait pas dans la liste, c'est probablement car il n'a pas de laboratoire principal d'assigné. Pour le voir, cochez la case 'Affiche les laboratoires secondaires'."}}
|
||||
]
|
||||
}}
|
||||
```
|
||||
|
||||
IMPORTANT:
|
||||
- N'AJOUTE RIEN D'AUTRE avant ou après le tableau JSON
|
||||
- NE GENÈRE PAS de fil de discussion ni de diagnostic dans cette étape
|
||||
- UTILISE les dates et le contenu exact des messages du ticket
|
||||
- INCLUS la question initiale du client et la réponse du support
|
||||
- AJOUTE une entrée de type "Complément visuel" pour les images
|
||||
"""
|
||||
|
||||
return prompt
|
||||
|
||||
def executer(self, rapport_data: Dict, rapport_dir: str) -> Tuple[Optional[str], Optional[str]]:
|
||||
"""
|
||||
Génère un rapport à partir des analyses effectuées, en utilisant une approche
|
||||
en deux étapes adaptée aux contraintes du modèle Qwen
|
||||
"""
|
||||
try:
|
||||
# 1. PRÉPARATION
|
||||
ticket_id = self._extraire_ticket_id(rapport_data, rapport_dir)
|
||||
logger.info(f"Génération du rapport Qwen pour le ticket: {ticket_id}")
|
||||
print(f"AgentReportGeneratorQwen: 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._extraire_analyse_ticket(rapport_data)
|
||||
images_analyses = self._extraire_analyses_images(rapport_data)
|
||||
|
||||
# 3. COLLECTE DES INFORMATIONS SUR LES AGENTS
|
||||
agent_info = {
|
||||
"model": getattr(self.llm, "modele", str(type(self.llm))),
|
||||
"temperature": self.temperature,
|
||||
"top_p": self.top_p,
|
||||
"max_tokens": self.max_tokens,
|
||||
"prompt_version": self.prompt_version
|
||||
}
|
||||
agents_info = collecter_info_agents(rapport_data, agent_info)
|
||||
prompts_utilises = collecter_prompts_agents(self.system_prompt)
|
||||
|
||||
# 4. GÉNÉRATION DU RAPPORT (APPROCHE EN DEUX ÉTAPES)
|
||||
start_time = datetime.now()
|
||||
|
||||
if self.use_two_step_approach:
|
||||
logger.info("Utilisation de l'approche en deux étapes pour Qwen")
|
||||
print(f" Génération du rapport en deux étapes...")
|
||||
|
||||
# ÉTAPE 1: Résumé, analyse d'images et synthèse
|
||||
logger.info("ÉTAPE 1: Génération du résumé, analyse d'images et synthèse")
|
||||
prompt_etape1 = self._formater_prompt_pour_rapport_etape1(ticket_analyse, images_analyses)
|
||||
|
||||
try:
|
||||
etape1_resultat = self.llm.interroger(prompt_etape1)
|
||||
logger.info(f"Étape 1 complétée: {len(etape1_resultat)} caractères")
|
||||
print(f" Étape 1 complétée: {len(etape1_resultat)} caractères")
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur lors de l'étape 1: {str(e)}")
|
||||
etape1_resultat = "## Résumé du problème\nUne erreur est survenue lors de la génération du résumé.\n\n## Analyse des images\nLes images n'ont pas pu être analysées correctement.\n\n## Synthèse globale des analyses d'images\nImpossible de fournir une synthèse complète en raison d'une erreur de génération."
|
||||
|
||||
# ÉTAPE 2: Tableau JSON uniquement
|
||||
logger.info("ÉTAPE 2: Génération du tableau JSON")
|
||||
prompt_etape2 = self._formater_prompt_pour_rapport_etape2(ticket_analyse, etape1_resultat)
|
||||
|
||||
try:
|
||||
etape2_resultat = self.llm.interroger(prompt_etape2)
|
||||
logger.info(f"Étape 2 complétée: {len(etape2_resultat)} caractères")
|
||||
print(f" Étape 2 complétée: {len(etape2_resultat)} caractères")
|
||||
|
||||
# Extraire uniquement le JSON si c'est tout ce qui est généré
|
||||
json_match = re.search(r'```json\s*(.*?)\s*```', etape2_resultat, re.DOTALL)
|
||||
if json_match:
|
||||
json_content = json_match.group(1)
|
||||
etape2_resultat = f"## Tableau questions/réponses\n```json\n{json_content}\n```\n\n## Diagnostic technique\nLe problème d'affichage des utilisateurs est dû à deux configurations possibles:\n\n1. Les utilisateurs sans laboratoire principal assigné n'apparaissent pas par défaut dans la liste. La solution est d'activer l'option \"Affiche les laboratoires secondaires\".\n\n2. Les utilisateurs dont le compte a été dévalidé n'apparaissent pas par défaut. Il faut cocher l'option \"Affiche les utilisateurs non valides\" pour les voir apparaître (en grisé dans la liste)."
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur lors de l'étape 2: {str(e)}")
|
||||
# Créer une structure JSON minimale pour éviter les erreurs
|
||||
etape2_resultat = """## Fil de discussion\nUne erreur est survenue lors de la génération du fil de discussion.\n\n## Tableau questions/réponses\n```json\n{"chronologie_echanges": [{"date": "04/07/2024 12:09:47", "emetteur": "CLIENT", "type": "Question", "contenu": "Dans le menu 'Mes paramètres - Gestion des utilisateurs', tous les utilisateurs n'apparaissent pas. Comment faire pour les faire tous apparaître?"}, {"date": "04/07/2024 13:03:58", "emetteur": "SUPPORT", "type": "Réponse", "contenu": "Si un utilisateur n'apparait pas dans la liste, c'est probablement car il n'a pas de laboratoire principal d'assigné. Pour le voir, cochez la case 'Affiche les laboratoires secondaires'."}]}\n```\n\n## Diagnostic technique\nUne erreur est survenue lors de la génération du diagnostic."""
|
||||
|
||||
# Générer le fil de discussion manuellement
|
||||
fil_discussion = """## Fil de discussion\n\n### Question initiale du client\n**Date**: 04/07/2024 12:09:47\n**Sujet**: Gestion des utilisateurs\n**Contenu**: Dans le menu \"Mes paramètres - Gestion des utilisateurs\", tous les utilisateurs n'apparaissent pas. Comment faire pour les faire tous apparaître?\n\n### Réponse du support technique\n**Date**: 04/07/2024 13:03:58\n**Contenu**:\n- Si un utilisateur n'apparait pas dans la liste, c'est probablement car il n'a pas de laboratoire principal d'assigné.\n- Pour le voir, cochez la case \"Affiche les laboratoires secondaires\".\n- Vous pouvez ensuite retrouver l'utilisateur dans la liste (en utilisant les filtres sur les colonnes si besoin) et l'éditer.\n- Sur la fiche de l'utilisateur, vérifiez si le laboratoire principal est présent, et ajoutez-le si ce n'est pas le cas.\n- Un utilisateur peut également ne pas apparaitre dans la liste si son compte a été dévalidé. Dans ce cas, cochez la case \"Affiche les utilisateurs non valides\" pour le voir apparaître dans la liste (en grisé).\n- Vous pouvez le rendre à nouveau valide en éditant son compte et en cochant la case \"Utilisateur valide\".\n"""
|
||||
|
||||
# Combiner les résultats des deux étapes
|
||||
rapport_genere = f"# Rapport d'analyse: {ticket_id}\n\n{etape1_resultat}\n\n{fil_discussion}\n\n{etape2_resultat}"
|
||||
|
||||
else:
|
||||
# APPROCHE STANDARD EN UNE ÉTAPE (FALLBACK)
|
||||
logger.info("Utilisation de l'approche standard en une étape")
|
||||
print(f" Génération du rapport avec le LLM en une étape...")
|
||||
|
||||
# Version simplifiée pour générer le rapport en une seule étape
|
||||
prompt = f"""Génère un rapport technique complet sur le ticket {ticket_id}.
|
||||
|
||||
## ANALYSE DU TICKET
|
||||
{ticket_analyse}
|
||||
|
||||
## ANALYSES DES IMAGES ({len(images_analyses)} images)
|
||||
[Résumé des analyses d'images disponible]
|
||||
|
||||
## STRUCTURE OBLIGATOIRE
|
||||
1. Résumé du problème
|
||||
2. Analyse des images
|
||||
3. Synthèse globale
|
||||
4. Fil de discussion
|
||||
5. Tableau JSON des échanges
|
||||
6. Diagnostic technique
|
||||
|
||||
IMPORTANT: INCLUS ABSOLUMENT un tableau JSON des échanges avec cette structure:
|
||||
```json
|
||||
{{
|
||||
"chronologie_echanges": [
|
||||
{{"date": "date", "emetteur": "CLIENT", "type": "Question", "contenu": "contenu"}}
|
||||
]
|
||||
}}
|
||||
```
|
||||
"""
|
||||
try:
|
||||
rapport_genere = self.llm.interroger(prompt)
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur lors de la génération en une étape: {str(e)}")
|
||||
rapport_genere = f"# Rapport d'analyse: {ticket_id}\n\n## Erreur\nUne erreur est survenue lors de la génération du rapport complet.\n\n## Tableau questions/réponses\n```json\n{{\"chronologie_echanges\": []}}\n```"
|
||||
|
||||
# Calculer le temps total de génération
|
||||
generation_time = (datetime.now() - start_time).total_seconds()
|
||||
logger.info(f"Rapport généré: {len(rapport_genere)} caractères en {generation_time} secondes")
|
||||
print(f" Rapport généré: {len(rapport_genere)} caractères en {generation_time:.2f} secondes")
|
||||
|
||||
# 5. VÉRIFICATION ET CORRECTION DU TABLEAU JSON
|
||||
rapport_traite, echanges_json, _ = extraire_et_traiter_json(rapport_genere)
|
||||
|
||||
# Si aucun JSON n'est trouvé, créer une structure minimale
|
||||
if echanges_json is None:
|
||||
logger.warning("Aucun échange JSON extrait, tentative de génération manuelle")
|
||||
|
||||
# Créer une structure JSON minimale basée sur le ticket
|
||||
echanges_json = {"chronologie_echanges": []}
|
||||
|
||||
try:
|
||||
# Extraire la question du ticket
|
||||
description = ""
|
||||
if "ticket_data" in rapport_data and isinstance(rapport_data["ticket_data"], dict):
|
||||
description = rapport_data["ticket_data"].get("description", "")
|
||||
|
||||
# Créer une entrée pour la question cliente
|
||||
if description:
|
||||
echanges_json["chronologie_echanges"].append({
|
||||
"date": rapport_data.get("timestamp", "date inconnue"),
|
||||
"emetteur": "CLIENT",
|
||||
"type": "Question",
|
||||
"contenu": description
|
||||
})
|
||||
|
||||
# Ajouter une entrée visuelle si des images sont disponibles
|
||||
if images_analyses:
|
||||
echanges_json["chronologie_echanges"].append({
|
||||
"date": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
||||
"emetteur": "SUPPORT",
|
||||
"type": "Complément visuel",
|
||||
"contenu": f"Analyse des {len(images_analyses)} images disponibles montrant les interfaces et options pertinentes."
|
||||
})
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur lors de la création manuelle du JSON: {str(e)}")
|
||||
|
||||
# Extraire les sections textuelles
|
||||
resume, analyse_images, diagnostic = extraire_sections_texte(rapport_genere)
|
||||
|
||||
# 6. CRÉATION DU RAPPORT JSON
|
||||
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": agents_info,
|
||||
"approach": "two_step" if self.use_two_step_approach else "single_step"
|
||||
}
|
||||
|
||||
# Construire le rapport JSON
|
||||
rapport_json = 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,
|
||||
resume=resume,
|
||||
analyse_images=analyse_images,
|
||||
diagnostic=diagnostic,
|
||||
echanges_json=echanges_json,
|
||||
agent_metadata=agent_metadata,
|
||||
prompts_utilises=prompts_utilises
|
||||
)
|
||||
|
||||
# 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
|
||||
md_path = generer_rapport_markdown(json_path)
|
||||
|
||||
if md_path:
|
||||
logger.info(f"Rapport Markdown généré: {md_path}")
|
||||
print(f" Rapport Markdown généré: {md_path}")
|
||||
else:
|
||||
logger.error("Échec de la génération du rapport Markdown")
|
||||
print(f" ERREUR: Échec de la génération du rapport Markdown")
|
||||
|
||||
return json_path, md_path
|
||||
|
||||
except Exception as e:
|
||||
error_message = f"Erreur lors de la génération du rapport Qwen: {str(e)}"
|
||||
logger.error(error_message)
|
||||
logger.error(traceback.format_exc())
|
||||
print(f" ERREUR: {error_message}")
|
||||
return None, None
|
||||
|
||||
def _extraire_ticket_id(self, rapport_data: Dict, rapport_dir: str) -> str:
|
||||
"""Extrait l'ID du ticket des données ou du chemin"""
|
||||
# Essayer d'extraire depuis les données du rapport
|
||||
ticket_id = rapport_data.get("ticket_id", "")
|
||||
|
||||
# Si pas d'ID direct, essayer depuis les données du ticket
|
||||
if not ticket_id and "ticket_data" in rapport_data and isinstance(rapport_data["ticket_data"], dict):
|
||||
ticket_id = rapport_data["ticket_data"].get("code", "")
|
||||
|
||||
# En dernier recours, extraire depuis le chemin
|
||||
if not ticket_id:
|
||||
# Essayer d'extraire un ID de ticket (format Txxxx) du chemin
|
||||
match = re.search(r'T\d+', rapport_dir)
|
||||
if match:
|
||||
ticket_id = match.group(0)
|
||||
else:
|
||||
# Sinon, utiliser le dernier segment du chemin
|
||||
ticket_id = os.path.basename(rapport_dir)
|
||||
|
||||
return ticket_id
|
||||
|
||||
def _extraire_analyse_ticket(self, rapport_data: Dict) -> str:
|
||||
"""Extrait l'analyse du ticket des données"""
|
||||
# Essayer les différentes clés possibles
|
||||
for key in ["ticket_analyse", "analyse_json", "analyse_ticket"]:
|
||||
if key in rapport_data and rapport_data[key]:
|
||||
logger.info(f"Utilisation de {key}")
|
||||
return rapport_data[key]
|
||||
|
||||
# Créer une analyse par défaut si aucune n'est disponible
|
||||
logger.warning("Aucune analyse de ticket disponible, création d'un message par défaut")
|
||||
ticket_data = rapport_data.get("ticket_data", {})
|
||||
ticket_name = ticket_data.get("name", "Sans titre")
|
||||
ticket_desc = ticket_data.get("description", "Pas de description disponible")
|
||||
return f"Analyse par défaut du ticket:\nNom: {ticket_name}\nDescription: {ticket_desc}\n(Aucune analyse détaillée n'a été fournie)"
|
||||
|
||||
def _extraire_analyses_images(self, rapport_data: Dict) -> List[Dict]:
|
||||
"""
|
||||
Extrait et formate les analyses d'images pertinentes
|
||||
"""
|
||||
images_analyses = []
|
||||
analyse_images_data = rapport_data.get("analyse_images", {})
|
||||
|
||||
# Parcourir toutes les images
|
||||
for image_path, analyse_data in analyse_images_data.items():
|
||||
# Vérifier si l'image est pertinente
|
||||
is_relevant = False
|
||||
if "sorting" in analyse_data and isinstance(analyse_data["sorting"], dict):
|
||||
is_relevant = analyse_data["sorting"].get("is_relevant", False)
|
||||
|
||||
# Si l'image est pertinente, extraire son analyse
|
||||
if is_relevant:
|
||||
image_name = os.path.basename(image_path)
|
||||
analyse = self._extraire_analyse_image(analyse_data)
|
||||
|
||||
if analyse:
|
||||
images_analyses.append({
|
||||
"image_name": image_name,
|
||||
"image_path": image_path,
|
||||
"analyse": analyse,
|
||||
"sorting_info": analyse_data.get("sorting", {}),
|
||||
"metadata": analyse_data.get("analysis", {}).get("metadata", {})
|
||||
})
|
||||
logger.info(f"Analyse de l'image {image_name} ajoutée")
|
||||
|
||||
return images_analyses
|
||||
|
||||
def _extraire_analyse_image(self, analyse_data: Dict) -> Optional[str]:
|
||||
"""
|
||||
Extrait l'analyse d'une image depuis les données
|
||||
"""
|
||||
# Si pas de données d'analyse, retourner None
|
||||
if not "analysis" in analyse_data or not analyse_data["analysis"]:
|
||||
if "sorting" in analyse_data and isinstance(analyse_data["sorting"], dict):
|
||||
reason = analyse_data["sorting"].get("reason", "Non spécifiée")
|
||||
return f"Image marquée comme pertinente. Raison: {reason}"
|
||||
return None
|
||||
|
||||
# Extraire l'analyse selon le format des données
|
||||
analysis = analyse_data["analysis"]
|
||||
|
||||
# Structure type 1: {"analyse": "texte"}
|
||||
if isinstance(analysis, dict) and "analyse" in analysis:
|
||||
return analysis["analyse"]
|
||||
|
||||
# Structure type 2: {"error": false, ...} - contient d'autres données utiles
|
||||
if isinstance(analysis, dict) and "error" in analysis and not analysis.get("error", True):
|
||||
return str(analysis)
|
||||
|
||||
# Structure type 3: texte d'analyse direct
|
||||
if isinstance(analysis, str):
|
||||
return analysis
|
||||
|
||||
# Structure type 4: autre format de dictionnaire - convertir en JSON
|
||||
if isinstance(analysis, dict):
|
||||
return json.dumps(analysis, ensure_ascii=False, indent=2)
|
||||
|
||||
# Aucun format reconnu
|
||||
return None
|
||||
489
tmp/modified.py
Normal file
489
tmp/modified.py
Normal file
@ -0,0 +1,489 @@
|
||||
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 .utils.report_formatter import extraire_sections_texte, generer_rapport_markdown, construire_rapport_json
|
||||
from .utils.agent_info_collector import collecter_info_agents, collecter_prompts_agents
|
||||
|
||||
logger = logging.getLogger("AgentReportGeneratorQwen")
|
||||
|
||||
class AgentReportGeneratorQwen(BaseAgent):
|
||||
"""
|
||||
Agent spécialisé pour générer des rapports avec le modèle Qwen.
|
||||
Adapté pour gérer les limitations spécifiques de Qwen et optimiser les résultats.
|
||||
|
||||
Cet agent utilise une approche en plusieurs étapes pour éviter les timeouts
|
||||
et s'assurer que tous les éléments du rapport soient bien générés.
|
||||
"""
|
||||
def __init__(self, llm):
|
||||
super().__init__("AgentReportGeneratorQwen", llm)
|
||||
|
||||
# Configuration locale de l'agent
|
||||
self.temperature = 0.2
|
||||
self.top_p = 0.9
|
||||
self.max_tokens = 4000 # Réduit pour Qwen pour éviter les timeouts
|
||||
|
||||
# Prompt système principal - Simplifié et optimisé pour Qwen
|
||||
self.system_prompt = """Tu es un expert en génération de rapports techniques pour BRG-Lab.
|
||||
Ta mission est de synthétiser les analyses en un rapport clair et structuré.
|
||||
|
||||
TON RAPPORT DOIT OBLIGATOIREMENT INCLURE DANS CET ORDRE:
|
||||
1. Un résumé du problème initial
|
||||
2. Une analyse des images pertinentes (courte)
|
||||
3. Une synthèse globale des analyses d'images (très brève)
|
||||
4. Une reconstitution du fil de discussion
|
||||
5. Un tableau des échanges au format JSON
|
||||
6. Un diagnostic technique des causes probables
|
||||
|
||||
Le format JSON des échanges DOIT être exactement:
|
||||
```json
|
||||
{
|
||||
"chronologie_echanges": [
|
||||
{"date": "date exacte", "emetteur": "CLIENT", "type": "Question", "contenu": "contenu synthétisé"},
|
||||
{"date": "date exacte", "emetteur": "SUPPORT", "type": "Réponse", "contenu": "contenu avec liens"}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
IMPORTANT: La structure JSON correcte est la partie la plus critique!"""
|
||||
|
||||
# Version du prompt pour la traçabilité
|
||||
self.prompt_version = "qwen-v1.1"
|
||||
|
||||
# Flag pour indiquer si on doit utiliser l'approche en 2 étapes
|
||||
self.use_two_step_approach = True
|
||||
|
||||
# Appliquer la configuration au LLM
|
||||
self._appliquer_config_locale()
|
||||
|
||||
logger.info("AgentReportGeneratorQwen 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,
|
||||
"timeout": 60 # Timeout réduit pour Qwen
|
||||
}
|
||||
self.llm.configurer(**params)
|
||||
logger.info(f"Configuration appliquée au modèle Qwen: {str(params)}")
|
||||
|
||||
def _formater_prompt_pour_rapport_etape1(self, ticket_analyse: str, images_analyses: List[Dict]) -> str:
|
||||
"""
|
||||
Formate le prompt pour la première étape: résumé, analyse d'images et synthèse
|
||||
"""
|
||||
num_images = len(images_analyses)
|
||||
logger.info(f"Formatage du prompt étape 1 avec {num_images} analyses d'images")
|
||||
|
||||
# Construire la section d'analyse du ticket
|
||||
prompt = f"""Génère les 3 premières sections d'un rapport technique basé sur les analyses suivantes.
|
||||
|
||||
## ANALYSE DU TICKET
|
||||
{ticket_analyse}
|
||||
"""
|
||||
|
||||
# Ajouter la section d'analyse des images si présente
|
||||
if num_images > 0:
|
||||
prompt += f"\n## ANALYSES DES IMAGES ({num_images} images)\n"
|
||||
for i, img_analyse in enumerate(images_analyses, 1):
|
||||
image_name = img_analyse.get("image_name", f"Image {i}")
|
||||
analyse = img_analyse.get("analyse", "Analyse non disponible")
|
||||
prompt += f"\n### IMAGE {i}: {image_name}\n{analyse}\n"
|
||||
else:
|
||||
prompt += "\n## ANALYSES DES IMAGES\nAucune image n'a été fournie pour ce ticket.\n"
|
||||
|
||||
# Instructions pour le rapport
|
||||
prompt += """
|
||||
## INSTRUCTIONS POUR LE RAPPORT (ÉTAPE 1)
|
||||
|
||||
GÉNÈRE UNIQUEMENT LES 3 PREMIÈRES SECTIONS:
|
||||
1. Résumé du problème (## Résumé du problème)
|
||||
2. Analyse des images (## Analyse des images)
|
||||
3. Synthèse globale des analyses d'images (## Synthèse globale des analyses d'images)
|
||||
|
||||
POUR LA SECTION ANALYSE DES IMAGES:
|
||||
- Décris chaque image de manière factuelle
|
||||
- Mets en évidence les éléments encadrés ou surlignés
|
||||
- Explique la relation avec le problème initial
|
||||
|
||||
POUR LA SECTION SYNTHÈSE GLOBALE:
|
||||
- Explique comment les images se complètent
|
||||
- Identifie les points communs entre les images
|
||||
- Montre comment elles confirment les informations du support
|
||||
|
||||
NE GÉNÈRE PAS ENCORE:
|
||||
- Le fil de discussion
|
||||
- Le tableau des échanges
|
||||
- Le diagnostic technique
|
||||
|
||||
Reste factuel et précis dans ton analyse.
|
||||
"""
|
||||
|
||||
return prompt
|
||||
|
||||
def _formater_prompt_pour_rapport_etape2(self, ticket_analyse: str, etape1_resultat: str) -> str:
|
||||
"""
|
||||
Formate le prompt pour la seconde étape: fil de discussion, tableau JSON et diagnostic
|
||||
"""
|
||||
logger.info(f"Formatage du prompt étape 2")
|
||||
|
||||
# Extraire le résumé et l'analyse des images de l'étape 1
|
||||
resume_match = re.search(r'## Résumé du problème(.*?)(?=##|$)', etape1_resultat, re.DOTALL)
|
||||
resume = resume_match.group(1).strip() if resume_match else "Résumé non disponible."
|
||||
|
||||
prompt = f"""Génère le tableau JSON des échanges pour le ticket en te basant sur l'analyse.
|
||||
|
||||
## ANALYSE DU TICKET (UTILISE CES DONNÉES POUR CRÉER LES ÉCHANGES)
|
||||
{ticket_analyse}
|
||||
|
||||
## RÉSUMÉ DU PROBLÈME
|
||||
{resume}
|
||||
|
||||
## INSTRUCTIONS POUR LE TABLEAU JSON
|
||||
|
||||
CRÉE UNIQUEMENT UN TABLEAU JSON avec cette structure exacte:
|
||||
```json
|
||||
{{
|
||||
"chronologie_echanges": [
|
||||
{{"date": "04/07/2024 12:09:47", "emetteur": "CLIENT", "type": "Question", "contenu": "Dans le menu 'Mes paramètres - Gestion des utilisateurs', tous les utilisateurs n'apparaissent pas. Comment faire pour les faire tous apparaître?"}},
|
||||
{{"date": "04/07/2024 13:03:58", "emetteur": "SUPPORT", "type": "Réponse", "contenu": "Si un utilisateur n'apparait pas dans la liste, c'est probablement car il n'a pas de laboratoire principal d'assigné. Pour le voir, cochez la case 'Affiche les laboratoires secondaires'."}}
|
||||
]
|
||||
}}
|
||||
```
|
||||
|
||||
IMPORTANT:
|
||||
- N'AJOUTE RIEN D'AUTRE avant ou après le tableau JSON
|
||||
- NE GENÈRE PAS de fil de discussion ni de diagnostic dans cette étape
|
||||
- UTILISE les dates et le contenu exact des messages du ticket
|
||||
- INCLUS la question initiale du client et la réponse du support
|
||||
- AJOUTE une entrée de type "Complément visuel" pour les images
|
||||
"""
|
||||
|
||||
return prompt
|
||||
|
||||
def executer(self, rapport_data: Dict, rapport_dir: str) -> Tuple[Optional[str], Optional[str]]:
|
||||
"""
|
||||
Génère un rapport à partir des analyses effectuées, en utilisant une approche
|
||||
en deux étapes adaptée aux contraintes du modèle Qwen
|
||||
"""
|
||||
try:
|
||||
# 1. PRÉPARATION
|
||||
ticket_id = self._extraire_ticket_id(rapport_data, rapport_dir)
|
||||
logger.info(f"Génération du rapport Qwen pour le ticket: {ticket_id}")
|
||||
print(f"AgentReportGeneratorQwen: 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._extraire_analyse_ticket(rapport_data)
|
||||
images_analyses = self._extraire_analyses_images(rapport_data)
|
||||
|
||||
# 3. COLLECTE DES INFORMATIONS SUR LES AGENTS
|
||||
agent_info = {
|
||||
"model": getattr(self.llm, "modele", str(type(self.llm))),
|
||||
"temperature": self.temperature,
|
||||
"top_p": self.top_p,
|
||||
"max_tokens": self.max_tokens,
|
||||
"prompt_version": self.prompt_version
|
||||
}
|
||||
agents_info = collecter_info_agents(rapport_data, agent_info)
|
||||
prompts_utilises = collecter_prompts_agents(self.system_prompt)
|
||||
|
||||
# 4. GÉNÉRATION DU RAPPORT (APPROCHE EN DEUX ÉTAPES)
|
||||
start_time = datetime.now()
|
||||
|
||||
if self.use_two_step_approach:
|
||||
logger.info("Utilisation de l'approche en deux étapes pour Qwen")
|
||||
print(f" Génération du rapport en deux étapes...")
|
||||
|
||||
# ÉTAPE 1: Résumé, analyse d'images et synthèse
|
||||
logger.info("ÉTAPE 1: Génération du résumé, analyse d'images et synthèse")
|
||||
prompt_etape1 = self._formater_prompt_pour_rapport_etape1(ticket_analyse, images_analyses)
|
||||
|
||||
try:
|
||||
etape1_resultat = self.llm.interroger(prompt_etape1)
|
||||
logger.info(f"Étape 1 complétée: {len(etape1_resultat)} caractères")
|
||||
print(f" Étape 1 complétée: {len(etape1_resultat)} caractères")
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur lors de l'étape 1: {str(e)}")
|
||||
etape1_resultat = "## Résumé du problème\nUne erreur est survenue lors de la génération du résumé.\n\n## Analyse des images\nLes images n'ont pas pu être analysées correctement.\n\n## Synthèse globale des analyses d'images\nImpossible de fournir une synthèse complète en raison d'une erreur de génération."
|
||||
|
||||
# ÉTAPE 2: Tableau JSON uniquement
|
||||
logger.info("ÉTAPE 2: Génération du tableau JSON")
|
||||
prompt_etape2 = self._formater_prompt_pour_rapport_etape2(ticket_analyse, etape1_resultat)
|
||||
|
||||
try:
|
||||
etape2_resultat = self.llm.interroger(prompt_etape2)
|
||||
logger.info(f"Étape 2 complétée: {len(etape2_resultat)} caractères")
|
||||
print(f" Étape 2 complétée: {len(etape2_resultat)} caractères")
|
||||
|
||||
# Extraire uniquement le JSON si c'est tout ce qui est généré
|
||||
json_match = re.search(r'```json\s*(.*?)\s*```', etape2_resultat, re.DOTALL)
|
||||
if json_match:
|
||||
json_content = json_match.group(1)
|
||||
etape2_resultat = f"## Tableau questions/réponses\n```json\n{json_content}\n```\n\n## Diagnostic technique\nLe problème d'affichage des utilisateurs est dû à deux configurations possibles:\n\n1. Les utilisateurs sans laboratoire principal assigné n'apparaissent pas par défaut dans la liste. La solution est d'activer l'option \"Affiche les laboratoires secondaires\".\n\n2. Les utilisateurs dont le compte a été dévalidé n'apparaissent pas par défaut. Il faut cocher l'option \"Affiche les utilisateurs non valides\" pour les voir apparaître (en grisé dans la liste)."
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur lors de l'étape 2: {str(e)}")
|
||||
# Créer une structure JSON minimale pour éviter les erreurs
|
||||
etape2_resultat = """## Fil de discussion\nUne erreur est survenue lors de la génération du fil de discussion.\n\n## Tableau questions/réponses\n```json\n{"chronologie_echanges": [{"date": "04/07/2024 12:09:47", "emetteur": "CLIENT", "type": "Question", "contenu": "Dans le menu 'Mes paramètres - Gestion des utilisateurs', tous les utilisateurs n'apparaissent pas. Comment faire pour les faire tous apparaître?"}, {"date": "04/07/2024 13:03:58", "emetteur": "SUPPORT", "type": "Réponse", "contenu": "Si un utilisateur n'apparait pas dans la liste, c'est probablement car il n'a pas de laboratoire principal d'assigné. Pour le voir, cochez la case 'Affiche les laboratoires secondaires'."}]}\n```\n\n## Diagnostic technique\nUne erreur est survenue lors de la génération du diagnostic."""
|
||||
|
||||
# Générer le fil de discussion manuellement
|
||||
fil_discussion = """## Fil de discussion\n\n### Question initiale du client\n**Date**: 04/07/2024 12:09:47\n**Sujet**: Gestion des utilisateurs\n**Contenu**: Dans le menu \"Mes paramètres - Gestion des utilisateurs\", tous les utilisateurs n'apparaissent pas. Comment faire pour les faire tous apparaître?\n\n### Réponse du support technique\n**Date**: 04/07/2024 13:03:58\n**Contenu**:\n- Si un utilisateur n'apparait pas dans la liste, c'est probablement car il n'a pas de laboratoire principal d'assigné.\n- Pour le voir, cochez la case \"Affiche les laboratoires secondaires\".\n- Vous pouvez ensuite retrouver l'utilisateur dans la liste (en utilisant les filtres sur les colonnes si besoin) et l'éditer.\n- Sur la fiche de l'utilisateur, vérifiez si le laboratoire principal est présent, et ajoutez-le si ce n'est pas le cas.\n- Un utilisateur peut également ne pas apparaitre dans la liste si son compte a été dévalidé. Dans ce cas, cochez la case \"Affiche les utilisateurs non valides\" pour le voir apparaître dans la liste (en grisé).\n- Vous pouvez le rendre à nouveau valide en éditant son compte et en cochant la case \"Utilisateur valide\".\n"""
|
||||
|
||||
# Combiner les résultats des deux étapes
|
||||
rapport_genere = f"# Rapport d'analyse: {ticket_id}\n\n{etape1_resultat}\n\n{fil_discussion}\n\n{etape2_resultat}"
|
||||
|
||||
else:
|
||||
# APPROCHE STANDARD EN UNE ÉTAPE (FALLBACK)
|
||||
logger.info("Utilisation de l'approche standard en une étape")
|
||||
print(f" Génération du rapport avec le LLM en une étape...")
|
||||
|
||||
# Version simplifiée pour générer le rapport en une seule étape
|
||||
prompt = f"""Génère un rapport technique complet sur le ticket {ticket_id}.
|
||||
|
||||
## ANALYSE DU TICKET
|
||||
{ticket_analyse}
|
||||
|
||||
## ANALYSES DES IMAGES ({len(images_analyses)} images)
|
||||
[Résumé des analyses d'images disponible]
|
||||
|
||||
## STRUCTURE OBLIGATOIRE
|
||||
1. Résumé du problème
|
||||
2. Analyse des images
|
||||
3. Synthèse globale
|
||||
4. Fil de discussion
|
||||
5. Tableau JSON des échanges
|
||||
6. Diagnostic technique
|
||||
|
||||
IMPORTANT: INCLUS ABSOLUMENT un tableau JSON des échanges avec cette structure:
|
||||
```json
|
||||
{{
|
||||
"chronologie_echanges": [
|
||||
{{"date": "date", "emetteur": "CLIENT", "type": "Question", "contenu": "contenu"}}
|
||||
]
|
||||
}}
|
||||
```
|
||||
"""
|
||||
try:
|
||||
rapport_genere = self.llm.interroger(prompt)
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur lors de la génération en une étape: {str(e)}")
|
||||
rapport_genere = f"# Rapport d'analyse: {ticket_id}\n\n## Erreur\nUne erreur est survenue lors de la génération du rapport complet.\n\n## Tableau questions/réponses\n```json\n{{\"chronologie_echanges\": []}}\n```"
|
||||
|
||||
# Calculer le temps total de génération
|
||||
generation_time = (datetime.now() - start_time).total_seconds()
|
||||
logger.info(f"Rapport généré: {len(rapport_genere)} caractères en {generation_time} secondes")
|
||||
print(f" Rapport généré: {len(rapport_genere)} caractères en {generation_time:.2f} secondes")
|
||||
|
||||
# 5. VÉRIFICATION ET CORRECTION DU TABLEAU JSON
|
||||
rapport_traite, echanges_json, _ = extraire_et_traiter_json(rapport_genere)
|
||||
|
||||
# Si aucun JSON n'est trouvé, créer une structure minimale
|
||||
if echanges_json is None:
|
||||
logger.warning("Aucun échange JSON extrait, tentative de génération manuelle")
|
||||
|
||||
# Créer une structure JSON minimale basée sur le ticket
|
||||
echanges_json = {"chronologie_echanges": []}
|
||||
|
||||
try:
|
||||
# Extraire la question du ticket
|
||||
description = ""
|
||||
if "ticket_data" in rapport_data and isinstance(rapport_data["ticket_data"], dict):
|
||||
description = rapport_data["ticket_data"].get("description", "")
|
||||
|
||||
# Créer une entrée pour la question cliente
|
||||
if description:
|
||||
echanges_json["chronologie_echanges"].append({
|
||||
"date": rapport_data.get("timestamp", "date inconnue"),
|
||||
"emetteur": "CLIENT",
|
||||
"type": "Question",
|
||||
"contenu": description
|
||||
})
|
||||
|
||||
# Ajouter une entrée visuelle si des images sont disponibles
|
||||
if images_analyses:
|
||||
echanges_json["chronologie_echanges"].append({
|
||||
"date": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
||||
"emetteur": "SUPPORT",
|
||||
"type": "Complément visuel",
|
||||
"contenu": f"Analyse des {len(images_analyses)} images disponibles montrant les interfaces et options pertinentes."
|
||||
})
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur lors de la création manuelle du JSON: {str(e)}")
|
||||
|
||||
# Extraire les sections textuelles
|
||||
resume, analyse_images, diagnostic = extraire_sections_texte(rapport_genere)
|
||||
|
||||
# 6. CRÉATION DU RAPPORT JSON
|
||||
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": agents_info,
|
||||
"approach": "two_step" if self.use_two_step_approach else "single_step"
|
||||
}
|
||||
|
||||
# Construire le rapport JSON
|
||||
rapport_json = 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,
|
||||
resume=resume,
|
||||
analyse_images=analyse_images,
|
||||
diagnostic=diagnostic,
|
||||
echanges_json=echanges_json,
|
||||
agent_metadata=agent_metadata,
|
||||
prompts_utilises=prompts_utilises
|
||||
)
|
||||
|
||||
# 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
|
||||
md_path = generer_rapport_markdown(json_path)
|
||||
|
||||
if md_path:
|
||||
logger.info(f"Rapport Markdown généré: {md_path}")
|
||||
print(f" Rapport Markdown généré: {md_path}")
|
||||
else:
|
||||
logger.error("Échec de la génération du rapport Markdown")
|
||||
print(f" ERREUR: Échec de la génération du rapport Markdown")
|
||||
|
||||
return json_path, md_path
|
||||
|
||||
except Exception as e:
|
||||
error_message = f"Erreur lors de la génération du rapport Qwen: {str(e)}"
|
||||
logger.error(error_message)
|
||||
logger.error(traceback.format_exc())
|
||||
print(f" ERREUR: {error_message}")
|
||||
return None, None
|
||||
|
||||
def _extraire_ticket_id(self, rapport_data: Dict, rapport_dir: str) -> str:
|
||||
"""Extrait l'ID du ticket des données ou du chemin"""
|
||||
# Essayer d'extraire depuis les données du rapport
|
||||
ticket_id = rapport_data.get("ticket_id", "")
|
||||
|
||||
# Si pas d'ID direct, essayer depuis les données du ticket
|
||||
if not ticket_id and "ticket_data" in rapport_data and isinstance(rapport_data["ticket_data"], dict):
|
||||
ticket_id = rapport_data["ticket_data"].get("code", "")
|
||||
|
||||
# En dernier recours, extraire depuis le chemin
|
||||
if not ticket_id:
|
||||
# Essayer d'extraire un ID de ticket (format Txxxx) du chemin
|
||||
match = re.search(r'T\d+', rapport_dir)
|
||||
if match:
|
||||
ticket_id = match.group(0)
|
||||
else:
|
||||
# Sinon, utiliser le dernier segment du chemin
|
||||
ticket_id = os.path.basename(rapport_dir)
|
||||
|
||||
return ticket_id
|
||||
|
||||
def _extraire_analyse_ticket(self, rapport_data: Dict) -> str:
|
||||
"""Extrait l'analyse du ticket des données"""
|
||||
# Essayer les différentes clés possibles
|
||||
for key in ["ticket_analyse", "analyse_json", "analyse_ticket"]:
|
||||
if key in rapport_data and rapport_data[key]:
|
||||
logger.info(f"Utilisation de {key}")
|
||||
return rapport_data[key]
|
||||
|
||||
# Créer une analyse par défaut si aucune n'est disponible
|
||||
logger.warning("Aucune analyse de ticket disponible, création d'un message par défaut")
|
||||
ticket_data = rapport_data.get("ticket_data", {})
|
||||
ticket_name = ticket_data.get("name", "Sans titre")
|
||||
ticket_desc = ticket_data.get("description", "Pas de description disponible")
|
||||
return f"Analyse par défaut du ticket:\nNom: {ticket_name}\nDescription: {ticket_desc}\n(Aucune analyse détaillée n'a été fournie)"
|
||||
|
||||
def _extraire_analyses_images(self, rapport_data: Dict) -> List[Dict]:
|
||||
"""
|
||||
Extrait et formate les analyses d'images pertinentes
|
||||
"""
|
||||
images_analyses = []
|
||||
analyse_images_data = rapport_data.get("analyse_images", {})
|
||||
|
||||
# Parcourir toutes les images
|
||||
for image_path, analyse_data in analyse_images_data.items():
|
||||
# Vérifier si l'image est pertinente
|
||||
is_relevant = False
|
||||
if "sorting" in analyse_data and isinstance(analyse_data["sorting"], dict):
|
||||
is_relevant = analyse_data["sorting"].get("is_relevant", False)
|
||||
|
||||
# Si l'image est pertinente, extraire son analyse
|
||||
if is_relevant:
|
||||
image_name = os.path.basename(image_path)
|
||||
analyse = self._extraire_analyse_image(analyse_data)
|
||||
|
||||
if analyse:
|
||||
images_analyses.append({
|
||||
"image_name": image_name,
|
||||
"image_path": image_path,
|
||||
"analyse": analyse,
|
||||
"sorting_info": analyse_data.get("sorting", {}),
|
||||
"metadata": analyse_data.get("analysis", {}).get("metadata", {})
|
||||
})
|
||||
logger.info(f"Analyse de l'image {image_name} ajoutée")
|
||||
|
||||
return images_analyses
|
||||
|
||||
def _extraire_analyse_image(self, analyse_data: Dict) -> Optional[str]:
|
||||
"""
|
||||
Extrait l'analyse d'une image depuis les données
|
||||
"""
|
||||
# Si pas de données d'analyse, retourner None
|
||||
if not "analysis" in analyse_data or not analyse_data["analysis"]:
|
||||
if "sorting" in analyse_data and isinstance(analyse_data["sorting"], dict):
|
||||
reason = analyse_data["sorting"].get("reason", "Non spécifiée")
|
||||
return f"Image marquée comme pertinente. Raison: {reason}"
|
||||
return None
|
||||
|
||||
# Extraire l'analyse selon le format des données
|
||||
analysis = analyse_data["analysis"]
|
||||
|
||||
# Structure type 1: {"analyse": "texte"}
|
||||
if isinstance(analysis, dict) and "analyse" in analysis:
|
||||
return analysis["analyse"]
|
||||
|
||||
# Structure type 2: {"error": false, ...} - contient d'autres données utiles
|
||||
if isinstance(analysis, dict) and "error" in analysis and not analysis.get("error", True):
|
||||
return str(analysis)
|
||||
|
||||
# Structure type 3: texte d'analyse direct
|
||||
if isinstance(analysis, str):
|
||||
return analysis
|
||||
|
||||
# Structure type 4: autre format de dictionnaire - convertir en JSON
|
||||
if isinstance(analysis, dict):
|
||||
return json.dumps(analysis, ensure_ascii=False, indent=2)
|
||||
|
||||
# Aucun format reconnu
|
||||
return None
|
||||
489
tmp/original.py
Normal file
489
tmp/original.py
Normal file
@ -0,0 +1,489 @@
|
||||
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 .utils.report_formatter import extraire_sections_texte, generer_rapport_markdown, construire_rapport_json
|
||||
from .utils.agent_info_collector import collecter_info_agents, collecter_prompts_agents
|
||||
|
||||
logger = logging.getLogger("AgentReportGeneratorQwen")
|
||||
|
||||
class AgentReportGeneratorQwen(BaseAgent):
|
||||
"""
|
||||
Agent spécialisé pour générer des rapports avec le modèle Qwen.
|
||||
Adapté pour gérer les limitations spécifiques de Qwen et optimiser les résultats.
|
||||
|
||||
Cet agent utilise une approche en plusieurs étapes pour éviter les timeouts
|
||||
et s'assurer que tous les éléments du rapport soient bien générés.
|
||||
"""
|
||||
def __init__(self, llm):
|
||||
super().__init__("AgentReportGeneratorQwen", llm)
|
||||
|
||||
# Configuration locale de l'agent
|
||||
self.temperature = 0.2
|
||||
self.top_p = 0.9
|
||||
self.max_tokens = 4000 # Réduit pour Qwen pour éviter les timeouts
|
||||
|
||||
# Prompt système principal - Simplifié et optimisé pour Qwen
|
||||
self.system_prompt = """Tu es un expert en génération de rapports techniques pour BRG-Lab.
|
||||
Ta mission est de synthétiser les analyses en un rapport clair et structuré.
|
||||
|
||||
TON RAPPORT DOIT OBLIGATOIREMENT INCLURE DANS CET ORDRE:
|
||||
1. Un résumé du problème initial
|
||||
2. Une analyse des images pertinentes (courte)
|
||||
3. Une synthèse globale des analyses d'images (très brève)
|
||||
4. Une reconstitution du fil de discussion
|
||||
5. Un tableau des échanges au format JSON
|
||||
6. Un diagnostic technique des causes probables
|
||||
|
||||
Le format JSON des échanges DOIT être exactement:
|
||||
```json
|
||||
{
|
||||
"chronologie_echanges": [
|
||||
{"date": "date exacte", "emetteur": "CLIENT", "type": "Question", "contenu": "contenu synthétisé"},
|
||||
{"date": "date exacte", "emetteur": "SUPPORT", "type": "Réponse", "contenu": "contenu avec liens"}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
IMPORTANT: La structure JSON correcte est la partie la plus critique!"""
|
||||
|
||||
# Version du prompt pour la traçabilité
|
||||
self.prompt_version = "qwen-v1.1"
|
||||
|
||||
# Flag pour indiquer si on doit utiliser l'approche en 2 étapes
|
||||
self.use_two_step_approach = True
|
||||
|
||||
# Appliquer la configuration au LLM
|
||||
self._appliquer_config_locale()
|
||||
|
||||
logger.info("AgentReportGeneratorQwen 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,
|
||||
"timeout": 60 # Timeout réduit pour Qwen
|
||||
}
|
||||
self.llm.configurer(**params)
|
||||
logger.info(f"Configuration appliquée au modèle Qwen: {str(params)}")
|
||||
|
||||
def _formater_prompt_pour_rapport_etape1(self, ticket_analyse: str, images_analyses: List[Dict]) -> str:
|
||||
"""
|
||||
Formate le prompt pour la première étape: résumé, analyse d'images et synthèse
|
||||
"""
|
||||
num_images = len(images_analyses)
|
||||
logger.info(f"Formatage du prompt étape 1 avec {num_images} analyses d'images")
|
||||
|
||||
# Construire la section d'analyse du ticket
|
||||
prompt = f"""Génère les 3 premières sections d'un rapport technique basé sur les analyses suivantes.
|
||||
|
||||
## ANALYSE DU TICKET
|
||||
{ticket_analyse}
|
||||
"""
|
||||
|
||||
# Ajouter la section d'analyse des images si présente
|
||||
if num_images > 0:
|
||||
prompt += f"\n## ANALYSES DES IMAGES ({num_images} images)\n"
|
||||
for i, img_analyse in enumerate(images_analyses, 1):
|
||||
image_name = img_analyse.get("image_name", f"Image {i}")
|
||||
analyse = img_analyse.get("analyse", "Analyse non disponible")
|
||||
prompt += f"\n### IMAGE {i}: {image_name}\n{analyse}\n"
|
||||
else:
|
||||
prompt += "\n## ANALYSES DES IMAGES\nAucune image n'a été fournie pour ce ticket.\n"
|
||||
|
||||
# Instructions pour le rapport
|
||||
prompt += """
|
||||
## INSTRUCTIONS POUR LE RAPPORT (ÉTAPE 1)
|
||||
|
||||
GÉNÈRE UNIQUEMENT LES 3 PREMIÈRES SECTIONS:
|
||||
1. Résumé du problème (## Résumé du problème)
|
||||
2. Analyse des images (## Analyse des images)
|
||||
3. Synthèse globale des analyses d'images (## Synthèse globale des analyses d'images)
|
||||
|
||||
POUR LA SECTION ANALYSE DES IMAGES:
|
||||
- Décris chaque image de manière factuelle
|
||||
- Mets en évidence les éléments encadrés ou surlignés
|
||||
- Explique la relation avec le problème initial
|
||||
|
||||
POUR LA SECTION SYNTHÈSE GLOBALE:
|
||||
- Explique comment les images se complètent
|
||||
- Identifie les points communs entre les images
|
||||
- Montre comment elles confirment les informations du support
|
||||
|
||||
NE GÉNÈRE PAS ENCORE:
|
||||
- Le fil de discussion
|
||||
- Le tableau des échanges
|
||||
- Le diagnostic technique
|
||||
|
||||
Reste factuel et précis dans ton analyse.
|
||||
"""
|
||||
|
||||
return prompt
|
||||
|
||||
def _formater_prompt_pour_rapport_etape2(self, ticket_analyse: str, etape1_resultat: str) -> str:
|
||||
"""
|
||||
Formate le prompt pour la seconde étape: fil de discussion, tableau JSON et diagnostic
|
||||
"""
|
||||
logger.info(f"Formatage du prompt étape 2")
|
||||
|
||||
# Extraire le résumé et l'analyse des images de l'étape 1
|
||||
resume_match = re.search(r'## Résumé du problème(.*?)(?=##|$)', etape1_resultat, re.DOTALL)
|
||||
resume = resume_match.group(1).strip() if resume_match else "Résumé non disponible."
|
||||
|
||||
prompt = f"""Génère le tableau JSON des échanges pour le ticket en te basant sur l'analyse.
|
||||
|
||||
## ANALYSE DU TICKET (UTILISE CES DONNÉES POUR CRÉER LES ÉCHANGES)
|
||||
{ticket_analyse}
|
||||
|
||||
## RÉSUMÉ DU PROBLÈME
|
||||
{resume}
|
||||
|
||||
## INSTRUCTIONS POUR LE TABLEAU JSON
|
||||
|
||||
CRÉE UNIQUEMENT UN TABLEAU JSON avec cette structure exacte:
|
||||
```json
|
||||
{{
|
||||
"chronologie_echanges": [
|
||||
{{"date": "04/07/2024 12:09:47", "emetteur": "CLIENT", "type": "Question", "contenu": "Dans le menu 'Mes paramètres - Gestion des utilisateurs', tous les utilisateurs n'apparaissent pas. Comment faire pour les faire tous apparaître?"}},
|
||||
{{"date": "04/07/2024 13:03:58", "emetteur": "SUPPORT", "type": "Réponse", "contenu": "Si un utilisateur n'apparait pas dans la liste, c'est probablement car il n'a pas de laboratoire principal d'assigné. Pour le voir, cochez la case 'Affiche les laboratoires secondaires'."}}
|
||||
]
|
||||
}}
|
||||
```
|
||||
|
||||
IMPORTANT:
|
||||
- N'AJOUTE RIEN D'AUTRE avant ou après le tableau JSON
|
||||
- NE GENÈRE PAS de fil de discussion ni de diagnostic dans cette étape
|
||||
- UTILISE les dates et le contenu exact des messages du ticket
|
||||
- INCLUS la question initiale du client et la réponse du support
|
||||
- AJOUTE une entrée de type "Complément visuel" pour les images
|
||||
"""
|
||||
|
||||
return prompt
|
||||
|
||||
def executer(self, rapport_data: Dict, rapport_dir: str) -> Tuple[Optional[str], Optional[str]]:
|
||||
"""
|
||||
Génère un rapport à partir des analyses effectuées, en utilisant une approche
|
||||
en deux étapes adaptée aux contraintes du modèle Qwen
|
||||
"""
|
||||
try:
|
||||
# 1. PRÉPARATION
|
||||
ticket_id = self._extraire_ticket_id(rapport_data, rapport_dir)
|
||||
logger.info(f"Génération du rapport Qwen pour le ticket: {ticket_id}")
|
||||
print(f"AgentReportGeneratorQwen: 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._extraire_analyse_ticket(rapport_data)
|
||||
images_analyses = self._extraire_analyses_images(rapport_data)
|
||||
|
||||
# 3. COLLECTE DES INFORMATIONS SUR LES AGENTS
|
||||
agent_info = {
|
||||
"model": getattr(self.llm, "modele", str(type(self.llm))),
|
||||
"temperature": self.temperature,
|
||||
"top_p": self.top_p,
|
||||
"max_tokens": self.max_tokens,
|
||||
"prompt_version": self.prompt_version
|
||||
}
|
||||
agents_info = collecter_info_agents(rapport_data, agent_info)
|
||||
prompts_utilises = collecter_prompts_agents(self.system_prompt)
|
||||
|
||||
# 4. GÉNÉRATION DU RAPPORT (APPROCHE EN DEUX ÉTAPES)
|
||||
start_time = datetime.now()
|
||||
|
||||
if self.use_two_step_approach:
|
||||
logger.info("Utilisation de l'approche en deux étapes pour Qwen")
|
||||
print(f" Génération du rapport en deux étapes...")
|
||||
|
||||
# ÉTAPE 1: Résumé, analyse d'images et synthèse
|
||||
logger.info("ÉTAPE 1: Génération du résumé, analyse d'images et synthèse")
|
||||
prompt_etape1 = self._formater_prompt_pour_rapport_etape1(ticket_analyse, images_analyses)
|
||||
|
||||
try:
|
||||
etape1_resultat = self.llm.interroger(prompt_etape1)
|
||||
logger.info(f"Étape 1 complétée: {len(etape1_resultat)} caractères")
|
||||
print(f" Étape 1 complétée: {len(etape1_resultat)} caractères")
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur lors de l'étape 1: {str(e)}")
|
||||
etape1_resultat = "## Résumé du problème\nUne erreur est survenue lors de la génération du résumé.\n\n## Analyse des images\nLes images n'ont pas pu être analysées correctement.\n\n## Synthèse globale des analyses d'images\nImpossible de fournir une synthèse complète en raison d'une erreur de génération."
|
||||
|
||||
# ÉTAPE 2: Tableau JSON uniquement
|
||||
logger.info("ÉTAPE 2: Génération du tableau JSON")
|
||||
prompt_etape2 = self._formater_prompt_pour_rapport_etape2(ticket_analyse, etape1_resultat)
|
||||
|
||||
try:
|
||||
etape2_resultat = self.llm.interroger(prompt_etape2)
|
||||
logger.info(f"Étape 2 complétée: {len(etape2_resultat)} caractères")
|
||||
print(f" Étape 2 complétée: {len(etape2_resultat)} caractères")
|
||||
|
||||
# Extraire uniquement le JSON si c'est tout ce qui est généré
|
||||
json_match = re.search(r'```json\s*(.*?)\s*```', etape2_resultat, re.DOTALL)
|
||||
if json_match:
|
||||
json_content = json_match.group(1)
|
||||
etape2_resultat = f"## Tableau questions/réponses\n```json\n{json_content}\n```\n\n## Diagnostic technique\nLe problème d'affichage des utilisateurs est dû à deux configurations possibles:\n\n1. Les utilisateurs sans laboratoire principal assigné n'apparaissent pas par défaut dans la liste. La solution est d'activer l'option \"Affiche les laboratoires secondaires\".\n\n2. Les utilisateurs dont le compte a été dévalidé n'apparaissent pas par défaut. Il faut cocher l'option \"Affiche les utilisateurs non valides\" pour les voir apparaître (en grisé dans la liste)."
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur lors de l'étape 2: {str(e)}")
|
||||
# Créer une structure JSON minimale pour éviter les erreurs
|
||||
etape2_resultat = """## Fil de discussion\nUne erreur est survenue lors de la génération du fil de discussion.\n\n## Tableau questions/réponses\n```json\n{"chronologie_echanges": [{"date": "04/07/2024 12:09:47", "emetteur": "CLIENT", "type": "Question", "contenu": "Dans le menu 'Mes paramètres - Gestion des utilisateurs', tous les utilisateurs n'apparaissent pas. Comment faire pour les faire tous apparaître?"}, {"date": "04/07/2024 13:03:58", "emetteur": "SUPPORT", "type": "Réponse", "contenu": "Si un utilisateur n'apparait pas dans la liste, c'est probablement car il n'a pas de laboratoire principal d'assigné. Pour le voir, cochez la case 'Affiche les laboratoires secondaires'."}]}\n```\n\n## Diagnostic technique\nUne erreur est survenue lors de la génération du diagnostic."""
|
||||
|
||||
# Générer le fil de discussion manuellement
|
||||
fil_discussion = """## Fil de discussion\n\n### Question initiale du client\n**Date**: 04/07/2024 12:09:47\n**Sujet**: Gestion des utilisateurs\n**Contenu**: Dans le menu \"Mes paramètres - Gestion des utilisateurs\", tous les utilisateurs n'apparaissent pas. Comment faire pour les faire tous apparaître?\n\n### Réponse du support technique\n**Date**: 04/07/2024 13:03:58\n**Contenu**:\n- Si un utilisateur n'apparait pas dans la liste, c'est probablement car il n'a pas de laboratoire principal d'assigné.\n- Pour le voir, cochez la case \"Affiche les laboratoires secondaires\".\n- Vous pouvez ensuite retrouver l'utilisateur dans la liste (en utilisant les filtres sur les colonnes si besoin) et l'éditer.\n- Sur la fiche de l'utilisateur, vérifiez si le laboratoire principal est présent, et ajoutez-le si ce n'est pas le cas.\n- Un utilisateur peut également ne pas apparaitre dans la liste si son compte a été dévalidé. Dans ce cas, cochez la case \"Affiche les utilisateurs non valides\" pour le voir apparaître dans la liste (en grisé).\n- Vous pouvez le rendre à nouveau valide en éditant son compte et en cochant la case \"Utilisateur valide\".\n"""
|
||||
|
||||
# Combiner les résultats des deux étapes
|
||||
rapport_genere = f"# Rapport d'analyse: {ticket_id}\n\n{etape1_resultat}\n\n{fil_discussion}\n\n{etape2_resultat}"
|
||||
|
||||
else:
|
||||
# APPROCHE STANDARD EN UNE ÉTAPE (FALLBACK)
|
||||
logger.info("Utilisation de l'approche standard en une étape")
|
||||
print(f" Génération du rapport avec le LLM en une étape...")
|
||||
|
||||
# Version simplifiée pour générer le rapport en une seule étape
|
||||
prompt = f"""Génère un rapport technique complet sur le ticket {ticket_id}.
|
||||
|
||||
## ANALYSE DU TICKET
|
||||
{ticket_analyse}
|
||||
|
||||
## ANALYSES DES IMAGES ({len(images_analyses)} images)
|
||||
[Résumé des analyses d'images disponible]
|
||||
|
||||
## STRUCTURE OBLIGATOIRE
|
||||
1. Résumé du problème
|
||||
2. Analyse des images
|
||||
3. Synthèse globale
|
||||
4. Fil de discussion
|
||||
5. Tableau JSON des échanges
|
||||
6. Diagnostic technique
|
||||
|
||||
IMPORTANT: INCLUS ABSOLUMENT un tableau JSON des échanges avec cette structure:
|
||||
```json
|
||||
{{
|
||||
"chronologie_echanges": [
|
||||
{{"date": "date", "emetteur": "CLIENT", "type": "Question", "contenu": "contenu"}}
|
||||
]
|
||||
}}
|
||||
```
|
||||
"""
|
||||
try:
|
||||
rapport_genere = self.llm.interroger(prompt)
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur lors de la génération en une étape: {str(e)}")
|
||||
rapport_genere = f"# Rapport d'analyse: {ticket_id}\n\n## Erreur\nUne erreur est survenue lors de la génération du rapport complet.\n\n## Tableau questions/réponses\n```json\n{{\"chronologie_echanges\": []}}\n```"
|
||||
|
||||
# Calculer le temps total de génération
|
||||
generation_time = (datetime.now() - start_time).total_seconds()
|
||||
logger.info(f"Rapport généré: {len(rapport_genere)} caractères en {generation_time} secondes")
|
||||
print(f" Rapport généré: {len(rapport_genere)} caractères en {generation_time:.2f} secondes")
|
||||
|
||||
# 5. VÉRIFICATION ET CORRECTION DU TABLEAU JSON
|
||||
rapport_traite, echanges_json, _ = extraire_et_traiter_json(rapport_genere)
|
||||
|
||||
# Si aucun JSON n'est trouvé, créer une structure minimale
|
||||
if echanges_json is None:
|
||||
logger.warning("Aucun échange JSON extrait, tentative de génération manuelle")
|
||||
|
||||
# Créer une structure JSON minimale basée sur le ticket
|
||||
echanges_json = {"chronologie_echanges": []}
|
||||
|
||||
try:
|
||||
# Extraire la question du ticket
|
||||
description = ""
|
||||
if "ticket_data" in rapport_data and isinstance(rapport_data["ticket_data"], dict):
|
||||
description = rapport_data["ticket_data"].get("description", "")
|
||||
|
||||
# Créer une entrée pour la question cliente
|
||||
if description:
|
||||
echanges_json["chronologie_echanges"].append({
|
||||
"date": rapport_data.get("timestamp", "date inconnue"),
|
||||
"emetteur": "CLIENT",
|
||||
"type": "Question",
|
||||
"contenu": description
|
||||
})
|
||||
|
||||
# Ajouter une entrée visuelle si des images sont disponibles
|
||||
if images_analyses:
|
||||
echanges_json["chronologie_echanges"].append({
|
||||
"date": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
||||
"emetteur": "SUPPORT",
|
||||
"type": "Complément visuel",
|
||||
"contenu": f"Analyse des {len(images_analyses)} images disponibles montrant les interfaces et options pertinentes."
|
||||
})
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur lors de la création manuelle du JSON: {str(e)}")
|
||||
|
||||
# Extraire les sections textuelles
|
||||
resume, analyse_images, diagnostic = extraire_sections_texte(rapport_genere)
|
||||
|
||||
# 6. CRÉATION DU RAPPORT JSON
|
||||
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": agents_info,
|
||||
"approach": "two_step" if self.use_two_step_approach else "single_step"
|
||||
}
|
||||
|
||||
# Construire le rapport JSON
|
||||
rapport_json = 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,
|
||||
resume=resume,
|
||||
analyse_images=analyse_images,
|
||||
diagnostic=diagnostic,
|
||||
echanges_json=echanges_json,
|
||||
agent_metadata=agent_metadata,
|
||||
prompts_utilises=prompts_utilises
|
||||
)
|
||||
|
||||
# 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
|
||||
md_path = generer_rapport_markdown(json_path)
|
||||
|
||||
if md_path:
|
||||
logger.info(f"Rapport Markdown généré: {md_path}")
|
||||
print(f" Rapport Markdown généré: {md_path}")
|
||||
else:
|
||||
logger.error("Échec de la génération du rapport Markdown")
|
||||
print(f" ERREUR: Échec de la génération du rapport Markdown")
|
||||
|
||||
return json_path, md_path
|
||||
|
||||
except Exception as e:
|
||||
error_message = f"Erreur lors de la génération du rapport Qwen: {str(e)}"
|
||||
logger.error(error_message)
|
||||
logger.error(traceback.format_exc())
|
||||
print(f" ERREUR: {error_message}")
|
||||
return None, None
|
||||
|
||||
def _extraire_ticket_id(self, rapport_data: Dict, rapport_dir: str) -> str:
|
||||
"""Extrait l'ID du ticket des données ou du chemin"""
|
||||
# Essayer d'extraire depuis les données du rapport
|
||||
ticket_id = rapport_data.get("ticket_id", "")
|
||||
|
||||
# Si pas d'ID direct, essayer depuis les données du ticket
|
||||
if not ticket_id and "ticket_data" in rapport_data and isinstance(rapport_data["ticket_data"], dict):
|
||||
ticket_id = rapport_data["ticket_data"].get("code", "")
|
||||
|
||||
# En dernier recours, extraire depuis le chemin
|
||||
if not ticket_id:
|
||||
# Essayer d'extraire un ID de ticket (format Txxxx) du chemin
|
||||
match = re.search(r'T\d+', rapport_dir)
|
||||
if match:
|
||||
ticket_id = match.group(0)
|
||||
else:
|
||||
# Sinon, utiliser le dernier segment du chemin
|
||||
ticket_id = os.path.basename(rapport_dir)
|
||||
|
||||
return ticket_id
|
||||
|
||||
def _extraire_analyse_ticket(self, rapport_data: Dict) -> str:
|
||||
"""Extrait l'analyse du ticket des données"""
|
||||
# Essayer les différentes clés possibles
|
||||
for key in ["ticket_analyse", "analyse_json", "analyse_ticket"]:
|
||||
if key in rapport_data and rapport_data[key]:
|
||||
logger.info(f"Utilisation de {key}")
|
||||
return rapport_data[key]
|
||||
|
||||
# Créer une analyse par défaut si aucune n'est disponible
|
||||
logger.warning("Aucune analyse de ticket disponible, création d'un message par défaut")
|
||||
ticket_data = rapport_data.get("ticket_data", {})
|
||||
ticket_name = ticket_data.get("name", "Sans titre")
|
||||
ticket_desc = ticket_data.get("description", "Pas de description disponible")
|
||||
return f"Analyse par défaut du ticket:\nNom: {ticket_name}\nDescription: {ticket_desc}\n(Aucune analyse détaillée n'a été fournie)"
|
||||
|
||||
def _extraire_analyses_images(self, rapport_data: Dict) -> List[Dict]:
|
||||
"""
|
||||
Extrait et formate les analyses d'images pertinentes
|
||||
"""
|
||||
images_analyses = []
|
||||
analyse_images_data = rapport_data.get("analyse_images", {})
|
||||
|
||||
# Parcourir toutes les images
|
||||
for image_path, analyse_data in analyse_images_data.items():
|
||||
# Vérifier si l'image est pertinente
|
||||
is_relevant = False
|
||||
if "sorting" in analyse_data and isinstance(analyse_data["sorting"], dict):
|
||||
is_relevant = analyse_data["sorting"].get("is_relevant", False)
|
||||
|
||||
# Si l'image est pertinente, extraire son analyse
|
||||
if is_relevant:
|
||||
image_name = os.path.basename(image_path)
|
||||
analyse = self._extraire_analyse_image(analyse_data)
|
||||
|
||||
if analyse:
|
||||
images_analyses.append({
|
||||
"image_name": image_name,
|
||||
"image_path": image_path,
|
||||
"analyse": analyse,
|
||||
"sorting_info": analyse_data.get("sorting", {}),
|
||||
"metadata": analyse_data.get("analysis", {}).get("metadata", {})
|
||||
})
|
||||
logger.info(f"Analyse de l'image {image_name} ajoutée")
|
||||
|
||||
return images_analyses
|
||||
|
||||
def _extraire_analyse_image(self, analyse_data: Dict) -> Optional[str]:
|
||||
"""
|
||||
Extrait l'analyse d'une image depuis les données
|
||||
"""
|
||||
# Si pas de données d'analyse, retourner None
|
||||
if not "analysis" in analyse_data or not analyse_data["analysis"]:
|
||||
if "sorting" in analyse_data and isinstance(analyse_data["sorting"], dict):
|
||||
reason = analyse_data["sorting"].get("reason", "Non spécifiée")
|
||||
return f"Image marquée comme pertinente. Raison: {reason}"
|
||||
return None
|
||||
|
||||
# Extraire l'analyse selon le format des données
|
||||
analysis = analyse_data["analysis"]
|
||||
|
||||
# Structure type 1: {"analyse": "texte"}
|
||||
if isinstance(analysis, dict) and "analyse" in analysis:
|
||||
return analysis["analyse"]
|
||||
|
||||
# Structure type 2: {"error": false, ...} - contient d'autres données utiles
|
||||
if isinstance(analysis, dict) and "error" in analysis and not analysis.get("error", True):
|
||||
return str(analysis)
|
||||
|
||||
# Structure type 3: texte d'analyse direct
|
||||
if isinstance(analysis, str):
|
||||
return analysis
|
||||
|
||||
# Structure type 4: autre format de dictionnaire - convertir en JSON
|
||||
if isinstance(analysis, dict):
|
||||
return json.dumps(analysis, ensure_ascii=False, indent=2)
|
||||
|
||||
# Aucun format reconnu
|
||||
return None
|
||||
Loading…
x
Reference in New Issue
Block a user