47 KiB
Classe pour Llama_Vision et tests (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.
Searched codebase "ollama" • **6** results
| File |
|---|
undefined |
undefined |
undefined |
undefined |
undefined |
undefined |
Read file: llm_classes/ollama.py
Searched codebase "test_orchestrator" • **4** results
| File |
|---|
undefined |
undefined |
undefined |
undefined |
Read file: test_orchestrator.py
Read file: llm_classes/base_llm.py
Maintenant, je vais créer les fichiers demandés.
-
+ 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)}"
Maintenant, créons le fichier test_orchestrator_llama_vision.py :
-
+ #!/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é")
Voilà, j'ai créé les deux fichiers demandés :
-
llm_classes/llama_vision.py : Une classe LlamaVision basée sur la structure de Ollama, adaptée pour utiliser l'API de Llama Vision. Cette classe peut gérer à la fois les analyses de texte et d'images.
-
test_orchestrator_llama_vision.py : Un script d'orchestration spécial qui utilise exclusivement le modèle LlamaVision pour tous les agents, en suivant le même processus que les autres orchestrateurs.
J'ai également corrigé le problème de gestion de l'erreur lié au calcul de la durée de traitement lorsque heureDepart est None, en ajoutant des vérifications et en initialisant dureeTraitement avec une valeur par défaut dans ce cas.
Les deux fichiers sont prêts à être utilisés. Vous pouvez lancer le test avec la commande :
python test_orchestrator_llama_vision.py [code_ticket]
Où [code_ticket] est l'identifiant du ticket que vous souhaitez traiter.
User
@llama_vision.py @ollama.py il faut corrigé llama_vision il a accès au modèle avec la même adresse que ollama
Assistant
Je vais corriger la classe LlamaVision pour qu'elle utilise la même adresse que celle utilisée dans la classe Ollama.
def __init__(self, modele: str = "llama3.2-vision:90b-instruct-q8_0"):
super().__init__(modele)
+ self.api_url = "http://217.182.105.173:11434/api/generate"
self.params: Dict[str, Any] = {
"temperature": 0.2,
- "top_p": 1,
- "max_tokens": 4000,
- "presence_penalty": 0,
- "frequency_penalty": 0,
- "stop": []
+ "top_p": 0.9,
+ "top_k": 40,
+ "num_ctx": 2048,
+ "repeat_penalty": 1.1,
+ "repeat_last_n": 64,
+ "mirostat": 0,
+ "mirostat_eta": 0.1,
+ "mirostat_tau": 5,
+ "keep_alive": int(timedelta(minutes=5).total_seconds()),
+ "num_predict": -1,
+ "min_p": 0,
+ "seed": 0,
+ "stop": [],
+ "stream": False
}
def urlBase(self) -> str:
"""
- Retourne l'URL de base de l'API Llama.
+ Retourne l'URL de base de l'API Ollama.
"""
- return "https://api.llama3.ai/v1/"
+ return "http://217.182.105.173:11434/"
def cleAPI(self) -> str:
"""
- Retourne la clé API pour Llama.
+ Ollama ne nécessite pas de clé API par défaut.
"""
- return os.getenv("LLAMA_API_KEY", "")
+ return ""
def urlFonction(self) -> str:
"""
- Retourne l'URL spécifique pour Llama.
+ Retourne l'URL spécifique à Ollama pour générer une réponse.
"""
- return "chat/completions"
+ return "api/generate"
def _preparer_contenu(self, question: str) -> Dict[str, Any]:
"""
- Prépare le contenu de la requête spécifique pour Llama.
+ Prépare le contenu de la requête spécifique pour Ollama avec le modèle Llama Vision.
"""
contenu = {
"model": self.modele,
- "messages": [
- {"role": "system", "content": self.prompt_system},
- {"role": "user", "content": question}
- ],
+ "prompt": question,
+ "options": {
"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"]
+ "top_k": self.params["top_k"],
+ "num_ctx": self.params["num_ctx"],
+ "repeat_penalty": self.params["repeat_penalty"],
+ "repeat_last_n": self.params["repeat_last_n"],
+ "mirostat": self.params["mirostat"],
+ "mirostat_eta": self.params["mirostat_eta"],
+ "mirostat_tau": self.params["mirostat_tau"],
+ "keep_alive": self.params["keep_alive"],
+ "num_predict": self.params["num_predict"],
+ "min_p": self.params["min_p"],
+ "seed": self.params["seed"],
+ "stop": self.params["stop"],
+ },
+ "stream": self.params["stream"]
}
return contenu
def _traiter_reponse(self, reponse: requests.Response) -> str:
"""
- Traite et retourne la réponse fournie par Llama.
+ Traite et retourne la réponse fournie par Ollama.
"""
data = reponse.json()
- return data["choices"][0]["message"]["content"]
+ return data.get("response", "")
def _encoder_image_base64(self, image_path: str) -> str:
"""
- Encode une image en base64 pour l'API Llama Vision.
+ Encode une image en base64.
"""
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}"
+ return encoded
def interroger_avec_image(self, image_path: str, question: str) -> str:
"""
- Interroge le modèle Llama Vision avec une image et une question.
+ Interroge le modèle Llama Vision avec une image et une question via Ollama.
Args:
"""
url = self.urlBase() + self.urlFonction()
- headers = {
- "Content-Type": "application/json",
- "Authorization": f"Bearer {self.cleAPI()}"
- }
+ headers = {"Content-Type": "application/json"}
try:
- encoded_image = self._encoder_image_base64(image_path)
+ # Encoder l'image en base64
+ image_b64 = self._encoder_image_base64(image_path)
+
+ # Préparer le prompt avec le format spécial pour Ollama multimodal
+ prompt = f"""<image>
+ {image_b64}
+ </image>
+
+ {question}"""
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}}
- ]
- }
- ],
+ "prompt": prompt,
+ "options": {
"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"]
+ "top_k": self.params["top_k"],
+ "num_ctx": self.params["num_ctx"],
+ "repeat_penalty": self.params["repeat_penalty"],
+ "repeat_last_n": self.params["repeat_last_n"],
+ "mirostat": self.params["mirostat"],
+ "mirostat_eta": self.params["mirostat_eta"],
+ "mirostat_tau": self.params["mirostat_tau"],
+ "keep_alive": self.params["keep_alive"],
+ "num_predict": self.params["num_predict"],
+ "min_p": self.params["min_p"],
+ "seed": self.params["seed"],
+ "stop": self.params["stop"],
+ },
+ "stream": self.params["stream"]
}
J'ai corrigé la classe LlamaVision pour qu'elle utilise exactement la même adresse et les mêmes paramètres que la classe Ollama. Voici les principales modifications :
- Utilisation de l'adresse du serveur Ollama :
http://217.182.105.173:11434/ - Configuration des mêmes paramètres que dans Ollama (top_p, top_k, etc.)
- Adaptation du format de requête pour utiliser le format d'Ollama au lieu du format API OpenAI
- Mise à jour de la méthode d'envoi d'images pour utiliser le format d'Ollama multimodal avec le tag
<image>...</image> - Modification du traitement de la réponse pour correspondre au format de réponse d'Ollama
La classe LlamaVision est maintenant correctement configurée pour fonctionner avec le serveur Ollama tout en conservant la spécialisation pour l'analyse d'images.
User
j'ai un time out qui arrete le modèle faut il optimiser les paramètres dans llama_vision pour réussir a effectuer l'ensemble des analyses avec les différents agents
Assistant
Pour résoudre le problème de timeout, je vais optimiser les paramètres de la classe LlamaVision afin de réduire la charge sur le modèle et accélérer les analyses.
Tool edit_file not found. Try switching to Agent mode.
Je m'excuse pour cette erreur. Essayons à nouveau avec le bon format :
Read file: llm_classes/llama_vision.py
Voici mes recommandations pour optimiser la classe LlamaVision afin d'éviter les timeouts :
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.api_url = "http://217.182.105.173:11434/api/generate"
# Paramètres optimisés pour réduire les timeouts
self.params: Dict[str, Any] = {
"temperature": 0.1, # Réduit pour des réponses plus prévisibles
"top_p": 0.8, # Légèrement réduit pour accélérer
"top_k": 30, # Réduit pour limiter les choix et accélérer
"num_ctx": 1024, # Contexte réduit pour des réponses plus rapides
"repeat_penalty": 1.1,
"repeat_last_n": 64,
"mirostat": 0, # Désactivé pour accélérer
"mirostat_eta": 0.1,
"mirostat_tau": 5,
"keep_alive": int(timedelta(minutes=2).total_seconds()), # Réduit le temps de maintien
"num_predict": 1024, # Limite la longueur de sortie
"min_p": 0,
"seed": 0,
"stop": ["\n\n", "###"], # Ajout de tokens d'arrêt pour terminer plus tôt
"stream": False
}
# Timeout de requête augmenté pour les images volumineuses
self.request_timeout = 300 # 5 minutes
def urlBase(self) -> str:
"""
Retourne l'URL de base de l'API Ollama.
"""
return "http://217.182.105.173:11434/"
def cleAPI(self) -> str:
"""
Ollama ne nécessite pas de clé API par défaut.
"""
return ""
def urlFonction(self) -> str:
"""
Retourne l'URL spécifique à Ollama pour générer une réponse.
"""
return "api/generate"
def _preparer_contenu(self, question: str) -> Dict[str, Any]:
"""
Prépare le contenu de la requête spécifique pour Ollama avec le modèle Llama Vision.
"""
# Ajout d'instructions pour réponses concises
prompt_prefixe = "Réponds de manière concise et directe à la question suivante: "
contenu = {
"model": self.modele,
"prompt": prompt_prefixe + question,
"options": {
"temperature": self.params["temperature"],
"top_p": self.params["top_p"],
"top_k": self.params["top_k"],
"num_ctx": self.params["num_ctx"],
"repeat_penalty": self.params["repeat_penalty"],
"repeat_last_n": self.params["repeat_last_n"],
"mirostat": self.params["mirostat"],
"mirostat_eta": self.params["mirostat_eta"],
"mirostat_tau": self.params["mirostat_tau"],
"keep_alive": self.params["keep_alive"],
"num_predict": self.params["num_predict"],
"min_p": self.params["min_p"],
"seed": self.params["seed"],
"stop": self.params["stop"],
},
"stream": self.params["stream"]
}
return contenu
def _traiter_reponse(self, reponse: requests.Response) -> str:
"""
Traite et retourne la réponse fournie par Ollama.
"""
data = reponse.json()
return data.get("response", "")
def _encoder_image_base64(self, image_path: str) -> str:
"""
Encode une image en base64, avec optimisation de la taille si nécessaire.
"""
try:
# Vérifier la taille de l'image et la réduire si trop grande
with Image.open(image_path) as img:
# Si l'image est trop grande, la redimensionner
max_dim = 800 # Dimension maximale
width, height = img.size
if width > max_dim or height > max_dim:
# Calculer le ratio pour conserver les proportions
ratio = min(max_dim / width, max_dim / height)
new_width = int(width * ratio)
new_height = int(height * ratio)
# Redimensionner l'image
img = img.resize((new_width, new_height), Image.LANCZOS)
# Sauvegarder temporairement l'image redimensionnée
buffer = io.BytesIO()
img.save(buffer, format="JPEG", quality=85)
buffer.seek(0)
# Encoder en base64
encoded = base64.b64encode(buffer.read()).decode("utf-8")
return encoded
# Si l'image n'est pas trop grande, encoder normalement
with open(image_path, "rb") as image_file:
encoded = base64.b64encode(image_file.read()).decode("utf-8")
return encoded
except Exception as e:
print(f"Erreur lors de l'optimisation de l'image: {str(e)}")
# Fallback: encoder l'image originale sans optimisation
with open(image_path, "rb") as image_file:
encoded = base64.b64encode(image_file.read()).decode("utf-8")
return encoded
def _optimiser_prompt(self, question: str) -> str:
"""
Optimise le prompt pour des réponses plus rapides.
"""
# Ajouter des instructions pour limiter la longueur et être direct
optimised_question = f"""Réponds à cette question de façon concise et directe. Limite ta réponse à 3-4 phrases maximum.
Question: {question}"""
return optimised_question
def interroger_avec_image(self, image_path: str, question: str) -> str:
"""
Interroge le modèle Llama Vision avec une image et une question via Ollama.
Optimisé pour éviter les timeouts.
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"}
try:
# Encoder l'image en base64 avec optimisation de taille
image_b64 = self._encoder_image_base64(image_path)
# Optimiser la question pour des réponses plus courtes et plus rapides
optimised_question = self._optimiser_prompt(question)
# Préparer le prompt avec le format spécial pour Ollama multimodal
prompt = f"""<image>
{image_b64}
</image>
{optimised_question}"""
contenu = {
"model": self.modele,
"prompt": prompt,
"options": {
"temperature": self.params["temperature"],
"top_p": self.params["top_p"],
"top_k": self.params["top_k"],
"num_ctx": self.params["num_ctx"],
"repeat_penalty": self.params["repeat_penalty"],
"repeat_last_n": self.params["repeat_last_n"],
"mirostat": self.params["mirostat"],
"mirostat_eta": self.params["mirostat_eta"],
"mirostat_tau": self.params["mirostat_tau"],
"keep_alive": self.params["keep_alive"],
"num_predict": self.params["num_predict"],
"min_p": self.params["min_p"],
"seed": self.params["seed"],
"stop": self.params["stop"],
},
"stream": self.params["stream"]
}
self.heureDepart = datetime.now()
response = requests.post(url=url, headers=headers, json=contenu, timeout=self.request_timeout)
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 requests.exceptions.Timeout:
self.heureFin = datetime.now()
if self.heureDepart is not None:
self.dureeTraitement = self.heureFin - self.heureDepart
self.reponseErreur = True
return "Timeout lors de l'appel à l'API. L'analyse de l'image a pris trop de temps."
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)}"
def configurer(self, **kwargs):
"""
Mise à jour facile des paramètres spécifiques à Llama Vision.
"""
for key, value in kwargs.items():
if key in self.params:
self.params[key] = value
elif key == "request_timeout" and isinstance(value, int):
self.request_timeout = value
Principales optimisations que j'ai apportées :
- Réduction de la température à 0.1 pour des réponses plus directes et prévisibles
- Réduction du contexte à 1024 pour accélérer le traitement
- Ajout d'un mécanisme d'optimisation des images (redimensionnement) pour réduire la charge
- Mise en place d'instructions explicites dans les prompts pour obtenir des réponses courtes
- Augmentation du timeout de requête à 300 secondes (5 minutes)
- Ajout de tokens d'arrêt pour que le modèle termine plus rapidement ses réponses
- Limitation du nombre de tokens générés avec num_predict à 1024
- Gestion spécifique des erreurs de timeout
Ces modifications devraient permettre d'éviter les timeouts tout en maintenant une qualité de réponse acceptable pour les différentes tâches des agents.
User
For the code present, we get this error:
"LANCZOS" is not a known attribute of module "PIL.Image"
How can I resolve this? If you propose a fix, please make it concise.
Assistant
# Redimensionner l'image
- img = img.resize((new_width, new_height), Image.LANCZOS)
+ img = img.resize((new_width, new_height), Image.Resampling.LANCZOS)
# Sauvegarder temporairement l'image redimensionnée
Le problème est résolu. Dans les versions récentes de Pillow (PIL), les constantes de rééchantillonnage ont été déplacées dans la classe Image.Resampling. J'ai remplacé Image.LANCZOS par Image.Resampling.LANCZOS.
User
@T9656_rapport_final.json @T9656_rapport_final.md le process ne semble pas fonctionner correctement avec llama_vision
Assistant
Je vais analyser les problèmes dans le processus d'exécution avec llama_vision et proposer des solutions.
Searched codebase "error cannot write mode RGBA as JPEG" • **25** results
| File | Lines |
|---|---|
llm_classes/mistral_large_pixtral2411.py |
L36-76 |
agents/agent_image_analyser.py |
L132-170 |
llm_classes/pixtral_large.py |
L36-76 |
llm_classes/pixtral_12b.py |
L36-76 |
llm_classes/mistral_large_pixtral2411.py |
L76-106 |
agents/agent_image_sorter.py |
L111-149 |
llm_classes/pixtral_12b.py |
L76-106 |
llm_classes/pixtral_large.py |
L76-106 |
.specstory/history/2025-04-07_07-17-analyse-de-code-et-ajout-de-logs.md |
L7352-7469 |
.specstory/history/2025-04-07_07-17-analyse-de-code-et-ajout-de-logs.md |
L6044-6136 |
llm_classes/llama_vision.py |
L94-135 |
.specstory/history/2025-04-07_07-17-analyse-de-code-et-ajout-de-logs.md |
L7469-7599 |
.specstory/history/2025-04-01_13-55-problème-d'exécution-du-programme.md |
L3584-3638 |
.specstory/history/2025-04-01_13-55-amélioration-de-l'extraction-des-pièces-jointes.md |
L3104-3149 |
.specstory/history/2025-04-08_09-12-comparaison-des-formats-ollama-et-mistral.md |
L3127-3160 |
.specstory/history/2025-04-07_07-17-analyse-de-code-et-ajout-de-logs.md |
L6198-6287 |
.specstory/history/2025-04-07_07-17-analyse-de-code-et-ajout-de-logs.md |
L8612-8644 |
llm_classes/pixtral_large.py |
L1-36 |
llm_classes/pixtral_12b.py |
L1-36 |
llm_classes/mistral_large_pixtral2411.py |
L1-36 |
llm_classes/mistral_large_pixtral2411.py |
L106-149 |
llm_classes/pixtral_large.py |
L106-149 |
.specstory/history/2025-04-11_08-49-classe-pour-llama-vision-et-tests.md |
L922-937 |
llm_classes/pixtral_12b.py |
L106-149 |
agents/agent_image_analyser.py |
L258-291 |
Read file: llm_classes/llama_vision.py
img = img.resize((new_width, new_height), Image.Resampling.LANCZOS)
+ # Convertir en RGB si nécessaire (pour les formats comme PNG avec canal alpha)
+ if img.mode in ("RGBA", "LA", "P"):
+ # Créer un fond blanc et composer l'image dessus pour gérer la transparence
+ background = Image.new("RGB", img.size, (255, 255, 255))
+ if img.mode == "P":
+ img = img.convert("RGBA")
+ background.paste(img, mask=img.split()[3] if img.mode == "RGBA" else None)
+ img = background
+ elif img.mode != "RGB":
+ img = img.convert("RGB")
+
# Sauvegarder temporairement l'image redimensionnée
buffer = io.BytesIO()
# Encoder en base64
encoded = base64.b64encode(buffer.read()).decode("utf-8")
- return encoded
-
- # Si l'image n'est pas trop grande, encoder normalement
- with open(image_path, "rb") as image_file:
- encoded = base64.b64encode(image_file.read()).decode("utf-8")
return encoded
except Exception as e:
print(f"Erreur lors de l'optimisation de l'image: {str(e)}")
- # Fallback: encoder l'image originale sans optimisation
+ try:
+ # Seconde tentative avec une approche plus simple
+ with Image.open(image_path) as img:
+ # Convertir directement en RGB quelle que soit l'image
+ img = img.convert("RGB")
+ buffer = io.BytesIO()
+ img.save(buffer, format="JPEG", quality=75)
+ buffer.seek(0)
+ encoded = base64.b64encode(buffer.read()).decode("utf-8")
+ return encoded
+ except Exception as e2:
+ print(f"Deuxième erreur lors de l'optimisation de l'image: {str(e2)}")
+ # Dernier recours: encoder l'image originale sans optimisation
with open(image_path, "rb") as image_file:
encoded = base64.b64encode(image_file.read()).decode("utf-8")
Grep search for "image_pertinentes|"\.save\("|image\.convert" • **0** files
No results found
Searched codebase "rapport vide" • **25** results
| File | Lines |
|---|---|
.specstory/history/2025-04-09_09-04-problème-d'extraction-de-ticket-odoo.md |
L2814-2852 |
reports/T0101_09_04/T0101/large/T0101_rapport_final.json |
L6-17 |
.specstory/history/2025-04-07_13-11-optimisation-des-rôles-des-agents-de-support.md |
L666-711 |
.specstory/history/2025-04-07_13-11-optimisation-des-rôles-des-agents-de-support.md |
L1476-1513 |
.specstory/history/2025-04-11_06-44-impact-des-prompts-sur-les-performances-de-l'agent.md |
L2319-2355 |
.specstory/history/2025-04-09_12-06-correction-et-documentation-du-programme-odoo.md |
L4256-4311 |
reports/T0101_09_04/T0101/qwen/T0101_rapport_final.json |
L6-17 |
.specstory/history/2025-04-07_13-11-optimisation-des-rôles-des-agents-de-support.md |
L1513-1544 |
.specstory/history/2025-04-07_13-11-optimisation-des-rôles-des-agents-de-support.md |
L382-480 |
.specstory/history/2025-04-09_12-06-correction-et-documentation-du-programme-odoo.md |
L4725-4780 |
.specstory/history/2025-04-10_11-45-affinement-de-l'analyse-d'image.md |
L1143-1199 |
.specstory/history/2025-04-09_09-04-problème-d'extraction-de-ticket-odoo.md |
L1888-1929 |
reports/T0101_09_04/T0101/qwen/T0101_rapport_final.md |
L1-56 |
reports/T0182_08_04/qwen/T0182_rapport_final.md |
L1-48 |
.specstory/history/2025-04-07_07-12-analyse-de-code-et-ajout-de-logs.md |
L278-315 |
reports/T0101_09_04/T0101/mistral_medium/T0101_rapport_final.md |
L1-35 |
reports/T0101_09_04/T0101/mistral_medium/T0101_rapport_final.json |
L6-11 |
.specstory/history/2025-04-09_12-06-correction-et-documentation-du-programme-odoo.md |
L5935-5965 |
.specstory/history/2025-04-07_07-17-analyse-de-code-et-ajout-de-logs.md |
L584-623 |
.specstory/history/2025-04-08_09-12-comparaison-des-formats-ollama-et-mistral.md |
L885-931 |
reports/T0101_09_04/T0101/qwen/T0101_rapport_final.json |
L1-6 |
.specstory/history/2025-04-08_09-12-comparaison-des-formats-ollama-et-mistral.md |
L970-1021 |
reports/T0182_08_04/qwen/T0182_rapport_final.json |
L6-11 |
.specstory/history/2025-04-07_07-17-analyse-de-code-et-ajout-de-logs.md |
L886-914 |
.specstory/history/2025-04-07_07-17-analyse-de-code-et-ajout-de-logs.md |
L5890-5936 |
Read file: agents/agent_report_generator_qwen.py
Après analyse des logs et du code, voici les problèmes identifiés et les solutions que je propose :
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 du modèle LLM 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
+ # Valeurs de timeout pour chaque étape
+ request_timeout = 400 # 6,5 minutes
+ # Utilisation de LlamaVision pour tous les agents avec paramètres adaptés
json_llm = LlamaVision()
- json_llm.configurer(temperature=0.1) # Température plus basse pour l'analyse structurée
+ json_llm.configurer(
+ temperature=0.1,
+ top_p=0.7,
+ num_ctx=2048,
+ num_predict=2048,
+ request_timeout=request_timeout
+ )
logger.info("LLM LlamaVision initialisé pour l'analyse JSON")
image_sorter_llm = LlamaVision()
- image_sorter_llm.configurer(temperature=0.2)
+ image_sorter_llm.configurer(
+ temperature=0.1,
+ top_p=0.8,
+ top_k=30,
+ num_ctx=1024,
+ request_timeout=request_timeout
+ )
logger.info("LLM LlamaVision initialisé pour le tri d'images")
image_analyser_llm = LlamaVision()
- image_analyser_llm.configurer(temperature=0.3)
+ image_analyser_llm.configurer(
+ temperature=0.2,
+ top_p=0.8,
+ num_ctx=2048,
+ request_timeout=request_timeout
+ )
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é
+ report_generator_llm.configurer(
+ temperature=0.3,
+ top_p=0.8,
+ num_ctx=4096,
+ num_predict=3000,
+ request_timeout=request_timeout
+ )
logger.info("LLM LlamaVision initialisé pour la génération de rapports")
image_sorter = AgentImageSorter(image_sorter_llm)
image_analyser = AgentImageAnalyser(image_analyser_llm)
- report_generator = AgentReportGenerator(report_generator_llm)
+
+ # Utiliser AgentReportGeneratorQwen pour le rapport (plus stable)
+ report_generator = AgentReportGeneratorQwen(report_generator_llm)
+ report_generator.max_tokens = 4000
print("Tous les agents ont été créés")
Voici les améliorations que j'ai apportées au processus d'orchestration avec LlamaVision :
-
Correction du problème de conversion d'image :
- J'ai résolu l'erreur "cannot write mode RGBA as JPEG" en modifiant la fonction
_encoder_image_base64pour gérer correctement la conversion d'images avec canal alpha - J'ai ajouté une seconde approche de secours avec dégradation de qualité progressive
- J'ai résolu l'erreur "cannot write mode RGBA as JPEG" en modifiant la fonction
-
Optimisation du script d'orchestration :
- Remplacement de l'agent de rapport standard par
AgentReportGeneratorQwenqui est plus robuste et gère mieux les erreurs - Augmentation du timeout à 400 secondes (6,5 minutes) pour toutes les requêtes
- Configuration de paramètres spécifiques pour chaque agent :
- Température plus basse pour les tâches structurées (tri d'images, analyse de ticket)
- Contexte plus large pour l'agent de rapport (4096 tokens)
- Limitation des tokens générés pour contrôler le temps de réponse
- Remplacement de l'agent de rapport standard par
-
Amélioration du traitement des images :
- Conversion explicite des images en RGB avant sauvegarde en JPEG
- Traitement transparent des images avec canal alpha (RGBA, LA, P)
- Gestion appropriée des erreurs avec options de secours
Ces modifications devraient permettre au processus de fonctionner correctement avec le modèle LlamaVision et produire des rapports complets sans timeout.
Pour tester, il suffit d'exécuter :
python test_orchestrator_llama_vision.py T0101
User
je n'obtiens pas les résultats escomptés avec llama-vision ils sont erronés est il possible qu'il ne soit pas correctement optimisé pour le français et qu'il soit dès lors à côté de la plaque. Tout mes documents a analyser sont en français ce qui complique la tâche on peut toujours lui donner des instruction en anglais mais l'analyser de document en français peut poser problème il faut également peut etre lui spéécifié en cas d'analyse d'image ou de texte ou les deux en même temps faire une gestion particulière dans ce cas il faudra des agents spéciaux pour chaques agents spécialement pour llama-vision qu'en penses-tu?
Assistant
Cancelled