feat(orchestrator): multi-agent native metrics - #3165
Conversation
Adapt to verifiers#2187 (Episode derived aggregates): episode.last_error
rename, submodule bump. Mirror the episode/trace hierarchy in the wandb
layout: count metrics read at the episode level (per-episode sums), and
a new {scope}/{subset}/<agent>/<metric>/<stat> level reports trace-level
metrics per agent, averaging in-episode fan-outs first.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…s -> TraceMetrics Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ation span renamed agent
Env level now carries only episode-level facts (count sums, pipeline
rates, eval scores); reward, truncation, errors, stop conditions, timing,
custom metrics and solve rates move under {scope}/{subset}/<agent>/.
Adapts to the vf span rename (Timing.agent, AgentSpan, split_agent_time)
and re-pins deps/verifiers. Overview reward/error/truncation panels match
the per-agent keys by regex.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…udge debug config Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
verifiers f646beb37 -> f6e420b99 (0.2.2.dev66, 17 commits) and
research-environments 6a2dee6fc -> ccb375ce5 (main + the scicode-v1
pin relax from research-environments#740).
Adaptations to the verifiers changes:
- Episode.error is now Episode.errors + a last_error property (vf#2187)
- verifiers.v1.loaders / verifiers.v1.push moved into
verifiers.v1.utils.{loaders,platform} (vf#2204)
- verifiers floors bumped to 0.2.2.dev66 (root and prime-rl-configs)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
ccb375ce5 -> 9a57c713c: research-environments#740 refreshed on current main, picking up the ViRL39K v1 taskset (RE#689) and the taskset prompt config fixes (RE#742). verifiers main is unchanged since f6e420b99. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
f6e420b99 -> 576506d66 (7 commits). vf#2218 builds one client per rollout with a process-wide elastic renderer pool: TrainClientConfig drops pool_size for a multiplex knob, and the config module moved from clients.config to configs.client. The orchestrator config follows suit (pool_size -> multiplex, None = client default). Floors to dev73. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…rchestrator.multiplex deps/research-environments -> 21a284618 (research-environments#740 on main). The orchestrator-level renderer knob (pool_size, briefly multiplex) is removed instead of renamed: clients resolve inside env- server workers, whose concurrency is already bounded per env by serve.pool.multiplex (default 128, below the client-side renderer default of 256), so each worker warms one renderer and the elastic pool grows on demand. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…etrics Takes #3172's newer verifiers pin (576506d66, contains the vf#2187 merge). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adopts main's timing kwarg name in the metrics test fixture; the timing assertions stay on the agent subtree. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
is_trainable / is_filtered / filters and avg@k / pass@k all score a single trace, so they read per agent like every other trace-level metric instead of pooling an episode's seats. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
is_truncated / is_completed rode the per-episode mean path with the distributions, so an uneven fan-out — what the effective subset leaves whenever a sibling errors — reweighted them away from the plain fraction their siblings and the docs promise. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
One aggregation per level: the episode level sums an episode's traces, the agent level takes each trace as a sample. Reward, avg@k and the per-agent counts had been collapsing each episode's fan-out to a mean first, which left them the odd ones out among the seat's own metrics and made avg@k's k — a trace count — disagree with what it averaged. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Keeps #3182's unscored-as-zero coverage, rekeyed onto the agent subtree. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 46fbe0d. Configure here.
| per_agent: dict[str, list[Rollout]] = {} | ||
| for r in self.rollouts: | ||
| per_agent.setdefault(r.agent.name, []).append(r) | ||
| return {name: TraceMetrics(rollouts) for name, rollouts in sorted(per_agent.items())} |
There was a problem hiding this comment.
Synthetic errors mis-attributed by agent
Medium Severity
by_agent groups every rollout by agent.name, including dispatcher-synthesized error and cancel markers that use a default AgentInfo. In multi-agent envs those failures land under a default seat (typically agent) instead of the real seats, so per-agent has_error and related rates miss complete episode failures and cancellations.
Reviewed by Cursor Bugbot for commit 46fbe0d. Configure here.
There was a problem hiding this comment.
ouf ya, this is annoying. externally failed episodes should get a separate type (not try to imitate the internal data type)
There was a problem hiding this comment.
will fix this in follow up
There was a problem hiding this comment.
Fixed in #3183 — cancellations and task failures now arrive as a first-class EpisodeFailure on an EpisodeResult envelope rather than a stand-in Rollout, so they are counted at the episode level and can no longer land in a phantom agent subtree.
main carries #3165 as a squash; this branch was cut from its pre-squash head, so the conflicts are the same content seen twice and resolve to ours (which already has it, plus this PR's work). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Took main's multi-agent metrics rename (#3165) wholesale: verifiers' generation->agent span rename is now upstream, so this branch's TRACE_PHASES compatibility shim is deleted. deps/verifiers advances to the PR branch's merge of main (contains the renderer pool #2218 and main's d30a3f4 pin); uv.lock relocked with submodules at their merged pins. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Catches this branch up ~23 commits, including the vLLM 0.26 move (serving_tokens imports from scale_out.token_in_token_out, mm_input takes a MultiModalKwargsItems wrapper, online_renderer rename) resolved the same way as the offload branch, with the inline raw_image_data decode path re-applied on top. Takes main's agent metrics rename (#3165) and drops tests main superseded. Submodule pins advance to the companion PR merges; uv.lock relocked with submodules at their merged pins. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>


Summary
Rollout metrics now mirror the episode/trace hierarchy that
vf.Episodeexposes, so a multi-agent env's seats never mix into one distribution.Every metric key lives at exactly one of two levels:
{scope}/{subset}/...) — facts about the whole episode: the token/turn/branch counts, summed across the episode's traces (the same aggregation asEpisode.num_turns/num_*_tokens).{scope}/{subset}/<agent>/...) — everything trace-scoped, grouped by agent name (mirroringEpisode.by_agent).scopeistrain/agg(all train envs) ortrain/<env>/eval/<env>;subsetisalloreffective;<stat>is{mean,max,min,p10,p90}.Before
Every metric sat at one flat level, pooling all of an episode's agents into a single distribution:
For a proposer-solver env that meant
.../reward/meanaveraged the proposer's reward together with its solvers', and.../num_turns/meanmixed a 1-turn proposer with 8-turn solvers — neither number describing anything real.After
A single-agent env has one subtree, named for its one agent (usually
agent):A proposer-solver env separates the seats, and the episode level sums them:
Aggregation rules
One aggregation per level, so a metric's unit is never ambiguous:
episode_id(legacy envs, synthesized error markers) count as single-trace episodes.is_truncated,is_completed,has_error,is_trainable,is_filtered,filters/<name>) emit/meanonly — a 0/1 distribution's other stats carry no information.avg@k's k is the largest number of traces one example drew, so it matches the sample count the value averages.Also
RolloutMetricsis renamedEpisodeMetrics; the per-agent view isTraceMetrics.avg@kpanels match the per-agent keys by regex (one panel per agent, env names regex-escaped); the changed panel set rolls the saved view viaview_signature.monitor-runskill documents both levels.envs.py/dispatcher.py(no more "env-rollout").Breaking
Trace-level metrics move under the agent subtree. These keys no longer exist at
{scope}/{subset}/:Single-agent envs read them under their one agent name (usually
agent).One further break: the count metrics at
{scope}/{subset}are distributions over episodes (per-episode sums), no longer over loose traces. Numerically unchanged for single-agent envs; multi-agent envs previously mixed every agent's traces into one distribution.Verification
uv run pytest tests/unit/orchestrator tests/unit/utils(159 passed), including a proposer-solver fan-out test asserting episode sums, per-agent means, and the agent-only key set.ruff format --check/ruff checkon the touched files.Live runs in wandb: episode-agent-metrics, one per shape. The env level is the same 50 keys in both — it does not grow with agent count — while the agent level fans out:
agent/solver/+judge/The two-agent run shows the split the flat layout could not express, and the episode level summing it:
(That judge run also carries a 75% rollout error rate — its
max_turnsis set too low in a local-only debug config, unrelated to this change. Errors are reported faithfully ashas_error/meanon both subtrees.)A longer 20-step reverse-text run on an earlier revision of this branch: https://wandb.ai/primeintellect/reverse-text/runs/0988528e8b62439097ad6f1557afd571
🤖 Generated with Claude Code
Note
Medium Risk
Breaking W&B metric key paths affect dashboards and alerts; orchestrator logging behavior changes but training logic is unchanged—risk is mainly observability and downstream consumers of old metric names.
Overview
Rollout metrics now follow the episode/trace hierarchy instead of one flat distribution over all traces. Episode-level keys (
{scope}/{subset}/num_*,num_turns,num_branches) aggregate per episode by summing traces; trace-level metrics (reward, truncation, errors, timing, filters, eval scores) emit under{scope}/{subset}/<agent>/..., grouped byagent.namewith one sample per trace.RolloutMetricsbecomesEpisodeMetricsplusTraceMetricsper agent; train/evalto_wandbandpass_at_kare scoped per agent. W&B overview panels use regexes for per-agent reward,has_error, truncation, andavg@k;monitor-rundocuments the two-level key layout. Minor episode wording indispatcher.py/envs.py.Breaking: flat keys like
{scope}/{subset}/reward/meanmove to{scope}/{subset}/<agent>/reward/mean(single-agent runs typically useagent). Top-level token/turn stats are over episodes, not loose traces (unchanged for single-agent, different for multi-agent).Reviewed by Cursor Bugbot for commit 46fbe0d. Bugbot is set up for automated code reviews on this repo. Configure here.