TRL documentation

Integrasi OpenEnv untuk Pelatihan LLM dengan Lingkungan

You are viewing main version, which requires installation from source. If you'd like regular pip install, checkout the latest stable version (v1.18.0).
Hugging Face's logo
Join the Hugging Face community

and get access to the augmented documentation experience

to get started

Integrasi OpenEnv untuk Pelatihan LLM dengan Lingkungan

OpenEnv adalah kerangka kerja sumber terbuka untuk mendefinisikan, mendeploymen, dan berinteraksi dengan lingkungan dalam penguatan penguatan (RL) dan alur kerja agen. Kerangka kerja ini menyediakan API standar untuk interaksi lingkungan dan mendukung eksekusi lingkungan sebagai server backend (melalui WebSocket atau eksekusi terkontainerisasi). Anda dapat menemukan kumpulan lingkungan OpenEnv yang siap digunakan di Hugging Face Hub.

Panduan ini membahas bagaimana mengintegrasikan OpenEnv dengan TRL. Untuk informasi lebih lanjut tentang OpenEnv itu sendiri, lihat dokumentasi OpenEnv.

Contoh OpenEnv siap pakai: grpo_echo (minimal), grpo_catch, grpo_wordle, grpo_sudoku, grpo_multi_env, grpo_browsergym, grpo_carla, dan async_grpo_harbor (lokal dan HF Jobs).

Kapan menggunakan lingkungan

GRPOTrainer dapat digunakan untuk melatih agen. Untuk tugas agentic, ia mendukung dua mode: tools, di mana model dapat memanggil fungsi eksternal tetapi setiap panggilan bersifat stateless dan independen, serta environments, yang mempertahankan state antar giliran, memungkinkan interaksi multi-turn yang sesungguhnya di mana tindakan agen membentuk observasi berikutnya. Gunakan environments ketika kontinuitas penting — misalnya, menavigasi sebuah game, menelusuri halaman web, atau tugas apa pun di mana apa yang dilihat agen selanjutnya bergantung pada apa yang dilakukannya sebelumnya.

Memilih integrasi lingkungan

OpenEnv adalah jalur native yang didokumentasikan di sini. Dua integrasi tambahan — OpenReward dan Harbor — mematuhi kontrak environment_factory yang sama dan dapat dipertukarkan pada level TRL. Lihat perbandingan integrasi environment di panduan GRPO untuk memilih yang ekosistemnya sesuai dengan tugas Anda.

Instalasi

Lingkungan OpenEnv dihosting sebagai Hugging Face Spaces, yang juga merupakan repositori Git yang dapat diinstal melalui pip:

# Echo environment
pip install "openenv-echo-env @ git+https://huggingface.co/spaces/openenv/echo_env"

# Wordle (TextArena) environment
pip install "openenv-textarena @ git+https://huggingface.co/spaces/openenv/wordle"

# Catch (OpenSpiel) environment
pip install "openenv-openspiel-env @ git+https://huggingface.co/spaces/openenv/openspiel_env"

Ini menginstal klien lingkungan (misalnya, EchoEnv) yang berkomunikasi dengan server lingkungan jarak jauh melalui WebSocket, beserta model aksi/observasi dan semua dependensi yang diperlukan (termasuk openenv).

Anda dapat menemukan perintah instalasi untuk lingkungan apa pun di halaman HF Space-nya. Klik menu ⋮ (tiga titik) dan pilih “Use this Space” untuk melihat instruksi instalasi.

Anda juga dapat menginstal paket inti dari PyPI dengan pip install "openenv>=0.3.1", tetapi perhatikan bahwa dependensi yang spesifik untuk lingkungan mungkin perlu diinstal secara terpisah.

Untuk pengembangan, Anda dapat mengkloning repo OpenEnv dan menginstalnya secara lokal:

git clone https://github.com/openenv/OpenEnv.git
cd OpenEnv/envs/echo_env
pip install -e .

Setiap skrip lingkungan di TRL mencakup metadata dependensi inline (PEP 723) sehingga Anda juga dapat menjalankannya secara langsung dengan uv:

uv run examples/grpo_echo/grpo_echo.py

Ini secara otomatis menginstal paket lingkungan yang diperlukan di lingkungan virtual yang terisolasi.

Mulai cepat

Cara tercepat untuk memahami integrasi adalah melalui contoh lengkap. Skrip echo.py melatih model dengan Echo environment, yang memberikan imbalan untuk kelengkapan berdasarkan panjang teksnya:

from datasets import Dataset
from echo_env import EchoEnv
from echo_env.models import EchoAction

from trl import GRPOConfig, GRPOTrainer

ENV_URL = "https://huggingface.co/proxy/openenv-echo-env.hf.space"

class EchoToolEnv:
    def __init__(self):
        self.env = EchoEnv(base_url=ENV_URL)
        self.reward = 0.0

    def reset(self, **kwargs) -> str | None:
        self.reward = 0.0
        return None

    def echo(self, message: str) -> str:
        """
        Echo the message back from the environment.

        Args:
            message: The message to echo

        Returns:
            The echoed message.
        """
        observation = self.env.step(EchoAction(message=message))
        self.reward = observation.observation.reward
        return observation.observation.echoed_message

def reward_func(environments, **kwargs):
    return [env.reward for env in environments]

