Navigation : Index | << Précédent

Diagrammes scientifiques avec GenAI

Module : Exemples sectoriels — sciences
Niveau : Intermédiaire
Technologies : gpt-image-1, gpt-5.6-terra Vision, API OpenAI
Durée estimée : 35 minutes

Objectifs d’apprentissage

  • Générer des diagrammes biologiques (cellules, systèmes, anatomie)
  • Créer des illustrations de physique et chimie
  • Produire des graphiques mathématiques et repérer leurs limites
  • Concevoir des schémas techniques annotés
  • Optimiser les prompts pour la précision scientifique
  • Vérifier les légendes et l’exactitude des images générées

Prérequis

  • Notebooks Foundation (01-1, 01-2) complétés
  • Compréhension des concepts scientifiques de base
  • Familiarité avec le prompt engineering
# Paramètres Papermill - JAMAIS modifier ce commentaire

# Configuration notebook
notebook_mode = "interactive"        # "interactive" ou "batch"
skip_widgets = False               # True pour mode batch MCP
debug_level = "INFO"               

# Configuration génération
science_domain = "all"             # "all", "biology", "physics", "chemistry", "math"
diagram_style = "educational"      # "educational", "textbook", "poster", "technical"
image_quality = "hd"               # "standard" ou "hd"
include_labels = True              # Ajouter labels et annotations
generate_variations = True         # Créer variations par sujet

# Sujets scientifiques
biology_topics = ["cell_structure", "photosynthesis", "dna_replication"]
physics_topics = ["circuit_diagram", "wave_interference", "forces"]
chemistry_topics = ["molecular_structure", "periodic_table", "reactions"]
math_topics = ["quadratic_functions", "geometric_shapes", "probability"]

# Sauvegarde et export
save_diagrams = True               # Sauvegarder images générées
export_lesson_pack = True          # Créer pack pédagogique complet
# Parameters
science_domain = "all"
save_diagrams = False
export_lesson_pack = False

Les imports chargent les bibliothèques de génération d’images, de traitement visuel et de journalisation. Le notebook prépare un client HTTP requests avec la clé API et l’URL de base pour appeler directement gpt-image-1.

Pourquoi cette configuration : l’appel HTTP brut rend explicites l’authentification, les paramètres de qualité et le schéma de réponse. Le code ne délègue ni les relances ni la lecture des erreurs au SDK OpenAI : un échec HTTP est affiché avec son statut et doit être diagnostiqué avant de poursuivre. L’URL de base est configurable, mais le fournisseur effectif doit prendre en charge /images/generations et /chat/completions.

# Verification des dependances (import guards)
try:
    import requests
    requests_AVAILABLE = True
except ImportError:
    requests_AVAILABLE = False
    print(f'WARNING: requests non installe. Installez avec: pip install requests')

try:
    from PIL import Image
    PIL_AVAILABLE = True
except ImportError:
    PIL_AVAILABLE = False
    print(f'WARNING: Pillow non installe. Installez avec: pip install Pillow')

try:
    import matplotlib.pyplot as plt
    matplotlib_AVAILABLE = True
except ImportError:
    matplotlib_AVAILABLE = False
    print(f'WARNING: matplotlib non installe. Installez avec: pip install matplotlib')

try:
    from dotenv import load_dotenv
    dotenv_AVAILABLE = True
except ImportError:
    dotenv_AVAILABLE = False
    print(f'WARNING: python-dotenv non installe. Installez avec: pip install python-dotenv')


from pathlib import Path
from dotenv import load_dotenv
# Chargement Token Auth - Recherche du .env dans les parents (robuste pour Papermill)
current_path = Path.cwd()
# Chargement robuste de la configuration .env
import os
# Recherche du .env dans tous les parents (pour Papermill qui change le cwd)
current_path = Path.cwd()
env_loaded = False
for _ in range(10):
    env_path = current_path / ".env"
    if env_path.exists():
        load_dotenv(env_path)
        print(f".env charge depuis: {env_path.name}")
        env_loaded = True
        break
    if len(current_path.parts) <= 1:
        break
    current_path = current_path.parent

if not env_loaded:
    print("WARNING: .env non trouve, utilisation variables environnement")

# Definir GENAI_ROOT a partir de current_path (apres la boucle de recherche)
GENAI_ROOT = current_path if current_path.name == "GenAI" else current_path / "GenAI"

if env_path.exists():
    print(f"Fichier .env charge depuis: {env_path.name}")
else:
    print("Aucun fichier .env trouve dans l'arborescence")

# Setup environnement et imports
import os
import sys
import json
import requests
from datetime import datetime
from typing import Dict, List, Any, Optional, Tuple
import base64
from io import BytesIO
from PIL import Image
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec
import logging

# Configuration paths
OUTPUT_DIR = GENAI_ROOT / 'outputs' / 'science_diagrams'
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)

# Configuration logging
logging.basicConfig(level=getattr(logging, debug_level))
logger = logging.getLogger('science_diagrams')

# Configuration API
# gpt-image-1 est le modele de generation d'images actuel d'OpenAI (dall-e-3 retire
# sur ce compte : HTTP 400 "model does not exist"). OpenRouter ne sert pas
# l'endpoint /images/generations (0 modeles image dispo) : on route vers OpenAI direct.
openai_key = os.getenv('OPENAI_API_KEY')
if not openai_key:
    raise ValueError("❌ OPENAI_API_KEY manquante")

api_base_url = os.getenv("OPENAI_BASE_URL", "https://api.openai.com/v1")
headers = {
    "Authorization": f"Bearer {openai_key}",
    "Content-Type": "application/json"
}

