add-benchmark

Par nvidia · skills

Guide pour ajouter un nouveau benchmark ou environnement d'entraînement à NeMo-Gym. À utiliser quand l'utilisateur souhaite ajouter, créer ou intégrer un benchmark, une évaluation, un environnement d'entraînement ou un serveur de ressources dans NeMo-Gym. Également applicable pour encapsuler une bibliothèque de benchmark tierce existante. Couvre l'ensemble du workflow : préparation des données, implémentation du serveur de ressources, câblage de l'agent, configuration YAML, tests et profilage des récompenses (baselining). Déclenché par : « add benchmark », « new resources server », « integrate benchmark », « wrap benchmark », « add training environment », « add eval ».

npx skills add https://github.com/nvidia/skills --skill add-benchmark

Ajouter un Benchmark à NeMo-Gym

Déterminer le Type d'Intégration

Avant de commencer, déterminez quel type de benchmark vous ajoutez :

Benchmark natif — logique de vérification implémentée directement dans un serveur de ressources Gym :

  • Le serveur de ressources implémente verify() avec la logique de récompense
  • Le serveur agent orchestre les appels du modèle (utilise simple_agent pour un tour unique, ou un agent personnalisé pour plusieurs tours)
  • Exemple : code_gen, instruction_following, math_with_judge

Benchmark externe — encapsulation d'une bibliothèque tierce qui a sa propre orchestration :

  • Intégration au niveau du serveur agent (non au serveur de ressources)
  • Le endpoint /run de l'agent encapsule la bibliothèque externe
  • Pré-traitement du schéma Gym vers l'entrée de la bibliothèque, post-traitement vers BaseVerifyResponse
  • Reproduisez d'abord les nombres publiquement rapportés avec le dépôt d'origine, puis à nouveau après l'intégration à Gym
  • Ajoutez la dépendance dans requirements.txt

Flux de Travail

Étape 1 : Structurer le serveur

Exécutez ng_init_resources_server pour générer la structure de répertoires :

ng_init_resources_server +entrypoint=resources_servers/my_benchmark

Cela crée :

resources_servers/my_benchmark/
├── app.py              # Modèle de serveur
├── configs/my_benchmark.yaml
├── data/.gitignore
├── tests/test_app.py
├── requirements.txt
└── README.md

Pour les benchmarks externes, créez manuellement le serveur agent sous responses_api_agents/my_agent/ avec la même structure.

Étape 2 : Préparer les données

Convertissez votre dataset source au format JSONL de Gym. Chaque ligne doit avoir responses_create_params.input (format de message OpenAI). Les données de vérification spécifiques à la tâche vont dans verifier_metadata.

{
  "responses_create_params": {
    "input": [
      {"role": "system", "content": "Invite système"},
      {"role": "user", "content": "Énoncé du problème"}
    ]
  },
  "verifier_metadata": {
    "test_cases": [{"input": "...", "expected_output": "..."}],
    "task_id": "id_unique"
  }
}

Conversion de données : Écrivez les scripts de conversion dans le dépôt source (p. ex. votre dépôt de dataset), non dans NeMo-Gym. Les fichiers de prompt appartiennent aussi au dépôt source. Exception : quand il n'y a pas de dépôt source externe. Voir references/patterns.md § « Data Conversion Script Pattern ».

example.jsonl : Générez 5 entrées pour un test de fumée. Ce fichier est committé directement dans data/example.jsonl.

Datasets train/validation : Téléchargez dans le registre de datasets GitLab — ces données NE DOIVENT PAS être commitées dans git.

ng_upload_dataset_to_gitlab \
    +dataset_name=my_benchmark \
    +version=0.0.1 \
    +input_jsonl_fpath=resources_servers/my_benchmark/data/my_dataset.jsonl

Nécessite les identifiants MLflow dans env.yaml (ou passés via CLI) :

mlflow_tracking_uri: <your-gitlab-mlflow-tracking-uri>
mlflow_tracking_token: <your-gitlab-api-token>

data/.gitignore : Le scaffold génère des motifs par défaut (*train.jsonl, *validation.jsonl, etc.). Si votre nom de fichier ne correspond pas (p. ex. my_eval.jsonl), ajoutez un motif personnalisé (p. ex. *eval.jsonl). Si les données étaient précédemment suivies, exécutez git rm --cached <file>.

Validez vos données :