dataset = Dataset.from_dict(
    {"prompt": [[{"role": "user", "content": "Try to echo 'Hello World!' in the environment."}]] * 64}
)

trainer = GRPOTrainer(
    model="Qwen/Qwen3-0.6B",
    train_dataset=dataset,
    reward_funcs=reward_func,
    args=GRPOConfig(
        chat_template_kwargs={"enable_thinking": False},
        log_completions=True,
    ),
    environment_factory=EchoToolEnv,
)
trainer.train()

Itu saja. Berikut yang terjadi di balik layar:

  1. environment_factory=EchoToolEnv: Trainer membuat satu instance EchoToolEnv per generasi (lalu kelasnya, bukan instansinya).
  2. reset() dipanggil di awal setiap episode untuk menginisialisasi state. Mengembalikan string observasi (atau None).
  3. Penemuan alat: Trainer menemukan semua metode publik pada instance environment (di sini, echo()) dan mengeksposnya sebagai alat pemanggilan fungsi. Setiap metode harus memiliki docstring yang tepat dengan argumen bertipe, yang digunakan trainer untuk membangun skema alat.
  4. Loop multi-turn: Trainer menghasilkan completion, mem-parsing panggilan tool, mengeksekusi echo(), menambahkan hasilnya, dan menghasilkan lagi, hingga model berhenti memanggil tool atau max_completion_length tercapai.
  5. Fungsi reward: Membaca env.reward dari setiap instance lingkungan setelah episode (sebelum lingkungan di-reset).
# Run the example
python examples/grpo_echo/grpo_echo.py

# Customize model and environment URL
python examples/grpo_echo/grpo_echo.py --model Qwen/Qwen3-0.6B --env-host https://huggingface.co/proxy/openenv-echo-env.hf.space

Di bawah ini adalah kurva reward dari pelatihan:

Anda dapat menjelajahi lebih banyak contoh OpenEnv siap pakai dalam catatan di bagian atas halaman ini.

Cara kerja environment_factory

GRPOTrainer dari TRL mendukung pelatihan lingkungan interaktif melalui argumen environment_factory. Ketika diberikan, trainer secara otomatis menangani loop pemanggilan alat multi-turn: menghasilkan kelengkapan, mengurai panggilan alat, mengeksekusinya terhadap lingkungan, dan memberikan hasilnya kembali ke model. Semua ini tanpa kode rollout kustom.

Persyaratan kelas Environment

Kelas lingkungan Anda harus mengikuti aturan-aturan berikut:

  • __init__(self) (opsional): Jika disediakan, harus tidak menerima argumen. Gunakan untuk menginisialisasi state atau klien. Jika Anda memerlukan konfigurasi eksternal (misalnya, URL), tangkap dari scope yang melingkupinya atau variabel tingkat modul.
  • reset(self, **kwargs): Dipanggil di awal setiap episode. Menerima semua kolom dataset sebagai argumen kata kunci. Kembalikan pengamatan berupa string (atau None jika tidak ada pengamatan awal).
  • Metode tool: Setiap metode publik (tidak diawali dengan _) selain reset secara otomatis diekspos sebagai tool. Setiap metode tool harus memiliki docstring dengan deskripsi Args:, karena trainer menggunakan informasi ini untuk menghasilkan skema tool bagi model.

Tips untuk kelas lingkungan

  • State untuk reward: Anda dapat menyimpan state apa pun pada instance environment (misalnya, self.reward, self.done, dll.) dan mengaksesnya di fungsi reward Anda melalui parameter environments. Lihat panduan Quick Start untuk contoh pola ini.
  • Penanganan kesalahan: Jika metode alat melempar pengecualian (misalnya, ValueError("Game over.")), trainer menangkapnya dan mengirimkan pesan kesalahan kembali ke model sebagai respons alat. Ini adalah cara yang direkomendasikan untuk menandakan bahwa suatu tindakan tidak valid atau episode telah berakhir.
ENV_URL = "https://huggingface.co/proxy/my-env.hf.space"

class MyEnv:
    def __init__(self):
        self.client = MyClient(base_url=ENV_URL)  # captured from enclosing scope
        self.reward = 0.0

    def reset(self, **kwargs) -> str | None:
        self.reward = 0.0
        return "Initial observation for the model"

    def my_tool(self, arg1: str, arg2: int) -> str:
        """
        Description of what this tool does.

        Args:
            arg1: Description of arg1
            arg2: Description of arg2

        Returns:
            The result message.
        """
        self.reward = 1.0
        return "Tool result"

Tools harus berupa metode individual dengan nama yang deskriptif dan argumen bertipe (misalnya, guess(word: str), move(direction: str)). Kami tidak merekomendasikan penggunaan metode generik seperti step(action), karena model membutuhkan nama tools dan deskripsi argumen yang bermakna untuk mempelajari tool calling.

Fungsi reward

Fungsi reward menerima parameter environments (sebuah daftar instance lingkungan), sehingga Anda dapat mengakses keadaan apa pun yang disimpan selama episode:

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

Untuk informasi lebih lanjut tentang fungsi reward, lihat GRPO - Custom Reward Functions.

Tips untuk fungsi reward

