Lab 13: Web Search pour Modèles SOTA (MLE-STAR Component)

Navigation : Lab 12 << | Index | >> Lab 14

Objectifs d’apprentissage

À la fin de ce laboratoire, vous saurez : 1. Implémenter la recherche web pour trouver les modèles SOTA 2. Extraire des informations depuis arXiv et autres sources académiques 3. Générer du code initial basé sur les résultats de recherche 4. Intégrer la recherche dans un pipeline ML automatisé

Prérequis

  • Lab 12 (DS-STAR Workshop) complété
  • Compréhension des architectures ML
  • Configuration multi-provider active

Durée estimée : 35-45 minutes

Repères bibliographiques. Ce lab implémente le composant recherche web (search) de l’agent MLE-STAR — un agent d’ingénierie ML de Google Research (arXiv:2506.15692, MLE-STAR: Machine Learning Engineering Agent via Search and Targeted Refinement, 2025) qui, plutôt que de s’appuyer sur la connaissance paramétrique du LLM, recherche activement les modèles SOTA (arXiv, leaderboards) avant de générer du code. Ce principe — augmenter le LLM par récupération externe de connaissances — est le paradigme RAG (Retrieval-Augmented Generation) formalisé par P. Lewis et al., Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks, arXiv:2005.11401, NeurIPS 2020.

1. Configuration

Le lab s’appuie sur la même infrastructure multi-provider que les Labs 10-12 : le module config expose get_settings() qui renvoie le provider LLM actif (ici OpenRouter), et utils.LLMClient est le wrapper d’appel unifié. Isoler la configuration dans un module dédié (plutôt que des variables globales) est ce qui permet de changer de provider (OpenAI, Anthropic, local) sans toucher au code du pipeline — le patron Strategy appliqué aux backends LLM.

import sys
sys.path.insert(0, '..')

import json
import re
import requests
from typing import List, Dict, Optional
from dataclasses import dataclass
from datetime import datetime

from config import get_settings
from utils import LLMClient

print("Imports OK : json, re, requests, dataclasses, config, utils")
Imports OK : json, re, requests, dataclasses, config, utils

Chargement des paramètres de configuration.

settings = get_settings()
print(f'Provider: {settings.active_provider}')
Provider: openai

2. Data Classes

Deux @dataclass définissent le contrat de données qui circule dans le pipeline :

  • SearchResult : un résultat brut de recherche (titre, URL, snippet, source, date). C’est l’unité produite par le retrieval.
  • ModelInfo : un modèle SOTA structuré (nom, URL du papier, description, URL du code, performance). C’est l’unité extraite par le LLM et consommée par le générateur de code.

Pourquoi des dataclasses plutôt que des dictionnaires ? Elles apportent un typage nominal (l’IDE et le lecteur savent qu’un ModelInfo a un champ code_url), une construction explicite (on ne passe pas un dict vague), et l’auto-représentation (print(model) est lisible). Dans un pipeline où un LLM produit du JSON qu’on désérialise en objets, ce contrat typé est la frontière entre « sortie non-structurée du modèle » et « données manipulables par le code ».

@dataclass
class SearchResult:
    title: str
    url: str
    snippet: str
    source: str
    date: str = None

@dataclass
class ModelInfo:
    name: str
    paper_url: str
    description: str
    code_url: str = None
    performance: str = None

print("Dataclasses definies : SearchResult (titre, url, snippet, source), ModelInfo (nom, paper, description, code_url, performance)")
Dataclasses definies : SearchResult (titre, url, snippet, source), ModelInfo (nom, paper, description, code_url, performance)

Pourquoi séparer SearchResult et ModelInfo ?

Un même SearchResult (un papier arXiv) peut mentionner plusieurs modèles, et un même modèle peut apparaître dans plusieurs SearchResult. Séparer les deux types de données permet de découpler le retrieval de l’extraction : la recherche produit des résultats bruts, l’extracteur en déduit des modèles. C’est l’application du principe de séparation des responsabilités au pipeline RAG.

3. Web Search Module

Ce module incarne l’étape de retrieval du paradigme RAG (Lewis et al. 2020) : l’agent interroge une source externe (web, arXiv) pour obtenir un contexte fraîchement récupéré, puis le passe au LLM qui génère du code fondé sur l’état de l’art observé plutôt que sur sa mémoire paramétrique seule.