# Valider les données d'exemple (pour la soumission de PR)
ng_prepare_data "+config_paths=[resources_servers/my_benchmark/configs/my_benchmark.yaml]" \
    +output_dirpath=/tmp/prepare +mode=example_validation

# Télécharger et préparer train/validation depuis GitLab
ng_prepare_data "+config_paths=[resources_servers/my_benchmark/configs/my_benchmark.yaml]" \
    +output_dirpath=data/my_benchmark +mode=train_preparation +should_download=true +data_source=gitlab

Étape 3 : Implémenter verify()

Modifiez app.py. La méthode verify() reçoit la sortie du modèle + verifier_metadata, retourne une récompense.

Pour les benchmarks d'exécution de code, consultez references/patterns.md § « Subprocess Execution with Ray » et « Resources Server Pattern ».

Règles critiques :

  • Retournez reward comme 0,0 ou 1,0 (binaire)
  • Gérez gracieusement les sorties vides/manquantes du modèle — retournez 0,0, ne plantez pas
  • Doit gérer 4k-65k requêtes concurrentes sans planter
  • Utilisez asyncio.Semaphore pour le contrôle de concurrence des sous-processus
  • Pour les tâches Ray distantes : result = await future (les futures Ray sont directement attendables). N'appelez jamais ray.get() dans un contexte async.
  • Décodez la sortie des sous-processus avec errors="replace"
  • Supprimez les blocs <think>/<thinking> avant d'analyser la sortie du modèle (les modèles de réflexion émettent ceux-ci)
  • Les tests doivent pytest.mark.skipif quand les outils externes ne sont pas installés
  • Si le benchmark installe automatiquement son outil (voir Étape 3b), ajoutez un hook pytest_configure dans conftest.py pour exécuter l'installation avant la collecte de tests — skipif s'évalue au moment de l'import, avant l'exécution des fixtures

Étape 3b : Auto-installer les outils externes (si applicable)

Si le benchmark nécessite un outil externe (compilateur, runtime, etc.), installez-le automatiquement au démarrage du serveur pour que les utilisateurs n'aient pas besoin d'une configuration manuelle. Voir references/patterns.md § « External Tool Auto-Install Pattern ».

Points clés :

  • Créez setup_<tool>.py avec ensure_<tool>() — vérifie PATH, bifurque sur sys.platform (brew sur macOS, compilation à partir de la source sur Linux)
  • Appelez-le dans model_post_init() avant l'initialisation du sémaphore
  • Les scripts de compilation doivent être idempotents et installer dans un préfixe local ignoré par git
  • Ajoutez un hook pytest_configure dans tests/conftest.py qui appelle ensure_<tool>() avant la collecte

Étape 4 : Câbler la config YAML

Modifiez configs/my_benchmark.yaml. Définissez l'instance du serveur de ressources et le(s) appairage(s) d'agent. Voir references/patterns.md § « YAML Config Pattern ».

Points clés :

  • verified: false est auto-ajouté par le hook pre-commit (définissez à true après le baseline)
  • license est obligatoire pour les datasets train et validation
  • L'agent référence le serveur de ressources et le serveur de modèle par nom d'instance

Pour les benchmarks multi-tour, utilisez soit proof_refinement_agent soit créez un agent personnalisé. Voir references/patterns.md § « Agent Patterns ».

Pour les datasets train/validation, ajoutez gitlab_identifier à côté de jsonl_fpath :

datasets:
- name: my_dataset
  type: train
  jsonl_fpath: resources_servers/my_benchmark/data/my_dataset.jsonl
  gitlab_identifier:
    dataset_name: my_benchmark
    version: 0.0.1
    artifact_fpath: my_dataset.jsonl
  license: MIT
- name: example
  type: example
  jsonl_fpath: resources_servers/my_benchmark/data/example.jsonl

Les deux champs doivent coexister : jsonl_fpath est la destination de téléchargement local, gitlab_identifier indique au système d'où faire la récupération. Les datasets example n'ont pas besoin de gitlab_identifier — ils sont committes directement dans git.

Étape 5 : Tester

# Exécuter les tests du serveur (crée un .venv isolé, lent au premier lancement)
ng_test +entrypoint=resources_servers/my_benchmark

# Exécuter les tests de la bibliothèque principale pour vérifier que rien n'a cassé
pytest tests/unit_tests/ -x

La couverture de test doit être >= 95%. Écrivez des tests pour : verify réussit, verify échoue (sortie incorrecte), verify échoue (aucun code extrait), verify échoue (erreur de compilation si applicable), verify timeout.