Beberapa hal yang kami temukan sangat membantu saat bekerja dengan lingkungan OpenEnv dan GRPO:

  • Pastikan grup-grup tersebut berbeda. GRPO membandingkan penyelesaian di dalam sebuah grup, sehingga peringkat relatif lebih penting daripada nilai absolut, dan grup di mana setiap penyelesaian mendapatkan reward yang sama tidak memberikan sinyal. Reward biner (1,0 untuk sukses, 0,0 selain itu) bersih ketika model berhasil sesekali. Ketika model jarang berhasil, tambahkan kredit parsial: contoh Sudoku menambahkan fraksi sel yang terisi ke reward penyelesaian, sedangkan contoh Wordle menambahkan jumlah hijau dari tebakan terbaik.
  • Periksa keadaan akhir, bukan jalurnya. Jika memungkinkan, biarkan lingkungan menilai hasilnya (misalnya, “apakah model menyelesaikan teka-teki?”) alih-alih memeriksa apakah model mengikuti urutan tindakan tertentu. Hal ini memberi model kebebasan untuk menemukan strateginya sendiri.
  • Uji reward Anda sebelum pelatihan. Jalankan beberapa episode secara manual (lihat notebook contoh Wordle) untuk memastikan lingkungan mengembalikan reward yang masuk akal. Jika model yang mumpuni tidak dapat mencetak skor lebih tinggi dari baseline acak, sinyal reward mungkin perlu disesuaikan.

max_completion_length dalam episode multi-turn

Parameter max_completion_length membatasi jumlah total token di seluruh percakapan multi-turn (gabungan semua generasi model + hasil tool), bukan hanya satu generasi. Untuk lingkungan dengan banyak turn (misalnya, Sudoku dengan puluhan langkah), Anda mungkin perlu meningkatkannya:

args = GRPOConfig(
    max_completion_length=4096,  # default is usually 256-1024, increase for long episodes
    # ...
)

Jika episode dipotong di tengah (model berhenti di tengah permainan), kemungkinan besar ini adalah penyebabnya.

Jalankan panjang terhadap sebuah Space

A Space dapat menutup WebSockets yang tetap terbuka selama berjam-jam (kami mengamati hal ini terjadi sekitar setiap 2 jam). Klien OpenEnv hingga versi 0.11.0 kemudian akan menyambung ulang ke sesi lingkungan baru, dan jika sebuah permainan sedang berlangsung, pemanggilan step() berikutnya akan berjalan pada permainan yang tidak pernah di-reset oleh klien, tanpa menghasilkan error. Sejak OpenEnv 0.12.0, klien akan melempar SessionLostError sebagai gantinya, sehingga permainan akan gagal. Untuk menjaga agar gangguan ini tidak memengaruhi pelatihan, buka klien baru di dalam reset(), sehingga setiap koneksi hanya berlangsung selama satu episode, dan atur MAX_CONCURRENT_ENVS sedikit di atas jumlah permainan yang dimainkan secara bersamaan, karena sesi lama dilepaskan secara asinkron:

def reset(self, **kwargs) -> str | None:
    self.client.close()
    self.client = TextArenaEnv(base_url=env_url).sync()
    result = self.client.reset()
    ...

Contoh lanjutan: Wordle

Mari kita latih model untuk bermain Wordle menggunakan lingkungan TextArena. Hal ini mendemonstrasikan interaksi multi-turn, penanganan umpan balik kumulatif, reward yang dipecah menjadi beberapa istilah, dan terminasi episode melalui exception.

Tutorial OpenEnv Wordle mencakup seluruh pipeline: data SFT, warmup SFT, langkah GRPO ini, dan evaluasi, lengkap dengan detail hardware dan biaya. Versi notebook dari contoh ini adalah grpo_wordle.ipynb.

Lingkungan TextArena

TextArena adalah kumpulan game berbasis teks kompetitif open-source yang dirancang untuk mengevaluasi kemampuan penalaran pada LLM menggunakan game teks seperti Wordle, Snake, Tic-Tac-Toe, dan lainnya.

image of TextArena

Mengapa Wordle?

Wordle adalah benchmark yang baik untuk RL berbasis lingkungan karena memerlukan penalaran tentang umpan balik, sepenuhnya berbasis teks, dan model dengan 1B parameter dapat meningkat kemampuannya.

Bagaimana cara kerja Wordle? Wordle adalah permainan menebak kata di mana pemain harus menebak kata berhuruf 5 dalam 6 percobaan. Setelah setiap tebakan, lingkungan memberikan umpan balik per huruf:

G U E S S
X G Y X X

X = tidak ada dalam kata, G = posisi benar (hijau), Y = posisi salah (kuning). Di sini, “U” benar dan berada di tempatnya, “E” ada dalam kata tetapi posisinya salah.

Mulai dari pemanasan SFT

Qwen3-1.7B hampir tidak pernah memenangkan permainan, sehingga GRPO dari model dasar memiliki sedikit hal untuk dipelajari: sekelompok permainan di mana tidak ada yang dimenangkan tidak memberikan sinyal. Contoh ini dimulai dari openenv/Qwen3-1.7B-Wordle-SFT, yang dilatih selama satu epoch pada 1.000 permainan yang dimainkan oleh solver dalam format pemanggilan alat yang sama, dengan penalaran singkat sebelum setiap tebakan.

Kelas Environment