class WebSearcher:
    """Recherche web pour trouver des modeles SOTA."""

    def __init__(self):
        self.headers = {'User-Agent': 'Mozilla/5.0 (compatible; DS-Star/1.0)'}

    def search_arxiv(self, query: str, max_results: int = 5) -> List[SearchResult]:
        """Recherche sur arXiv via API."""
        url = f"http://export.arxiv.org/api/query?search_query=all:{query}&max_results={max_results}"
        try:
            response = requests.get(url, headers=self.headers, timeout=10)
            results = []
            # Parse Atom feed
            entries = response.text.split('<entry>')[1:]
            for entry in entries:
                title = re.search(r'<title>(.*?)</title>', entry, re.DOTALL)
                link = re.search(r'<id>(.*?)</id>', entry)
                summary = re.search(r'<summary>(.*?)</summary>', entry, re.DOTALL)
                if title and link:
                    results.append(SearchResult(
                        title=title.group(1).strip().replace('\n', ' '),
                        url=link.group(1).strip(),
                        snippet=summary.group(1).strip()[:200] if summary else '',
                        source='arxiv'
                    ))
            return results
        except Exception as e:
            print(f"Erreur arXiv: {e}")
            return []

    def search_papers_with_code(self, task: str) -> List[SearchResult]:
        """Simule une recherche sur Papers With Code (API publique)."""
        # Papers With Code a une API publique mais limitee
        # Pour ce lab, on simule avec des resultats types
        mock_results = [
            SearchResult(
                title="State-of-the-Art Image Classification",
                url="https://paperswithcode.com/sota/image-classification-on-imagenet",
                snippet="Vision Transformers achieve 90.5% top-1 accuracy",
                source="paperswithcode"
            ),
            SearchResult(
                title="Object Detection Benchmarks",
                url="https://paperswithcode.com/sota/object-detection-on-coco",
                snippet="YOLOv9 and DINO lead COCO detection",
                source="paperswithcode"
            )
        ]
        return mock_results

print("Classe WebSearcher definie : recherche arXiv (API Atom) et Papers With Code (simule)")
Classe WebSearcher definie : recherche arXiv (API Atom) et Papers With Code (simule)

Lecture du code : deux backends, un même contrat

search_arxiv interroge la vraie API d’arXiv (Atom feed XML, parsé au regex — léger, pas de dépendance). search_papers_with_code est simulé : l’API publique de Papers With Code est limitée en rate, donc le lab renvoie des résultats types pré-définis. Les deux méthodes retournent le même type List[SearchResult] — c’est ce contrat uniforme qui permet au pipeline en aval de les traiter symétriquement. Remplacer le mock par une vraie API Tavily ou Serper ne changerait qu’une méthode, pas l’architecture.

4. LLM-Based Model Extractor

C’est le cœur de l’étape de génération du RAG : le LLM reçoit le contexte récupéré (les SearchResult concaténés) et produit une structure (une liste JSON de ModelInfo). Deux choix techniques méritent d’être soulignés :

  • temperature=0.2 : on demande au LLM d’être peu créatif. L’extraction doit être fidèle aux résultats de recherche, pas inventer des modèles. Une température basse ancre la génération dans le contexte fourni.
  • Extraction re.search(r'\[.*\]', ...) : le LLM est invité à produire du JSON, mais sa sortie contient souvent du texte autour (préambules, code fences). On extrait la première sous-chaîne qui ressemble à un tableau JSON, puis json.loads la valide. C’est le patron « LLM-to-structured-output » avec tolérance au bruit — robuste aux variations de formatage du modèle.
class ModelExtractor:
    """Extrait les informations de modeles depuis les resultats de recherche."""

    def __init__(self, llm: LLMClient):
        self.llm = llm

    def extract_models(self, search_results: List[SearchResult], task: str) -> List[ModelInfo]:
        """Utilise le LLM pour extraire les modeles pertinents."""
        context = "\n".join([
            f"- {r.title}: {r.snippet} ({r.url})"
            for r in search_results[:5]
        ])

        prompt = f"""Analyse ces resultats de recherche pour la tache: {task}

RESULTATS:
{context}

Extrait les 2-3 modeles les plus pertinents avec leurs informations.
Format JSON:
[
  {{"name": "...", "paper_url": "...", "description": "...", "code_url": "..."}}
]

JSON:"""

        response = self.llm.generate(prompt, temperature=0.2)

        # Extract JSON
        try:
            match = re.search(r'\[.*\]', response, re.DOTALL)
            if match:
                data = json.loads(match.group(0))
                return [ModelInfo(**m) for m in data]
        except:
            pass
        return []

