Skip to main content
Module: rllm.trainer.unified_trainer
The UnifiedTrainer is the central orchestrator for backend-agnostic training in rLLM. It manages the full training loop — episode generation, data transformation, advantage computation, policy updates, validation, and logging — while delegating all backend-specific operations to a pluggable BackendProtocol implementation. This page contains the technical details of the unified trainer training loop.

Architecture overview

The trainer itself is backend-agnostic: it knows nothing about model weights, optimizers, or inference servers. All of that is encapsulated behind the BackendProtocol interface (see backend protocol).

Entry points

There are two ways to start training: A convenience wrapper that selects the correct TrainerLauncher for the backend string ("verl" or "tinker") and handles environment setup.

UnifiedTrainer (direct, for custom backends)

When using a custom backend class, instantiate the trainer directly:
Constructor parameters:

TrainerState

A mutable dataclass that serves as the shared context between the trainer and the backend throughout a training step. It is reset at the start of each batch via reset_batch().
Convenience properties: has_episodes, has_trajectory_groups, has_backend_batch — used by the trainer to detect early-return conditions (e.g. all episodes filtered).

Initialization sequence

When the UnifiedTrainer is constructed, the following happens in order: The workflow pool initialization (initialize_pool) happens when fit_async() starts, not in the constructor.

Training loop

Calling trainer.fit() runs fit_async() via asyncio.run(...). The high-level flow:
If val_before_train=true and val_only=true, training returns after initial validation and does not enter _fit_async().

The 8-stage batch pipeline

Each call to _train_batch_async executes the following stages. Stages 1-3 are framework-managed. Stages 4-7 are delegated to the backend. Early returns: The pipeline returns early (skipping stages 4-8) if no episodes are generated in stage 1, or if all trajectory groups are filtered out in stage 3. The lifecycle hooks (on_batch_end, logger.log) still execute even after an early return.

Validation loop

Validation is triggered:
  • Before training (if config.rllm.trainer.val_before_train is true)
  • Periodically during training (every config.rllm.trainer.test_freq steps)
  • After training completes (only when test_freq > 0)
The validation loop calls backend.generate_episodes(..., is_validation=True), transforms the results, and computes reward metrics (no advantage computation or policy updates). Pass@1 and pass@K metrics are computed per data source and logged. The backend can control validation via hooks:
  • on_validation_start returns a bool — return False to skip validation entirely
  • on_validation_end is called when validation actually runs (i.e. not skipped)

Configuration

The trainer reads configuration from config.rllm (the rLLM sub-config within the full Hydra config). Key config groups: For the full configuration reference, see configuration.

AlgorithmConfig fields

Async design

The trainer uses an async-prioritized design:
  • fit() is the sync entry point and runs fit_async() via asyncio.run(...)
  • fit_async() is available directly if you are already in an async context
  • The pipeline mixes async and sync steps:
    • async: generate_episodes, process_backend_batch, compute_advantages, update_policy
    • sync: transformation/rejection-sampling steps, dataloader access, logging, visualization

Shutdown

Always call trainer.shutdown() when done (or use a try/finally block). This:
  1. Shuts down the workflow engine
  2. Calls backend.shutdown() for backend-specific cleanup
  3. Calls logger.finish() to flush and close the tracking backend (e.g. wandb)