Vibe Engines
YouTube

Vibe Engines is free, interactive AI engineering & system design — runs in your browser. Pick a learning path

AI System Design

Design an RL Environment Farm

Step 1 / 9

Learn AI system design by building the infrastructure that trains reasoning models — not the preference-tuning of RLHF (that's the fine-tuning pipeline design), but the environment-and-verifier…

The whole design, in writing

Every step of the build above, written out: the problem each piece solves, the option that was taken and the ones that were not, the numbers, and how it fails in production.

The big idea

The environment is the training signal

The reasoning models everyone celebrates are trained by RL — but the algorithm gets too much credit. What actually teaches the model is the environment: which tasks it attempts, run how, scored how. This isn't the preference-tuning of RLHF (that's the fine-tuning pipeline, learning from human comparisons) — it's the machine that produces a huge stream of task attempts, each honestly scored, for the trainer to learn from.

Trainerruns the RL update loop
New in this step: Trainer.

We'll build the environment farm: sandboxed rollout workers that safely run an agent attempting a task, a scheduler that scales them, a hardened verifier that scores each trajectory (because a gameable one corrupts training), a task bank that keeps difficulty in the productive band, and a trajectory store that closes the loop. The learning-signal math — reward variance and verifier precision — is the RL Environments Engineering handbook; this is the infrastructure that runs it at scale.

What the new pieces do

Trainerclient
The training loop that improves the policy from scored trajectories. It needs a large, steady stream of task attempts, each honestly scored — but it should not itself be running or sandboxing agent code.

Step 1 · The baseline

Run rollouts inline in the trainer

The naive setup: the trainer, in its own process, runs each task attempt itself — spins up the agent, executes the code the agent generates, scores it, updates. It works for a toy. What breaks the moment the agent generates real code and you need volume?

TrainerRollout Worker
New in this step: Rollout Worker. · swipe to pan the diagram