print("Classe ModelExtractor definie : extraction de modeles pertinents depuis resultats de recherche via LLM")
Classe ModelExtractor definie : extraction de modeles pertinents depuis resultats de recherche via LLM

Le point critique : le prompt structuré

Le prompt de extract_models est soigneusement construit : il donne (a) le contexte (les résultats de recherche), (b) une instruction claire (« extraire 2-3 modèles pertinents »), (c) un format de sortie imposé (un tableau JSON avec des clés nommées). Ce prompt structuré est ce qui transforme la sortie libre du LLM en données exploitables par le code Python en aval. La robustesse vient du fallback re.search(r'\[.*\]') qui tolère le texte parasite autour du JSON.

5. Code Generator from SOTA

Le SOTACodeGenerator enchaîne sur l’extraction : il reçoit les ModelInfo et génère un script Python initial (chargement → modèle → entraînement → évaluation). Deux points notables :

  • temperature=0.3 : légèrement plus élevée que l’extracteur (0.2). On veut un code plausible et idiomatique, pas une recopie exacte — un peu de variété aide à produire un squelette naturel.
  • Extraction du bloc code : comme pour l’extracteur, on tolère le bruit en extrayant le premier bloc ```python ... ```. Le code généré est un point de départ, pas une solution finale — le lab suivant (Lab 14) en fera l’ablation et le raffinement ciblé.
class SOTACodeGenerator:
    """Genere du code initial base sur les modeles SOTA trouves."""

    def __init__(self, llm: LLMClient):
        self.llm = llm

    def generate_initial_code(self, task: str, models: List[ModelInfo]) -> str:
        """Genere du code de base utilisant les modeles SOTA."""
        models_desc = "\n".join([
            f"- {m.name}: {m.description}"
            for m in models[:2]
        ])

        prompt = f"""Genere du code Python initial pour cette tache ML.

TACHE: {task}

MODELES SOTA DISPONIBLES:
{models_desc}

Genere un script Python simple et commente qui:
1. Charge les donnees
2. Prepare le modele
3. Entraîne et evalue

```python
# Ton code ici
```"""

        response = self.llm.generate(prompt, temperature=0.3)
        match = re.search(r'```python\s*(.*?)\s*```', response, re.DOTALL)
        return match.group(1).strip() if match else response

print("Classe SOTACodeGenerator definie : generation de code Python initial base sur modeles SOTA")
Classe SOTACodeGenerator definie : generation de code Python initial base sur modeles SOTA

Température et déterminisme

Notez la différence de température entre l’extracteur (0.2) et le générateur de code (0.3) : - Extracteur bas : on veut une extraction fidèle au contexte récupéré, pas d’invention. - Générateur légèrement plus haut : on veut un code idiomatique qui combine les modèles SOTA de façon naturelle, pas une recopie verbatim d’un seul papier.

Cette gradation est un levier fin : trop bas, le code est stéréotypé ; trop haut, il dérive hors-sujet.

6. MLE-STAR Pipeline Partiel

MLEStarSearcher orchestre les trois composants précédents en un pipeline séquentiel :

WebSearcher.search_arxiv + search_papers_with_code   (retrieval)
        ↓ SearchResult[]
ModelExtractor.extract_models                          (génération structurée)
        ↓ ModelInfo[]
SOTACodeGenerator.generate_initial_code                (génération de code)
        ↓ str (script Python)

C’est l’architecture canonique d’un agent outillé : l’agent ne s’appuie pas sur sa seule mémoire paramétrique, il interroge des outils externes (ici une API de recherche), transforme leurs sorties via le LLM, puis agit (génère du code). Le print à chaque étape rend la trace d’exécution lisible — importante pour déboguer un pipeline multi-LLM où une étape peut échouer silencieusement.