Kelas WordleEnv membungkus klien TextArena, mengekspos guess() sebagai tool, dan menyimpan apa yang dibutuhkan reward:

import re

from textarena_env import TextArenaAction, TextArenaEnv

FEEDBACK = re.compile(r"Feedback:\n([A-Z ]+)\n([GYX ]+)")

class WordleEnv:
    def __init__(self):
        self.client = TextArenaEnv(base_url="https://<user>-wordle.hf.space")

    def reset(self, **kwargs) -> str | None:
        result = self.client.reset()
        self._last_full_feedback = result.observation.messages[0].content
        self.done = False
        self.won = False
        self.num_guesses = 0
        self.best_greens = 0
        self.invalid_moves = 0
        return self._last_full_feedback

    def guess(self, guess: str) -> str:
        """
        Make a guess in the Wordle environment.

        Args:
            guess: The guessed word, formatted as '[abcde]'

        Returns:
            The feedback message from the environment.
        """
        if self.done:
            raise ValueError("Game over.")
        result = self.client.step(TextArenaAction(message=guess))
        _full_feedback = result.observation.messages[0].content
        feedback = _full_feedback[len(self._last_full_feedback):]
        self._last_full_feedback = _full_feedback
        if "invalid move" in feedback.lower():
            self.invalid_moves += 1
        else:
            self.num_guesses += 1
            match = FEEDBACK.search(feedback)
            if match:
                self.best_greens = max(self.best_greens, match.group(2).count("G"))
            self.won = result.done and result.reward >= 1.0
        self.done = result.done
        return feedback

Pilihan desain utama:

  • reset() mengembalikan pesan permainan awal sebagai observasi pertama yang dilihat model.
  • guess() adalah satu-satunya alat. Model memanggilnya setiap giliran dengan kata berhuruf lima.
  • Pemotongan umpan balik kumulatif: TextArena mengembalikan seluruh riwayat permainan setiap giliran. Kami memotong hanya bagian baru untuk menghindari pengulangan konteks.
  • Pengecualian saat selesai: Jika model mencoba menebak setelah permainan berakhir, guess() akan melempar ValueError. Trainer menangkap ini dan memberikan "Game over." kembali ke model sebagai respons alat, serta menghitungnya dalam tools/failure_frequency.

Fungsi reward dan pelatihan

Hadiah dari lingkungan itu sendiri memberikan imbalan kemenangan yang sama baik setelah 1 tebakan maupun 6 tebakan, dan hanya memberikan kredit parsial di akhir permainan. Dengan mode berpikir aktif, sebuah kebijakan dapat belajar untuk berpikir lebih sedikit dan menggunakan semua 6 tebakan untuk mencapai akhir. Tiga istilah hadiah menghindari hal itu:

from datasets import Dataset
from trl import GRPOConfig, GRPOTrainer

def reward_win(environments, **kwargs) -> list[float]:
    return [1.0 + 0.5 * (1 - env.num_guesses / 6) if env.won else 0.0 for env in environments]

def reward_partial(environments, **kwargs) -> list[float]:
    return [0.0 if env.won else 0.1 * env.best_greens for env in environments]

def reward_invalid(environments, **kwargs) -> list[float]:
    return [-0.1 * env.invalid_moves for env in environments]

prompt = """You are an expert Wordle solver with deep knowledge of English vocabulary...
Use the tool `guess` to make a guess."""

dataset = Dataset.from_dict({"prompt": [[{"role": "user", "content": prompt}]] * 600})

trainer = GRPOTrainer(
    model="openenv/Qwen3-1.7B-Wordle-SFT",
    reward_funcs=[reward_win, reward_partial, reward_invalid],
    train_dataset=dataset,
    args=GRPOConfig(
        use_vllm=True,
        vllm_mode="colocate",
        chat_template_kwargs={"enable_thinking": True},
        max_completion_length=4096,
        max_tool_calling_iterations=12,
        vllm_importance_sampling_mode="token_truncate",
        num_generations=16,
        per_device_train_batch_size=8,
        gradient_accumulation_steps=8,
        learning_rate=1e-6,
        lr_scheduler_type="constant",
        max_steps=150,
    ),
    environment_factory=WordleEnv,
)
trainer.train()

Kemenangan memberikan imbalan lebih besar jika terjadi lebih cepat (1,42 dalam satu tebakan, 1,0 dalam enam), kekalahan memberikan imbalan berdasarkan tebakan terbaik dari permainan terlepas dari apakah permainan berakhir atau tidak, dan setiap langkah tidak valid dikenakan biaya 0,1. TRL mencatat setiap istilah secara terpisah, sehingga rewards/reward_win/mean meningkat seiring dengan tingkat kemenangan.

Menjalankan contoh

Setiap langkah memainkan 64 permainan, masing-masing satu sesi lingkungan. Duplikasi Space openenv/wordle dan atur variabel MAX_CONCURRENT_ENVS=64 pada salinan Anda.

colocate
server

Mode Colocate (1 GPU, direkomendasikan)

python examples/grpo_wordle/grpo_wordle.py --vllm-mode colocate --env-url https://<user>-wordle.hf.space

Ini menjalankan vLLM dalam proses yang sama dengan pelatihan, sehingga hanya membutuhkan satu GPU. Dalam eksekusi kami, 150 langkah memakan waktu sekitar 2 jam 15 menit di satu A100 80GB.