The trainer runs each rollout inline — executing agent-generated code in its own process. What's the core problem?

  1. Simplicity that runs untrusted, model-generated code in your training process is a liability, not a virtue. One rollout that generates code which loops forever, reads the wrong files, or crashes takes the whole trainer down — and you can't scale rollout throughput without scaling the trainer with it.

  2. Exactly two problems: safety (running arbitrary agent-generated code in-process means a bad rollout can crash, hang, or compromise the trainer) and scale (rollouts and training are coupled, so you can't throw more machines at generating trajectories without touching the trainer). Both are fatal at real scale.

  3. Format is trivial. The real problems are that the trainer runs untrusted agent code in-process (unsafe and unstable) and that rollout throughput is bolted to the trainer (unscalable).

Inline rollouts run untrusted, model-generated code inside the training process — a bad rollout can hang, crash, or compromise the trainer — and they chain rollout throughput to the trainer, so you can't scale the environment independently. Both safety and scale demand pulling rollouts out of the trainer.

Why this piece earns its place

Both of those reasons are already on the table. What the split quietly hands you is a third thing nobody argues for up front: the two halves get to run on different machines, bought differently. A trainer is long-lived, holds optimizer state, and losing it costs a checkpoint reload plus every in-flight batch, so it wants reserved, reliable hardware. A rollout worker is disposable by construction: kill it mid-attempt and you have lost exactly one trajectory out of an enormous stream. That asymmetry is unavailable while the two share a process, and it is where a lot of the farm's bill is decided — the disposable half can sit on preemptible capacity, the irreplaceable half cannot. The second thing you buy is a readable dashboard. Inline, rollout time and training time are one number, so when throughput sags nobody can say which side stalled, and a stack trace does not tell you whether the bug is in your update step or in code the model wrote thirty seconds earlier. Two processes means two error budgets, two sets of logs, and a bottleneck you can point at.

What the new pieces do

Rollout Workerservice
Runs one rollout: an agent (loaded with the current policy) attempts a task, executing whatever code and tool calls it generates. Because that code is untrusted and model-generated, each worker is an isolated, resource-capped sandbox.

Step 2 · Isolation

Sandbox every rollout

Pull rollouts into dedicated workers — but those workers run whatever code the agent generates, which is untrusted by definition. What has to be true of a rollout worker before it's safe to run at scale?

TrainerRollout Worker
The system as it stands at this step. · swipe to pan the diagram

Rollout workers execute agent-generated code. What must each worker be?

  1. Speed without isolation is how one malicious or buggy rollout escapes to read another's data, reach the network, or exhaust the host. Running untrusted model-generated code at scale is only safe if each rollout is contained, even at some speed cost.

  2. Shared state between rollouts is exactly what you must prevent — one rollout could poison another's trajectory, and a resource hog could starve its neighbors. Rollouts must be isolated, not shared.

  3. Right: every rollout runs in a sandbox with strict resource caps and no ambient access. The agent can generate and execute whatever code it wants, but that code is boxed — no network, no cross-rollout access, bounded CPU/memory/time. A rollout that misbehaves is contained to itself and can't take down the farm or poison other trajectories.

Each rollout runs in an isolated, resource-capped sandbox: no network, no access to other rollouts' data, bounded CPU, memory, and time. Agent-generated code can do whatever it likes inside the box, but a rogue or runaway rollout is contained to its own worker — the only way running untrusted model-generated code at scale is safe.

Why this piece earns its place

Two consequences of the box that only show up once you have run one. First, no network means every dependency a task needs has to be baked into the sandbox image, which makes that image part of the task definition and something you version with it. Skip that and the same task scores differently this month than last because a package moved underneath you, and you can no longer tell whether the policy improved or the environment drifted — the one comparison the whole farm exists to make. Second, the resource caps are part of the reward function whether you meant them to be. A rollout killed at its CPU or wall-clock limit produces a trajectory that looks, to everything downstream, identical to an honest failure. Score it that way at volume and you are teaching the model to avoid whatever runs long, including the correct approaches that happen to be slow. So a worker has to record why an attempt ended — solved, failed, or terminated by the box — and that distinction has to survive into the stored trajectory, because nothing later can reconstruct it.

Step 3 · Scale

Schedule rollouts across a fleet

One sandboxed worker is safe but slow. Training needs a huge volume of trajectories. What sits between the trainer and the workers, and what does decoupling them buy?

TrainerRollout WorkerRollout Scheduler
New in this step: Rollout Scheduler. · swipe to pan the diagram

How should rollouts be produced at the volume RL training needs?

  1. That re-couples the trainer to rollout execution — the exact problem you just escaped. The trainer should consume trajectories, not micromanage a fleet of workers; that's the scheduler's job.

  2. Right: the scheduler decouples producing trajectories from learning. It fans rollout jobs out across as many workers as you provision, hands each worker the current policy to run, and collects results — so you scale trajectory throughput by adding workers, without touching the trainer. Rollout generation and training now scale on independent axes.

  3. Sequential rollouts throttle throughput to one-at-a-time, starving the trainer. Rollouts are independent and embarrassingly parallel — the whole point is to run thousands concurrently across a fleet.

A Rollout Scheduler dispatches jobs across a fleet of sandboxed workers and distributes the current policy each worker runs. Rollout throughput now scales by adding workers, independently of the trainer, which simply consumes the resulting trajectories — production and learning on separate axes.

Why this piece earns its place

A rollout is not a web request, and the difference shapes the whole scheduler. Its duration is long and wildly variable — one attempt gives up in seconds, another runs until its time cap — so the tail sets throughput, not the average, and the number to watch is queue depth rather than worker utilization, because a fleet can be fully busy and still starving the trainer. If the trainer wants a group of attempts on the same task, that group finishes when its slowest member does, and the tempting fix is to over-dispatch and cancel whatever is still running once enough have come back. Resist that shape of it: letting the race pick the survivors discards the slow ones systematically, the same bias the resource cap already threatens. Cancel on the task's own wall-clock budget instead — identical for every attempt, and handed back as a recorded termination rather than a gap in the group. Retry is that trap inverted. A rollout lost to infrastructure, a preempted node or a failed image pull, should be retried. A rollout that ended because the agent's own code failed must not be, because that is a result — and re-running it does not repeat the attempt, it samples a fresh one, so tasks whose generated code fails often quietly collect more rollouts than the scheduler budgeted.

What the new pieces do

Rollout Schedulergateway
Dispatches rollout jobs across a fleet of workers, so the farm scales rollout throughput independently of the trainer, and distributes the current policy the workers load for each attempt.

Step 4 · Honest scoring

Score trajectories with a sound verifier

A worker finishes a rollout. Now the critical question: did the attempt actually succeed? Everything the model learns rides on this score. What makes a verifier good enough to train on?

Rollout Workersandboxed, cappedVerifier Servicescore the trajectory
New in this step: Verifier Service.

A rollout finished. What matters most about how its trajectory is scored?

  1. Speed is nice but secondary to correctness. A fast verifier that can be fooled feeds the trainer false positives — the model learns to satisfy the check, not do the task. A slightly slower, sound verifier beats a fast, gameable one every time.

  2. Generous partial credit toward a gameable check is how reward hacking gets rewarded. What matters is that a success score means the task was actually solved — a verifier that hands out credit for near-misses that don't work teaches the wrong lesson.

  3. Right: verifier soundness is a training-data-integrity problem. If a degenerate solution (print the expected answer, exploit a loose test) scores as success, the model learns that exploit — reward hacking. The verifier must be hardened (hidden tests, checking the process not just a final string) so that a "success" genuinely means the task was solved. Precision — the fraction of rewarded trajectories that are actually correct — is the number to protect.

The Verifier Service must be SOUND — hardened with hidden tests and process checks — because every false positive is a lie the model trains on. A gameable verifier makes the model learn the exploit instead of the skill (reward hacking); protecting verifier precision (rewarded trajectories that are genuinely correct) is protecting the integrity of the training data itself.

Why this piece earns its place

Where the verifier runs is an architectural decision the diagram makes and nobody states out loud. It is a separate service, outside the sandbox, and it has to be: hidden tests are not hidden if they sit on a filesystem the agent's code can read, and a checker running inside the box is being graded by the very thing it is grading. The rollout produces the attempt; something the rollout cannot touch decides what it was worth. Worth naming the opposite error too, since everything above is about false positives. A verifier that is too strict — rejecting a correct solution over output formatting, or because the task was solved by a route the checker did not anticipate — is a lie in the same training data pointing the other way: gradient pushed away from behaviour that genuinely worked, and specifically away from the unanticipated routes you would most want kept. It is the harder error to find, because it never looks like a bug. A false positive eventually surfaces as a model doing something absurd that scores well. A false negative surfaces as tasks going quiet — successes suppressed on exactly the ones the model is just starting to get right, pushed out of the productive band for a reason that has nothing to do with the policy. Both are corrupted training data. Only one of them announces itself.

What the new pieces do

Verifier Serviceservice
Scores each completed trajectory — did the attempt actually succeed? Hardened against gaming (hidden tests, process checks) because every false positive is a lie the model learns from.

Step 5 · The curriculum

Serve tasks in the difficulty band

The farm can safely run and honestly score rollouts. But which tasks should it run? Feeding random tasks wastes compute. What makes a task worth attempting for RL?

Rollout Schedulerdispatch & scaleTask Bankcurriculum · difficulty band
New in this step: Task Bank.

Which tasks should the Task Bank serve to the rollout workers?

  1. Correct: RL learns from reward variance, which for a pass/fail task peaks when the model solves it about half the time and vanishes when it always or never succeeds. A task the model always solves (or never solves) produces no gradient — every attempt gets the same reward. The task bank should serve tasks in that middle band, and shift them as the model improves (a curriculum), so compute is spent where learning actually happens.

  2. Tasks the model never solves produce all-failure trajectories with zero variance — no signal to learn from. Maximal difficulty teaches nothing until the model can occasionally succeed; the productive band is intermediate, not maximal.

  3. Tasks the model always solves produce all-success trajectories with zero variance — equally useless. High reward with no spread means no gradient. Learning needs tasks the model solves SOMETIMES.

The Task Bank serves tasks in the productive difficulty band — solved sometimes, failed sometimes — because the learning signal is the reward variance, which vanishes when a task is always or never solved. As the model improves, tasks that used to sit in the band drift out, so the bank acts as a curriculum, continuously supplying tasks where learning actually happens.

Why this piece earns its place

The awkward part is that the band is a measurement of the current policy, not a property of a task, and the measurement decays with every update. So the bank needs a live pass-rate estimate per task, which means the component at the end of the loop feeds the component at the front — whether that arrives as an aggregate over the trajectory store, a counter the verifier emits as it scores, or a tally the scheduler keeps as results return. The store is the natural home because it already holds the history; what matters is that one component owns the number. It is not free either: the only way to learn how hard a task is for this policy is to spend rollouts finding out, so budget for measuring, not only for training. The constraint that actually bites, though, is supply. A farm running well burns through its usable tasks faster than it burns through compute, so authoring or generating tasks becomes the ceiling long before hardware does — a staffing decision someone had to make months earlier. And hold a slice back the workers never see. Keep it out of the store too, because a held-out task that lands there and gets replayed is contaminated as thoroughly as one that was trained on.

What the new pieces do

Task Bankservice
Supplies the tasks workers attempt, keeping them in the productive difficulty band — hard enough to fail sometimes, easy enough to succeed sometimes — because a task always-solved or never-solved teaches nothing.

Step 6 · Close the loop

Store trajectories and feed the trainer

Rollouts are run, sandboxed, and honestly scored on band-appropriate tasks. The last piece: get those scored trajectories back to the trainer. What has to be captured, and why store rather than stream straight in?

TrainerRollout SchedulerVerifier ServiceTask BankTrajectory Store
New in this step: Trajectory Store. · swipe to pan the diagram

What should happen to each scored trajectory?

  1. The trajectory — the task, the actions taken, the outcome, the reward — is the training data. A bare reward number with no record of what the agent did can't drive a policy update or be re-analyzed. You need the full trajectory, stored.

  2. Right: each trajectory is stored with everything the trainer needs — the task, the sequence of actions, the outcome, the reward — and flows back to the trainer to update the policy. Storing (rather than only streaming) means trajectories can be batched, replayed, re-scored if the verifier improves, and inspected when something looks wrong. That closes the loop: environment produces scored trajectories, trainer updates the policy, workers run the new policy.

  3. Trajectories are training data, not production output — they include failures and exploited attempts. They flow to the trainer to improve the policy, not to end users.

The Trajectory Store keeps each full scored trajectory — task, actions, outcome, reward — and feeds it to the trainer, closing the loop from environment to policy update. Storing rather than only streaming lets trajectories be batched, replayed, re-scored if the verifier improves, and inspected when a result looks wrong — the environment produces, the trainer learns, the workers run the improved policy.

Why this piece earns its place

Two components stand on both sides of the split: the scheduler carries policy out to the fleet, the store carries trajectories back. The store is the one whose schema is a contract rather than a table. Adding a field is cheap. Changing what an existing one means is not — redefine reward and every record already written silently means something else, so a batch that mixes old and new trajectories is training on two different definitions of success with nothing to tell them apart. Stamp each record with the policy and verifier versions that produced it and you can at least filter; leave that out and the damage is unrecoverable after the fact. The second thing to design for is that the two sides read and write in opposite shapes. The farm appends continuously, at the fleet's rate, in whatever order attempts happen to finish. The trainer wants shuffled random access across a large window, fast enough to keep expensive accelerators fed. Choose one engine that does both adequately and the store quietly becomes the thing idling the costly half of the system.

What the new pieces do

Trajectory Storedata
Stores every scored trajectory (task, actions, outcome, reward) and feeds them back to the trainer, closing the loop from environment to policy update.

The payoff

A training engine, not just a scorer

Put it together: the trainer gets a steady, scalable stream of honestly-scored trajectories on band-appropriate tasks, produced by sandboxed workers that safely run untrusted agent code — and a reward-hacking exploit or a rogue rollout is caught, not learned.

TrainerRollout WorkerRollout SchedulerVerifier ServiceTask BankTrajectory Store
The finished design, end to end. · swipe to pan the diagram

That's the RL environment farm. Sandboxed rollout workers, a scheduler for scale, a sound verifier that protects training-data integrity, a difficulty curriculum, and a trajectory store that closes the loop — the environment-and-verifier infrastructure where the real leverage in modern RL sits.

Everything you assembled, in order

  • Rollouts are pulled out of the trainer: the farm PRODUCES scored trajectories, the trainer CONSUMES them — separating environment from learning.
  • Every rollout runs in an isolated, resource-capped sandbox, because RL on code/tool tasks means executing untrusted model-generated code.
  • A scheduler fans rollouts across a fleet and distributes the current policy, so trajectory throughput scales independently of the trainer.
  • The verifier must be SOUND — hardened against gaming — because every false positive is a poisoned lesson the model learns (reward hacking).
  • The task bank serves the productive difficulty band (a curriculum), and the trajectory store closes the loop back to the trainer.

Where an interviewer pokes next

Getting the boxes right is the easy half. These are the questions that separate a candidate who drew the diagram from one who has run the thing. Answer each one out loud before you open it.

  1. How is this different from the fine-tuning / RLHF pipeline design?

    The RLHF / fine-tuning pipeline is about learning from PREFERENCES — collecting human (or AI) comparisons, training a reward model, and tuning the policy against it, with the central problems being preference-data quality, reward-model reliability, and the alignment tax. This design is the ENVIRONMENT-and-VERIFIER side of RL with VERIFIABLE rewards: there's no learned reward model, the "reward" comes from a programmatic verifier that checks whether a task was actually solved, and the hard problems become safely running untrusted agent code at scale (sandboxing), scoring soundly (verifier gaming), and keeping tasks in the learnable band (curriculum). They're two different RL regimes: one learns what humans prefer, the other learns to succeed at checkable tasks — and this design is the infrastructure for the second.

  2. Why not just use a learned reward model instead of a programmatic verifier here?

    For tasks with a checkable notion of success — code that must pass tests, math with a verifiable answer, a tool-use goal that either completed or didn't — a programmatic verifier is both cheaper and far harder to game in the ways a learned reward model is: a learned reward model can be exploited by adversarial inputs that score high without being good (the classic reward-model hacking), whereas a well-designed programmatic check with hidden tests is grounded in ground truth. The trade-off is coverage: programmatic verifiers only work where success is actually checkable. For fuzzy, preference-shaped objectives (is this response helpful/harmless?), you're back to a learned reward model and the RLHF pipeline. Many real systems use both, on the parts of the objective each fits.

  3. The verifier caught a reward-hacking exploit in the chaos — but how do you find the exploits it DOESN'T catch?

    This is the ongoing arms race, and the farm is built to support it. First, the trajectory store makes it possible: because full trajectories are kept and inspectable, you can mine for suspicious patterns — trajectories that scored success with anomalously short action sequences, or clusters of solutions that look degenerate. Second, a spike in success rate on a task family that shouldn't suddenly be easy is a red flag that a hole was found. When a new exploit is discovered, it becomes a new hardening rule in the verifier (and often a re-score of past trajectories), exactly like turning a production incident into a regression test. The design assumption is that exploits WILL be found; the store + re-scoring capability is what lets you fix them retroactively and prevent the model from having learned them.

  4. How much does sandboxing every rollout cost, and is it worth it?

    Sandboxing adds per-rollout overhead — process/container isolation, resource limits, teardown — which is real but modest relative to the rollout itself (running an agent through a multi-step task with LLM calls and code execution). The cost is absolutely worth it because the alternative is unbounded: a single un-sandboxed rollout that executes model-generated code with network access and no resource cap can exfiltrate data, corrupt shared state that poisons other trajectories, or take down a worker (or the whole farm) with a fork bomb or infinite loop. At the scale of millions of rollouts running untrusted code, "it'll probably be fine" is a guarantee of eventual disaster. The engineering work goes into making sandboxes cheap to spin up and tear down (lightweight isolation, pooled/pre-warmed sandboxes) so isolation doesn't throttle throughput — not into skipping it.

  5. Workers run "the current policy" — how fresh does that need to be, and does staleness matter?

    It depends on the RL algorithm's on-policy-ness. Strictly on-policy methods want trajectories generated by the very latest policy, so a worker running a policy that's several updates stale produces data that's slightly off-distribution and can bias or destabilize the update. In practice, large-scale RL farms tolerate some staleness (near-on-policy / asynchronous setups) as a deliberate throughput trade: the scheduler pushes updated policy weights to workers periodically rather than after every single update, accepting bounded staleness in exchange for keeping the whole fleet busy instead of stalling on synchronization. How much staleness is acceptable is an algorithm-and-scale tuning decision — the infrastructure point is that the scheduler is the control point for distributing policy versions and bounding how far behind any worker is allowed to drift.

Check yourself — the answers, and why

Seven steps in, these are the calls you should be able to make cold. Pick one, then read why.

  1. An RL environment farm differs from the RLHF / fine-tuning pipeline because it…

    RLHF learns what humans prefer via a reward model; this is the environment-and-verifier infrastructure for learning to succeed at checkable tasks.

  2. Every rollout runs in a sandbox because…

    RL on code/tool tasks means running code the model wrote — inherently untrusted — so containment per rollout is non-negotiable at scale.

  3. A sound verifier matters most because…

    The verifier writes the training data: what it rewards is what the model learns, so verifier precision is a training-integrity problem.

  4. The task bank should serve tasks that are…

    A task always or never solved has zero reward variance and no gradient — learning lives where success is uncertain.

  5. Trajectories are stored (not just streamed) so they can be…

    The stored trajectory is the training data and the audit trail — inspectable and re-scorable, which is how reward-hacking holes get found and fixed retroactively.

How you’d open this design in an interview

Before any boxes: agree what it must do, pin the qualities that shape everything, then build — naming each trade-off as you make it. The walkthrough above is that exact order.

What it must do

Agree on these before drawing a single box.

  • Run attempts at scale: rollout workers load the current policy and run an agent attempting a task.
  • Sandbox execution: run untrusted, model-generated code in isolated, resource-capped sandboxes.
  • Schedule the fleet: a scheduler fans rollouts across many workers and distributes the current policy.
  • Score soundly: a hardened verifier scores each trajectory, resisting reward-gaming with hidden tests and process checks.
  • Curriculum + store: a task bank keeps difficulty in the productive band, and a trajectory store closes the loop to the trainer.

The qualities that shape everything

Each one names the mechanism that buys it.

Safety and scale of running agent code
Separate producing scored trajectories (the farm) from learning from them (the trainer), so a bad rollout can’t crash the training process and throughput isn’t chained to it.
Run untrusted model-generated code safely
Each rollout runs in an isolated, resource-capped sandbox: no network, no access to other rollouts’ data, bounded CPU, memory, and time.
Scale trajectory throughput independently
A Rollout Scheduler dispatches jobs across a fleet of sandboxed workers and distributes the current policy — add workers without touching the trainer.
Protect training-data integrity
A SOUND verifier hardened with hidden tests and process checks, because every false positive is a lie the model trains on (reward hacking).
Spend compute where learning happens
A Task Bank serving the productive difficulty band — solved sometimes, failed sometimes — since the learning signal is the reward variance, and shifting it as a curriculum.
Close the loop, keep trajectories inspectable
A Trajectory Store keeps each full scored trajectory (task, actions, outcome, reward) and feeds it to the trainer, so it can be batched, replayed, and re-scored.

The trade-offs you say out loud

Senior signal isn’t the boxes — it’s naming what you gave up and why it was the right price.

Rollouts in dedicated sandboxed workers over running rollouts inline in the trainer

Inline rollouts execute untrusted model-generated code in the training process (a bad rollout hangs, crashes, or compromises it) and chain throughput to the trainer — both fatal at real scale.

An isolated, resource-capped sandbox per rollout over a fast bare-metal or shared process

Speed without isolation lets one buggy or malicious rollout reach the network, read another’s data, or exhaust the host; running untrusted code at scale is only safe if every rollout is boxed.

A scheduler fanning out across a fleet over the trainer managing each worker itself

Rollouts are embarrassingly parallel, so a scheduler decouples producing from learning — scale throughput by adding workers without touching the training loop.

A sound, gaming-hardened verifier over a fast or generous verifier

A fast verifier that can be fooled feeds false positives, and every false positive is a lie the model trains on — it learns the exploit instead of the skill.

Tasks in the productive difficulty band over the hardest (or easiest) tasks

The learning signal is reward variance, which vanishes when a task is always or never solved — a maximal- or minimal-difficulty task produces no gradient.

The answer, out loud

What a strong answer to “Design an RL Environment Farm” sounds like, first question to last trade-off. It is about 7 minutes of talking; the whiteboard and the interviewer fill the rest of the 45. Read it aloud once, then close the page and give it yourself.

  1. 0–3 min

    Frame it: the environment is the product

    I'd start by being precise about what we're designing, because RL is an overloaded word. This isn't preference tuning — no human comparisons, no learned reward model. This is RL on tasks where success is checkable, so the reward comes from a program that decides whether the attempt actually worked. Which means the thing we're building isn't the algorithm. It's the machine that produces a huge stream of task attempts, each one honestly scored, for the trainer to learn from. The environment is the training signal, and almost every hard problem in here turns out to be an infrastructure problem rather than a maths one.

  2. 3–8 min

    Get the rollouts out of the trainer

    So the first thing I'd draw is the naive version, mostly to knock it down. The trainer runs each attempt itself: spins up the agent, executes the code the agent generates, scores it, updates. Two things kill that. It's executing untrusted, model-generated code inside the training process, so one bad rollout can hang, crash or compromise the thing holding all your state. And rollout throughput is chained to the trainer, so you can't buy more trajectories without touching the training loop. I'd split it right there — the farm produces scored trajectories, the trainer consumes them.

    Built in step 1: Run rollouts inline in the trainer
  3. 8–14 min

    Box every attempt

    Once rollouts live in their own workers, those workers are running whatever the model wrote, which is untrusted by definition. So each rollout gets an isolated, resource-capped sandbox: no network, no access to another rollout's data, bounded CPU, memory and time. Inside the box the agent can do as it likes; outside it, nothing. I'd present that as non-negotiable rather than a trade-off, because the alternative isn't slower, it's unbounded — one rollout reaching the network or exhausting the host takes down a worker and can poison other trajectories. Containment per rollout is what makes it safe to point this at real code-executing tasks.

    Built in step 2: Sandbox every rollout
  4. 14–19 min

    Fan out the parallel part

    One sandboxed worker is safe and slow, and training wants volume. Rollouts are independent — embarrassingly parallel — so I'd put a scheduler between the trainer and the fleet. It dispatches rollout jobs across as many workers as we provision and hands each one the current policy to run. The payoff is that production and learning scale on separate axes: I add workers to get more trajectories and the training loop doesn't change. The trainer stops micromanaging machines and goes back to consuming results.

    Built in step 3: Schedule rollouts across a fleet
  5. 19–26 min

    The verifier is where I would spend the time

    If I only get to go deep on one box, it's this one. The verifier decides whether an attempt succeeded, and that score is literally the training data. Every false positive is a lie the model trains on, repeated across every trajectory that finds the same hole. So a degenerate solution — printing the expected output, exploiting a loose test — has to come out as a failure, which means hidden tests and checking the process rather than a final string. The number I'd name is precision: of the trajectories we reward, how many genuinely solved the task. A fast verifier that can be fooled teaches the exploit instead of the skill, and it teaches it thoroughly.

    Built in step 4: Score trajectories with a sound verifier
  6. 26–32 min

    Spend compute where learning happens

    Then: which tasks? Not the hardest ones — that's the intuitive answer and it's wrong. The signal is reward variance, so a task the model never solves and a task it always solves are equally useless; every attempt comes back with the same reward and there's no spread to learn from. I'd have the task bank serve the band where the model succeeds sometimes and fails sometimes. And that band moves: tasks drift out of it as the model improves, so the bank has to behave like a curriculum rather than a fixed list.

    Built in step 5: Serve tasks in the difficulty band
  7. 32–38 min

    Close the loop

    The last piece is getting scored trajectories back, and I'd be deliberate that what flows back is the whole attempt — task, actions, outcome, reward — not just the number at the end. The argument for a store in the middle, rather than a pipe straight into the trainer, is that the reward is the verifier's opinion at a point in time, and opinions get revised. Keep only the score and every hardening pass we make on the verifier helps future data and nothing else; keep the attempt and the rollouts we already paid for are still worth something to a better checker. It's also the one artifact here that outlives everything around it — the policy gets overwritten on every update, the workers are disposable by design, but the trajectories are what all that compute actually bought. That closes the loop: the environment produces, the trainer updates, the workers run the improved policy.

    Built in step 6: Store trajectories and feed the trainer
  8. 38–45 min

    Close on the trade-off

    The honest trade-off is that every choice here spends throughput to buy trust. Sandboxing is slower than bare metal. A sound verifier is slower than a generous one. Keeping tasks in the productive band means deliberately not running the tasks that look most impressive. I'd take all three, because the failures on the other side are silent — a model that learned an exploit looks exactly like a model that got better. With more time I'd push on two things. First, the tension between the sandbox's no-network rule and the tasks we most want to train on: a tool-use task is only realistic if the agent can call something, so the work is bringing a believable version of that service inside the box rather than cutting a hole in it. Second, credit assignment within a trajectory — as drawn, the reward lands on the attempt as a whole, which is blunt when one step in a long sequence is what actually decided it. Both are the same theme as the rest of this: what limits the model is the environment, not the algorithm.

What this teaches

Learn AI system design by building the infrastructure that trains reasoning models — not the preference-tuning of RLHF (that's the fine-tuning pipeline design), but the environment-and-verifier side: the fleet that runs an agent attempting tasks at massive scale, in sandboxes so it can execute untrusted code safely, scores each trajectory with a sound verifier (because a gameable one corrupts training), supplies tasks in the productive difficulty band, and stores scored trajectories for the trainer. An interactive guide covering inline-rollout limits, sandboxed rollout workers, a scheduler, verifier soundness, a difficulty curriculum, and the trajectory store — with reward-hacking and sandbox-escape chaos.

Key takeaways

  • Rollouts are pulled out of the trainer: the farm PRODUCES scored trajectories, the trainer CONSUMES them — separating environment from learning.
  • Every rollout runs in an isolated, resource-capped sandbox, because RL on code/tool tasks means executing untrusted model-generated code.
  • A scheduler fans rollouts across a fleet and distributes the current policy, so trajectory throughput scales independently of the trainer.
  • The verifier must be SOUND — hardened against gaming — because every false positive is a poisoned lesson the model learns (reward hacking).
  • The task bank serves the productive difficulty band (a curriculum), and the trajectory store closes the loop back to the trainer.

Concepts covered

  • The environment is the training signal
  • Run rollouts inline in the trainer
  • Sandbox every rollout
  • Schedule rollouts across a fleet
  • Score trajectories with a sound verifier
  • Serve tasks in the difficulty band
  • Store trajectories and feed the trainer
RUN IT YOURSELF

The episode you cannot run twice

One batch of rollouts, run and then replayed under a different fleet ordering — once with the environment drawing from a process-wide generator, once with every episode owning a seeded stream. It prints how many trajectories came back identical, then the bill: the same two policies scored on independently sampled episodes and on a common seed set, with the measured gap between them, the spread of that gap, and how often the worse policy wins. Change EPISODES_PER_EVAL, hit Run, and watch how little four times the rollouts buys.

HOW TO READ THE CODE — 5 IDEAS
  1. Only one function differs between the two regimes: whether supply hands an episode the next number off a shared stream or the number addressed by its key.
  2. Under the shared generator 0 of 8 episodes replayed — yet the batch pass count only moved 4 → 3, so nothing downstream looked broken.
  3. Order-invariance is the whole property: the scheduler decides arrival order (step 3), which is why a retried rollout samples a fresh attempt instead of repeating one unless the episode owns a seed.
  4. Step 6 sells the trajectory store on trajectories being replayed and re-scored. That promise is only worth something if the episode is reproducible in the first place.
  5. The bill is in the last two columns: both regimes measure the same +5.1 pp gap, so pairing is variance reduction and not a thumb on the scale — but 160 independently sampled episodes still spread 5.5 pp against 4.4 pp for 40 on a common seed set, and the worse policy takes 19.0% of those comparisons against 6.0% (step 5 — you pay for difficulty measurement in rollouts).
CPython · WebAssembly
built so a reward-hacking exploit is caught by the verifier and a rogue rollout stays in its sandbox — make the calls, game the reward, break out, and watch the farm hold.
Finished this one? 0 / 65 AI System Designs done

This idea in other formats

Explore the topic

See this alongside everything else on the same subject — handbooks, system designs, challenges and tools, in one place.

More AI System Designs