class MLEStarSearcher:
    """Pipeline de recherche SOTA (partie de MLE-STAR)."""

    def __init__(self):
        self.llm = LLMClient()
        self.web_searcher = WebSearcher()
        self.extractor = ModelExtractor(self.llm)
        self.code_gen = SOTACodeGenerator(self.llm)

    def find_sota_models(self, task: str) -> Dict:
        """Trouve les modeles SOTA pour une tache donnee."""
        print(f"[WEB SEARCH] Recherche pour: {task}")

        # Search arXiv
        print("  - Recherche arXiv...")
        arxiv_results = self.web_searcher.search_arxiv(task)

        # Search Papers With Code (simule)
        print("  - Recherche Papers With Code...")
        pwc_results = self.web_searcher.search_papers_with_code(task)

        all_results = arxiv_results + pwc_results
        print(f"  - {len(all_results)} resultats trouves")

        # Extract models with LLM
        print("[EXTRACTOR] Extraction des modeles...")
        models = self.extractor.extract_models(all_results, task)
        print(f"  - {len(models)} modeles identifies")

        return {
            'task': task,
            'search_results': all_results,
            'models': models
        }

    def generate_baseline_code(self, task: str, models: List[ModelInfo]) -> str:
        """Genere du code de base."""
        print("[CODE GEN] Generation du code initial...")
        code = self.code_gen.generate_initial_code(task, models)
        return code

print("Classe MLEStarSearcher definie : pipeline complet WebSearch -> ExtractModel -> GenerateCode")
Classe MLEStarSearcher definie : pipeline complet WebSearch -> ExtractModel -> GenerateCode

Lecture : Verbatim : « Classe MLEStarSearcher definie : pipeline complet WebSearch -> ExtractModel -> GenerateCode ». Les trois étapes s’impriment chacune au run : « [WEB SEARCH] Recherche pour: image classification imagenet », « [EXTRACTOR] Extraction des modeles… », « [CODE GEN] Generation du code initial… » — une seule classe qui enchaîne retrieval, extraction LLM et génération de code.

7. Test du Pipeline

On lance le pipeline complet sur une tâche ML emblématique : classification d’images sur ImageNet. C’est le cas-test canonique de la littérature (leaderboard Papers With Code saturé), donc on s’attend à un retrieval riche. Observez la trace : chaque étape imprime sa progression, ce qui permet de voir où le temps passe et où le signal pourrait se dégrader (mauvaise requête, extraction creuse).

# Test avec une tache ML typique
searcher = MLEStarSearcher()

task = "image classification imagenet"
result = searcher.find_sota_models(task)

print("\n" + "="*50)
print("MODELES TROUVES:")
print("="*50)
for m in result['models']:
    print(f"\n- {m.name}")
    print(f"  Description: {m.description[:80]}...")
    print(f"  Paper: {m.paper_url}")
[WEB SEARCH] Recherche pour: image classification imagenet
  - Recherche arXiv...
  - Recherche Papers With Code...
  - 2 resultats trouves
[EXTRACTOR] Extraction des modeles...
  - 3 modeles identifies

==================================================
MODELES TROUVES:
==================================================

- Vision Transformers
  Description: Vision Transformers achieve 90.5% top-1 accuracy on ImageNet, setting a new stat...
  Paper: https://paperswithcode.com/sota/image-classification-on-imagenet

- YOLOv9
  Description: YOLOv9 is a leading model in object detection benchmarks on the COCO dataset, kn...
  Paper: https://paperswithcode.com/sota/object-detection-on-coco

- DINO
  Description: DINO is a top-performing model in object detection on the COCO dataset, excellin...
  Paper: https://paperswithcode.com/sota/object-detection-on-coco

Lecture du résultat : un retrieval riche mais bruité

La trace dit tout : 2 resultats trouves puis 3 modeles identifies — l’extracteur rend PLUS de modèles qu’il n’a reçu de résultats, preuve vive que chaque SearchResult porte plusieurs candidats (la propriété qui justifie la séparation des deux dataclasses). Les 3 modèles extraits : - Vision Transformers — le seul on-subject : annoncé à 90.5 % top-1 sur ImageNet (sota/image-classification-on-imagenet). - YOLOv9 et DINO — modèles de détection COCO (leurs deux paper_url pointent object-detection-on-coco).

Ratio 1 sur 3 : la requête ramène, l’extraction ne re-filtre pas par tâche. C’est la limitation fondamentale du retrieval arXiv brut : la requête textuelle renvoie des papiers qui mentionnent les mots-clés, pas nécessairement les leaders du benchmark. Un système MLE-STAR mature ajouterait une étape de re-ranking par leaderboard pour filtrer ce bruit — le run démontre moins une découverte automatique du SOTA que la mécanique qui permettrait de l’approcher. La leçon pédagogique : le RAG n’est pas magique — sa qualité est plafonnée par celle du retrieval amont. (Une exécution antérieure extrayait VIPriors / HistoGAN / Ensembles — la sortie committée fait foi.)

