Aggregate Metrics
After rollout collection, NeMo Gym computes aggregate metrics for each agent by calling the /aggregate_metrics endpoint on the agent server. The results are written to a single _aggregate_metrics.json file.
How It Works
- Rollouts complete —
gym eval run --no-servegathers verify responses (reward + custom fields) for every task/rollout pair. - Group by agent — responses are partitioned by agent name.
- Call
/aggregate_metrics— for each agent, the stripped verify responses are POSTed to the agent’s/aggregate_metricsendpoint. - Compute stats — per-task and overall statistics (
mean,max,min,median,std) are computed for every numeric field. If the resources server overridescompute_metrics()orget_key_metrics(), those are called to add additional metrics. - Compute variability stats — when an agent has two or more repeats, per-repeat statistics are computed for every numeric field and summarized across repeats. See Repeat-Level Metrics.
- Write results — all per-agent metrics are written to
<output>_aggregate_metrics.json.
Output Format
The output file is a JSON array with one entry per agent:
Repeat-Level Metrics
A single run is a point estimate. Collecting repeats (--num-repeats) lets you separate a real score difference from sampling noise, and NeMo Gym reports that variability at three levels.
Within a repeat, across tasks
repeat_level_metrics holds one entry per (agent, repeat). It is produced only for agents with two or more repeats — an agent with a single repeat has nothing to compare against and is skipped. When no agent qualifies, the list is empty.
Each entry carries:
plus, for every numeric field (reward, token usage, and any numeric field your verifier returns):
missing_count is not measured against the expected task list. Its denominator is the set of tasks that completed in at least one repeat, so it only catches tasks that succeeded somewhere else and are absent here.
A task that fails in every repeat never appears in any repeat, so it is counted nowhere and missing_count stays 0 for it. Those rollouts are in <output>_failures.jsonl — or, for kill_shaped failures (Slurm SIGTERM, Ray actor died, OOM), nowhere at all, since the absence of a row is itself the signal.
To check real coverage, use the numbers that are measured against the materialized inputs: expected_num_rollouts and missing_num_rollouts in group_level_metrics, and the completion summary gym eval profile prints at the end of a run.
Across repeats
The per-repeat mean/{field} values are themselves summarized and merged into agent_metrics, treating each repeat as one observation. This answers a different question than the keys above: not “how much do tasks vary within a repeat” but “how much does the headline score move if I run the whole benchmark again.”
mean_across_repeats/mean/{field} and the per-rollout mean/{field} answer different questions but coincide numerically when every repeat covers the same tasks.
Per task, across repeats
group_level_metrics reports num_rollouts, mean, median, and std per task. It deliberately carries no confidence interval: a CI here would require assuming a distribution for a single task’s repeated outcomes, which are frequently binomial rather than normal, and the Central Limit Theorem does not rescue it because these are raw outcomes rather than averages.
Two cases worth knowing about
Both emit a UserWarning, so you will see them in your terminal:
- Unequal sample counts across repeats. If a task is missing from some repeats (a crashed rollout, an interrupted run), each repeat’s statistics are computed over a different task set, so they are not directly comparable — and
agent_metricsskews toward whichever tasks happened to complete. Collect the missing rollouts before drawing conclusions. Note this fires offmissing_count, so it inherits the blind spot above: tasks that failed in every repeat do not trigger it. - Zero standard error. If every value in a sample is identical, the confidence interval collapses to the single point
(mean, mean). This is reported rather than left null, because SciPy’st.intervalcomputes an indeterminate±inf * 0atscale=0and returnsNaN.
Confidence intervals are unbounded, so on a small number of tasks a 95% CI for a 0–1 reward can extend below 0 or above 1. That is expected for a t-interval and is a signal that you need more tasks or repeats, not a bug.
Where it lands on disk
The same statistics are laid out differently depending on which command produced them:
gym eval profile always writes its file, containing [] when there is only one repeat. Either way, the cross-repeat aggregates (mean_across_repeats/mean/*, se_across_repeats/mean/*, and the CI bounds) live in agent_metrics.
Custom Metrics
Override two hooks on your resources server to add custom metrics.
compute_metrics(tasks)
Receives all verify responses grouped by task. Use this for metrics that need the full dataset — pass@k, confidence intervals, cross-task statistics.
get_key_metrics(agent_metrics)
Selects headline numbers from the final agent_metrics dict. Default returns all mean/* entries.
Example: pass@k
Given 3 tasks with 4 rollouts each (task 0: all correct, task 1: all wrong, task 2: half correct), this produces: