modifatester

This commit is contained in:
Ladebeze66 2025-03-26 15:40:31 +01:00
parent 82c933bd5f
commit bec848f5ac
13 changed files with 1804 additions and 80 deletions

113
agents/analyse_image.py Normal file
View File

@ -0,0 +1,113 @@
import os
import json
from typing import Dict, List, Any, Optional
from datetime import datetime
from agents.base_agent import Agent
from core.factory import LLMFactory
class AgentAnalyseImage(Agent):
"""
Agent pour analyser des images avec LlamaVision
"""
def __init__(self, nom: str = "AgentAnalyseImage"):
"""
Initialisation de l'agent d'analyse d'image
Args:
nom: Nom de l'agent
"""
super().__init__(nom)
self.llm = LLMFactory.create("llamavision")
self.questions_standard = [
"Décris en détail ce que tu vois sur cette image.",
"Quels sont les éléments principaux visibles sur cette image?",
"Y a-t-il du texte visible sur cette image? Si oui, peux-tu le transcrire?",
"Quelle est l'ambiance générale de cette image?"
]
def executer(self, image_path: str, json_data: Optional[Dict[str, Any]] = None,
question: Optional[str] = None) -> Dict[str, Any]:
"""
Analyse une image avec Llama Vision
Args:
image_path: Chemin vers l'image à analyser
json_data: Données JSON optionnelles associées à l'image
question: Question à poser au modèle (si None, utilise les questions standard)
Returns:
Résultats de l'analyse sous forme de dictionnaire
"""
# Vérification de l'existence de l'image
if not os.path.exists(image_path):
resultat = {"erreur": f"L'image {image_path} n'existe pas"}
self.ajouter_historique("analyse_image_erreur", image_path, resultat)
return resultat
# Chargement de l'image
with open(image_path, "rb") as image_file:
image_data = image_file.read()
# Préparation de la liste des questions à poser
questions_a_poser = [question] if question else self.questions_standard
# Exécution de l'analyse pour chaque question
resultats = {
"image": image_path,
"timestamp": datetime.now().isoformat(),
"analyses": []
}
# Traitement pour chaque question
for q in questions_a_poser:
try:
# Ajout des données JSON si fournies
if json_data:
q_enrichie = f"{q}\n\nContexte: {json.dumps(json_data, ensure_ascii=False)}"
else:
q_enrichie = q
# Appel du modèle via la factory
reponse = self.llm.generate(q_enrichie, images=[image_data])
# Ajout du résultat
resultats["analyses"].append({
"question": q,
"reponse": reponse
})
# Ajout à l'historique
self.ajouter_historique("analyse_image", q, reponse[:200] + "...")
except Exception as e:
resultats["analyses"].append({
"question": q,
"erreur": str(e)
})
self.ajouter_historique("analyse_image_erreur", q, str(e))
return resultats
def sauvegarder_resultats(self, chemin_fichier: str, resultats: Dict[str, Any]) -> bool:
"""
Sauvegarde les résultats d'analyse dans un fichier JSON
Args:
chemin_fichier: Chemin sauvegarder le fichier de résultats
resultats: Dictionnaire de résultats à sauvegarder
Returns:
True si la sauvegarde a réussi, False sinon
"""
try:
# Création du dossier parent si nécessaire
os.makedirs(os.path.dirname(os.path.abspath(chemin_fichier)), exist_ok=True)
with open(chemin_fichier, 'w', encoding='utf-8') as f:
json.dump(resultats, f, ensure_ascii=False, indent=2)
self.ajouter_historique("sauvegarder_resultats", chemin_fichier, "Résultats sauvegardés")
return True
except Exception as e:
self.ajouter_historique("sauvegarder_resultats_erreur", chemin_fichier, str(e))
return False

177
agents/analyse_json.py Normal file
View File

@ -0,0 +1,177 @@
import json
import time
from typing import Dict, List, Any, Optional
from datetime import datetime
from agents.base_agent import Agent
from core.factory import LLMFactory
class AgentAnalyseJSON(Agent):
"""
Agent pour analyser des données JSON
"""
def __init__(self, nom: str = "AgentAnalyseJSON", modele: str = "mistral7b"):
"""
Initialisation de l'agent d'analyse JSON
Args:
nom: Nom de l'agent
modele: Identifiant du modèle LLM à utiliser (par défaut: mistral7b)
"""
super().__init__(nom)
# Création du modèle via la factory
self.llm = LLMFactory.create(modele)
# Configuration du modèle
self.llm.set_role("formateur", {
"system_prompt": "Tu es un expert en analyse de données JSON. Tu dois extraire des informations pertinentes, identifier des tendances et répondre à des questions sur les données.",
"params": {
"temperature": 0.4,
"top_p": 0.9
}
})
def executer(self, json_data: Dict[str, Any],
question: str = "Analyse ces données et extrait les informations principales.") -> Dict[str, Any]:
"""
Analyse des données JSON
Args:
json_data: Données JSON à analyser
question: Question à poser au modèle
Returns:
Résultats de l'analyse sous forme de dictionnaire
"""
# Conversion du JSON en chaîne formatée
json_str = json.dumps(json_data, ensure_ascii=False, indent=2)
# Construction du prompt avec le JSON et la question
prompt = f"{question}\n\nDonnées JSON à analyser:\n```json\n{json_str}\n```"
# Interrogation du modèle
reponse = self.llm.generate(prompt)
# Construction du résultat
resultats = {
"question": question,
"reponse": reponse,
"timestamp": datetime.now().isoformat(),
"taille_json": len(json_str)
}
# Ajout à l'historique
self.ajouter_historique("analyse_json", prompt[:200] + "...", reponse[:200] + "...")
return resultats
def extraire_structure(self, json_data: Dict[str, Any]) -> Dict[str, Any]:
"""
Extrait la structure d'un JSON (clés, types, profondeur)
Args:
json_data: Données JSON à analyser
Returns:
Dictionnaire contenant la structure et des statistiques
"""
resultat = {
"structure": {},
"statistiques": {
"nb_cles": 0,
"profondeur_max": 0,
"types": {}
}
}
def explorer_structure(data, chemin="", profondeur=0):
nonlocal resultat
# Mise à jour de la profondeur max
resultat["statistiques"]["profondeur_max"] = max(resultat["statistiques"]["profondeur_max"], profondeur)
if isinstance(data, dict):
structure = {}
for cle, valeur in data.items():
nouveau_chemin = f"{chemin}.{cle}" if chemin else cle
resultat["statistiques"]["nb_cles"] += 1
type_valeur = type(valeur).__name__
if type_valeur not in resultat["statistiques"]["types"]:
resultat["statistiques"]["types"][type_valeur] = 0
resultat["statistiques"]["types"][type_valeur] += 1
if isinstance(valeur, (dict, list)):
structure[cle] = explorer_structure(valeur, nouveau_chemin, profondeur + 1)
else:
structure[cle] = type_valeur
return structure
elif isinstance(data, list):
if data and isinstance(data[0], (dict, list)):
# Pour les listes de structures complexes, on analyse le premier élément
return [explorer_structure(data[0], f"{chemin}[0]", profondeur + 1)]
else:
# Pour les listes de valeurs simples
type_elements = "vide" if not data else type(data[0]).__name__
resultat["statistiques"]["nb_cles"] += 1
if "list" not in resultat["statistiques"]["types"]:
resultat["statistiques"]["types"]["list"] = 0
resultat["statistiques"]["types"]["list"] += 1
return f"list[{type_elements}]"
else:
return type(data).__name__
resultat["structure"] = explorer_structure(json_data)
self.ajouter_historique("extraire_structure", "JSON", resultat)
return resultat
def fusionner_jsons(self, json1: Dict[str, Any], json2: Dict[str, Any]) -> Dict[str, Any]:
"""
Fusionne deux structures JSON en conservant les informations des deux
Args:
json1: Premier dictionnaire JSON
json2: Second dictionnaire JSON
Returns:
Dictionnaire fusionné
"""
if not json1:
return json2
if not json2:
return json1
resultat = json1.copy()
# Fonction récursive pour fusionner
def fusionner(dict1, dict2):
for cle, valeur in dict2.items():
if cle in dict1:
# Si les deux sont des dictionnaires, fusion récursive
if isinstance(dict1[cle], dict) and isinstance(valeur, dict):
fusionner(dict1[cle], valeur)
# Si les deux sont des listes, concaténation
elif isinstance(dict1[cle], list) and isinstance(valeur, list):
dict1[cle].extend(valeur)
# Sinon, on garde les deux valeurs dans une liste
else:
if not isinstance(dict1[cle], list):
dict1[cle] = [dict1[cle]]
if isinstance(valeur, list):
dict1[cle].extend(valeur)
else:
dict1[cle].append(valeur)
else:
# Si la clé n'existe pas dans dict1, on l'ajoute simplement
dict1[cle] = valeur
fusionner(resultat, json2)
self.ajouter_historique("fusionner_jsons", "Fusion de deux JSON", "Fusion réussie")
return resultat