8. Génération de Code

Une fois les modèles SOTA extraits, on demande au générateur un script initial. Le code produit n’est pas exécuté dans ce lab — c’est un squelette de départ que le Lab 14 (ablation) raffinera. Observez les choix du LLM : il ancre le squelette sur le modèle extrait on-subject (vit_b_16) et complète avec sa connaissance paramétrique (constantes ImageNet canoniques).

# Generer du code initial
if result['models']:
    code = searcher.generate_baseline_code(task, result['models'])
    print("\n" + "="*50)
    print("CODE GENERE:")
    print("="*50)
    print(code[:800] + "..." if len(code) > 800 else code)
[CODE GEN] Generation du code initial...

==================================================
CODE GENERE:
==================================================
import torch
import torchvision
import torchvision.transforms as transforms
from torchvision.models import vit_b_16, ViT_B_16_Weights
from torch.utils.data import DataLoader
from torch import nn, optim

# 1. Charger les données
def load_data(batch_size=32):
    # Définir les transformations pour les données d'entraînement et de validation
    transform = transforms.Compose([
        transforms.Resize(256),
        transforms.CenterCrop(224),
        transforms.ToTensor(),
        transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
    ])

    # Charger le jeu de données ImageNet
    train_dataset = torchvision.datasets.ImageNet(root='path/to/imagenet/train', split='train', transform=transform)
    val_dataset = torchvision.datasets.ImageNet(root='path/to/imagenet/v...

Lecture du code généré : PyTorch + ViT

Le squelette généré ouvre sur from torchvision.models import vit_b_16, ViT_B_16_Weights : parmi les 3 modèles extraits, le générateur retient ViT — le seul aligné sur la tâche — et ignore YOLOv9/DINO. Les constantes canoniques d’ImageNet sont là, verbatim : transforms.Resize(256) puis transforms.CenterCrop(224), normalisation mean=[0.485, 0.456, 0.406]. Le prompt imposait 3 étapes (charger, préparer, entraîner/évaluer) et la première fonction du code généré est def load_data(batch_size=32) — l’étape 1 tenue dès la première fonction. Notez le chemin root='path/to/imagenet/train' — un placeholder que l’étudiant doit remplir. Statut, celui du lab lui-même : « le code produit n’est pas exécuté dans ce lab » (section 8) — squelette non validé, que le Lab 14 (ablation) doit raffiner ; « exécutable » serait un mot de trop — plausible, non prouvé.

Le mélange est instructif : le choix du modèle est ancré sur le retrieval (ViT, extrait on-subject), tandis que les constantes ImageNet canoniques viennent de la connaissance paramétrique du LLM — il synthétise en combinant contexte récupéré et formation propre. C’est précisément ce mélange que le Lab 14 (ablation) viendra tester et raffiner. (L’ancienne lecture décrivait ResNet50, vestige d’une exécution antérieure ; la sortie committée montre ViT.)

9. Résumé du Lab

Ce que nous avons implémenté

  1. WebSearcher : retrieval sur arXiv (API Atom réelle) et Papers With Code (simulé).
  2. ModelExtractor : extraction structurée SearchResult → ModelInfo via LLM (température basse).
  3. SOTACodeGenerator : génération d’un squelette de code Python depuis les modèles extraits.

Limitations (honnêtement observées)

  • Le retrieval est bruité. Sur la requête « image classification imagenet », la recherche renvoie des papiers pas nécessairement SOTA (2 des 3 modèles extraits — YOLOv9, DINO — sont des modèles de détection COCO). L’extracteur LLM agit comme un filtre, mais il ne re-filtre pas par tâche et hérite du bruit amont. Un vrai système MLE-STAR ajoute une étape de re-ranking (Papers With Code leaderboard) pour ancrer l’extraction dans des métriques mesurées.
  • Le code généré mélange deux sources. Le LLM ancre le squelette sur le modèle extrait on-subject (ViT) et le complète avec sa connaissance paramétrique (constantes ImageNet canoniques). Le résultat est un squelette plausible mais non validé — d’où le besoin du Lab 14 (évaluation statique puis raffinement).
  • Papers With Code est simulé (rate limit de l’API publique). La recherche web réelle nécessiterait une API dédiée (Tavily, Serper) — le patron architectural reste identique, seul le backend change.

Intégration MLE-STAR

Dans l’agent MLE-STAR complet, ce module alimente : (1) la compréhension de la compétition Kaggle, (2) la recherche des approches SOTA, (3) la génération du code initial — puis passe la main à l’ablation et raffinement ciblé (Lab 14).

Prochaine étape

  • Lab 14 : Ablation et Raffinement Ciblé — on prend le code généré ici et on l’améliore itérativement.

Exercice

À vous de rechercher des modèles SOTA pour une autre tâche ML et de comparer les résultats !

# Exercice : Recherchez des modeles SOTA pour une autre tache
# Utilisez le MLEStarSearcher pour trouver des modeles pour "object detection"

# TODO: Definissez la tache de recherche
task_detection = "object detection coco"

print("=" * 50)
print(f"Recherche SOTA pour : {task_detection}")
print("=" * 50)

# TODO: Utilisez searcher.find_sota_models() pour trouver les modeles
# Indice: meme pattern que le test de la section 7
result_detection = None  # Remplacez None

# TODO: Affichez les modeles trouves
# Indice: parcourez result_detection['models'] et affichez name, description, code_url

# TODO: Comparez avec les resultats de classification d'images
# Indice: comparez len(result['models']) vs len(result_detection['models'])

# REFLEXION (repondez dans un commentaire):
# Quelle est la difference principale entre les modeles
# de classification d'images et ceux de detection d'objets ?
# ...
==================================================
Recherche SOTA pour : object detection coco
==================================================

Exercice : Comparaison de Sources Academiques

Créez une fonction qui compare les résultats de recherche provenant de sources différentes (arXiv vs. Papers With Code) et identifie les modèles qui apparaissent dans les deux sources.

Objectifs

  1. Executer une recherche sur arXiv et sur Papers With Code pour la même tâche
  2. Identifier les modèles communs aux deux sources (intersection)
  3. Classer les modèles par nombre d’occurrences et pertinence

Indice : - Utilisez web_searcher.search_arxiv(task) et web_searcher.search_papers_with_code(task) - Comparez les titres en utilisant la similarite de chaînes (difflib.SequenceMatcher) - Les modèles presents dans les deux sources sont généralement plus fiables

# Exercice : Comparaison multi-source pour validation croisee
# Objectif : Identifier les modeles confirmes par plusieurs sources

import difflib

def compare_sources(web_searcher, task: str, similarity_threshold: float = 0.6) -> dict:
    """
    Compare les resultats arXiv et Papers With Code pour une meme tache.
    
    Args:
        web_searcher: instance de WebSearcher
        task: tache ML a rechercher
        similarity_threshold: seuil de similarite pour considerer deux resultats comme identiques
        
    Returns:
        Dictionnaire avec resultats par source, intersection et classement
    """
    # Etape 1: Recherchez sur les deux sources
    # arxiv_results = web_searcher.search_arxiv(task)
    # pwc_results = web_searcher.search_papers_with_code(task)
    
    # Etape 2: Pour chaque paire de resultats, calculez la similarite
    # Indice: difflib.SequenceMatcher(None, titre1, titre2).ratio()
    cross_source_matches = []
    
    # TODO etudiant : implentez la double boucle de comparaison
    # for ar in arxiv_results:
    #     for pwc in pwc_results:
    #         similarity = difflib.SequenceMatcher(None, ar.title.lower(), pwc.title.lower()).ratio()
    #         if similarity >= similarity_threshold:
    #             cross_source_matches.append({...})
    
    # Etape 3: Classez les resultats par pertinence
    # Indice: les matches cross-source sont plus fiables que les single-source
    
    return {
        'task': task,
        'cross_source_matches': cross_source_matches,
        'total_arxiv': 0,  # TODO
        'total_pwc': 0,     # TODO
    }

# TODO: Testez la comparaison
# searcher_web = WebSearcher()
# comparison = compare_sources(searcher_web, "image segmentation")
# print(f"Tache: {comparison['task']}")
# print(f"Resultats arXiv: {comparison['total_arxiv']}")
# print(f"Resultats PwC: {comparison['total_pwc']}")
# print(f"Matches cross-source: {len(comparison['cross_source_matches'])}")

print("Exercice a completer : comparaison multi-source pour validation croisee")
Exercice a completer : comparaison multi-source pour validation croisee

Exercice : Evaluation de la Qualite du Code Genere

Le SOTACodeGenerator produit du code initial a partir des modèles SOTA trouves. L’objectif est de créer un evaluateur automatique qui verifie la qualite de ce code avant de l’utiliser.

Objectifs

  1. Implementer des verifications statiques (imports valides, variables définies, pas de syntax errors)
  2. Verifier la presence d’étapes essentielles (chargement, preprocessing, entrainement, evaluation)
  3. Generer un score de completude du code

Indice : - ast.parse(code) pour verifier la syntaxe Python sans executer - Cherchez des mots-cles : import, fit, predict, score, print - Un code ML complet devrait contenir au minimum : chargement de données + entrainement + evaluation

# Exercice : Evaluateur statique de code ML genere
# Objectif : Verifier la qualite du code avant execution

import ast

class CodeQualityEvaluator:
    """Evaluateur statique de code ML genere par le LLM."""
    
    # Etapes essentielles d'un pipeline ML
    REQUIRED_STEPS = {
        'data_loading': ['read_csv', 'load', 'read_json', 'DataFrame'],
        'preprocessing': ['fillna', 'dropna', 'transform', 'fit_transform', 'scale', 'encode'],
        'training': ['fit(', 'train', 'compile'],
        'evaluation': ['score', 'accuracy', 'predict', 'evaluate', 'cross_val']
    }
    
    def evaluate_syntax(self, code: str) -> dict:
        """Verifie la syntaxe Python du code."""
        try:
            ast.parse(code)
            return {'valid': True, 'error': None}
        except SyntaxError as e:
            return {'valid': False, 'error': str(e)}
    
    def evaluate_completeness(self, code: str) -> dict:
        """
        Evalue la completude du code ML.
        
        Returns:
            Dictionnaire avec score par etape et score global
        """
        code_lower = code.lower()
        scores = {}
        
        # TODO etudiant : pour chaque etape dans REQUIRED_STEPS,
        # verifiez si au moins un mot-cle est present dans le code
        for step, keywords in self.REQUIRED_STEPS.items():
            found = any(kw in code_lower for kw in keywords)
            scores[step] = 1.0 if found else 0.0
        
        # Score global
        scores['global'] = sum(scores.values()) / len(self.REQUIRED_STEPS)
        
        return scores
    
    def evaluate_imports(self, code: str) -> list:
        """Liste les imports detectes dans le code."""
        imports = []
        for line in code.split('\n'):
            line = line.strip()
            if line.startswith('import ') or line.startswith('from '):
                imports.append(line)
        return imports

# TODO: Testez l'evaluateur sur le code genere precedemment
# evaluator = CodeQualityEvaluator()
# 
# # Utilisez le code genere dans la section precedente
# # code_genere = result['code'] si disponible, ou un exemple
# test_code = """
# import pandas as pd
# from sklearn.ensemble import RandomForestClassifier
# from sklearn.metrics import accuracy_score
# df = pd.read_csv('data.csv')
# model = RandomForestClassifier()
# model.fit(X_train, y_train)
# score = accuracy_score(y_test, model.predict(X_test))
# print(f"Accuracy: {score}")
# """
# 
# syntax = evaluator.evaluate_syntax(test_code)
# completeness = evaluator.evaluate_completeness(test_code)
# imports = evaluator.evaluate_imports(test_code)
# 
# print(f"Syntaxe valide: {syntax['valid']}")
# print(f"Completude: {completeness}")
# print(f"Imports: {imports}")

print("Exercice a completer : evaluation statique de la qualite du code ML genere")
Exercice a completer : evaluation statique de la qualite du code ML genere

Références

  1. P. Lewis et al., Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks, arXiv:2005.11401, NeurIPS 2020. Paradigme RAG : augmenter un LLM par récupération externe de connaissances avant génération — fondement du module Web Search de ce lab.
  2. Google Research, MLE-STAR: Machine Learning Engineering Agent via Search and Targeted Refinement, arXiv:2506.15692, 2025. Agent MLE dont ce lab implémente le composant recherche SOTA (combinaison recherche + raffinement ciblé).
  3. Z. Xi et al., The Rise and Potential of Large Language Model Based Agents: A Survey, arXiv:2309.07864, 2023. Cadre conceptuel des agents LLM (suite Labs 8-12).
Retour au sommet