Étape 6 : Test de fumée end-to-end

# Démarrer les serveurs
ng_run "+config_paths=[resources_servers/my_benchmark/configs/my_benchmark.yaml,responses_api_models/openai_model/configs/openai_model.yaml]"

# Test rapide avec données d'exemple
ng_collect_rollouts +agent_name=my_benchmark_simple_agent \
  +input_jsonl_fpath=resources_servers/my_benchmark/data/example.jsonl \
  +output_jsonl_fpath=results/example_rollouts.jsonl \
  +num_repeats=1 \
  "+responses_create_params={max_output_tokens: 16384, temperature: 1.0}"

# Inspectez les résultats

Étape 7 : Baseline (profilage des récompenses)

Exécutez avec plusieurs modèles pour valider la correction. Suite recommandée :

  • Votre modèle de politique d'intérêt
  • Au moins un modèle instruct open-source (p. ex. Qwen 3 30B A3B Instruct)
  • Au moins un modèle de réflexion open-source (p. ex. Qwen 3 30B A3B Thinking)
  • Au moins un modèle fermé (p. ex. GPT-5 Nano ou GPT-5)
# Collecter les rollouts
ng_collect_rollouts +agent_name=my_benchmark_simple_agent \
  +input_jsonl_fpath=resources_servers/my_benchmark/data/my_dataset.jsonl \
  +output_jsonl_fpath=results/rollouts.jsonl \
  +num_repeats=5 \
  "+responses_create_params={max_output_tokens: 16384, temperature: 1.0}"

# Calculer les taux de passage par tâche
ng_reward_profile +input_jsonl_fpath=resources_servers/my_benchmark/data/my_dataset.jsonl \
  +rollouts_jsonl_fpath=results/rollouts.jsonl \
  +output_jsonl_fpath=results/profiled.jsonl \
  +pass_threshold=1.0

# Agréger les métriques (pass@1 = avg_reward, pass@k à partir de max_reward)
python scripts/print_aggregate_results.py +jsonl_fpath=results/profiled.jsonl

Augmentez num_repeats jusqu'à ce que la variance < 1 % à travers les exécutions sur le même modèle.

Les modèles fermés doivent obtenir un score égal ou supérieur aux modèles open-source. Si ce n'est pas le cas, enquêtez sur les bugs. Inspectez les cas d'échec réels dans le JSONL des rollouts, pas seulement les chiffres agrégés.

Pour les benchmarks externes : reproduisez d'abord les nombres publiés du dépôt d'origine. Puis reproduisez après l'intégration à Gym. Les scores doivent correspondre.

Étape 8 : Pre-commit et PR

pre-commit run --all-files

La première exécution peut échouer car les hooks modifient automatiquement les fichiers (flag verified: false, tableau README). Mettez en scène les changements et exécutez à nouveau.

Définissez verified: true dans la configuration YAML après un baseline réussi. Incluez les liens W&B et les captures d'écran des résultats dans la description de la PR.

Pour éviter de committer des auto-corrections non liées d'autres serveurs, limitez pre-commit à vos fichiers :

pre-commit run --files resources_servers/my_benchmark/**/*

Si les hooks modifient des fichiers dans d'autres répertoires, annulez ces changements :

git checkout -- resources_servers/other_server/

Contraintes

  • Utilisez le client OpenAI de NeMo Gym (nemo_gym/openai_utils.py), non LiteLLM/Anthropic/autre
  • Utilisez aiohttp, non httpx, pour HTTP async. Tous les appels HTTP async doivent passer par nemo_gym.server_utils.request() (aiohttp). httpx a un pool de connexions O(n²) qui s'accroche à haute concurrence. Quand vous encapsulez des bibliothèques externes qui utilisent httpx en interne, remplacez leur transport HTTP par un adaptateur aiohttp — voir resources_servers/tavily_search/app.py (TavilySearchAIOHTTPClient) pour le motif et docs/infrastructure/engineering-notes/aiohttp-vs-httpx.md pour la justification.
  • Passez la configuration via la config Gym (YAML), non des variables d'environnement
  • Le code doit s'exécuter sur Linux
  • Le endpoint /run doit être async
  • Les erreurs d'exécution d'outil ou de sortie de modèle erronée doivent retourner des réponses d'erreur, non planter
  • Tous les commits nécessitent une signature DCO (-s) et une signature cryptographique (-S)

Référence

Pour des motifs de code détaillés, des schémas et des exemples : consultez references/patterns.md.

Skills similaires