TRL documentation
Integrasi OpenEnv untuk Pelatihan LLM dengan Lingkungan
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, danasync_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.pyIni 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:
environment_factory=EchoToolEnv: Trainer membuat satu instanceEchoToolEnvper generasi (lalu kelasnya, bukan instansinya).reset()dipanggil di awal setiap episode untuk menginisialisasi state. Mengembalikan string observasi (atauNone).- 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. - Loop multi-turn: Trainer menghasilkan completion, mem-parsing panggilan tool, mengeksekusi
echo(), menambahkan hasilnya, dan menghasilkan lagi, hingga model berhenti memanggil tool ataumax_completion_lengthtercapai. - Fungsi reward: Membaca
env.rewarddari 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.spaceDi 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 (atauNonejika tidak ada pengamatan awal).- Metode tool: Setiap metode publik (tidak diawali dengan
_) selainresetsecara otomatis diekspos sebagai tool. Setiap metode tool harus memiliki docstring dengan deskripsiArgs:, 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 parameterenvironments. 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 sepertistep(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.

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 XX = 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 feedbackPilihan 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 melemparValueError. Trainer menangkap ini dan memberikan"Game over."kembali ke model sebagai respons alat, serta menghitungnya dalamtools/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.
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.
Mode server (2+ GPU, dapat diskalakan)
# Terminal 1: Start vLLM inference server
CUDA_VISIBLE_DEVICES=0 VLLM_SERVER_DEV_MODE=1 vllm serve openenv/Qwen3-1.7B-Wordle-SFT --host 0.0.0.0 --port 8000 \
--weight-transfer-config '{"backend": "nccl"}' \
--logprobs-mode processed_logprobs \
--max-logprobs -1
# Terminal 2: Run GRPO training with OpenEnv
CUDA_VISIBLE_DEVICES=1 python examples/grpo_wordle/grpo_wordle.py --vllm-mode server --vllm-server-url http://localhost:8000 \
--env-url https://<user>-wordle.hf.spaceHasil
Dari eksekusi kami, dengan satu eksekusi pelatihan per model, pada 30 kata yang sama yang dimainkan secara serakah:
| Model | Kemenangan | Tebakan per kemenangan | Tebakan yang tidak valid |
|---|---|---|---|
| Qwen3-1.7B | 0/30 | - | 24 |
| + SFT | 10/30 | 3.2 | 41 |
| + SFT + GRPO (model) | 20/30 | 4.1 | 3 |
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
- Tambahkan kolom
"env"(atau serupa) ke dataset Anda yang mengidentifikasi lingkungan mana yang dimiliki setiap sampel. - Di
reset(**kwargs), bacakwargs["env"]untuk memilih lingkungan aktif untuk episode tersebut. - Ekspos alat dari semua lingkungan; trainer menemukan semua metode publik.
- Gunakan fungsi reward terpisah untuk setiap lingkungan, dengan mengembalikan
Noneuntuk sampel yang tidak termasuk dalam lingkungan tersebut. TRL menangani nilaiNonedengannansum/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 kereset()sebagai argumen kata kunci. - Semua tool diekspos secara bersamaan: Model melihat
guess,move, danstaysebagai tool yang tersedia terlepas dari lingkungan aktif. Jika model memanggil tool yang salah (misalnya,movesaat Wordle), metode ini akan melemparValueErroryang 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 rewardsTRL 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 8Saat melatih di beberapa lingkungan, pantau metrik per fungsi reward (
train/reward_func_0,train/reward_func_1, dst.) alih-alihtrain/rewardyang 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:
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.
Kontainer Docker (disarankan untuk produksi)
docker run -d -p 8001:8000 --platform linux/amd64 registry.hf.space/openenv-echo-env:latest
Lalu hubungkan:
env = EchoEnv(base_url="http://0.0.0.0:8001")Kami memetakan port 8001 ke 8000 agar port 8000 tetap tersedia untuk server vLLM.
Anda juga dapat memulai container secara programatik:
env = EchoEnv.from_docker_image("registry.hf.space/openenv-echo-env:latest")Anda dapat menemukan image Docker untuk Space apa pun di Hub: buka halaman Space → ⋮ (tiga titik) → “Run locally.”
Proses Python lokal (untuk pengembangan)
hf download openenv/echo_env --repo-type=space --local-dir=echo_env python -m uvicorn echo_env.src.envs.echo_env.server.app:app --host 0.0.0.0 --port 8001
Lalu hubungkan:
env = EchoEnv(base_url="http://0.0.0.0:8001")Untuk detail lebih lanjut, lihat katalog OpenEnv.
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:
- Di file lingkungan Anda, deklarasikan dukungan sesi konkuren:
SUPPORTS_CONCURRENT_SESSIONS: bool = True- 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_envsharus ≥generation_batch_size(yang secara default adalahper_device_train_batch_size × gradient_accumulation_steps). Sebagai contoh, dengangradient_accumulation_steps=64dan 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 penangkapanTrainingTrace: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:
- 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.
- Ketika agen berhenti, verifier dari tugas itu sendiri memberikan skor pada hasilnya, dan TRL menerima
TrainingTraceyang telah divalidasi dari OpenEnv serta membangun baris pelatihan dari token id dan mask-nya. TRL tidak melakukan tokenisasi ulang pada prompt yang ditangkap. - 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, atauNonejika 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_countdantool_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_fnjuga menerima trajektori (jumlah tool,timed_out, trace). Sebagai contoh, sebuah rollout dapat lolos beberapa tes tetapi tidak pernah menjalankanbash; hanyarollout_reward_fnyang 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 mengandungerror,failed,traceback, atauexception(tidak membedakan huruf besar/kecil), dan hasil dengan alat yang sama serta 200 karakter pertama yang sama hanya dihitung sekali.tools/latency_sdantools/<name>/latency_stidak 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 sebagaitools/latency_smereka, sehingga mencakup overhead dari harness itu sendiri. Turn terakhir tidak diukur. tools/unknown_name_count,tools/parallel_calls_meandantools/loop_exhausted_fracberasal dari tool loop bawaan dan tidak dicatat.- Trainer itu sendiri tidak memiliki alat, sehingga
tools/call_frequencydantools/failure_frequencyhanya menghitung grup di mana setidaknya satu rollout memanggil alat. rollout/correctness_meanhanya dicatat denganrollout_reward_fn:env_rewarddari 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"}'Update on GitHubPelatihan loop-owning berada di bawah
trl.experimentaldan API-nya dapat berubah. Contoh ini mengunci revisi OpenEnv yang digunakan untuk pengujian; lihat README untuk pengaturan yang tepat.