50
agents/base_agent.py Normal file
View File

@ -0,0 +1,50 @@
from datetime import datetime
from typing import Dict, List, Any, Optional
class Agent:
"""Classe de base pour tous les agents d'analyse"""
def __init__(self, nom: str = "Agent"):
"""
Initialisation d'un agent
Args:
nom: Nom de l'agent
"""
self.nom: str = nom
self.historique: List[Dict[str, Any]] = []
def ajouter_historique(self, action: str, input_data: Any, output_data: Any) -> None:
"""
Ajoute une entrée dans l'historique de l'agent
Args:
action: Type d'action effectuée
input_data: Données d'entrée de l'action
output_data: Résultat de l'action
"""
self.historique.append({
"timestamp": datetime.now().isoformat(),
"action": action,
"input": str(input_data)[:500], # Limite pour éviter des historiques trop grands
"output": str(output_data)[:500] # Limite pour éviter des historiques trop grands
})
def obtenir_historique(self) -> List[Dict[str, Any]]:
"""
Retourne l'historique complet de l'agent
Returns:
Liste des actions effectuées par l'agent
"""
return self.historique
def executer(self, *args, **kwargs) -> Any:
"""
Méthode abstraite à implémenter dans les classes dérivées
C'est la méthode principale qui exécute la tâche spécifique de l'agent
Returns:
Résultat de l'exécution, dépend de l'implémentation de chaque agent
"""
raise NotImplementedError("Chaque agent doit implémenter sa propre méthode executer()")

View File