Hasil

Dari eksekusi kami, dengan satu eksekusi pelatihan per model, pada 30 kata yang sama yang dimainkan secara serakah:

ModelKemenanganTebakan per kemenanganTebakan yang tidak valid
Qwen3-1.7B0/30-24
+ SFT10/303.241
+ SFT + GRPO (model)20/304.13

GRPO sebagian besar menghilangkan kata-kata yang berulang dan dibuat-buat, serta memenangkan lebih banyak permainan. Sebagian besar kemenangan baru membutuhkan 4 hingga 6 tebakan, sehingga jumlah tebakan per kemenangan meningkat. 30 kata tersebut berasal dari 156 kata benda yang menjadi sumber rahasia TextArena, yang juga dilihat oleh model selama pelatihan, dan jalankan baru akan menghasilkan beberapa kemenangan yang berbeda (dua jalankan SFT dengan resep yang sama memenangkan 10 dan 13 permainan). Tonton tiga model bermain di Wordle Arena dan lihat kurva pelatihan di Trackio.

Pelatihan multi-lingkungan

Anda dapat melatih satu model di beberapa lingkungan secara bersamaan. Hal ini berguna ketika Anda ingin model mempelajari keterampilan yang berbeda secara paralel. Misalnya, memainkan Wordle (penalaran bahasa) dan Catch (penalaran spasial) dalam satu sesi pelatihan.

Ide utamanya adalah membuat kelas meta-lingkungan yang membungkus beberapa lingkungan dan mengarahkan setiap sampel ke lingkungan yang tepat menggunakan kolom dataset.

Cara kerjanya

  1. Tambahkan kolom "env" (atau serupa) ke dataset Anda yang mengidentifikasi lingkungan mana yang dimiliki setiap sampel.
  2. Di reset(**kwargs), baca kwargs["env"] untuk memilih lingkungan aktif untuk episode tersebut.
  3. Ekspos alat dari semua lingkungan; trainer menemukan semua metode publik.
  4. Gunakan fungsi reward terpisah untuk setiap lingkungan, dengan mengembalikan None untuk sampel yang tidak termasuk dalam lingkungan tersebut. TRL menangani nilai None dengan nansum/nanmean.

Contoh: Wordle + Catch

Skrip grpo_multi_env.py melatih pada Wordle dan Catch secara bersamaan:

class MultiEnv:
    def __init__(self):
        self._wordle_client = None
        self._catch_client = None
        self.active = None
        self.reward = 0.0
        self.done = False

    def reset(self, **kwargs) -> str | None:
        self.active = kwargs.get("env", "wordle")
        self.reward = 0.0
        self.done = False

        if self.active == "wordle":
            if self._wordle_client is not None:
                try:
                    self._wordle_client.close()
                except Exception:
                    pass
            self._wordle_client = TextArenaEnv(base_url=WORDLE_URL)
            result = self._wordle_client.reset()
            self._last_full_feedback = result.observation.messages[0].content
            self.reward = 0.0
            return self._last_full_feedback
        elif self.active == "catch":
            if self._catch_client is not None:
                try:
                    self._catch_client.close()
                except Exception:
                    pass
            self._catch_client = OpenSpielEnv(base_url=CATCH_URL)
            result = self._catch_client.reset()
            self.done = result.observation.done
            return _format_catch_obs(result.observation.info_state)

    # Wordle tool
    def guess(self, guess: str) -> str:
        """Make a guess in the Wordle environment. ..."""
        ...

    # Catch tools
    def move(self, direction: str) -> str:
        """Move the paddle left or right. ..."""
        ...

    def stay(self) -> str:
        """Do nothing and let the ball fall one step. ..."""
        ...

Pola utama:

  • Inisialisasi klien secara lazy: Buat klien di reset(), bukan di __init__(), untuk menghindari koneksi WebSocket yang tidak perlu.
  • Tutup sebelum buka ulang: Tutup klien sebelumnya sebelum membuat yang baru untuk menghindari error kapasitas server.
  • Routing kwargs: Kolom "env" dari dataset diteruskan ke reset() sebagai argumen kata kunci.
  • Semua tool diekspos secara bersamaan: Model melihat guess, move, dan stay sebagai tool yang tersedia terlepas dari lingkungan aktif. Jika model memanggil tool yang salah (misalnya, move saat Wordle), metode ini akan melempar ValueError yang ditangkap dengan baik oleh trainer. Dalam praktiknya, model belajar menggunakan tool yang benar berdasarkan system prompt.

Fungsi reward per lingkungan

Setiap fungsi reward mengembalikan None untuk sampel dari lingkungan lain:

def wordle_reward(environments, **kwargs) -> list[float | None]:
    return [env.reward if env.active == "wordle" else None for env in environments]

def catch_reward(environments, **kwargs) -> list[float | None]:
    rewards = []
    for env in environments:
        if env.active != "catch":
            rewards.append(None)
        elif env.done:
            rewards.append(max(env.reward, 0.0))
        else:
            rewards.append(0.0)
    return rewards

TRL mengonversi None menjadi nan secara internal dan menggunakan nansum/nanmean untuk agregasi, sehingga setiap sampel hanya dinilai oleh fungsi reward yang relevan.

