TRL documentation

Intégration de Harbor pour l’entraînement de LLM avec des environnements

Hugging Face's logo
Join the Hugging Face community

and get access to the augmented documentation experience

to get started

Intégration de Harbor pour l’entraînement de LLM avec des environnements

Harbor est un framework pour exécuter des tâches agentiques dans des environnements isolés. Il découple la tâche (instruction + image de sandbox + vérificateur), le harness/agent (surface d’outils + boucle) et le sandbox (docker, e2b, daytona, gke, …) afin qu’ils puissent être combinés librement. Cela en fait un choix naturel pour le RL : la même suite de tâches peut être entraînée avec différentes surfaces d’outils, sur le backend de sandbox de votre choix.

Ce guide explique comment intégrer Harbor avec TRL. Pour Harbor lui-même, consultez la documentation de Harbor.

L'intégration se trouve dans trl.experimental.harbor et est conditionnée par l'extra trl[harbor] (importé paresseusement — les non-utilisateurs n'ont rien à payer).

Quand utiliser les environnements Harbor

GRPOTrainer prend en charge l’entraînement basé sur des environnements via le slot environment_factory — voir OpenEnv pour le contrat général. Utilisez Harbor lorsque vous souhaitez entraîner un modèle sur une suite de tâches Harbor : un arborescence de tâches, chacune étant un bac à sable autonome avec un vérificateur (par exemple, une suite d’agents d’analyse de données où le modèle explore des fichiers dans un bac à sable et rédige une réponse qu’un correcteur vérifie).

Installation

pip install trl[harbor]

Harbor pilote la génération via vLLM et utilise environment_factory, qui nécessite vllm et transformers>=5.2.0.

pip install vllm

Cela installe le framework harbor (Python >= 3.12). L’intégration importe harbor de manière paresseuse et l’exécute in-process, de sorte que les utilisateurs qui n’utilisent pas trl.experimental.harbor ne sont pas affectés.

Un backend sandbox doit également être installé et accessible au moment de l’entraînement. Harbor maintient les backends cloud derrière ses propres extras ; installez celui que vous comptez utiliser et fournissez ses identifiants :

pip install "harbor[e2b]"      # E2B cloud sandbox  -> environment_type="e2b",  needs E2B_API_KEY
# docker backend (environment_type="docker", Harbor's default) just needs a reachable Docker daemon

Démarrage rapide

HarborSpec connecte une seule suite de tâches Harbor aux trois emplacements d’entraîneur TRL — train_dataset, environment_factory, reward_funcs — en exposant des propriétés qui correspondent 1:1 à ces noms de mots-clés :

from trl import GRPOConfig, GRPOTrainer
from trl.experimental.harbor import HarborSpec

spec = HarborSpec("AdithyaSK/data_agent_rl_environment_train", agent="bash", num_tasks=64)

trainer = GRPOTrainer(
    model="Qwen/Qwen3-4B",
    args=GRPOConfig(
        num_generations=8,
        max_steps=50,
        max_tool_calling_iterations=25,
        log_completions=True,
    ),
    train_dataset=spec.train_dataset,
    environment_factory=spec.environment_factory,
    reward_funcs=spec.reward_funcs,
)
trainer.train()

Sous le capot, HarborSpec fait trois choses, de manière paresseuse lors du premier accès :

  1. spec.train_dataset : résout la suite de tâches vers des répertoires de tâches locaux (télécharge le jeu de données HF si nécessaire) et construit un datasets.Dataset avec prompt (vide — l’instruction de l’environnement est ajoutée lors du reset), task_dir, task_index, ainsi que des colonnes de métadonnées task.toml par tâche.
  2. spec.environment_factory : renvoie un appelable sans argument qui produit une nouvelle instance de HarborEnv pour chaque déroulement. Lors de l’appel à reset(task_dir), il démarre le bac à sable Harbor de la tâche et renvoie son instruction ; les méthodes d’outil s’exécutent dans le bac à sable ; env.reward exécute le vérificateur une seule fois après le déroulement.
  3. spec.reward_funcs : une récompense de résultat qui lit le scalaire du vérificateur Harbor par déroulement.

Le jeu de données

dataset est soit un identifiant de dépôt de jeu de données Hugging Face contenant un arbre de tâches Harbor, soit un chemin local contenant un sous-arbre tasks/. Chaque tâche est un répertoire :

tasks/<task_id>/
├── instruction.md          # the task prompt (returned by reset)
├── task.toml               # config + metadata (gold answer, difficulty, ...)
├── environment/            # Dockerfile (+ any pre-agent data hooks)
└── tests/                  # test.sh / grader → writes the reward

Sélectionnez un sous-ensemble avec num_tasks ou indices (mutuellement exclusifs) :

spec = HarborSpec("AdithyaSK/data_agent_rl_environment_train", num_tasks=10)        # first 10
spec = HarborSpec("AdithyaSK/data_agent_rl_environment_train", indices=[0, 5, 13])  # specific

Agents : externes vs installés

Harbor prend en charge deux façons pour un agent de mener à bien une tâche, et cette distinction détermine ce qui peut être entraîné par RL :

  • Les agents externes s’exécutent en dehors du bac à sable et pilotent la boucle eux-mêmes, en émettant des commandes dans le conteneur via l’interface d’environnement de Harbor (« généralement en exécutant des commandes bash via la méthode exec »). L’agent décide de chaque action et interprète chaque résultat ; le bac à sable ne fait que les exécuter.
  • Agents installés sont installés dans l’image du conteneur et s’exécutent en tant que sous-processus sans interface (en étendant BaseInstalledAgent). Harbor lance l’agent dans le bac à sable puis analyse son fichier de trajectoire (populate_context_post_run) ; l’agent fonctionne de manière autonome avec sa propre inférence.

L’intégration de TRL repose sur le motif d’agent externe, et c’est le seul motif pris en charge pour le moment. L’entraînement par RL exige que le formateur pilote le déroulement du jeu tour par tour : le modèle de politique en cours d’entraînement génère chaque tour, et TRL capture ses jetons et leurs log-probabilités, puis applique le masque de l’environnement — exactement ce que environment_factory fournit par rapport à une rollout_func en boîte noire. Un agent installé est opaque à cet égard : il s’exécute dans le conteneur avec son propre modèle et n’émet une trajectoire qu’a posteriori, si bien qu’il n’existe ni jetons de politique ni log-probabilités pour le formateur à optimiser, et que le modèle en cours d’entraînement n’est jamais appelé. Un HarborEnv est donc un agent externe — ses méthodes d’outil exec dans le bac à sable, mais la boucle, et le modèle en cours d’entraînement, restent dans TRL.

Sélection de l’agent de base (harness)

L'agent de base est le harnais — il définit les méthodes d'outils exposées par l'env et la manière dont il soumet. Sélectionnez-le avec agent= :

HarborSpec(dataset, agent="bash")                          # built-in single-bash-tool harness
HarborSpec(dataset, agent="my_pkg.harnesses:JupyterEnv")   # import path to your HarborEnv subclass
HarborSpec(dataset, agent="path/to/harness.py:JupyterEnv") # file path to your HarborEnv subclass
HarborSpec(dataset, agent=MyHarborEnv)                     # a HarborEnv subclass directly

Le harnais "bash" intégré (HarborBashEnv) expose un outil bash et soumet en écrivant dans /workdir/answer.txt. Deux harnais plus riches sont fournis en tant qu’exemples — chacun dans son propre dossier avec un README listant ses outils — sous examples/grpo_harbor/harnesses/ :

  • jupyter/ (JupyterEnv) — un noyau Python avec état (les variables persistent d'une cellule à l'autre) + un outil shell.
  • terminal_notes/ (TerminalNotesEnv) — 6 outils shell (dont des processus en arrière-plan) + un kit d’outils de notes persistantes à 4 outils.
HarborSpec(dataset, agent="examples/grpo_harbor/harnesses/jupyter/env.py:JupyterEnv")
HarborSpec(dataset, agent="examples/grpo_harbor/harnesses/terminal_notes/env.py:TerminalNotesEnv")

Pour écrire votre propre harnais, sous-classez HarborEnv et ajoutez des méthodes d’outils — chaque méthode publique devient un outil (TRL les détecte avec inspect.getmembers), donnez donc à chacune une signature typée et une docstring (utilisée pour construire le schéma de l’outil). Préfixez les méthodes utilitaires par un tiret bas. Utilisez self._exec(cmd) pour exécuter des commandes shell dans le bac à sable, et définissez PROMPT_SUFFIX pour ajouter les consignes du harnais à l’instruction de la tâche :

from trl.experimental.harbor import HarborEnv

class GrepEnv(HarborEnv):
    PROMPT_SUFFIX = "\n\nUse `grep` and `read_file`. Submit by writing /workdir/answer.txt."

    def grep(self, pattern: str, path: str) -> str:
        """Search for `pattern` under `path`.

        Args:
            pattern: The regex to search for.
            path: The file or directory to search.
        """
        return self._exec(f"grep -rn {pattern!r} {path!r}")

Le backend sandbox

environment_type est transmis tel quel à Harbor (non validé par TRL) :

HarborSpec(dataset, environment_type="e2b")   # cloud sandbox (offloads provisioning), needs E2B_API_KEY
HarborSpec(dataset, environment_type="docker")  # default; needs a local Docker daemon

e2b est recommandé pour l’entraînement en cluster : seule l’appel environment.exec est exécuté dans le bac à sable cloud, ce qui permet de garder les GPU dédiés à la politique et de lancer de nombreux déploiements en parallèle.

Fonctions de récompense

spec.reward_funcs est par défaut une récompense de résultat — pour chaque déroulement, elle lit le scalaire du vérificateur Harbor (env.reward), calculé une seule fois après le déroulement en exécutant le vérificateur tests/ de la tâche dans le bac à sable. Pour une récompense personnalisée, écrivez une fonction de récompense TRL standard :

def my_reward(environments, **kwargs) -> list[float]:
    return [env.reward for env in environments]

API

classe trl.experimental.harbor.HarborSpec

< >

( dataset: stragent: str | type[HarborEnv] = 'bash'environment_type: str = 'docker'num_tasks: int | None = Noneindices: list[int] | None = Noneinclude_metadata: bool = True )

Paramètres

  • dataset (str) — Identifiant d'un dépôt de jeu de données Hugging Face contenant un arbre de tâches Harbor (par exemple "AdithyaSK/data_agent_rl_environment_train"), ou un chemin local vers un répertoire contenant un sous-arbre tasks/. Chaque tâche est un répertoire contenant instruction.md / task.toml / environment/ / tests/.
  • agent (str ou type, facultatif, par défaut "bash") — L’agent de base / le harnais — c’est-à-dire la surface d’outils exposée par l’env. L’une des options suivantes : un nom intégré ("bash"), un chemin d’importation "package.module:ClassName", un chemin de fichier "path/to/file.py:ClassName", ou un sous-classe de HarborEnv directement.
  • environment_type (str, facultatif, par défaut "docker") — Backend de bac à sable Harbor, transmis à Harbor (tout ce qu’il prend en charge — "docker", "e2b", "daytona", "gke", "modal", "runloop", …). Non validé ici ; c’est Harbor qui le valide. "docker" est la valeur par défaut de Harbor ; choisissez "e2b" pour déléguer le bac à sable au cloud.
  • num_tasks (int, facultatif) — Limite le nombre de tâches intégrées au jeu de données. None utilise toutes les tâches de l'arborescence.
  • indices (list[int], facultatif) — Indices de tâches spécifiques (dans la liste des tâches triées). Mutuellement exclusif avec num_tasks.
  • include_metadata (bool, facultatif, valeur par défaut True) — Intègre les métadonnées task.toml de chaque tâche (gold_answer, difficulty, …) dans les lignes du jeu de données.

Objet de spécification unique qui connecte une suite de tâches Harbor à un formateur TRL.

classe trl.experimental.harbor.HarborEnv

< >

( environment_type: str = 'docker' )

Paramètres

  • environment_type (str, facultatif, par défaut "docker") — Backend de bac à sable Harbor, transmis à Harbor ("docker", "e2b", "daytona", …).

Environnement TRL de base basé sur un bac à sable Harbor + vérificateur.

Les sous-classes définissent les méthodes d’outils (le harnais). Le cycle de vie que TRL pilote pour chaque déroulement : reset(task_dir) (démarrer le bac à sable de la tâche, retourner son instruction) -> méthodes d’outils (exécuter dans le bac à sable) -> reward (exécuter le vérificateur une fois, de manière paresseuse, après le déroulement).

classe trl.experimental.harbor.HarborBashEnv

< >

( environment_type: str = 'docker' )

Harness à outil unique bash ; soumettez en écrivant dans /workdir/answer.txt.

bash

< >

( command: str )

Paramètres

  • command — La commande shell à exécuter.

Exécutez une commande shell dans le bac à sable et renvoyez sa sortie combinée stdout+stderr. Le shell n'est pas étatique entre les appels. Utilisez-le pour explorer les fichiers (ls, head, cat), exécuter du Python (python3 -c "...") et soumettre la réponse (echo -n "<value>" > /workdir/answer.txt).

Limites

  • L'intégration se trouve dans trl.experimental — les API peuvent changer. Définissez TRL_EXPERIMENTAL_SILENCE=1 pour supprimer l'avertissement dans les journaux CI.
  • Le client de bac à sable asynchrone de Harbor est lié à une seule boucle d’événements, de sorte que chaque env pilote start/exec/verify de manière synchrone sur sa propre boucle ; le provisionnement des bacs à sable est donc séquentiel au sein du lot de génération (les backends cloud comme e2b atténuent le coût par bac à sable).
  • Un seul HarborSpec couvre une suite de tâches + un harnais ; l’entraînement multi-suites n’est pas encore pris en charge.

Référence

Update on GitHub