Metrics Dictionary
This reference defines each metric that the NeMo Labs Voice Agent evaluation harness writes. The producers
are nemo_voice_agent/evaluation/runner.py for per-scenario scoring and run aggregation,
nemo_voice_agent/evaluation/bridge.py for turns, latency, and token counters, and
nemo_voice_agent/evaluation/utils.py for the large language model (LLM) judge. For metric concepts, refer
to Scoring. For artifact locations, refer to
Reading Results.
Value Conventions
Use these conventions to distinguish an absent signal from an applicable signal without evidence.
metrics.json — Always Present
The harness writes one metrics.json file in each scenario directory. These keys are present for every
scenario that completes a bridge run.
The latency_stats object contains count (int), plus mean_ms, p50_ms, p95_ms, min_ms, and max_ms
(floats). All values are zero when count is 0. One latency is recorded per user-to-agent handoff: the
wall-clock gap from the user bot’s last audio frame to the agent’s first subsequent audio frame. The value
therefore includes automatic speech recognition (ASR), LLM, and text-to-speech (TTS) time-to-first-byte, not
only LLM latency.
With conversation_end_policy: "valid-terminal-state", clean_exit is also true when the simulator reports
a successful end or [INACTIVITY_TIMEOUT] occurs after a final user turn. An inactivity timeout after a final
agent turn and the overall [TIMEOUT] both remain false. The --min-agent-turns check remains authoritative
and forces stalled scenarios to fail regardless of their conversation-end evidence.
The inactivity timer resets on meaningful activity from either bot, including speaking lifecycle events,
text-to-speech events, relevant server messages or actions, and audio frames detected as speech. It does not
reset on continuously streamed silent audio. inactivity_timeout_seconds records the configured interval,
while duration_seconds records actual elapsed runtime and scenario_duration includes runner overhead.
The conversation_end_reason value identifies the evidence used for the verdict:
metrics.json — Conditional
The harness writes the following keys only when the corresponding scenario signal or run feature applies.
Each db_state_assertion_verdicts entry contains func_name (registered predicate name) and side
("agent" or "user", which selects the pulled database). It also contains passed (bool), expected
(the assertion’s assert_value), actual (the predicate result, or null on error), and message
(optional upstream label). The error field is null or contains the failure reason. If the predicate is
missing, raises an exception, or cannot access the required database, passed is false.
success_breakdown Buckets
Names inside each bucket are the SuccessSignal string values: is_action_match, db_state_match,
db_state_assertion, nl_assertion, judge_passed, clean_exit.
is_task_successful is derived from this object: it is true when failed contains nothing other than
clean_exit.
Stalled Scenarios
When --min-agent-turns N is set (default 2), the runner counts the agent’s LLM calls — preferring
token_usage.agent.n_calls, falling back to the message count in bot_logs_agent/llm_context.json.
Below the floor, the scenario is forced to is_successful: false and is_task_successful: false,
gets insufficient_agent_turns: true, and is skipped in every per-signal bucket. It is a counted
failure in the composite rate, not an exclusion. Token usage still rolls up, so cost accounting remains
accurate. Refer to Resuming a Run to retry them.
Automatic Retry Provenance
auto_retry_count tells you whether the numbers in a metrics.json came from the first attempt or from a
re-run that the runner performed automatically during the session. The field is always written, so 0
means the first attempt was accepted rather than that the feature was unavailable. auto_retry_reasons
appears only when the count is at least 1 and carries one entry per discarded attempt, such as
0 conversation turns (bots never exchanged audio).
Each discarded attempt keeps its own directory at <scenario>.killed.autoretry<N>.<timestamp>/, holding
that attempt’s logs plus a __KILLED__ marker file. Read auto_retry_count when you need retry counts for
analysis. Matching directory names is fragile, because the same .killed. naming also covers subdirectories
that --resume moved aside. check_resume.py reads the same field: it lists retried scenarios that
finished under Completed after an automatic in-run retry, and annotates retried scenarios that would be
re-run again with ; already auto-retried <n>x in-run.
Automatic retry is controlled by --no-auto-resume-on-stale, --auto-resume-on-insufficient-turns,
--max-auto-retries, and --auto-retry-backoff-secs, documented in
Eval CLI Reference.
judge_result.json
The harness writes judge_result.json once per scenario when a judge is configured.
The user_content inside judge_input is assembled from tagged sections, in this order and only when
each input is non-empty: <reference> (the gold action list), <prediction> (the agent’s recorded
actions), <conversation> (the transcript turns, included only under --judge-include-conversation),
<nl_assertions> (the scenario’s assertions rendered as a 1-based numbered list), <agent_context_history>
(the agent bot’s LLM context, including its system prompt and every tool call with arguments and results),
and <user_context_history> (the simulated user’s LLM context, which carries the user-side tool calls in
dual-side domains). Scanning for those tags is the quickest way to confirm the judge actually saw the
evidence a surprising score should have rested on.
Each nl_assertion_verdicts entry has index (1-based, matching the numbered assertion the judge was
shown), assertion (the assertion text, inlined so the file is self-describing), passed (bool), and
reason (string). The list is normalized to exactly one entry per assertion: missing, out-of-range,
or non-boolean verdicts are filled as passed: false with an explanatory reason.
all_summary.txt Run-Level Aggregates
The runner renders these rates as text without serializing them to JSON. The first column contains internal variable names, and the second contains the labels in the file. A rate is omitted when its bucket is empty.
The two assertion rates use assertions rather than scenarios as the denominator so that they remain comparable across domains where scenarios carry different assertion counts.
Also in the file:
Overall Latency Statistics—overall_latency_stats, pooled over every latency measurement in the run:count,mean_ms,p50_ms,p95_ms,min_ms,max_ms. Each is-1when nothing was measured.Token Usage— per side,n_calls,prompt,completion, and their sum, plus aRun totalacross both sides. The entire block is suppressed when no token events were received.- Stall warning —
WARNING: N scenario(s) had fewer than N agent turn(s) ... and were counted as failures.followed by the scenario names and the suggested resume command.
Per-Domain Breakdowns
The runner prints per-domain breakdowns only when the run spans more than one domain bucket. Each breakdown
uses the scenario-name prefix before __ as its key. Breakdowns are available for success rate, task success
rate (excluding clean exit), DB-state match rate, NL-assertion pass rate, and DB-state-assertion pass rate.
Judge score, judge pass, action match, and clean exit have no per-domain block.
Aggregation on Resume
Scenarios skipped by --resume do not re-run. Their metrics.json is loaded from disk and folded into
the same buckets using RunAggregator.add_scenario. Each signal is appended only when its key is present
and well-typed, so an older metrics.json missing a field simply contributes nothing to that bucket.
NL-assertion verdicts are re-read from judge_result.json on this path, because only the pass rate
lives in metrics.json. The resulting all_summary.txt is identical whether a scenario ran live or
was loaded from a prior session.
Related
Use these pages to connect the field definitions to scoring behavior, result inspection, and domain-specific use.
- Scoring — how each of the six signals is derived and combined.
- Reading Results — the full artifact layout and triage recipes.
- Eval CLI Reference — the flags that switch these fields on.
- tau2-telecom — the domain that exercises
db_state_assertionsand dual-side DB pulls.