Dataset dengan routing lingkungan

n = 500
dataset = Dataset.from_dict({
    "prompt": (
        [[{"role": "user", "content": wordle_prompt}]] * n
        + [[{"role": "user", "content": catch_prompt}]] * n
    ),
    "env": ["wordle"] * n + ["catch"] * n,
})

Menjalankan contoh multi-lingkungan

python examples/grpo_multi_env/grpo_multi_env.py \
    --wordle-url https://huggingface.co/proxy/openenv-wordle.hf.space \
    --catch-url https://huggingface.co/proxy/openenv-openspiel-env.hf.space \
    --vllm-mode colocate \
    --gradient-accumulation-steps 4 \
    --num-generations 8

Saat melatih di beberapa lingkungan, pantau metrik per fungsi reward (train/reward_func_0, train/reward_func_1, dst.) alih-alih train/reward yang digabungkan. Metrik gabungan bergantian antar lingkungan dan dapat terlihat berisik.

Menjalankan lingkungan

Saat menggunakan environment_factory, trainer akan terhubung ke server environment secara otomatis. Anda hanya perlu memastikan server berjalan. Ada tiga cara untuk menjalankan server environment OpenEnv:

space
docker
lokal

Terhubung ke Hugging Face Space jarak jauh (paling sederhana)

Sebagian besar skrip contoh secara bawaan menggunakan Space yang dihosting (tidak perlu pengaturan):

env = EchoEnv(base_url="https://huggingface.co/proxy/openenv-echo-env.hf.space")

Untuk pelatihan, duplikasi Space ke akun Anda sendiri untuk menghindari masalah konkurensi. Trainer membuka N koneksi WebSocket simultan (satu per generasi), dan Space bersama mungkin tidak mendukung hal ini. Lihat Server concurrency untuk detailnya.

Katalog lingkungan

Cara terbaik untuk menjelajahi katalog lingkungan yang saat ini dipelihara adalah dengan mengunjungi katalog resmi OpenEnv.

Untuk membuat lingkungan Anda sendiri, lihat panduan tentang Membangun Lingkungan Anda Sendiri dengan OpenEnv. Lingkungan terintegrasi erat dengan Hub, sehingga Anda dapat mendorong lingkungan baru agar dapat digunakan kembali oleh komunitas.

Kesejajaran server

Saat menggunakan environment_factory, trainer membuat N instance lingkungan (satu per generasi), masing-masing membuka koneksi WebSocket ke server. Secara default, server OpenEnv hanya mengizinkan 1 sesi konkuren, yang akan menyebabkan kegagalan selama pelatihan.

Untuk mendukung pelatihan paralel, konfigurasikan server untuk konkurensi:

  1. Di file lingkungan Anda, deklarasikan dukungan sesi konkuren:
SUPPORTS_CONCURRENT_SESSIONS: bool = True
  1. Di aplikasi server Anda, atur batas konkurensi:
app = create_app(
    create_my_environment,
    MyAction,
    MyObservation,
    max_concurrent_envs=64,  # match or exceed generation_batch_size
)

max_concurrent_envs harus ≥ generation_batch_size (yang secara default adalah per_device_train_batch_size × gradient_accumulation_steps). Sebagai contoh, dengan gradient_accumulation_steps=64 dan batch size 1, Anda membutuhkan setidaknya 64 sesi konkuren.

environment_factory vs rollout_func

GRPOTrainer mendukung dua pendekatan untuk pelatihan berbasis lingkungan:

  • environment_factory (disarankan): Anda mendefinisikan kelas lingkungan dengan metode alat, dan trainer menangani pembuatan, parsing panggilan alat, dan loop multi-turn secara otomatis. Ini adalah pendekatan yang digunakan di seluruh panduan ini.
  • rollout_func: Anda menulis seluruh loop generasi dan interaksi lingkungan sendiri. Ini memberikan kendali penuh atas bagaimana penyelesaian dihasilkan, bagaimana alat dieksekusi, dan bagaimana reward dihitung.

Gunakan rollout_func ketika environment_factory tidak sesuai dengan kasus penggunaan Anda. Misalnya, server agen eksternal di mana server eksternal memiliki loop generasi dan mengelola protokol interaksi agen-lingkungannya sendiri.

Pelatihan pada harness: melatih agen coding sungguhan (Harbor)

Integrasi di atas bersifat white-box: TRL menggerakkan loop multi-turn itu sendiri. Ia mengambil sampel setiap giliran, mem-parsing panggilan alat, menjalankannya, dan memberikan hasilnya kembali.

Beberapa agen tidak dapat dikendalikan dengan cara ini karena mereka memiliki loop mereka sendiri. Harness agen coding produksi seperti opencode atau claude-code memiliki planner, set tool, manajemen konteks, dan kondisi berhenti mereka sendiri. Anda ingin melatih agen yang persis itu, bukan reimplementasi darinya.

Untuk hal ini, TRL menyediakan jalur eksperimental black-box (loop-owning) yang dibangun di atas AsyncGRPOTrainer: HarnessAsyncGRPOTrainer menggerakkan pabrik sesi OpenEnv. Agen dan sandbox-nya berasal dari Harbor, disajikan melalui OpenEnv dengan openenv harbor serve, sehingga setiap agen yang diinstal oleh Harbor dapat dilatih dengan cara yang sama: Anda memilih salah satunya dengan --harness. Lihat examples/async_grpo_harbor untuk contoh lengkap, termasuk peluncur Hugging Face Jobs.