@ -38,7 +38,8 @@ llm_lab-test/
├── config/
├── prompts/
├── outputs/
└── logs/
├── logs/
└── analyse_projets.md (ce document)
```
### Principales différences
@ -138,16 +139,14 @@ La différence principale est la séparation entre les modèles et les rôles (a
#### LlamaVision90B dans llm_lab-test
- Implémentation plus simple
- Moins de paramètres configurables
- Support pour la traduction automatique
- Support pour la traduction automatique (fonctionnalité absente de l'ancien projet)
- Structure plus légère
## 4. Recommandations pour le nouveau projet
## 4. Recommandations d'implémentations pour le nouveau projet
### Intégration de la classe Agent dans llm_lab-test
### 1. Proposition d'intégration de la classe Agent abstraite
L'approche avec une classe abstraite `Agent` présente des avantages significatifs qui pourraient être bénéfiques pour le nouveau projet sans perturber son fonctionnement actuel.
Voici comment elle pourrait être implémentée :
Il serait bénéfique de créer une classe abstraite `Agent` dans le nouveau projet sans perturber son fonctionnement actuel :
```python
# Proposition pour agents/base_agent.py
@ -179,9 +178,68 @@ class Agent:
raise NotImplementedError("Chaque agent doit implémenter sa propre méthode executer()")
```
### Adaptation de LlamaVision90B avec plus de paramètres
### 2. Proposition d'agents spécialisés
Le modèle LlamaVision90B du nouveau projet pourrait bénéficier des paramètres supplémentaires disponibles dans l'ancien projet :
Des agents spécialisés pourraient être créés pour hériter de cette classe abstraite :
#### AgentAnalyseImage
Un agent qui utiliserait LlamaVision pour analyser des images :
```python
# Proposition pour agents/analyse_image.py
class AgentAnalyseImage(Agent):
"""
Agent pour analyser des images avec LlamaVision
"""
def __init__(self, nom: str = "AgentAnalyseImage"):
super().__init__(nom)
self.llm = LLMFactory.create("llamavision")
self.questions_standard = [
"Décris en détail ce que tu vois sur cette image.",
"Quels sont les éléments principaux visibles sur cette image?",
"Y a-t-il du texte visible sur cette image? Si oui, peux-tu le transcrire?",
"Quelle est l'ambiance générale de cette image?"
]
def executer(self, image_path: str, json_data: Optional[Dict[str, Any]] = None,
question: Optional[str] = None) -> Dict[str, Any]:
# Implémentation de l'analyse d'image
```
#### AgentAnalyseJSON
Un agent qui analyserait des données JSON :
```python
# Proposition pour agents/analyseJSON.py
class AgentAnalyseJSON(Agent):
"""
Agent pour analyser des données JSON
"""
def __init__(self, nom: str = "AgentAnalyseJSON", modele: str = "mistral7b"):
super().__init__(nom)
# Choix du modèle
self.llm = LLMFactory.create(modele)
# Configuration du modèle
self.llm.set_role("formateur", {
"system_prompt": "Tu es un expert en analyse de données JSON...",
"params": {
"temperature": 0.4,
"top_p": 0.9
}
})
def executer(self, json_data: Dict[str, Any],
question: str = "Analyse ces données...") -> Dict[str, Any]:
# Implémentation de l'analyse JSON
```
### 3. Proposition d'adaptation de LlamaVision90B
L'implémentation de LlamaVision90B pourrait être enrichie pour inclure tous les paramètres et fonctionnalités de la version originale :
```python
# Proposition de mise à jour pour core/llama_vision90b.py
@ -193,54 +251,48 @@ class LlamaVision90B(BaseLLM):
self.api_url = "http://217.182.105.173:11434/api/chat"
default_params = {
# Paramètres existants
# Paramètres de créativité
"temperature": 0.3,
"top_p": 1.0,
"top_k": 40,
"repeat_penalty": 1.1,
"num_predict": 512,
"num_ctx": 4096,
# Nouveaux paramètres inspirés de l'ancien projet
# Paramètres de qualité
"repeat_penalty": 1.1,
"min_p": 0.0,
# Paramètres de contrôle avancé
"mirostat": 0,
"mirostat_eta": 0.1,
"mirostat_tau": 5.0,
# Paramètres de taille
"num_predict": 512,
"num_ctx": 4096,
# Paramètres de contrôle
"seed": 0,
"stop": [],
"min_p": 0.0,
# Paramètres existants
# Autres paramètres
"format": "json",
"stream": False,
"raw": False,
"keep_alive": "5m"
}
super().__init__(model_name=model_name, engine=engine, base_params=default_params)
# Support pour les données JSON associées
# Attributs pour la gestion des images et données JSON
self.image_data = None
self.json_data = {}
def set_json_data(self, json_data: Dict[str, Any]) -> bool:
"""Définit les données JSON à associer à l'image"""
try:
self.json_data = json_data
return True
except Exception as e:
print(f"Erreur lors de la définition des données JSON: {e}")
return False
# [...] Reste de l'implémentation
super().__init__(model_name=model_name, engine=engine, base_params=default_params)
```
### Création d'une classe Mistral compatible avec l'ancien projet
### 4. Proposition de création de MistralAPI
Une nouvelle classe MistralAPI compatible avec l'API Mistral externe pourrait être ajoutée :
```python
# Proposition pour core/mistral_api.py
from core.base_llm import BaseLLM
import requests
import json
class MistralAPI(BaseLLM):
"""Intégration avec l'API Mistral (similaire à la classe Mistral de l'ancien projet)"""
@ -249,45 +301,51 @@ class MistralAPI(BaseLLM):
engine = "MistralAPI"
self.api_url = "https://api.mistral.ai/v1/chat/completions"
self.api_key = "VOTRE_CLE_API" # À remplacer par la clé réelle ou une variable d'environnement
self.api_key = os.environ.get("MISTRAL_API_KEY", "")
default_params = {
# Paramètres de génération
"temperature": 0.7,
"top_p": 0.9,
"max_tokens": 1024,
# Paramètres de contrôle
"presence_penalty": 0,
"frequency_penalty": 0,
"stop": []
"stop": [],
# Paramètres divers
"random_seed": None
}
super().__init__(model_name=model_name, engine=engine, base_params=default_params)
def generate(self, user_prompt):
prompt = self._format_prompt(user_prompt)
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {self.api_key}"
}
payload = {
"model": self.model,
"messages": [
{"role": "system", "content": self.system_prompt},
{"role": "user", "content": user_prompt}
],
"temperature": self.params.get("temperature", 0.7),
"top_p": self.params.get("top_p", 0.9),
"max_tokens": self.params.get("max_tokens", 1024)
}
response = requests.post(self.api_url, headers=headers, json=payload)
if not response.ok:
raise Exception(f"Erreur API Mistral: {response.status_code} - {response.text}")
result_data = response.json()
result_text = result_data.get("choices", [{}])[0].get("message", {}).get("content", "")
return self._log_result(user_prompt, result_text)
```
### 5. Proposition de mise à jour de la Factory
La Factory pourrait être mise à jour pour inclure le nouveau modèle MistralAPI :
```python
# Proposition de mise à jour pour core/factory.py
class LLMFactory:
"""
Factory pour créer des instances de modèles LLM dynamiquement
"""
_registry = {
"mistral7b": Mistral7B,
"llamavision": LlamaVision90B,
"mistralapi": MistralAPI,
# Ajouter d'autres modèles LLM ici
}
```
## 5. Conclusion
Cette analyse a permis d'identifier des opportunités d'enrichissement du nouveau projet llm_lab-test avec les fonctionnalités clés de l'ancien projet AIagent2, tout en conservant sa structure modulaire et moderne. Les ajouts potentiels sont :
1. Une classe abstraite Agent pour unifier les comportements des agents
2. Des implémentations d'agents spécialisés (analyse d'image et JSON)
3. Une version enrichie de LlamaVision90B avec tous les paramètres de l'original
4. Une nouvelle classe MistralAPI compatible avec l'API Mistral externe
Ces modifications permettraient de bénéficier des avantages des deux approches : la richesse fonctionnelle de l'ancien projet et la structure modulaire du nouveau.

View File

@ -1,5 +1,6 @@
from core.mistral7b import Mistral7B
from core.llama_vision90b import LlamaVision90B
from core.mistral_api import MistralAPI
class LLMFactory:
"""
@ -8,6 +9,7 @@ class LLMFactory:
_registry = {
"mistral7b": Mistral7B,
"llamavision": LlamaVision90B,
"mistralapi": MistralAPI,
# Ajouter d'autres modèles LLM ici
}

View File

@ -2,6 +2,8 @@ from core.base_llm import BaseLLM
import requests
import json
import os
import base64
from typing import Dict, List, Any, Optional
from deep_translator import GoogleTranslator
class LlamaVision90B(BaseLLM):
@ -11,30 +13,143 @@ class LlamaVision90B(BaseLLM):
self.api_url = "http://217.182.105.173:11434/api/chat"
# Paramètres enrichis basés sur l'ancien projet
default_params = {
"temperature": 0.3, #Créativité basse pour analyse technique
# Paramètres de créativité
"temperature": 0.3, # Créativité basse pour analyse technique
"top_p": 1.0, # Conserve toute la distribution
"top_k": 40, #Limite vocabulaire
"repeat_penalty": 1.1, #Réduction des répétitions
"num_predict": 512, #longueur max sortie
"num-ctx": 4096, #Contexte étendu
"format": "json", #Réponse structurée JSON (optionnel)
"stream": False, #Réponse d'un seul bloc
"raw": False, #laisse le formatage systèmes
"keep_alive": "5m" #Durée de vie de la connexion
"top_k": 40, # Limite vocabulaire
# Paramètres de qualité
"repeat_penalty": 1.1, # Réduction des répétitions
"min_p": 0.0, # Seuil minimal pour la probabilité des tokens
# Paramètres de contrôle avancé
"mirostat": 0, # 0=désactivé, 1=v1, 2=v2
"mirostat_eta": 0.1, # Taux d'apprentissage pour mirostat
"mirostat_tau": 5.0, # Cible pour mirostat
# Paramètres de taille
"num_predict": 512, # Longueur max sortie
"num_ctx": 4096, # Contexte étendu
# Paramètres de contrôle
"seed": 0, # Graine pour reproductibilité (0=aléatoire)
"stop": [], # Séquences d'arrêt
# Paramètres de format
"format": "json", # Réponse structurée JSON (optionnel)
"stream": False, # Réponse d'un seul bloc
"raw": False, # Laisse le formatage systèmes
"keep_alive": "5m" # Durée de vie de la connexion
}
super().__init__(model_name=model_name, engine=engine, base_params=default_params)
def generate(self, user_prompt: str, images: list = None, translate: bool = False):
# Attributs spécifiques pour Llama Vision
self.image_data = None # Données de l'image en bytes
self.image_path = "" # Chemin vers l'image
self.json_data = {} # Données JSON associées
self.dernier_resultat = {} # Stockage du dernier résultat d'analyse
self.historique_resultats = [] # Historique des résultats d'analyse
def set_image(self, image_path: str) -> bool:
"""
Définit l'image à analyser à partir d'un chemin de fichier
Args:
image_path: Chemin vers l'image à analyser
Returns:
True si l'image a été chargée avec succès, False sinon
"""
if not os.path.exists(image_path):
return False
try:
with open(image_path, "rb") as f:
self.image_data = f.read()
self.image_path = image_path
return True
except Exception as e:
print(f"Erreur lors du chargement de l'image: {e}")
return False
def set_image_data(self, image_data: bytes) -> bool:
"""
Définit directement les données de l'image à analyser
Args:
image_data: Données binaires de l'image
Returns:
True si les données ont été définies avec succès, False sinon
"""
try:
self.image_data = image_data
self.image_path = ""
return True
except Exception as e:
print(f"Erreur lors de la définition des données d'image: {e}")
return False
def set_json_data(self, json_data: Dict[str, Any]) -> bool:
"""
Définit les données JSON à associer à l'image
Args:
json_data: Dictionnaire de données à associer
Returns:
True si les données ont été définies avec succès, False sinon
"""
try:
self.json_data = json_data
return True
except Exception as e:
print(f"Erreur lors de la définition des données JSON: {e}")
return False
def generate(self, user_prompt: str, images: Optional[List[str]] = None, translate: bool = False):
"""
Génère une réponse à partir d'un prompt utilisateur et d'images
Args:
user_prompt: Texte du prompt utilisateur
images: Liste d'images encodées en base64 (optionnel)
translate: Indique si la réponse doit être traduite
Returns:
Réponse du modèle, ou tuple (réponse, traduction) si translate=True
"""
prompt = self._format_prompt(user_prompt)
# Si des images sont fournies directement, utilisez-les
images_to_use = images if images is not None else []
# Si image_data est défini et aucune image n'est fournie explicitement
if self.image_data is not None and (images is None or len(images) == 0):
# Encodage en base64 si ce n'est pas déjà fait
try:
if isinstance(self.image_data, bytes):
encoded_image = base64.b64encode(self.image_data).decode('utf-8')
images_to_use = [encoded_image]
except Exception as e:
print(f"Erreur lors de l'encodage de l'image: {e}")
# Ajout des données JSON dans le prompt si disponibles
if self.json_data:
json_str = json.dumps(self.json_data, ensure_ascii=False, indent=2)
# On ajoute le JSON au prompt pour qu'il soit traité avec l'image
prompt = f"{prompt}\n\nVoici des données JSON associées à cette image:\n```json\n{json_str}\n```"
payload = {
"model": self.model,
"messages": [
{
"role": "user",
"content": prompt,
"images": images if images else []
"images": images_to_use
}
],
"options": self.params,
@ -49,6 +164,10 @@ class LlamaVision90B(BaseLLM):
result_data = response.json()
result_text = result_data.get("message", {}).get("content", "")
# Stockage du résultat pour fusion ultérieure
self.dernier_resultat = result_data
self.historique_resultats.append(result_data)
self._log_result(user_prompt, result_text)
if translate:
@ -56,3 +175,59 @@ class LlamaVision90B(BaseLLM):
return result_text, result_fr
return result_text
def fusionner_json_avec_resultats(self) -> Dict[str, Any]:
"""
Fusionne les données JSON existantes avec les résultats de l'analyse d'image
Returns:
Dictionnaire combinant le JSON initial et les résultats d'analyse
"""
if not hasattr(self, 'dernier_resultat') or not self.dernier_resultat:
return self.json_data
# Créer une copie du JSON original
resultat_fusionne = self.json_data.copy() if self.json_data else {}
# Ajouter le résultat de l'analyse d'image
if "analyse_image" not in resultat_fusionne:
resultat_fusionne["analyse_image"] = []
# Ajouter le résultat à la liste des analyses
nouvelle_analyse = {
"modele": self.model,
"reponse": self.dernier_resultat.get("message", {}).get("content", ""),
"parametres": {
"temperature": self.params.get("temperature"),
"top_p": self.params.get("top_p"),
"num_ctx": self.params.get("num_ctx")
}
}
resultat_fusionne["analyse_image"].append(nouvelle_analyse)
return resultat_fusionne
def sauvegarder_resultats(self, chemin_fichier: str) -> bool:
"""
Sauvegarde les résultats fusionnés dans un fichier JSON
Args:
chemin_fichier: Chemin sauvegarder le fichier de résultats
Returns:
True si la sauvegarde a réussi, False sinon
"""
try:
resultats_fusionnes = self.fusionner_json_avec_resultats()
# Création du dossier parent si nécessaire
os.makedirs(os.path.dirname(os.path.abspath(chemin_fichier)), exist_ok=True)
with open(chemin_fichier, 'w', encoding='utf-8') as f:
json.dump(resultats_fusionnes, f, ensure_ascii=False, indent=2)
return True
except Exception as e:
print(f"Erreur lors de la sauvegarde des résultats: {e}")
return False

134
core/mistral_api.py Normal file
View File

@ -0,0 +1,134 @@
from core.base_llm import BaseLLM
import requests
import json
import os
from typing import Dict, List, Any, Optional
class MistralAPI(BaseLLM):
"""Intégration avec l'API Mistral (similaire à la classe Mistral de l'ancien projet)"""
def __init__(self):
model_name = "mistral-large-latest"
engine = "MistralAPI"
self.api_url = "https://api.mistral.ai/v1/chat/completions"
# À remplacer par la clé réelle ou une variable d'environnement
self.api_key = os.environ.get("MISTRAL_API_KEY", "")
default_params = {
# Paramètres de génération
"temperature": 0.7,
"top_p": 0.9,
"max_tokens": 1024,
# Paramètres de contrôle
"presence_penalty": 0,
"frequency_penalty": 0,
"stop": [],
# Paramètres divers
"random_seed": None
}
super().__init__(model_name=model_name, engine=engine, base_params=default_params)
def generate(self, user_prompt: str) -> str:
"""
Génère une réponse à partir du prompt utilisateur via l'API Mistral
Args:
user_prompt: Texte du prompt utilisateur
Returns:
Réponse générée par le modèle
Raises:
ValueError: Si la clé API n'est pas définie
Exception: Si une erreur survient lors de l'appel à l'API
"""
prompt = self._format_prompt(user_prompt)
if not self.api_key:
raise ValueError("Clé API Mistral non définie. Définissez la variable d'environnement MISTRAL_API_KEY.")
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {self.api_key}"
}
# Préparation des messages
messages = []
# Ajout du prompt système si défini
if self.system_prompt:
messages.append({"role": "system", "content": self.system_prompt})
# Ajout du prompt utilisateur
messages.append({"role": "user", "content": user_prompt})
# Préparation du payload
payload = {
"model": self.model,
"messages": messages,
"temperature": self.params.get("temperature", 0.7),
"top_p": self.params.get("top_p", 0.9),
"max_tokens": self.params.get("max_tokens", 1024)
}
# Ajout des paramètres optionnels
if self.params.get("presence_penalty") is not None:
payload["presence_penalty"] = self.params.get("presence_penalty")
if self.params.get("frequency_penalty") is not None:
payload["frequency_penalty"] = self.params.get("frequency_penalty")
# Vérifier que stop est une liste non vide
stop_sequences = self.params.get("stop")
if isinstance(stop_sequences, list) and stop_sequences:
payload["stop"] = stop_sequences
if self.params.get("random_seed") is not None:
payload["random_seed"] = self.params.get("random_seed")
# Envoi de la requête
response = requests.post(self.api_url, headers=headers, json=payload)
if not response.ok:
raise Exception(f"Erreur API Mistral: {response.status_code} - {response.text}")
# Traitement de la réponse
result_data = response.json()
result_text = result_data.get("choices", [{}])[0].get("message", {}).get("content", "")
# Logging du résultat
filename = self._log_result(user_prompt, result_text)
return result_text
def obtenir_liste_modeles(self) -> List[str]:
"""
Récupère la liste des modèles disponibles via l'API Mistral
Returns:
Liste des identifiants de modèles disponibles
Raises:
ValueError: Si la clé API n'est pas définie
Exception: Si une erreur survient lors de l'appel à l'API
"""
if not self.api_key:
raise ValueError("Clé API Mistral non définie. Définissez la variable d'environnement MISTRAL_API_KEY.")
headers = {
"Authorization": f"Bearer {self.api_key}"
}
response = requests.get("https://api.mistral.ai/v1/models", headers=headers)
if not response.ok:
raise Exception(f"Erreur API Mistral: {response.status_code} - {response.text}")
result_data = response.json()
models = [model.get("id") for model in result_data.get("data", [])]
return models

123
docs/README.md Normal file
View File

@ -0,0 +1,123 @@
# LLM Lab - Documentation
## Vue d'ensemble
LLM Lab est une plateforme modulaire conçue pour l'expérimentation avec différents modèles de langage (LLM) et la création d'agents spécialisés. Cette architecture permet l'intégration facile de nouveaux modèles et la création rapide d'agents pour des tâches spécifiques.
## Architecture du projet
```
llm_lab-test/
├── agents/ # Agents spécialisés
│ ├── base_agent.py # Classe abstraite de base pour tous les agents
│ ├── analyse_image.py # Agent pour l'analyse d'images
│ └── analyse_json.py # Agent pour l'analyse de données JSON
├── core/ # Composants principaux
│ ├── factory.py # Factory pour l'instanciation des LLMs
│ ├── llama_vision90b.py # Implémentation de LlamaVision pour l'analyse d'images
│ └── mistral_api.py # Implémentation de l'API Mistral
├── examples/ # Exemples d'utilisation
│ ├── analyse_image_workflow.py # Workflow d'analyse d'images
│ ├── test_mistralapi.py # Test de l'API Mistral
│ └── agent_question_reponse.py # Agent de questions-réponses
├── config/ # Fichiers de configuration
├── prompts/ # Templates de prompts
├── outputs/ # Répertoire pour les sorties générées
├── logs/ # Journaux d'exécution
└── tests/ # Tests unitaires
```
## Modèles LLM disponibles
Le projet intègre plusieurs modèles de langage accessibles via la factory `LLMFactory` :
1. **LlamaVision90B** : Modèle spécialisé dans l'analyse d'images avec capacités de vision.
2. **MistralAPI** : Interface pour l'API Mistral, permettant d'accéder aux modèles comme mistral-large-latest.
## Agents disponibles
Les agents sont des composants spécialisés qui encapsulent des fonctionnalités spécifiques :
### Agent de base (`base_agent.py`)
Classe abstraite définissant l'interface commune à tous les agents, avec :
- Gestion d'un historique des actions
- Méthode abstraite `executer()` à implémenter par les sous-classes
### Agent d'analyse d'images (`analyse_image.py`)
Spécialisé dans l'analyse d'images via LlamaVision :
- Analyse d'images individuelles
- Intégration avec des données JSON contextuelles
- Extraction d'informations visuelles (texte, objets, scènes)
### Agent d'analyse JSON (`analyse_json.py`)
Analyse et traite des données JSON :
- Extraction de structure
- Fusion de données JSON
- Analyse approfondie du contenu
### Agent de questions-réponses (`agent_question_reponse.py`)
Agent conversationnel utilisant MistralAPI :
- Réponses à des questions avec ou sans contexte
- Génération de questions pertinentes
- Évaluation de la qualité des réponses
## Exemples d'utilisation
### Workflow d'analyse d'images
```python
# Initialisation d'un agent d'analyse d'image
agent = AgentAnalyseImage("AgentVision")
# Analyse d'une image
resultats = agent.executer("chemin/vers/image.jpg",
question="Décris cette image en détail")
```
### Utilisation de MistralAPI
```python
# Via la factory
llm = LLMFactory().create("mistralapi")
# Configuration des paramètres
llm.parametres_generation["temperature"] = 0.7
# Génération de texte
reponse = llm.generate("Explique-moi comment fonctionne un LLM")
```
### Agent de questions-réponses
```python
# Initialisation
agent = AgentQuestionReponse("AgentQR")
# Poser une question
resultat = agent.executer("Qu'est-ce que l'apprentissage par renforcement?")
print(resultat["reponse"])
# Générer des questions sur un sujet
questions = agent.generer_questions(contexte_json, nombre=3)
```
## Configuration requise
- Python 3.8+
- Clé API Mistral (pour MistralAPI)
- Dépendances listées dans `requirements.txt`
## Pour commencer
1. Cloner le dépôt
2. Installer les dépendances : `pip install -r requirements.txt`
3. Configurer les variables d'environnement nécessaires (ex: `MISTRAL_API_KEY`)
4. Explorer les exemples dans le dossier `examples/`

250
docs/guide_utilisation.md Normal file
View File

@ -0,0 +1,250 @@
# Guide d'utilisation des agents et classes LLM
Ce guide fournit des instructions détaillées sur l'utilisation des différents agents et classes LLM disponibles dans le projet LLM Lab.
## Table des matières
1. [Utilisation des modèles LLM](#utilisation-des-modèles-llm)
- [LlamaVision90B](#llamavision90b)
- [MistralAPI](#mistralapi)
2. [Utilisation des agents](#utilisation-des-agents)
- [Agent d'analyse d'images](#agent-danalyse-dimages)
- [Agent d'analyse JSON](#agent-danalyse-json)
- [Agent de questions-réponses](#agent-de-questions-réponses)
3. [Exemples de workflows complets](#exemples-de-workflows-complets)
## Utilisation des modèles LLM
Les modèles LLM sont accessibles via la factory `LLMFactory` pour garantir une instanciation cohérente et configurable.
### LlamaVision90B
Ce modèle est spécialisé dans l'analyse d'images avec des capacités multimodales (texte + image).
#### Configuration de base
```python
from core.factory import LLMFactory
# Création d'une instance via la factory
vision_llm = LLMFactory().create("llamavision")
# Configuration des paramètres
vision_llm.parametres_generation["temperature"] = 0.2
vision_llm.parametres_generation["max_tokens"] = 1000
vision_llm.parametres_avances["top_p"] = 0.9
```
#### Analyse d'une image
```python
# Définir le chemin de l'image
vision_llm.definir_image("chemin/vers/image.jpg")
# Générer une description de l'image
prompt = "Décris en détail ce que tu vois dans cette image."
description = vision_llm.generate(prompt)
print(description)
```
#### Analyse avec des données JSON complémentaires
```python
# Définir des données JSON complémentaires
donnees_complementaires = {
"metadata": {
"source": "caméra de surveillance",
"timestamp": "2023-03-15T14:30:00",
"location": "entrée principale"
}
}
vision_llm.definir_donnees_json(donnees_complementaires)
# Générer une analyse avec contexte
prompt = "Analyse cette image en tenant compte des métadonnées fournies."
resultat = vision_llm.generate(prompt)
```
### MistralAPI
Cette classe permet d'accéder aux modèles de langage de Mistral AI via leur API.
#### Configuration de base
```python
from core.factory import LLMFactory
import os
# Définir la clé API (ou via variable d'environnement)
os.environ["MISTRAL_API_KEY"] = "votre_clé_api"
# Création d'une instance via la factory
mistral = LLMFactory().create("mistralapi")
# Configuration des paramètres
mistral.parametres_generation["temperature"] = 0.7
mistral.parametres_generation["max_tokens"] = 500
```
#### Génération de texte
```python
# Génération simple
prompt = "Explique le concept de l'intelligence artificielle en 3 paragraphes."
reponse = mistral.generate(prompt)
print(reponse)
```
#### Obtention de la liste des modèles disponibles
```python
modeles = mistral.obtenir_liste_modeles()
print("Modèles disponibles:")
for modele in modeles:
print(f"- {modele}")
```
## Utilisation des agents
Les agents sont des composants de plus haut niveau qui encapsulent les modèles LLM avec des fonctionnalités spécifiques.
### Agent d'analyse d'images
Cet agent facilite l'analyse d'images en utilisant LlamaVision.
```python
from agents.analyse_image import AgentAnalyseImage
# Création de l'agent
agent = AgentAnalyseImage("AgentVision")
# Analyse simple d'une image
resultats = agent.executer(
"chemin/vers/image.jpg",
question="Que vois-tu dans cette image? Y a-t-il du texte visible?"
)
# Affichage des résultats
print(resultats["analyses"][0]["reponse"])
# Sauvegarde des résultats
agent.sauvegarder_resultats("resultats_analyse.json", resultats)
```
### Agent d'analyse JSON
Cet agent permet d'analyser et de manipuler des données JSON.
```python
from agents.analyse_json import AgentAnalyseJSON
# Création de l'agent
agent = AgentAnalyseJSON("AgentJSON")
# Données JSON à analyser
donnees = {
"utilisateurs": [
{"id": 1, "nom": "Dupont", "age": 32},
{"id": 2, "nom": "Martin", "age": 45},
{"id": 3, "nom": "Durand", "age": 28}
],
"statistiques": {
"moyenne_age": 35,
"total_utilisateurs": 3
}
}
# Analyse des données
resultats = agent.executer(
donnees,
question="Quelles informations importantes peux-tu extraire de ces données?"
)
print(resultats["analyse"])
# Extraction de la structure
structure = agent.extraire_structure(donnees)
print("Structure des données:", structure)
```
### Agent de questions-réponses
Cet agent utilise MistralAPI pour des interactions de type questions-réponses.
```python
from agents.agent_question_reponse import AgentQuestionReponse
# Création de l'agent avec le modèle souhaité
agent = AgentQuestionReponse("AgentQR", modele="mistral-large-latest")
# Poser une question simple
resultat = agent.executer("Comment fonctionne l'apprentissage par renforcement?")
print(resultat["reponse"])
# Poser une question avec contexte
contexte = {
"sujet": "Intelligence artificielle",
"domaine": "Apprentissage automatique",
"concepts_cles": ["réseaux de neurones", "deep learning", "données d'entraînement"]
}
resultat_avec_contexte = agent.executer(
"Quelles sont les différences entre l'apprentissage supervisé et non supervisé?",
contexte=contexte
)
print(resultat_avec_contexte["reponse"])
# Générer des questions à partir d'un contexte
questions = agent.generer_questions(contexte, nombre=5)
for i, question in enumerate(questions, 1):
print(f"{i}. {question}")
# Évaluer une réponse
evaluation = agent.evaluer_reponse(
question="Qu'est-ce que le deep learning?",
reponse="Le deep learning est une technique d'IA qui utilise des réseaux de neurones profonds."
)
print(evaluation["evaluation_detaillee"])
print("Scores:", evaluation["scores"])
```
## Exemples de workflows complets
### Workflow d'analyse d'image avec post-traitement
```python
from agents.analyse_image import AgentAnalyseImage
from agents.analyse_json import AgentAnalyseJSON
from agents.agent_question_reponse import AgentQuestionReponse
import json
# 1. Analyse de l'image
agent_image = AgentAnalyseImage("AgentVision")
resultats_image = agent_image.executer("chemin/vers/image.jpg")
# 2. Analyse des résultats JSON
agent_json = AgentAnalyseJSON("AgentJSON")
resultats_analyse = agent_json.executer(
resultats_image,
question="Extrais les éléments clés détectés dans l'image."
)
# 3. Génération de questions sur le contenu
agent_qr = AgentQuestionReponse("AgentQR")
questions = agent_qr.generer_questions(resultats_analyse, nombre=3)
# 4. Sauvegarde des résultats complets
workflow_results = {
"analyse_image": resultats_image,
"analyse_structure": resultats_analyse,
"questions_potentielles": questions
}
with open("resultats_workflow.json", "w", encoding="utf-8") as f:
json.dump(workflow_results, f, ensure_ascii=False, indent=2)
```
Ce workflow illustre comment combiner plusieurs agents pour créer une chaîne de traitement complète, de l'analyse d'image initiale jusqu'à la génération de questions pertinentes sur le contenu identifié.

View File

@ -0,0 +1,337 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Agent de questions-réponses utilisant MistralAPI
"""
import os
import sys
import json
import argparse
from pathlib import Path
from typing import Dict, List, Any, Optional
from datetime import datetime
# Ajouter le répertoire parent au sys.path pour importer les modules
sys.path.append(str(Path(__file__).parent.parent))
from agents.base_agent import Agent
from core.factory import LLMFactory
class AgentQuestionReponse(Agent):
"""
Agent spécialisé dans les questions-réponses utilisant Mistral API
"""
def __init__(self, nom: str, modele: str = "mistral-large-latest"):
"""
Initialise l'agent avec un nom et un modèle Mistral
Args:
nom: Nom de l'agent
modele: Modèle Mistral à utiliser
"""
super().__init__(nom)
self.llm = LLMFactory().create("mistralapi")
self.llm.modele = modele
def executer(self, question: str, contexte: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
"""
Exécute une requête de question-réponse
Args:
question: La question à poser
contexte: Contexte supplémentaire pour la question (optionnel)
Returns:
Dictionnaire contenant la réponse et les métadonnées
"""
# Formater le prompt avec le contexte si disponible
prompt = self._construire_prompt(question, contexte)
try:
# Générer la réponse
reponse = self.llm.generate(prompt)
# Enregistrer dans l'historique
input_data = {
"type": "question",
"contenu": question,
"contexte": contexte
}
output_data = {
"type": "reponse",
"contenu": reponse
}
self.ajouter_historique("generation_reponse", input_data, output_data)
# Préparer le résultat
resultat = {
"question": question,
"reponse": reponse,
"timestamp": datetime.now().isoformat(),
"modele": self.llm.modele
}
if contexte:
resultat["contexte_utilise"] = True
return resultat
except Exception as e:
return {
"erreur": str(e),
"question": question,
"timestamp": datetime.now().isoformat()
}
def _construire_prompt(self, question: str, contexte: Optional[Dict[str, Any]] = None) -> str:
"""
Construit le prompt en fonction de la question et du contexte
"""
if not contexte:
return question
# Formater le contexte en texte
contexte_texte = self._formater_contexte(contexte)
# Construire le prompt avec le contexte
prompt = f"""Contexte :
{contexte_texte}
Question : {question}
Réponds à la question ci-dessus en utilisant le contexte fourni.
Si le contexte ne suffit pas pour répondre complètement, indique-le clairement.
"""
return prompt
def _formater_contexte(self, contexte: Dict[str, Any]) -> str:
"""
Formate le contexte en texte lisible
"""
if isinstance(contexte, str):
return contexte
if isinstance(contexte, dict):
# Si le contexte est un dictionnaire JSON
try:
return json.dumps(contexte, ensure_ascii=False, indent=2)
except:
parts = []
for key, value in contexte.items():
parts.append(f"{key}: {value}")
return "\n".join(parts)
# Fallback pour d'autres types
return str(contexte)
def generer_questions(self, contexte: Dict[str, Any], nombre: int = 3) -> List[str]:
"""
Génère des questions pertinentes basées sur un contexte
Args:
contexte: Le contexte à partir duquel générer des questions
nombre: Nombre de questions à générer
Returns:
Liste des questions générées
"""
contexte_texte = self._formater_contexte(contexte)
prompt = f"""À partir du contexte suivant :
{contexte_texte}
Génère {nombre} questions pertinentes et intéressantes qui pourraient être posées sur ce contenu.
Format attendu : une question par ligne, numérotée (1., 2., etc.)
"""
try:
reponse = self.llm.generate(prompt)
# Extraire les questions de la réponse
questions = []
for ligne in reponse.split('\n'):
ligne = ligne.strip()
if ligne and (ligne[0].isdigit() and ligne[1:3] in ['. ', '- ', ') ']):
# Extraire la question après le numéro
question = ligne[ligne.find(' ')+1:].strip()
questions.append(question)
# Si l'extraction a échoué, retourner la réponse brute divisée
if not questions and reponse:
questions = [q.strip() for q in reponse.split('\n') if q.strip()]
return questions[:nombre] # Limiter au nombre demandé
except Exception as e:
print(f"Erreur lors de la génération de questions: {e}")
return []
def evaluer_reponse(self, question: str, reponse: str, reference: Optional[str] = None) -> Dict[str, Any]:
"""
Évalue la qualité d'une réponse à une question
Args:
question: La question posée
reponse: La réponse à évaluer
reference: Réponse de référence (optionnelle)
Returns:
Dictionnaire contenant l'évaluation et les scores
"""
if reference:
prompt = f"""Question : {question}
Réponse à évaluer :
{reponse}
Réponse de référence :
{reference}
Évalue la réponse fournie par rapport à la réponse de référence. Attribue un score pour chaque critère :
1. Précision (1-10) : La réponse est-elle factuelle et correcte ?
2. Complétude (1-10) : La réponse couvre-t-elle tous les aspects importants ?
3. Clarté (1-10) : La réponse est-elle claire et facile à comprendre ?
Pour chaque critère, justifie brièvement ton score. Termine par une note globale sur 10.
"""
else:
prompt = f"""Question : {question}
Réponse à évaluer :
{reponse}
Évalue la qualité de cette réponse. Attribue un score pour chaque critère :
1. Précision (1-10) : La réponse semble-t-elle factuelle et correcte ?
2. Complétude (1-10) : La réponse semble-t-elle couvrir tous les aspects importants ?
3. Clarté (1-10) : La réponse est-elle claire et facile à comprendre ?
Pour chaque critère, justifie brièvement ton score. Termine par une note globale sur 10.
"""
try:
evaluation = self.llm.generate(prompt)
# Tenter d'extraire les scores numériques
scores = {}
for ligne in evaluation.split('\n'):
if "précision" in ligne.lower() and ":" in ligne:
try:
score = int([s for s in ligne.split() if s.isdigit()][0])
scores["precision"] = score
except:
pass
elif "complétude" in ligne.lower() and ":" in ligne:
try:
score = int([s for s in ligne.split() if s.isdigit()][0])
scores["completude"] = score
except:
pass
elif "clarté" in ligne.lower() and ":" in ligne:
try:
score = int([s for s in ligne.split() if s.isdigit()][0])
scores["clarte"] = score
except:
pass
elif "globale" in ligne.lower() and ":" in ligne:
try:
score = int([s for s in ligne.split() if s.isdigit()][0])
scores["globale"] = score
except:
pass
return {
"evaluation_detaillee": evaluation,
"scores": scores
}
except Exception as e:
return {
"erreur": str(e),
"evaluation_detaillee": "Échec de l'évaluation"
}
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Agent de Questions-Réponses avec Mistral API")
parser.add_argument("--question", help="Question à poser")
parser.add_argument("--contexte", help="Chemin vers un fichier JSON de contexte")
parser.add_argument("--model", default="mistral-large-latest", help="Modèle Mistral à utiliser")
parser.add_argument("--generer-questions", action="store_true", help="Générer des questions à partir du contexte")
parser.add_argument("--nombre", type=int, default=3, help="Nombre de questions à générer")
parser.add_argument("--evaluer", help="Réponse à évaluer (nécessite --question)")
parser.add_argument("--reference", help="Réponse de référence pour l'évaluation")
args = parser.parse_args()
# Vérification de la clé API
if not os.environ.get("MISTRAL_API_KEY"):
print("Erreur: Variable d'environnement MISTRAL_API_KEY non définie.")
print("Veuillez définir cette variable avec votre clé API Mistral.")
print("Exemple: export MISTRAL_API_KEY=votre_clé_api")
sys.exit(1)
# Initialisation de l'agent
agent = AgentQuestionReponse("AgentQR", modele=args.model)
# Chargement du contexte si spécifié
contexte = None
if args.contexte:
try:
with open(args.contexte, 'r', encoding='utf-8') as f:
contexte = json.load(f)
print(f"Contexte chargé depuis {args.contexte}")
except Exception as e:
print(f"Erreur lors du chargement du contexte: {e}")
sys.exit(1)
# Génération de questions à partir du contexte
if args.generer_questions and contexte:
print(f"\n=== Génération de {args.nombre} questions à partir du contexte ===")
questions = agent.generer_questions(contexte, nombre=args.nombre)
print("\nQuestions générées:")
for i, question in enumerate(questions, 1):
print(f"{i}. {question}")
sys.exit(0)
# Évaluation d'une réponse
if args.evaluer and args.question:
print("\n=== Évaluation de la réponse ===")
evaluation = agent.evaluer_reponse(args.question, args.evaluer, args.reference)
if "erreur" in evaluation:
print(f"Erreur: {evaluation['erreur']}")
else:
print("\nÉvaluation détaillée:")
print(evaluation["evaluation_detaillee"])
if evaluation.get("scores"):
print("\nScores:")
for critere, score in evaluation["scores"].items():
print(f"- {critere.capitalize()}: {score}/10")
sys.exit(0)
# Traitement d'une question simple
if args.question:
print(f"\n=== Traitement de la question avec le modèle {args.model} ===")
print(f"Question: {args.question}")
resultat = agent.executer(args.question, contexte)
if "erreur" in resultat:
print(f"Erreur: {resultat['erreur']}")
else:
print("\nRéponse:")
print(resultat["reponse"])
sys.exit(0)
# Si aucune action spécifiée
parser.print_help()

View File

@ -0,0 +1,137 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Script d'exemple pour montrer l'utilisation des agents d'analyse d'image et JSON
"""
import os
import sys
import json
import argparse
from datetime import datetime
from pathlib import Path
# Ajouter le répertoire parent au sys.path pour importer les modules
sys.path.append(str(Path(__file__).parent.parent))
from agents.analyse_image import AgentAnalyseImage
from agents.analyse_json import AgentAnalyseJSON
def create_output_dir():
"""Crée un répertoire pour les sorties basé sur la date/heure"""
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
output_dir = f"outputs/vision_{timestamp}"
os.makedirs(output_dir, exist_ok=True)
return output_dir
def analyse_directe(image_path, output_dir):
"""Analyse directe avec l'agent d'analyse d'image"""
print(f"\n=== Analyse directe de l'image {os.path.basename(image_path)} ===")
# Création d'une instance AgentAnalyseImage
agent = AgentAnalyseImage("AgentVision")
# Analyse de l'image avec une question spécifique
question = "Décris en détail cette image. Qu'est-ce que tu y vois? Y a-t-il du texte visible?"
print(f"Question: {question}")
resultats = agent.executer(image_path, question=question)
if "erreur" in resultats:
print(f"Erreur: {resultats['erreur']}")
return
# Affichage du résultat
print("\nRéponse:")
for analyse in resultats["analyses"]:
print(f"\n{analyse['reponse']}")
# Sauvegarde du résultat
output_file = os.path.join(output_dir, "resultat_direct.json")
agent.sauvegarder_resultats(output_file, resultats)
print(f"\nRésultat sauvegardé dans: {output_file}")
def workflow_complet(image_path, json_path=None, output_dir=None):
"""Workflow complet: analyse image puis analyse JSON"""
print(f"\n=== Workflow complet pour l'image {os.path.basename(image_path)} ===")
if output_dir is None:
output_dir = create_output_dir()
# Étape 1: Analyse de l'image
print("\nÉtape 1: Analyse de l'image")
agent_image = AgentAnalyseImage("AgentVision")
# Chargement des données JSON si spécifiées
json_data = None
if json_path:
try:
with open(json_path, 'r', encoding='utf-8') as f:
json_data = json.load(f)
print(f"Données JSON chargées depuis {json_path}")
except Exception as e:
print(f"Erreur lors du chargement du JSON: {e}")
# Exécution de l'analyse d'image
resultats_image = agent_image.executer(image_path, json_data)
# Sauvegarde des résultats intermédiaires
image_output_file = os.path.join(output_dir, "analyse_image.json")
agent_image.sauvegarder_resultats(image_output_file, resultats_image)
print(f"Analyse d'image sauvegardée dans: {image_output_file}")
# Étape 2: Analyse des résultats JSON
print("\nÉtape 2: Analyse approfondie des résultats")
agent_json = AgentAnalyseJSON("AgentAnalyseJSON")
# Analyse des résultats de l'image
question_json = "Analyse les résultats de l'analyse d'image et extrait les éléments clés."
resultats_analyse = agent_json.executer(resultats_image, question_json)
# Sauvegarde des résultats d'analyse
json_output_file = os.path.join(output_dir, "analyse_complete.json")
with open(json_output_file, 'w', encoding='utf-8') as f:
json.dump(resultats_analyse, f, ensure_ascii=False, indent=2)
print(f"Analyse complète sauvegardée dans: {json_output_file}")
# Fusion des résultats
resultats_finaux = {
"image": image_path,
"timestamp": datetime.now().isoformat(),
"analyse_image": resultats_image,
"analyse_json": resultats_analyse
}
# Sauvegarde des résultats finaux
final_output_file = os.path.join(output_dir, "resultats_finaux.json")
with open(final_output_file, 'w', encoding='utf-8') as f:
json.dump(resultats_finaux, f, ensure_ascii=False, indent=2)
print(f"Résultats finaux sauvegardés dans: {final_output_file}")
return resultats_finaux
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Analyse d'images avec les agents")
parser.add_argument("--image", required=True, help="Chemin vers l'image à analyser")
parser.add_argument("--json", help="Chemin vers un fichier JSON complémentaire (optionnel)")
parser.add_argument("--mode", choices=["direct", "complet"], default="complet",
help="Mode d'analyse: direct (simple) ou complet (workflow)")
args = parser.parse_args()
# Vérification de l'existence de l'image
if not os.path.exists(args.image):
print(f"Erreur: L'image {args.image} n'existe pas")
sys.exit(1)
# Création du répertoire de sortie
output_dir = create_output_dir()
# Exécution du mode choisi
if args.mode == "direct":
analyse_directe(args.image, output_dir)
else:
workflow_complet(args.image, args.json, output_dir)
print("\nAnalyse terminée !")

103
examples/test_mistralapi.py Normal file
View File

@ -0,0 +1,103 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Script d'exemple pour montrer l'utilisation de la classe MistralAPI
"""
import os
import sys
import argparse
from pathlib import Path
# Ajouter le répertoire parent au sys.path pour importer les modules
sys.path.append(str(Path(__file__).parent.parent))
from core.factory import LLMFactory
def afficher_modeles_disponibles():
"""Affiche les modèles disponibles via l'API Mistral"""
# Création d'une instance MistralAPI via la factory
llm = LLMFactory().create("mistralapi")
try:
modeles = llm.obtenir_liste_modeles()
print("\n=== Modèles disponibles sur l'API Mistral ===")
if not modeles:
print("Aucun modèle trouvé ou erreur de récupération.")
return
for i, modele in enumerate(modeles, 1):
print(f"{i}. {modele}")
except Exception as e:
print(f"Erreur lors de la récupération des modèles: {e}")
def generer_reponse(prompt, modele=None, temperature=None, max_tokens=None):
"""Génère une réponse avec MistralAPI"""
# Création d'une instance MistralAPI via la factory
llm = LLMFactory().create("mistralapi")
# Configuration des paramètres si spécifiés
if modele:
llm.modele = modele
if temperature is not None:
llm.parametres_generation["temperature"] = temperature
if max_tokens is not None:
llm.parametres_generation["max_tokens"] = max_tokens
# Affichage des paramètres utilisés
print(f"\n=== Génération avec MistralAPI ===")
print(f"Modèle: {llm.modele}")
print(f"Température: {llm.parametres_generation['temperature']}")
print(f"Max tokens: {llm.parametres_generation.get('max_tokens', 'Non spécifié')}")
print(f"\nPrompt: {prompt}\n")
try:
# Génération de la réponse
reponse = llm.generate(prompt)
# Affichage de la réponse
print("\n=== Réponse ===")
print(reponse)
return reponse
except Exception as e:
print(f"Erreur lors de la génération: {e}")
return None
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Test de l'API Mistral")
parser.add_argument("--list-models", action="store_true", help="Afficher les modèles disponibles")
parser.add_argument("--prompt", help="Prompt pour générer une réponse")
parser.add_argument("--model", help="Nom du modèle à utiliser (ex: mistral-large-latest)")
parser.add_argument("--temperature", type=float, help="Température pour la génération (0.0-1.0)")
parser.add_argument("--max-tokens", type=int, help="Nombre maximum de tokens à générer")
args = parser.parse_args()
# Vérification de la clé API dans l'environnement
if not os.environ.get("MISTRAL_API_KEY"):
print("Erreur: Variable d'environnement MISTRAL_API_KEY non définie.")
print("Veuillez définir cette variable avec votre clé API Mistral.")
print("Exemple: export MISTRAL_API_KEY=votre_clé_api")
sys.exit(1)
# Exécution en fonction des arguments
if args.list_models:
afficher_modeles_disponibles()
elif args.prompt:
generer_reponse(
args.prompt,
modele=args.model,
temperature=args.temperature,
max_tokens=args.max_tokens
)
else:
parser.print_help()

65
tests/test_agent.py Normal file
View File

@ -0,0 +1,65 @@
import unittest
import os
import sys
import json
from datetime import datetime
from pathlib import Path
# Ajouter le répertoire parent au sys.path pour importer les modules
sys.path.append(str(Path(__file__).parent.parent))
from agents.base_agent import Agent
class TestAgent(unittest.TestCase):
"""Tests pour la classe Agent de base"""
def setUp(self):
"""Initialisation pour chaque test"""
self.agent = Agent(nom="TestAgent")
def test_initialisation(self):
"""Test de l'initialisation correcte de l'agent"""
self.assertEqual(self.agent.nom, "TestAgent")
self.assertEqual(len(self.agent.historique), 0)
def test_ajouter_historique(self):
"""Test de l'ajout d'entrées dans l'historique"""
self.agent.ajouter_historique("test_action", "données d'entrée", "données de sortie")
self.assertEqual(len(self.agent.historique), 1)
entry = self.agent.historique[0]
self.assertEqual(entry["action"], "test_action")
self.assertEqual(entry["input"], "données d'entrée")
self.assertEqual(entry["output"], "données de sortie")
self.assertTrue("timestamp" in entry)
def test_obtenir_historique(self):
"""Test de la récupération de l'historique"""
# Ajouter plusieurs entrées
self.agent.ajouter_historique("action1", "entrée1", "sortie1")
self.agent.ajouter_historique("action2", "entrée2", "sortie2")
historique = self.agent.obtenir_historique()
self.assertEqual(len(historique), 2)
self.assertEqual(historique[0]["action"], "action1")
self.assertEqual(historique[1]["action"], "action2")
def test_executer_not_implemented(self):
"""Test que la méthode executer lève une NotImplementedError"""
with self.assertRaises(NotImplementedError):
self.agent.executer()
def test_limite_taille_historique(self):
"""Test de la limite de taille dans l'historique"""
# Créer une entrée avec une chaîne très longue
longue_chaine = "x" * 1000
self.agent.ajouter_historique("test_limite", longue_chaine, longue_chaine)
entry = self.agent.historique[0]
self.assertEqual(len(entry["input"]), 500) # Limite à 500 caractères
self.assertEqual(len(entry["output"]), 500) # Limite à 500 caractères
if __name__ == "__main__":
unittest.main()