- Environment: a stopped sandbox with a small task suite installed, kept as a template. Each rollout forks it, so every agent starts on an identical, isolated machine. The fork is the environment’s
reset(). - Grader: hidden checks that enter the fork only after the agent finishes, and turn the result into a reward.
- Dataset: one JSONL record per rollout, appended to an Archil disk, then scored where it lives with a serverless command.
Prerequisites
Export your Archil API key, a region where sandboxes are available (aws-us-east-1, aws-us-west-2, or aws-eu-west-1), and an AI Gateway key for the model:
Define the tasks
The suite has three small, deterministic tasks: implement an expression evaluator from a spec, write a report from a messy CSV, and fix a buggy parser. This script runs once inside the template and creates a directory per task under/work, each with a TASK.md for the agent and the starting files.
setup.sh
1e3, and a CSV the agent never saw. It prints how many checks passed for one task.
grade.py
Build the environment template
Create the disk for the trajectories, then a sandbox that runssetup.sh. The tasks live on the sandbox’s root filesystem, which every fork copies, and the template doesn’t mount the disk: the orchestrator writes to it through the API. Once the tasks are in place, the template is stopped, so it doesn’t use compute while it waits.
Run the rollouts
The rollout script runs each task twice, at most four rollouts at a time. Each rollout:- Forks the template. A fork of a stopped sandbox comes up running, with a copy of its root filesystem, in about a second.
- Runs an agent with a
bashtool bound to its fork, capped at 10 steps. - Uploads
grade.py, runs it, and uses the fraction of checks passed as the reward. - Appends one JSONL record to
trajectories/<run>.jsonlon the disk: task, sample, model, reward, steps, token usage, timings, and every message and tool call, with tool output truncated to 2,000 characters. - Deletes the fork, in a
finallyblock so a failed rollout doesn’t leave a sandbox behind.
fork_ms in the dataset was around a second. One run looked like this:
Score the run
The dataset is plain JSONL on the disk, so you can score it without downloading it. This jq program groups the records by task and computes the number solved (every check passed), pass@1, pass@2, and the mean reward. pass@2 uses the unbiased estimator, so it needs at least two samples per task and stays correct when you collect more.score.jq
disk.exec, in a short-lived VM with the disk mounted. It scores every run on the disk, so samples from later runs add to the totals.
duration and sales-report rollout passed all its checks, including the CSV the agent never saw. Both calc rollouts passed 12 of 13 checks: they handled precedence and .5 but rejected 1_000, a number Python accepts. pass@1 counts those as failures, while the mean reward shows they were close. That partial credit is what lets RL tell a near miss from a wrong answer, and the trajectories show exactly where each agent went wrong.
Scale it up
- More samples: raise
SAMPLESandCONCURRENCY. Each fork is an independent machine, so rollouts don’t share state no matter how many run at once. - More tasks: add directories to
setup.shand checks tograde.py. Build a new template, such asrl-env-template-v2, instead of changing the old one, so earlier results stay reproducible. - Use the data: the records are ordinary JSONL. Filter for
reward == 1to build a fine-tuning set, compare models by grouping on.modelas well as.task, or read the files from any S3 client through the S3-compatible API.
Clean up
Rollout forks delete themselves, so the template and the disk are what’s left. The script also deletes forks left behind by an interrupted run; they have to go before the template they came from.Next steps
Parallel agents
Race several agents on forks of one sandbox and keep the best result.
Persistent sandboxes
Lifecycle, services, network access, and forking in detail.
Serverless execution
Run commands directly on a disk without managing a sandbox.
S3-compatible API
Append to objects and read them from any S3 client.