Jalur ini membutuhkan openenv>=0.7.0, rilis pertama dengan API penangkapan TrainingTrace: pip install "openenv>=0.7.0".

Cara kerjanya

TRL tidak mengambil sampel setiap giliran di sini. Agen berjalan hingga selesai secara mandiri, dan TRL membaca kembali apa yang telah dilakukan:

  1. Server OpenEnv Harbor memulai agen pada tugas Harbor di dalam sandbox (misalnya E2B). Setiap pemanggilan model yang dilakukan agen melewati proxy capture server menuju server vLLM Anda, yang merekam id token prompt dan completion milik engine, logprobs yang disampelkan, serta loss mask per token.
  2. Ketika agen berhenti, verifier dari tugas itu sendiri memberikan skor pada hasilnya, dan TRL menerima TrainingTrace yang telah divalidasi dari OpenEnv serta membangun baris pelatihan dari token id dan mask-nya. TRL tidak melakukan tokenisasi ulang pada prompt yang ditangkap.
  3. GRPO melatih pada baris-baris tersebut. Hadiah (reward) disebarkan ke setiap token yang dilatih melalui keuntungan relatif grup (group-relative advantage), dan bobot baru disinkronkan kembali ke server vLLM yang sama.

Setiap rollout berjalan dalam sesi terisolasi miliknya sendiri, dengan sandbox dan sesi capture yang terpisah. Isolasi ini penting karena dua alasan: proxy harus menangkap token dari rollout tersebut secara tepat, dan satu rollout tidak boleh mengganggu rollout lainnya. Pengaturan max_concurrent_rollouts mengontrol berapa banyak rollout yang berjalan secara bersamaan, dan MAX_CONCURRENT_ENVS server harus setidaknya max_concurrent_rollouts + 1 (factory menyisakan satu koneksi ekstra untuk metadata tugas).

Koneksi

HarnessAsyncGRPOTrainer menerima model, config, dan dataset seperti AsyncGRPOTrainer, dengan session factory menggantikan reward_funcs, tools, dan environment_factory. Tanpa harness_adapter, agen memiliki kendali penuh atas loop-nya. Trainer membangun session factory dengan kebijakan sampling miliknya sendiri, sehingga Anda meneruskan callable seperti partial(HarborSessionFactory, ...) alih-alih factory yang sudah dibangun. Setiap sesi mengembalikan OpenEnv TrainingTrace yang berisi token engine yang tepat, behavior logprobs, dan prefilled masks. rollout_reward_fn bersifat opsional; tanpanya, TRL menggunakan reward verifier dari sesi tersebut.

from functools import partial

from harbor_env.harness import HarborSessionFactory

from trl import AsyncGRPOConfig
from trl.experimental.async_grpo.openenv_harness import HarnessAsyncGRPOTrainer

factory = partial(
    HarborSessionFactory,
    "http://localhost:8200",       # the OpenEnv Harbor server
    split="<hf-dataset>",          # a Harbor task dataset the server serves
    harness="mini-swe-agent",      # or "opencode", "claude-code", ...
    sandbox="e2b",
    llm_url=vllm_url,
    model=model,
)
config = AsyncGRPOConfig(num_generations=8, max_concurrent_rollouts=8, vllm_server_base_url=vllm_url)
trainer = HarnessAsyncGRPOTrainer(
    model=model,
    harness_session_factory=factory,  # called by the trainer with its sampling policy
    args=config,
    train_dataset=dataset,
    rollout_reward_fn=my_reward,      # outcome -> float | None
)
trainer.train()

Yang perlu Anda definisikan

OpenEnv memiliki tanggung jawab atas validasi tangkapan dan pemilihan token. TRL mengubah mask sekuens penuh dari produsen menjadi mask penyelesaian internalnya, mengemas baris yang dihasilkan, dan menerapkan GRPO. TRL tidak merekonstruksi token atau menyimpulkan panggilan mana yang milik agen.

rollout_reward_fn : cara menilai sebuah rollout

Callable[[HarnessRolloutOutcome], float | None]

TRL tidak tahu apa yang dimaksud dengan keberhasilan untuk tugas Anda, sehingga Anda mengubah setiap rollout yang selesai menjadi reward skalar. Fungsi ini menerima HarnessRolloutOutcome yang menjelaskan apa yang dilakukan agen:

  • env_reward (float | None): reward dari verifier tugas, atau None jika rollout tidak dapat dinilai.
  • completion (list[dict]): transkrip pesan akhir.
  • trace (list[TraceEntry]): rekaman tangkapan OpenEnv yang dipilih, termasuk giliran yang dimasker nol.
  • tool_call_count dan tool_failure_count (int): berapa banyak pemanggilan tool yang dilakukan agen, dan berapa banyak yang tampak seperti kegagalan.
  • tool_calls_by_name (dict[str, int]): jumlah panggilan per tool, misalnya {"bash": 3, "edit": 2}.
  • timed_out (bool): apakah agen kehabisan anggaran waktu.