print(f"🔬 Diagrammes Scientifiques avec GenAI")
print(f"📅 {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print(f"🔧 Domaine: {science_domain}, Style: {diagram_style}")
print(f"📁 Output: {OUTPUT_DIR.relative_to(GENAI_ROOT)}")
print(f"✅ API configurée (gpt-image-1 via OpenAI direct)")
.env charge depuis: .env
Fichier .env charge depuis: .env
🔬 Diagrammes Scientifiques avec GenAI
📅 2026-09-26 19:36:20
🔧 Domaine: all, Style: educational
📁 Output: outputs\science_diagrams
✅ API configurée (gpt-image-1 via OpenAI direct)

🧬 Section 1: Diagrammes Biologiques

Génération de diagrammes pour concepts biologiques : cellules, systèmes, anatomie.

# Templates de prompts biologiques optimisés
biology_prompt_templates = {
    "cell_structure": {
        "title": "🔬 Structure Cellulaire",
        "prompt": """Create a detailed educational diagram of an animal cell cross-section showing:
- Cell membrane (outer boundary)
- Nucleus with nuclear envelope and chromatin
- Mitochondria (powerhouse)
- Endoplasmic reticulum (rough and smooth)
- Golgi apparatus
- Ribosomes
- Lysosomes
- Cytoplasm

Style: Clean textbook illustration with clear labels pointing to each organelle. 
Use soft colors: blue for nucleus, green for mitochondria, pink for ER. 
White background. Professional biology textbook quality. Highly detailed but not overwhelming.""",
        "learning_objective": "Identifier les organites cellulaires et leurs fonctions"
    },
    "photosynthesis": {
        "title": "🌱 Processus de Photosynthèse",
        "prompt": """Create an educational diagram showing the photosynthesis process in a plant leaf:
- Sunlight arrows entering the leaf
- Chloroplast with thylakoid stacks
- CO2 entering through stomata
- H2O from roots
- Light-dependent reactions in thylakoids
- Calvin cycle in stroma
- O2 release
- Glucose production

Style: Simplified but scientifically accurate. Use green for chlorophyll, yellow for light energy, 
blue for water, red for oxygen. Clear arrows showing flow. Educational poster style.""",
        "learning_objective": "Comprendre les étapes de la photosynthèse"
    },
    "dna_replication": {
        "title": "🧬 Réplication de l'ADN",
        "prompt": """Create an educational diagram of DNA replication showing:
- Double helix unwinding
- Helicase enzyme separating strands
- DNA polymerase adding nucleotides
- Leading strand (continuous synthesis)
- Lagging strand (Okazaki fragments)
- RNA primers
- Ligase joining fragments

Style: Scientific textbook illustration. Use classic DNA double helix colors (blue and red for strands),
yellow for enzymes. Clear labels. Show directionality (5' to 3'). Clean white background.""",
        "learning_objective": "Maîtriser le mécanisme de réplication de l'ADN"
    },
    "human_heart": {
        "title": "❤️ Anatomie du Cœur Humain",
        "prompt": """Create a detailed anatomical diagram of the human heart showing:
- Four chambers: right/left atria and ventricles
- Valves: tricuspid, pulmonary, mitral, aortic
- Major blood vessels: aorta, vena cava, pulmonary arteries/veins
- Blood flow direction with arrows (blue for deoxygenated, red for oxygenated)
- Heart walls and septum

Style: Medical textbook quality. Use red for oxygenated blood, blue for deoxygenated. 
Clear labels. Cross-sectional view. Professional anatomical illustration.""",
        "learning_objective": "Comprendre l'anatomie et la circulation cardiaque"
    }
}

print("\n🧬 DIAGRAMMES BIOLOGIQUES")
print("=" * 50)
print(f"\n📚 {len(biology_prompt_templates)} templates biologiques disponibles:")
for key, template in biology_prompt_templates.items():
    print(f"\n{template['title']}")
    print(f"   🎯 Objectif: {template['learning_objective']}")
    print(f"   📝 Prompt: {len(template['prompt'])} caractères")

🧬 DIAGRAMMES BIOLOGIQUES
==================================================

📚 4 templates biologiques disponibles:

🔬 Structure Cellulaire
   🎯 Objectif: Identifier les organites cellulaires et leurs fonctions
   📝 Prompt: 533 caractères

🌱 Processus de Photosynthèse
   🎯 Objectif: Comprendre les étapes de la photosynthèse
   📝 Prompt: 488 caractères

🧬 Réplication de l'ADN
   🎯 Objectif: Maîtriser le mécanisme de réplication de l'ADN
   📝 Prompt: 469 caractères

❤️ Anatomie du Cœur Humain
   🎯 Objectif: Comprendre l'anatomie et la circulation cardiaque
   📝 Prompt: 492 caractères

Exercice 1 : Template biologique personnalise

Les templates ci-dessus couvrent la cellule, la photosynthese, la replication de l’ADN et le coeur. L’objectif est de créer un nouveau template biologique pour un sujet qui n’est pas encore couvert.

  • Choisir un sujet biologique (ex : système nerveux, appareil digestif, division cellulaire/mitose, ecosysteme)
  • Rediger un prompt detaille qui specifie chaque élément attendu, les conventions de couleur et le niveau scolaire
  • Structurer le template avec les mêmes cles que les templates existants (title, prompt, learning_objective)
  • Ajouter le template au dictionnaire biology_prompt_templates
# TODO etudiant : creer un nouveau template biologique
my_biology_template = {
    "title": None,              # TODO etudiant : titre du diagramme
    "prompt": None,             # TODO etudiant : prompt detaille avec elements, couleurs, style
    "learning_objective": None  # TODO etudiant : objectif pedagogique
}

# TODO etudiant : ajouter au dictionnaire biology_prompt_templates
# biology_prompt_templates["mon_sujet"] = my_biology_template

# Validation rapide de la structure
required_keys = ["title", "prompt", "learning_objective"]
missing = [k for k in required_keys if not my_biology_template.get(k)]
if missing:
    print(f"Exercice a completer : cles manquantes = {missing}")
else:
    print(f"Template '{my_biology_template['title']}' pret ({len(my_biology_template['prompt'])} car.)")
Exercice a completer : cles manquantes = ['title', 'prompt', 'learning_objective']

Les fonctions generate_science_diagram et download_and_display factorisent les appels de génération et la gestion des résultats pour les sections biologie, physique-chimie et mathématiques. generate_science_diagram associe le prompt à un titre et à une catégorie, convertit l’option pédagogique hd en qualité high pour gpt-image-1, puis renvoie l’image et ses métadonnées. Le modèle fournit normalement une image en base64 ; une URL reste prise en charge si le fournisseur en renvoie une.

Pourquoi factoriser : le même chemin contrôle le statut HTTP, récupère la réponse et prépare l’affichage matplotlib sans répéter ces étapes pour chaque template. Les contraintes pédagogiques restent explicites dans chaque prompt : éléments attendus, annotations lisibles, conventions et fond clair. Les métadonnées conservées (durée de la requête, prompt initial et éventuel prompt révisé) aident à examiner la génération ; elles ne démontrent pas que les légendes sont correctes. L’échec de la génération doit être visible et résolu avant toute interprétation de l’image.

# Fonction de génération avec validation
def generate_science_diagram(prompt: str,
                           title: str,
                           category: str,
                           quality: str = "hd") -> Dict[str, Any]:
    """Génère un diagramme scientifique avec gpt-image-1."""
    gpt_quality = {"hd": "high", "standard": "medium"}.get(quality, quality)
    payload = {
        "model": "gpt-image-1",
        "prompt": prompt,
        "n": 1,
        "size": "1536x1024",
        "quality": gpt_quality,
    }

    try:
        print(f"\nGénération: {title}")
        print(f"   Catégorie: {category}")
        print(f"   Qualité: {quality} -> {gpt_quality}")
        response = requests.post(
            f"{api_base_url}/images/generations",
            headers=headers,
            json=payload,
            timeout=180,
        )
        if response.status_code != 200:
            try:
                error_payload = response.json()
            except ValueError:
                error_payload = response.text
            error = error_payload.get("error", error_payload) if isinstance(error_payload, dict) else error_payload
            if isinstance(error, dict):
                error = error.get("message", str(error))
            print(f"   Échec HTTP {response.status_code}: {str(error)[:200]}")
            return {"success": False, "error": f"HTTP {response.status_code}: {error}"}

        result = response.json()
        data = result.get("data") if isinstance(result, dict) else None
        if not isinstance(data, list) or not data or not isinstance(data[0], dict):
            raise ValueError("Réponse image sans élément data exploitable")
        image_b64 = data[0].get("b64_json")
        image_url = data[0].get("url")
        if not image_b64 and not image_url:
            raise ValueError("Réponse image sans b64_json ni url")
        revised_prompt = data[0].get("revised_prompt") or prompt
        print(f"   Généré en {response.elapsed.total_seconds():.1f}s")
        return {
            "success": True,
            "image_url": image_url,
            "image_b64": image_b64,
            "title": title,
            "category": category,
            "original_prompt": prompt,
            "revised_prompt": revised_prompt,
            "timestamp": datetime.now().isoformat(),
            "quality": gpt_quality,
        }
    except (requests.RequestException, ValueError, KeyError, TypeError) as exc:
        print(f"   Échec génération: {str(exc)[:200]}")
        return {"success": False, "error": str(exc)}

def download_and_display(result: Dict[str, Any], save: bool = True) -> Optional[Image.Image]:
    """Affiche et sauvegarde éventuellement le diagramme reçu."""
    if not result.get("success"):
        return None

    if result.get("image_b64"):
        image = Image.open(BytesIO(base64.b64decode(result["image_b64"])))
    elif result.get("image_url"):
        img_response = requests.get(result["image_url"], timeout=30)
        img_response.raise_for_status()
        image = Image.open(BytesIO(img_response.content))
    else:
        raise ValueError("Réponse image sans image")

    plt.figure(figsize=(16, 9))
    plt.imshow(image)
    plt.title(f"Diagramme scientifique - {result['category']}",
              fontsize=14, fontweight="bold", pad=20)
    plt.axis("off")
    plt.tight_layout()
    plt.show()

    if save:
        category_dir = OUTPUT_DIR / result["category"]
        category_dir.mkdir(exist_ok=True)
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        safe_title = result["title"].replace(" ", "_").replace("/", "_")[:50]
        filename = f"{safe_title}_{timestamp}.png"
        image.save(category_dir / filename)
        print(f"   Sauvegardé: {filename}")
        meta = {key: value for key, value in result.items() if key != "image_b64"}
        with open(category_dir / f"{safe_title}_{timestamp}_meta.json", "w", encoding="utf-8") as file:
            json.dump(meta, file, indent=2, ensure_ascii=False)
    return image

print("Fonctions de génération prêtes (gpt-image-1)")
Fonctions de génération prêtes (gpt-image-1)

La première génération applique le template de cellule animale avec gpt-image-1. Le diagramme obtenu doit illustrer clairement les organites demandés — noyau, mitochondries, réticulum endoplasmique, appareil de Golgi, ribosomes et lysosomes — dans un style lisible au niveau lycée. Leur présence visuelle ne suffit toutefois pas : chaque légende doit désigner la bonne structure, et les relations spatiales doivent rester scientifiquement défendables.

Pourquoi commencer par la cellule : la membrane, le noyau et les organites fournissent des critères observables de précision structurelle ; une flèche mal orientée peut enseigner une association fausse même dans une image soignée. Le contrôle de la section 4 sert à signaler ces défauts potentiels, sans transformer une analyse Vision en validation scientifique. Si l’appel au fournisseur échoue, il faut d’abord réparer la génération ; si l’image comporte des défauts, ajuster le prompt, régénérer et vérifier le nouveau résultat avant de passer aux autres domaines.

# Génération: Structure Cellulaire
if science_domain in ["biology", "all"]:
    print("\n" + "=" * 60)
    print("GÉNÉRATION: STRUCTURE CELLULAIRE")
    print("=" * 60)

    cell_template = biology_prompt_templates["cell_structure"]
    cell_result = generate_science_diagram(
        prompt=cell_template["prompt"],
        title=cell_template["title"],
        category="biology",
        quality=image_quality,
    )
    if not cell_result["success"]:
        raise RuntimeError(f"Génération échouée : {cell_result['error']}")

    cell_image = download_and_display(cell_result, save=save_diagrams)
    if cell_image is None:
        raise RuntimeError("Image générée mais affichage impossible")
    print(f"\nDiagramme cellulaire généré avec gpt-image-1")
    print(f"Objectif: {cell_template['learning_objective']}")
    print("Utilisations pédagogiques: biologie cellulaire, annotation et comparaison de cellules")
else:
    print(f"Domaine biology non sélectionné (science_domain={science_domain})")

============================================================
GÉNÉRATION: STRUCTURE CELLULAIRE
============================================================

Génération: 🔬 Structure Cellulaire
   Catégorie: biology
   Qualité: hd -> high
   Généré en 40.5s


Diagramme cellulaire généré avec gpt-image-1
Objectif: Identifier les organites cellulaires et leurs fonctions
Utilisations pédagogiques: biologie cellulaire, annotation et comparaison de cellules

Lecture de la génération — La cellule précédente appelle réellement gpt-image-1 avec une image de format 1536×1024 et la qualité API high (option hd du carnet). Elle affiche la durée mesurée de la requête, puis l’image retournée. Le temps varie selon le fournisseur et la charge : celui de cette exécution est écrit dans la sortie adjacente, pas figé dans ce commentaire. La présence d’une image prouve la génération, pas la justesse de son contenu ou de ses légendes.

Pourquoi examiner séparément le prompt et l’image : le dictionnaire de résultat conserve le prompt initial et, uniquement si le fournisseur le renvoie, un prompt révisé. Dans le cas contraire, le champ revised_prompt reprend le prompt initial ; il ne faut donc pas raconter une réécriture observée sans preuve dans la réponse API. On peut comparer les contraintes demandées — membrane, noyau, mitochondries, réticulum endoplasmique et lisibilité des annotations — à ce que l’image montre effectivement. Un concept biologique reconnu par le générateur ne garantit pas que les traits des légendes rejoignent les bons organites.

Pourquoi séparer génération et validation : l’image peut montrer les organites tout en attribuant un nom à la mauvaise structure. Le contrôle Vision de la section 4 relève les défauts possibles, puis une vérification disciplinaire tranche ceux qui comptent pour l’enseignement. Une nouvelle génération peut déplacer les flèches ou modifier les textes : son analyse doit être relue à neuf, sans recycler le score ou le délai d’une ancienne image.

⚛️ Section 2: Illustrations Physique & Chimie

Circuits électriques, structures moléculaires, phénomènes physiques.

# Templates physique/chimie
physics_chemistry_templates = {
    "circuit_diagram": {
        "title": "⚡ Schéma de Circuit Électrique",
        "prompt": """Create an educational electrical circuit diagram showing:
- Battery (power source) with + and - terminals
- Switch (open and closed states)
- Resistors (2-3 in series and parallel)
- LED or light bulb
- Ammeter measuring current
- Voltmeter measuring voltage
- Clear wire connections
- Current flow direction arrows

Style: Standard electrical engineering symbols. Clean, technical drawing. 
Black lines on white background. Clear labels for each component. 
Show values (e.g., 9V battery, 100Ω resistor). Professional schematic style.""",
        "learning_objective": "Comprendre les circuits électriques et la loi d'Ohm"
    },
    "molecular_structure": {
        "title": "🧪 Structure Moléculaire (Eau H2O)",
        "prompt": """Create an educational molecular structure diagram of water (H2O) showing:
- Central oxygen atom (red sphere)
- Two hydrogen atoms (small white spheres)
- Covalent bonds (solid lines)
- Bond angle (104.5 degrees)
- Partial charges (δ+ and δ-)
- Electron pairs (lone pairs on oxygen)
- 3D ball-and-stick model

Style: Chemistry textbook illustration. Use standard CPK coloring (red oxygen, white hydrogen).
Clean white background. Show molecular geometry. Include labels and measurements.""",
        "learning_objective": "Visualiser la géométrie et la polarité moléculaire"
    },
    "wave_interference": {
        "title": "🌊 Interférence d'Ondes",
        "prompt": """Create an educational physics diagram showing wave interference:
- Two wave sources creating circular ripples
- Constructive interference regions (bright/high amplitude)
- Destructive interference regions (dark/low amplitude)
- Wave crests and troughs clearly visible
- Pattern of nodes and antinodes
- Distance measurements (wavelength λ)
- Color coding: blue for constructive, red for destructive

Style: Clear physics textbook illustration. Top view of water surface. 
Smooth gradients showing interference pattern. Labels and annotations.""",
        "learning_objective": "Comprendre les phénomènes d'interférence ondulatoire"
    },
    "forces_diagram": {
        "title": "💪 Diagramme de Forces",
        "prompt": """Create an educational free body diagram showing forces on an object:
- Box or sphere on an inclined plane
- Weight force (mg) pointing downward (red arrow)
- Normal force perpendicular to surface (blue arrow)
- Friction force along surface (green arrow)
- Applied force if any (yellow arrow)
- Angle of incline marked
- Force vectors with magnitude labels
- Coordinate system (x, y axes)

Style: Clean physics diagram. Bold colored arrows for forces. 
White background. Clear labels with force equations. Professional textbook quality.""",
        "learning_objective": "Analyser les forces et le mouvement sur plan incliné"
    }
}

print("\n⚛️ PHYSIQUE & CHIMIE")
print("=" * 50)
print(f"\n📚 {len(physics_chemistry_templates)} templates disponibles:")
for key, template in physics_chemistry_templates.items():
    print(f"\n{template['title']}")
    print(f"   🎯 {template['learning_objective']}")

⚛️ PHYSIQUE & CHIMIE
==================================================

📚 4 templates disponibles:

⚡ Schéma de Circuit Électrique
   🎯 Comprendre les circuits électriques et la loi d'Ohm

🧪 Structure Moléculaire (Eau H2O)
   🎯 Visualiser la géométrie et la polarité moléculaire

🌊 Interférence d'Ondes
   🎯 Comprendre les phénomènes d'interférence ondulatoire

💪 Diagramme de Forces
   🎯 Analyser les forces et le mouvement sur plan incliné

Les templates de physique et chimie couvrent circuits électriques, structures moléculaires et phénomènes ondulatoires. La génération utilise ces templates pour produire des diagrammes techniquement précis avec annotations vectorielles.

Pourquoi la précision technique change de registre : un circuit électrique exige une topologie correcte (les fils forment bien une boucle, les symboles respectent les conventions IEC) — un modèle qui « invente » un circuit plausible mais non fonctionnel (boucle ouverte, court-circuit) produit une image séduisante mais pédagogiquement nuisible. Les templates forcent donc des contraintes explicites (« schéma de circuit au trait, symboles normalisés, sens du courant indiqué ») plutôt que des descriptions esthétiques. C’est le même écart qu’entre « dessine une jolie molécule » et « dessine la structure de Lewis de H₂O avec ses doublets non liants ».

# Génération: Circuit Électrique
if science_domain in ["physics", "all"]:
    print("\n" + "=" * 60)
    print("⚡ GÉNÉRATION: CIRCUIT ÉLECTRIQUE")
    print("=" * 60)
    
    circuit_template = physics_chemistry_templates["circuit_diagram"]
    
    circuit_result = generate_science_diagram(
        prompt=circuit_template["prompt"],
        title=circuit_template["title"],
        category="physics",
        quality=image_quality
    )
    
    if circuit_result['success']:
        circuit_image = download_and_display(circuit_result, save=save_diagrams)
        
        print(f"\n✅ Schéma de circuit généré")
        print(f"\n💡 Applications pédagogiques:")
        print(f"   • Cours de physique électricité")
        print(f"   • TP circuits séries et parallèles")
        print(f"   • Calculs loi d'Ohm")
        print(f"   • Exercices de schématisation")
else:
    print(f"\n⏭️  Domaine physics non sélectionné")

# Génération: Structure Moléculaire
if science_domain in ["chemistry", "all"]:
    print("\n" + "=" * 60)
    print("🧪 GÉNÉRATION: STRUCTURE MOLÉCULAIRE")
    print("=" * 60)
    
    molecule_template = physics_chemistry_templates["molecular_structure"]
    
    molecule_result = generate_science_diagram(
        prompt=molecule_template["prompt"],
        title=molecule_template["title"],
        category="chemistry",
        quality=image_quality
    )
    
    if molecule_result['success']:
        molecule_image = download_and_display(molecule_result, save=save_diagrams)
        
        print(f"\n✅ Structure moléculaire générée")
        print(f"\n💡 Applications pédagogiques:")
        print(f"   • Cours de chimie liaisons")
        print(f"   • Géométrie moléculaire VSEPR")
        print(f"   • Polarité et liaisons hydrogène")
        print(f"   • Propriétés de l'eau")
else:
    print(f"\n⏭️  Domaine chemistry non sélectionné")

============================================================
⚡ GÉNÉRATION: CIRCUIT ÉLECTRIQUE
============================================================

Génération: ⚡ Schéma de Circuit Électrique
   Catégorie: physics
   Qualité: hd -> high
   Généré en 36.8s


✅ Schéma de circuit généré

💡 Applications pédagogiques:
   • Cours de physique électricité
   • TP circuits séries et parallèles
   • Calculs loi d'Ohm
   • Exercices de schématisation

============================================================
🧪 GÉNÉRATION: STRUCTURE MOLÉCULAIRE
============================================================

Génération: 🧪 Structure Moléculaire (Eau H2O)
   Catégorie: chemistry
   Qualité: hd -> high
   Généré en 33.2s


✅ Structure moléculaire générée

💡 Applications pédagogiques:
   • Cours de chimie liaisons
   • Géométrie moléculaire VSEPR
   • Polarité et liaisons hydrogène
   • Propriétés de l'eau

Lecture des deux images précédentes — La molécule d’eau représente O en rouge, deux H blancs, l’angle annoncé de 104,5° et des charges partielles de signes cohérents. Elle n’est pas pour autant une mesure de cet angle ni une vérification de la géométrie 3D. Le circuit, lui, n’est pas utilisable comme schéma électrique de référence : l’interrupteur est ouvert tout en portant une flèche de courant, les résistances et appareils de mesure ne sont pas reliés selon une topologie univoque, et l’ampèremètre central crée un chemin parallèle trompeur. Ne pas demander aux étudiants d’en déduire une intensité ou une tension ; comparer les connexions à un circuit construit et vérifié avant usage.

📐 Section 3: Graphiques Mathématiques

Fonctions, géométrie, probabilités - visualisations mathématiques.

# Templates mathématiques
math_templates = {
    "quadratic_function": {
        "title": "📈 Fonction Quadratique (Parabole)",
        "prompt": """Create an educational graph of a quadratic function showing:
- Coordinate system with x and y axes clearly labeled
- Parabola curve (y = ax² + bx + c)
- Vertex point clearly marked with coordinates
- X-intercepts (roots) marked
- Y-intercept marked
- Axis of symmetry (dashed vertical line)
- Grid lines for reading values
- Key points labeled with coordinates

Style: Clean mathematical graph. Use classic colors: blue curve, red points. 
White background with light gray grid. Professional textbook quality. 
Clear, readable labels and numbers.""",
        "learning_objective": "Comprendre les propriétés des fonctions quadratiques"
    },
    "geometric_shapes": {
        "title": "📐 Théorème de Pythagore",
        "prompt": """Create an educational geometric diagram illustrating the Pythagorean theorem:
- Right triangle with sides a, b, and hypotenuse c clearly labeled
- Right angle marked with small square
- Squares drawn on each side showing areas
- Area of each square labeled (a², b², c²)
- Visual proof that a² + b² = c²
- Clean colors: different color for each square
- Measurements shown (e.g., a=3, b=4, c=5)

Style: Geometric diagram, educational poster style. Bright, clear colors. 
White background. Professional mathematical illustration.""",
        "learning_objective": "Visualiser et comprendre le théorème de Pythagore"
    },
    "probability_tree": {
        "title": "🎲 Arbre de Probabilités",
        "prompt": """Create an educational probability tree diagram showing:
- Two coin flips (or dice rolls)
- Clear branches for each outcome
- Probabilities labeled on each branch (fractions or decimals)
- All possible outcomes at the end
- Total probability for each final outcome
- Clean structure: vertical or horizontal tree
- Color coding for different paths

Style: Educational diagram. Use simple, clear graphics. 
Circles or boxes for nodes. Straight lines for branches. 
Clear labels. Professional statistics textbook style.""",
        "learning_objective": "Comprendre les probabilités composées"
    },
    "3d_geometry": {
        "title": "📦 Géométrie 3D (Solides)",
        "prompt": """Create an educational 3D geometry diagram showing common solids:
- Cube with visible edges and faces
- Rectangular prism (box)
- Cylinder
- Cone
- Sphere
- Pyramid
Each shape labeled with:
- Name
- Key measurements (length, width, height, radius)
- Volume formula
- Surface area formula (if space permits)

Style: Isometric or perspective view. Soft shading to show 3D depth. 
Pastel colors for different shapes. White background. Clear labels. 
Educational geometry textbook quality.""",
        "learning_objective": "Reconnaître et calculer volumes des solides"
    }
}

print("\n📐 MATHÉMATIQUES")
print("=" * 50)
print(f"\n📚 {len(math_templates)} templates mathématiques:")
for key, template in math_templates.items():
    print(f"\n{template['title']}")
    print(f"   🎯 {template['learning_objective']}")

📐 MATHÉMATIQUES
==================================================

📚 4 templates mathématiques:

📈 Fonction Quadratique (Parabole)
   🎯 Comprendre les propriétés des fonctions quadratiques

📐 Théorème de Pythagore
   🎯 Visualiser et comprendre le théorème de Pythagore

🎲 Arbre de Probabilités
   🎯 Comprendre les probabilités composées

📦 Géométrie 3D (Solides)
   🎯 Reconnaître et calculer volumes des solides

Exercice 2 : Comparaison multi-domaine avec optimisation de prompt

Les templates ci-dessus couvrent trois domaines (biologie, physique, mathematiques). L’objectif est de créer un système qui genere le même concept dans les trois domaines et compare les résultats.

  • Créer une fonction qui prend un concept transversal (ex : “cycles”, “equilibre”, “transformation”) et genere un prompt adapte a chaque domaine
  • Implementer une logique de vote majoritaire pour sélectionner la meilleure generation
  • Afficher les trois versions cote a cote dans une galerie comparative
def build_cross_domain_prompts(concept: str, domains: list = None) -> list:
    """
    Cree des prompts adaptes a chaque domaine scientifique pour un concept transversal.

    # Indice : Mappez chaque domaine vers son vocabulaire et ses conventions visuelles
    # Etape 1 : Definir le vocabulaire specifique par domaine
    # Etape 2 : Construire un prompt base commun + adaptation par domaine
    # Etape 3 : Retourner la liste des prompts enrichis

    Args:
        concept: Concept transversal (ex: "cycles", "equilibre", "transformation")
        domains: Liste des domaines cibles (defaut: ["biology", "physics", "math"])

    Returns:
        Liste de dictionnaires avec domain, prompt, title
    """
    # TODO etudiant : implementer la generation multi-domaine
    pass

def display_comparison_gallery(results: list, concept: str):
    """
    Affiche une galerie comparative cote a cote avec matplotlib.

    # Indice : Utilisez plt.subplots(1, len(results)) pour la disposition horizontale
    # Ajoutez le nom du domaine comme titre de chaque sous-graphique

    Args:
        results: Liste de resultats de generation (dictionnaires)
        concept: Nom du concept pour le titre global
    """
    # TODO etudiant : implementer la galerie comparative
    pass

# Test avec le concept transversal "cycles"
cross_domain_prompts = build_cross_domain_prompts("cycles")
print(f"Exercice a completer : {len(cross_domain_prompts) if cross_domain_prompts else 0} prompts generes")
Exercice a completer : 0 prompts generes

Les templates mathématiques décrivent des fonctions, figures géométriques et arbres de probabilités. Le générateur d’images peut toutefois dessiner une courbe plausible avec des axes ou des coordonnées incorrects.

Pourquoi la validation change de registre : un schéma convaincant n’est pas une preuve mathématique. La section 4 soumet l’image produite à gpt-5.6-terra pour relever les éléments visibles et les erreurs potentielles ; cette seconde analyse reste un contrôle de lecture, non une certification des équations ou des mesures. Les axes, les valeurs et les angles utilisés en cours doivent encore être vérifiés par un calcul ou par une personne compétente.

# Génération: Fonction Quadratique
if science_domain in ["math", "all"]:
    print("\n" + "=" * 60)
    print("📈 GÉNÉRATION: FONCTION QUADRATIQUE")
    print("=" * 60)
    
    quadratic_template = math_templates["quadratic_function"]
    
    quadratic_result = generate_science_diagram(
        prompt=quadratic_template["prompt"],
        title=quadratic_template["title"],
        category="math",
        quality=image_quality
    )
    
    if quadratic_result['success']:
        quadratic_image = download_and_display(quadratic_result, save=save_diagrams)
        
        print(f"\n✅ Graphique quadratique généré")
        print(f"\n💡 Applications pédagogiques:")
        print(f"   • Cours de mathématiques seconde/première")
        print(f"   • Étude de fonctions")
        print(f"   • Résolution équations du second degré")
        print(f"   • Exercices de lecture graphique")

# Génération: Théorème de Pythagore
if science_domain in ["math", "all"]:
    print("\n" + "=" * 60)
    print("📐 GÉNÉRATION: THÉORÈME DE PYTHAGORE")
    print("=" * 60)
    
    pythagoras_template = math_templates["geometric_shapes"]
    
    pythagoras_result = generate_science_diagram(
        prompt=pythagoras_template["prompt"],
        title=pythagoras_template["title"],
        category="math",
        quality=image_quality
    )
    
    if pythagoras_result['success']:
        pythagoras_image = download_and_display(pythagoras_result, save=save_diagrams)
        
        print(f"\n✅ Diagramme Pythagore généré")
        print(f"\n💡 Applications pédagogiques:")
        print(f"   • Cours de géométrie collège")
        print(f"   • Démonstration visuelle du théorème")
        print(f"   • Calculs de distances")
        print(f"   • Exercices d'application")
else:
    print(f"\n⏭️  Domaine math non sélectionné")

============================================================
📈 GÉNÉRATION: FONCTION QUADRATIQUE
============================================================

Génération: 📈 Fonction Quadratique (Parabole)
   Catégorie: math
   Qualité: hd -> high
   Généré en 36.1s


✅ Graphique quadratique généré

💡 Applications pédagogiques:
   • Cours de mathématiques seconde/première
   • Étude de fonctions
   • Résolution équations du second degré
   • Exercices de lecture graphique

============================================================
📐 GÉNÉRATION: THÉORÈME DE PYTHAGORE
============================================================

Génération: 📐 Théorème de Pythagore
   Catégorie: math
   Qualité: hd -> high
   Généré en 33.3s


✅ Diagramme Pythagore généré

💡 Applications pédagogiques:
   • Cours de géométrie collège
   • Démonstration visuelle du théorème
   • Calculs de distances
   • Exercices d'application

Lecture des deux images mathématiques précédentes — La parabole affiche un sommet (1, −2) qui n’est pas au point marqué sur le tracé, des coordonnées dupliquées (0, −1) sur deux points distincts de l’axe horizontal et des « intersections » non conformes à la courbe. Le dessin de Pythagore montre bien trois carrés et la relation a² + b² = c², mais place a, b, c sur des côtés incompatibles avec les carrés correspondants ; le triangle et les longueurs annoncées 3, 4, 5 ne constituent pas une preuve géométrique lisible. Ces images servent à repérer des erreurs de génération, pas à enseigner les valeurs qu’elles montrent : produire une figure exacte avec un outil de tracé ou une construction contrôlée avant utilisation en cours.

Section 4 : Validation et qualité éducative

Analyse du diagramme généré avec gpt-5.6-terra Vision. Ce modèle peut signaler des défauts ; il ne remplace pas une vérification disciplinaire humaine.

# Validation avec gpt-5.6-terra Vision
def validate_scientific_accuracy(image_url: str = None,
                               image_b64: str = None,
                               diagram_type: str = "",
                               expected_content: str = "") -> Dict[str, Any]:
    """Valide un diagramme avec gpt-5.6-terra et une image réelle."""
    if image_b64:
        image_ref = f"data:image/png;base64,{image_b64}"
    elif image_url:
        image_ref = image_url
    else:
        return {"success": False, "error": "Aucune image fournie"}

    validation_prompt = f"""Vous êtes un expert pédagogue en sciences.
Analysez ce diagramme scientifique ({diagram_type}) et évaluez:

1. EXACTITUDE SCIENTIFIQUE (1-10):
   - Les concepts sont-ils représentés correctement?
   - Y a-t-il des erreurs factuelles?
   - Les proportions/échelles sont-elles réalistes?

2. CLARTÉ PÉDAGOGIQUE (1-10):
   - Le diagramme est-il facile à comprendre?
   - Les labels sont-ils clairs et lisibles?
   - Les couleurs aident-elles la compréhension?

3. COMPLÉTUDE:
   - Éléments attendus: {expected_content}
   - Quels éléments sont présents?
   - Quels éléments manquent?

4. RECOMMANDATIONS:
   - Améliorations suggérées
   - Points forts à conserver

Format de réponse: JSON avec scores et commentaires détaillés."""

    payload = {
        "model": "gpt-5.6-terra",
        "messages": [{
            "role": "user",
            "content": [
                {"type": "text", "text": validation_prompt},
                {"type": "image_url", "image_url": {"url": image_ref}},
            ],
        }],
        "max_completion_tokens": 4000,
        "reasoning_effort": "low",
    }

    try:
        response = requests.post(
            f"{api_base_url}/chat/completions",
            headers=headers,
            json=payload,
            timeout=120,
        )
        if response.status_code != 200:
            return {"success": False, "error": f"HTTP {response.status_code}: {response.text[:200]}"}
        result = response.json()
        analysis = result["choices"][0]["message"].get("content")
        if not isinstance(analysis, str) or not analysis.strip():
            return {"success": False, "error": "Réponse Vision vide : vérifier le budget de tokens"}
        return {
            "success": True,
            "analysis": analysis,
            "diagram_type": diagram_type,
            "timestamp": datetime.now().isoformat(),
        }
    except (requests.RequestException, ValueError, KeyError, IndexError, TypeError) as exc:
        return {"success": False, "error": str(exc)}

print("\nVALIDATION DE LA QUALITÉ ÉDUCATIVE")
print("=" * 50)

if science_domain in ["biology", "all"] and 'cell_result' in locals() and cell_result.get('success'):
    print("\nValidation: Structure Cellulaire")
    validation = validate_scientific_accuracy(
        image_b64=cell_result.get('image_b64'),
        image_url=cell_result.get('image_url'),
        diagram_type="cell_structure",
        expected_content="organites: noyau, mitochondries, RE, Golgi, ribosomes, lysosomes",
    )
    if not validation['success']:
        raise RuntimeError(f"Validation Vision échouée : {validation['error']}")
    print("\nAnalyse gpt-5.6-terra Vision:")
    print(validation['analysis'])
    print("\nValidation terminée")
else:
    print("\nValidation disponible après génération des diagrammes")

VALIDATION DE LA QUALITÉ ÉDUCATIVE
==================================================

Validation: Structure Cellulaire

Analyse gpt-5.6-terra Vision:
{
  "exactitude_scientifique": {
    "score": 4,
    "commentaires": [
      "Le diagramme représente bien une cellule animale générale : absence de paroi cellulaire et de chloroplastes, présence d'un noyau central, de mitochondries, de membranes internes et de petites vésicules.",
      "Le noyau est visuellement identifiable au centre, avec une enveloppe nucléaire double et une structure interne pouvant représenter la chromatine. Cependant, les traits de repérage associés à « Nucleus », « Chromatin » et « Nuclear » ne pointent pas de façon cohérente vers ces structures.",
      "Les mitochondries vertes sont correctement reconnaissables grâce à leur forme ovale et à leurs replis internes, assimilables aux crêtes mitochondriales. En revanche, l'étiquette « Mitochondria » semble pointer vers le réseau rose à gauche, qui ressemble plutôt au réticulum endoplasmique.",
      "Le réticulum endoplasmique est probablement représenté par les membranes roses ondulées à gauche. Toutefois, il n'est pas distingué en réticulum endoplasmique rugueux et lisse, et aucun ribosome fixé au RE n'est clairement montré.",
      "L'appareil de Golgi semble être le petit empilement de saccules roses incurvés dans la partie inférieure de la cellule, mais le label « Golgi apparatus » paraît pointer vers la structure rose de gauche, probablement le RE. Cela constitue une erreur importante de légendage.",
      "Les lysosomes peuvent correspondre à la grosse vésicule beige-jaune située en bas à gauche du noyau, mais le trait associé à « Lysosomes » ne semble pas la désigner.",
      "Les petits points bleus pourraient symboliser des ribosomes libres ou de petites vésicules, mais le label « Ribosomes » ne les désigne pas clairement. Leur représentation est donc ambiguë.",
      "Deux étiquettes « Chromatin » semblent pointer vers des organites différents, dont une mitochondrie verte à droite ; cela est scientifiquement incorrect.",
      "L'étiquette « Nuclear » est incomplète : elle devrait probablement être « Nuclear envelope » ou « Nuclear membrane ».",
      "Les tailles relatives sont acceptables pour une illustration pédagogique schématique, mais elles ne doivent pas être interprétées comme une représentation à l'échelle : le noyau, les mitochondries, les lysosomes et les ribosomes ne respectent pas des proportions réalistes."
    ]
  },
  "clarte_pedagogique": {
    "score": 5,
    "commentaires": [
      "Le titre « ANIMAL CELL » annonce clairement le sujet, et l'organisation générale de la cellule est immédiatement reconnaissable.",
      "Les couleurs différencient relativement bien les grandes structures : noyau bleu, mitochondries vertes, membranes internes roses et cytoplasme bleu clair.",
      "Le contraste entre les textes noirs et le fond clair est bon ; les labels sont globalement lisibles.",
      "Plusieurs lignes de légende se croisent ou pointent vers des structures éloignées de leur texte, ce qui rend l'interprétation difficile.",
      "Le schéma contient des légendes incorrectes, redondantes ou incomplètes, notamment « Chromatin » répété, « Cytoplasm » répété et « Nuclear » tronqué.",
      "L'usage d'anglais est cohérent à l'intérieur du diagramme, mais il ne correspond pas à une ressource francophone. Pour un public scolaire français, les légendes devraient être traduites ou présentées en version bilingue.",
      "La proximité visuelle entre certaines structures roses rend la distinction entre RE et appareil de Golgi difficile sans légendes correctes."
    ]
  },
  "completude": {
    "elements_attendus": {
      "noyau": {
        "present": true,
        "evaluation": "Présent visuellement au centre, avec une enveloppe nucléaire apparente et une structure interne associable à la chromatine. Le légendage correspondant est toutefois confus."
      },
      "mitochondries": {
        "present": true,
        "evaluation": "Présentes visuellement sous forme d'organites ovales verts avec crêtes internes. Au moins une autre structure rose possède aussi une apparence mitochondriale, ce qui ajoute une ambiguïté."
      },
      "reticulum_endoplasmique": {
        "present": true,
        "evaluation": "Présent sous forme de réseau membranaire rose à gauche du noyau. Le label semble relativement proche de la bonne structure, mais le type de RE n'est pas précisé."
      },
      "appareil_de_Golgi": {
        "present": true,
        "evaluation": "Probablement représenté par les saccules roses incurvés situés dans la partie basse de la cellule. Le trait de légende « Golgi apparatus » semble cependant pointer vers le mauvais organite."
      },
      "ribosomes": {
        "present": true,
        "evaluation": "Probablement suggérés par les petits points bleus dispersés dans le cytoplasme. Leur identité n'est pas visuellement assez distinctive et leur étiquette est mal reliée."
      },
      "lysosomes": {
        "present": true,
        "evaluation": "Une vésicule ronde beige-jaune peut correspondre à un lysosome. L'étiquette « Lysosomes » ne pointe toutefois pas vers elle."
      }
    },
    "autres_elements_visibles": [
      {
        "element": "Membrane plasmique",
        "evaluation": "Présente sous forme de contour périphérique double, mais l'épaisseur et l'aspect multicouche sont stylisés."
      },
      {
        "element": "Cytoplasme",
        "evaluation": "Présent sous forme de milieu bleu clair entourant les organites. Il est étiqueté deux fois."
      },
      {
        "element": "Enveloppe nucléaire",
        "evaluation": "Visuellement présente autour du noyau, mais l'étiquette est incomplète."
      },
      {
        "element": "Chromatine",
        "evaluation": "Une structure filamenteuse interne au noyau peut représenter la chromatine, mais les deux labels associés sont mal placés."
      }
    ],
    "elements_manquants_ou_insuffisamment_precises": [
      "Nucléole : il n'est pas clairement représenté ni légendé.",
      "Distinction entre réticulum endoplasmique rugueux et lisse : absente.",
      "Ribosomes associés au RE rugueux : absents ou non reconnaissables.",
      "Vésicules de transport associées au Golgi : elles ne sont pas explicitement identifiées.",
      "Cytosquelette, centrosome et centrioles : non représentés, ce qui est acceptable pour un schéma simplifié mais limite la complétude d'une cellule animale."
    ]
  },
  "recommandations": {
    "ameliorations_suggerees": [
      "Corriger systématiquement les traits de légende afin que chaque ligne aboutisse sans ambiguïté à la structure nommée.",
      "Faire pointer « Mitochondria » vers une mitochondrie verte avec crêtes internes, et non vers le réseau rose du réticulum endoplasmique.",
      "Faire pointer « Golgi apparatus » vers l'empilement de saccules courbes situé dans la partie basse de la cellule.",
      "Faire pointer « Lysosomes » vers la vésicule ronde beige-jaune, ou représenter plusieurs petits lysosomes clairement distincts.",
      "Faire pointer « Ribosomes » vers les petits granules et les représenter en grand nombre ; différencier les ribosomes libres des ribosomes attachés au RE rugueux si ce niveau de détail est visé.",
      "Associer « Nucleus » au noyau central, « Chromatin » au matériel filamenteux interne et remplacer « Nuclear » par « Nuclear envelope » ou « Nuclear membrane ».",
      "Supprimer les doublons de « Chromatin » et de « Cytoplasm », sauf si deux sous-types explicitement différents sont voulus.",
      "Ajouter une distinction visuelle entre RE rugueux et RE lisse, par exemple avec des petits points ribosomiques sur le RE rugueux.",
      "Employer une palette plus systématique : une couleur pour le noyau, une autre pour le RE, une autre pour le Golgi et une autre pour les vésicules, sans donner à une mitochondrie rose une apparence trop proche des membranes du RE.",
      "Indiquer clairement qu'il s'agit d'un schéma non à l'échelle, par exemple avec la mention « représentation simplifiée, non à l'échelle ».",
      "Pour un public francophone, traduire les labels : membrane plasmique, mitochondrie, réticulum endoplasmique, appareil de Golgi, ribosomes, lysosomes, noyau, chromatine, enveloppe nucléaire et cytoplasme."
    ],
    "points_forts_a_conserver": [
      "La forme globale de cellule animale est claire et visuellement attractive.",
      "Le noyau central est bien mis en évidence.",
      "Les mitochondries vertes avec replis internes sont facilement reconnaissables.",
      "La palette de couleurs pastel et les contours sombres facilitent la séparation générale des organites.",
      "La présence de la plupart des organites essentiels demandés offre une bonne base pour un schéma pédagogique après correction des légendes."
    ]
  },
  "verdict_global": {
    "score_indicatif": 4.5,
    "commentaire": "Le dessin constitue une base visuelle attractive et relativement complète pour présenter une cellule animale, mais les erreurs de légendage sont nombreuses et suffisamment importantes pour risquer d'enseigner des associations erronées entre organites et fonctions. Une révision des lignes de repérage et des intitulés est indispensable avant utilisation pédagogique."
  }
}

Validation terminée

Lecture du contrôle Vision — La cellule précédente imprime l’analyse de l’image réellement générée. Pour cette exécution, elle signale notamment des flèches pointant vers un autre organite que celui de leur légende. Les scores, les éléments manquants et les recommandations se lisent dans cette sortie : ils décrivent cette image, et ne sont pas des constantes à recopier pour les générations suivantes.

Conséquence pédagogique : ne pas présenter un diagramme comportant des légendes erronées comme un support de référence. La composition générale et les couleurs ne compensent pas une attribution incorrecte. Corriger le prompt, régénérer puis vérifier chaque flèche avec une source disciplinaire ou une personne compétente avant réutilisation. Une autre exécution peut produire une autre image et exige une nouvelle lecture de son analyse.

Exercice 3 : Fonction de scoring d’exactitude scientifique

La fonction validate_scientific_accuracy retourne une analyse qualitative. L’objectif ici est de créer une fonction complementaire qui produit un score numérique (0-100) permettant de comparer objectivement plusieurs generations d’un même diagramme.

  • Définir des critères ponderes (completude des éléments, precision des labels, coherence des couleurs avec les conventions scientifiques)
  • Retourner un dictionnaire structure avec le score global et le detail par critere
  • Implementer une fonction de comparaison qui classe plusieurs résultats
def compute_science_score(elements_expected: list, elements_found: list,
                         label_accuracy: float = 0.0,
                         color_convention: float = 0.0) -> dict:
    """
    Calcule un score d'exactitude scientifique pour un diagramme genere.

    # Indice : Ponderez les criteres : completude (50%), labels (30%), couleurs (20%)
    # Etape 1 : Calculer le ratio elements trouves / elements attendus
    # Etape 2 : Appliquer les ponderations
    # Etape 3 : Retourner le dictionnaire structure

    Args:
        elements_expected: Liste des elements scientifiques attendus
        elements_found: Liste des elements detectes dans l'image
        label_accuracy: Precision des labels (0.0 a 1.0)
        color_convention: Respect des conventions couleur (0.0 a 1.0)

    Returns:
        Dictionnaire avec score_global (0-100), detail par critere, elements manquants
    """
    # TODO etudiant : implementer le calcul du score
    pass

def compare_diagram_scores(results: list) -> list:
    """
    Classe plusieurs generations d'un meme diagramme par score decroissant.

    # Indice : Utilisez compute_science_score pour chaque resultat
    # puis triez la liste par score_global

    Args:
        results: Liste de dictionnaires, chacun contenant les elements generes

    Returns:
        Liste triee par score_global decroissant
    """
    # TODO etudiant : implementer la comparaison
    pass

print("Exercice a completer : scoring d'exactitude scientifique")
Exercice a completer : scoring d'exactitude scientifique

📦 Section 5: Pack Pédagogique Complet

Création d’un pack de cours avec diagrammes, descriptions, et exercices.

if export_lesson_pack:
    print("\n📦 EXPORT PACK PÉDAGOGIQUE")
    print("=" * 50)

    # Création du pack
    pack_dir = OUTPUT_DIR / f"lesson_pack_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
    pack_dir.mkdir(exist_ok=True)

    # Structure du pack
    (pack_dir / 'images').mkdir(exist_ok=True)
    (pack_dir / 'descriptions').mkdir(exist_ok=True)
    (pack_dir / 'exercises').mkdir(exist_ok=True)

    print(f"\n📁 Structure créée: {pack_dir.name}")

    # Index des ressources
    pack_index = {
        "title": f"Pack Pédagogique Sciences - {science_domain.upper()}",
        "created": datetime.now().isoformat(),
        "domain": science_domain,
        "style": diagram_style,
        "quality": image_quality,
        "resources": []
    }

    # Collecte des diagrammes générés
    generated_diagrams = []
    if 'cell_result' in locals() and cell_result.get('success'):
        generated_diagrams.append(('cell', cell_result, biology_prompt_templates['cell_structure']))
    if 'circuit_result' in locals() and circuit_result.get('success'):
        generated_diagrams.append(('circuit', circuit_result, physics_chemistry_templates['circuit_diagram']))
    if 'molecule_result' in locals() and molecule_result.get('success'):
        generated_diagrams.append(('molecule', molecule_result, physics_chemistry_templates['molecular_structure']))
    if 'quadratic_result' in locals() and quadratic_result.get('success'):
        generated_diagrams.append(('quadratic', quadratic_result, math_templates['quadratic_function']))
    if 'pythagoras_result' in locals() and pythagoras_result.get('success'):
        generated_diagrams.append(('pythagoras', pythagoras_result, math_templates['geometric_shapes']))

    print(f"\n📊 {len(generated_diagrams)} diagrammes à inclure")

    # Export de chaque diagramme avec métadonnées
    for key, result, template in generated_diagrams:
        print(f"\n📝 Export: {result['title']}")

        # Récupération image (gpt-image-1 = b64, dall-e-3 legacy = url)
        if result.get('image_b64'):
            image = Image.open(BytesIO(base64.b64decode(result['image_b64'])))
        elif result.get('image_url'):
            img_response = requests.get(result['image_url'], timeout=30)
            if img_response.status_code != 200:
                print(f"   ⚠️  Téléchargement échoué pour {key}, ignoré")
                continue
            image = Image.open(BytesIO(img_response.content))
        else:
            print(f"   ⚠️  Aucune image pour {key}, ignoré")
            continue

        # Sauvegarde image
        img_file = pack_dir / 'images' / f"{key}_diagram.png"
        image.save(img_file)
        print(f"   ✅ Image: {img_file.name}")

        # Description pédagogique
        description = f"""# {result['title']}

## 🎯 Objectif Pédagogique
{template['learning_objective']}

## 📋 Description
Ce diagramme illustre {result['category']} de manière claire et pédagogique.

## 🔍 Éléments Clés à Observer
(À compléter en fonction du contenu spécifique)

## 💡 Questions de Compréhension
1. Identifiez les principaux éléments du diagramme
2. Expliquez la fonction de chaque élément
3. Quelles sont les relations entre les éléments?

## 📚 Ressources Complémentaires
- Lien vers cours théorique
- Vidéos explicatives
- Exercices pratiques
"""
        desc_file = pack_dir / 'descriptions' / f"{key}_description.md"
        with open(desc_file, 'w', encoding='utf-8') as f:
            f.write(description)
        print(f"   ✅ Description: {desc_file.name}")

        # Ajout à l'index
        pack_index['resources'].append({
            "id": key,
            "title": result['title'],
            "category": result['category'],
            "image_file": f"images/{key}_diagram.png",
            "description_file": f"descriptions/{key}_description.md",
            "learning_objective": template['learning_objective']
        })

    # Export index
    index_file = pack_dir / 'README.md'
    readme_content = f"""# {pack_index['title']}

**Créé le:** {datetime.now().strftime('%Y-%m-%d %H:%M')}
**Domaine:** {science_domain}
**Qualité:** {image_quality}

## 📚 Contenu du Pack

"""
    for resource in pack_index['resources']:
        readme_content += f"""### {resource['title']}
- **Objectif:** {resource['learning_objective']}
- **Image:** `{resource['image_file']}`
- **Description:** `{resource['description_file']}`

"""

    with open(index_file, 'w', encoding='utf-8') as f:
        f.write(readme_content)

    # Export JSON
    json_file = pack_dir / 'pack_index.json'
    with open(json_file, 'w', encoding='utf-8') as f:
        json.dump(pack_index, f, indent=2, ensure_ascii=False)

    print(f"\n" + "=" * 50)
    print(f"✅ Pack pédagogique exporté: {pack_dir.name}")
    print(f"📁 {len(pack_index['resources'])} ressources incluses")
    print(f"📄 README.md créé")
    print(f"📊 pack_index.json créé")
    print(f"\n💡 Utilisez ce pack pour:")
    print(f"   • Préparation de cours")
    print(f"   • Supports de présentation")
    print(f"   • Exercices et évaluations")
    print(f"   • Ressources numériques élèves")
else:
    print("\n⏭️  Export pack désactivé (export_lesson_pack=False)")

⏭️  Export pack désactivé (export_lesson_pack=False)

📚 Récapitulatif et Bonnes Pratiques

Consignes pour génération optimale de diagrammes scientifiques.

print("\nRÉCAPITULATIF ET BONNES PRATIQUES")
print("=" * 50)
print("\nDomaines couverts :")
print("   Biologie : cellules, systèmes et anatomie")
print("   Physique : circuits, ondes et forces")
print("   Chimie : molécules, réactions et structures")
print("   Mathématiques : graphiques, géométrie et probabilités")

print("\nConseils pour rédiger un prompt scientifique :")
print("   Nommer les éléments et leurs relations, plutôt que demander une jolie image")
print("   Préciser les conventions, proportions et mesures attendues")
print("   Donner le niveau pédagogique cible et le contexte d'utilisation")
print("   Demander des annotations lisibles et reliées aux bonnes structures")
print("   Définir un style sobre : illustration de manuel, fond clair, contraste élevé")
print("   Préciser les codes couleur disciplinaires, puis vérifier leur application")
print("   Pour les circuits, demander une topologie complète et des symboles normalisés")
print("   Pour les molécules, vérifier les liaisons, angles, charges et doublets")
print("   Pour les graphiques, vérifier chaque axe, coordonnée et valeur par calcul")

print("\nParamètres techniques de cet exemple :")
print("   Génération : gpt-image-1, 1536x1024, qualité high (option hd du notebook)")
print("   Réponse image : b64_json, affichée puis sauvegardée si demandé")
print("   Contrôle de lecture : gpt-5.6-terra Vision sur l'image réellement générée")
print("   Une autre génération nécessite une nouvelle analyse de ses propres sorties")

print("\nPoints de vigilance avant une utilisation en cours :")
print("   Une image vraisemblable n'est pas une preuve d'exactitude scientifique")
print("   Contrôler chaque trait de légende, élément manquant et terme inventé")
print("   Vérifier la lisibilité sur les supports utilisés par les étudiants")
print("   Faire confirmer les défauts ou les résultats par une source disciplinaire")
print("   Ne pas utiliser comme référence un diagramme dont les légendes sont erronées")

print("\nRessources complémentaires :")
print("   ../../tutorials/dalle3-complete-guide.md (historique DALL-E 3)")
print("   ../../tutorials/gpt5-image-analysis-guide.md")
print("   ../../tutorials/educational-workflows.md")
print("\nProchaine étape : corriger les défauts relevés, régénérer et revalider")
print("=" * 50)

RÉCAPITULATIF ET BONNES PRATIQUES
==================================================

Domaines couverts :
   Biologie : cellules, systèmes et anatomie
   Physique : circuits, ondes et forces
   Chimie : molécules, réactions et structures
   Mathématiques : graphiques, géométrie et probabilités

Conseils pour rédiger un prompt scientifique :
   Nommer les éléments et leurs relations, plutôt que demander une jolie image
   Préciser les conventions, proportions et mesures attendues
   Donner le niveau pédagogique cible et le contexte d'utilisation
   Demander des annotations lisibles et reliées aux bonnes structures
   Définir un style sobre : illustration de manuel, fond clair, contraste élevé
   Préciser les codes couleur disciplinaires, puis vérifier leur application
   Pour les circuits, demander une topologie complète et des symboles normalisés
   Pour les molécules, vérifier les liaisons, angles, charges et doublets
   Pour les graphiques, vérifier chaque axe, coordonnée et valeur par calcul

Paramètres techniques de cet exemple :
   Génération : gpt-image-1, 1536x1024, qualité high (option hd du notebook)
   Réponse image : b64_json, affichée puis sauvegardée si demandé
   Contrôle de lecture : gpt-5.6-terra Vision sur l'image réellement générée
   Une autre génération nécessite une nouvelle analyse de ses propres sorties

Points de vigilance avant une utilisation en cours :
   Une image vraisemblable n'est pas une preuve d'exactitude scientifique
   Contrôler chaque trait de légende, élément manquant et terme inventé
   Vérifier la lisibilité sur les supports utilisés par les étudiants
   Faire confirmer les défauts ou les résultats par une source disciplinaire
   Ne pas utiliser comme référence un diagramme dont les légendes sont erronées

Ressources complémentaires :
   ../../tutorials/dalle3-complete-guide.md (historique DALL-E 3)
   ../../tutorials/gpt5-image-analysis-guide.md
   ../../tutorials/educational-workflows.md

Prochaine étape : corriger les défauts relevés, régénérer et revalider
==================================================
Retour au sommet