Skip to main content
A rollout is one complete episode of agent-environment interaction: the agent takes actions, the environment transitions, and rewards accumulate until the episode ends. A rollout is reproducible when the same random seed and action sequence produce exactly the same observations, transitions, and rewards. Reinforcement learning (RL) engineering depends on this property. Without it, you can’t reliably compare 2 policy versions, reproduce a training bug, or tell a real reward spike from noise. Real training runs hundreds or thousands of rollouts in parallel. Each worker must be isolated from the others: no shared filesystem, no shared process state, no network side effects across episodes. If any state passes between workers, your “reproducible” seed no longer controls the outcome. Tensorlake sandboxes enforce this isolation at the infrastructure level, so every rollout gets its own fresh environment and the seed is the only variable.

Core concepts

Three properties make a rollout reproducible:
  • Isolation: Each rollout runs in its own compute environment with no shared resources. Workers seeded with different values must not influence each other’s trajectories through any shared channel: a pip cache, a /tmp directory, or network state. In production this matters most at hundreds of rollouts per training step, where any shared state becomes variance that your reward signal can’t explain.
  • Stateful resets: The environment always starts from a known, controlled baseline when a new rollout begins. A reset that partially inherits state from a previous episode is a common and hard-to-debug source of non-reproducibility. Because every rollout gets a fresh sandbox, the reset is total: there is no prior episode state to inherit.
  • Determinism: The environment seeds its random number generator (RNG) before any interaction begins, and the seed is the sole source of randomness for the entire episode. Given the same seed, initial observation, and action sequence, the trajectory must be identical byte for byte. This lets you replay any episode from training history, compare policy versions on equal footing, and write regression tests against specific trajectories.

How Tensorlake sandboxes enforce isolation and determinism

Sandbox.create() starts a fresh, isolated compute environment and returns a box handle. Every sandbox is a separate process tree with its own filesystem and memory. Two sandboxes created from the same client share no state. The harness embeds the seed as a string literal in the Python script that runs inside the sandbox, instead of setting it on the host process. This keeps the host’s random state separate from the environment’s, which matters when you dispatch rollouts from a single host thread pool. Parallel rollouts fit ThreadPoolExecutor: each thread creates its own sandbox, runs its episode, collects its trajectory, and terminates the sandbox. The executor manages concurrency; the sandboxes manage isolation.

Prerequisites

You need Python, the packages below, and a Tensorlake API key:
Create a .env file in your project root. The script calls load_dotenv() to read it:

TypeScript SDK starter

The same reproducibility pattern works from Node.js: embed the seed in the harness, run one rollout per sandbox, and compare trajectories across identical seeds.
For batch collection, replace the final Promise.all() with one rollout per seed and aggregate the returned JSON results by seed.

Full example

The script below runs seed 42 twice and asserts that both trajectories match, then collects 4 parallel rollouts with one sandbox per seed:
Expected output:
In CartPole, the reward is 1.0 per step regardless of action, so total reward equals step count. The episode ends when the pole tips past 12 degrees or the cart leaves the track. Different seeds produce different episode lengths because the initial pole angle varies. The reproducibility assertion confirms that seed 42 always produces the same 30-step trajectory in 2 independent sandboxes.

Tic-tac-toe: policy evaluation

This example extends the CartPole setup to a custom two-player environment where sandboxes matter more. Policies are code strings, the same pattern RL Training with GSPO uses for completions from a large language model (LLM). A policy that crashes, loops, or misbehaves only kills its own sandbox, and the rest of the evaluation continues. The data model is the same as CartPole. TttConfig extends RolloutConfig by replacing env_name with policy_x and policy_o, and run_ttt_batch returns the same RolloutResult. The total_reward field becomes the mean return per game from X’s perspective: +1 for a win, −1 for a loss, 0 for a draw. evaluate_matchup follows the same parallel dispatch pattern as collect_parallel_rollouts, running one sandbox per seed to get a reliable return estimate. This is the policy evaluation step in policy iteration. Call it after each policy update to measure how much the return improved.
Expected output of the evaluation:
Expected output of the Q-learning training:
The training loop passes the Q-table from each iteration into the next via JSON. Mean reward moving from −0.47 to +0.05 over 8 iterations shows the policy improving against a greedy opponent. Each iteration is a separate sandbox call: the host owns the Q-table and the loop control; the sandbox owns the episode dynamics. The jump in Q-states from 393 to 1297 reflects the agent exploring new board positions as its policy improves. Early iterations barely escape losing positions; later ones have enough coverage to exploit the greedy opponent’s fork blind spot. After training, q_policy_code() serializes the Q-table into a choose_action string with the same interface as random and greedy. The learned policy then works with evaluate_matchup and play_against without changes to those functions. After the evaluation the script prompts you to play. Choose X to move first or O to let the opponent open. Expected output of an interactive game as O against greedy:
The opponent’s policy code runs inside the sandbox on every turn. The choose_action function never executes in your host process. The sandbox stays open for the whole game session (timeout_secs=300); only the move oracle harness re-runs on each turn.

Key design decisions

Three choices keep the seed in control: embedding it in the harness, one sandbox per rollout, and determinism in the environment itself.

Why the seed is embedded in the harness string

The harness formats the seed directly into the Python script that runs inside the sandbox, instead of setting it through an environment variable or a host-side call. The host process’s random state then has no path into the episode. If you seeded on the host and passed the environment object into the sandbox, any host-side RNG call between setup and rollout would shift the environment’s random state. Embedding the seed in the harness makes the episode self-contained. In gymnasium specifically, env.reset(seed=seed) only seeds the observation and transition RNG. The action space has a separate RNG, so you must also call env.action_space.seed(seed). Forgetting the second call produces non-deterministic trajectories even when everything else is correct.

Why each rollout gets its own sandbox

Sharing a sandbox across rollouts would mean sharing filesystem state, installed package versions, and any residual process state from prior episodes. Even if you call env.reset() correctly, state outside the environment object (temporary files, cached computations, mutated globals) can persist and affect the next episode. Creating a fresh sandbox per rollout makes the isolation structural rather than depending on careful cleanup.

How this relates to the GSPO pattern

In RL Training with GSPO, the sandbox is a reward oracle: each model completion goes to a sandbox that runs a hidden test suite and returns a score. The reproducibility concern there is a fair evaluation of each completion, not a deterministic environment. The mechanism is the same: one sandbox per evaluation, no shared state. Use the pattern on this page when the environment itself, not only the evaluator, must be deterministic across training runs.

What to build next

RL Training with GSPO

Use sandboxes as a reward oracle to fine-tune a language model on code generation tasks.

Agentic Swarm Intelligence

Dispatch parallel sandboxes across a swarm of worker agents for large-scale rollout collection.

Snapshots

Freeze environment state mid-rollout to create branching experiments without re-running from scratch.