Kembalikan float, atau kembalikan None untuk menandai rollout sebagai tidak dapat dinilai sehingga dihapus dari baseline grup alih-alih dihitung sebagai nol. Jika Anda tidak meneruskan fungsi ini, env_reward mentah digunakan apa adanya. Contoh Harbor mempertahankan kebenaran verifier sebagai sinyal utama dan menambahkan bonus efisiensi alat yang kecil hanya ketika tugas terselesaikan.

Mengapa tidak langsung menggunakan verifier? Verifier adalah tugas lingkungan dan menjawab satu pertanyaan, “seberapa benar hasilnya,” yang menjaganya tetap bersih dan dapat digunakan kembali untuk evaluasi. Reward yang Anda gunakan untuk pelatihan adalah keputusan terpisah pada saat pelatihan (membinarisasi skor, menghukum perilaku degeneratif, membuang rollout yang tidak dapat dinilai). Hal ini juga membutuhkan sinyal yang tidak pernah dilihat oleh verifier, karena verifier hanya memeriksa hasil akhir, sedangkan rollout_reward_fn juga menerima trajektori (jumlah tool, timed_out, trace). Sebagai contoh, sebuah rollout dapat lolos beberapa tes tetapi tidak pernah menjalankan bash; hanya rollout_reward_fn yang dapat melihat hal tersebut dan memberinya penalti.

Kelayakan token berasal dari OpenEnv

session.fetch_training_trace() mengembalikan TrainingTrace yang telah divalidasi. Setiap giliran memiliki node_id, prompt_token_ids, completion_token_ids, per_token_logps yang unik, serta loss_mask yang wajib mencakup prompt dan completion. Posisi prompt bernilai nol. Mask completion parsial dipertahankan, termasuk nol di dalam respons asisten.

Sebagai contoh, prompt [10, 11], completion [20, 21, 22] dan mask [0, 0, 1, 0, 1] melatih pada token 20 dan 22. Token 21 tetap menjadi konteks. Completion yang sepenuhnya di-mask disimpan untuk konteks dan akuntansi penggunaan.

Producer mengecualikan panggilan bantu dan percobaan ulang yang dibuang. Tidak ada parameter train_turn_fn atau agent_turn_fn dalam API ini. Integrasi yang ada harus memindahkan seleksi ke dalam mask OpenEnv mereka. Verifier tugas tetap independen dari kelayakan token.

Kaptur yang tidak valid gagal di batas OpenEnv dan menghentikan pelatihan. Kegagalan transport tetap tidak dapat dinilai. Riwayat yang ditulis ulang memulai baris terpisah, dengan mempertahankan token dan logprobs yang disampelkan. Beberapa baris masih berbagi reward rollout; API ini tidak mengubah pembobotan GRPO.

Metrik

Pelatih harness mencatat metrik alat dari AsyncGRPOTrainer dengan nama yang sama, dengan perbedaan berikut:

  • Ketika agen memiliki kendali penuh atas loop-nya (tanpa harness_adapter), kegagalan diperkirakan dari hasil alat dalam permintaan terakhir: sebuah hasil dianggap gagal jika teksnya mengandung error, failed, traceback, atau exception (tidak membedakan huruf besar/kecil), dan hasil dengan alat yang sama serta 200 karakter pertama yang sama hanya dihitung sekali. tools/latency_s dan tools/<name>/latency_s tidak dicatat, karena trace OpenEnv tidak memiliki timestamp.
  • Dengan harness_adapter, sebuah tool call dianggap gagal ketika harness melaporkan error untuknya. Waktu antara satu balasan model dan permintaan berikutnya dibagi rata di antara tool calls pada turn tersebut dan dicatat sebagai tools/latency_s mereka, sehingga mencakup overhead dari harness itu sendiri. Turn terakhir tidak diukur.
  • tools/unknown_name_count, tools/parallel_calls_mean dan tools/loop_exhausted_frac berasal dari tool loop bawaan dan tidak dicatat.
  • Trainer itu sendiri tidak memiliki alat, sehingga tools/call_frequency dan tools/failure_frequency hanya menghitung grup di mana setidaknya satu rollout memanggil alat.
  • rollout/correctness_mean hanya dicatat dengan rollout_reward_fn: env_reward dari verifier, dirata-ratakan atas rollout yang memilikinya.

Persyaratan

Server OpenEnv Harbor memiliki dataset dan sandbox. Server ini memerlukan kredensial backend sandbox (misalnya E2B_API_KEY):

MAX_CONCURRENT_ENVS=9 openenv harbor serve --dataset <hf-dataset> --port 8200 --capture-port 8300 --expose gradio

Server vLLM harus mengekspos pemanggilan alat dan id token asli, serta mengaktifkan sinkronisasi bobot NCCL agar agen selalu mengakses kebijakan terkini. Rollout dan pembaruan bobot harus menggunakan server vLLM yang sama:

vllm serve <model> \
    --enable-auto-tool-choice --tool-call-parser <parser> \
    --logprobs-mode processed_logprobs \
    --return-tokens-as-token-ids \
    --weight-transfer-config '{"backend":"nccl"}'

Pelatihan loop-owning berada di bawah trl.experimental dan API-nya dapat berubah. Contoh ini mengunci revisi OpenEnv yang digunakan untuk pengujian; lihat README untuk pengaturan yang tepat.

Update on GitHub