Skip to content

fix(benchmarking): report how many queries the RAG benchmark actually scored - #6414

Open
arthi-arumugam-git wants to merge 3 commits into
ogx-ai:mainfrom
arthi-arumugam-git:report-dropped-queries-in-rag-benchmark
Open

fix(benchmarking): report how many queries the RAG benchmark actually scored#6414
arthi-arumugam-git wants to merge 3 commits into
ogx-ai:mainfrom
arthi-arumugam-git:report-dropped-queries-in-rag-benchmark

Conversation

@arthi-arumugam-git

Copy link
Copy Markdown

What happens now

The RAG benchmark runners skip a whole conversation when something raises:

except Exception as e:
    logger.error(f"Conversation {dial_id} failed: {e}")
    continue

(doc2dial_bench.py L152, and the same in qrecc_bench.py for both ingest and query.)

Those query ids never make it into results, and retrieval_metrics scores the intersection:

common_qids = set(qrels.keys()) & set(results.keys())

So the averages are over whatever survived, and on the retrieval side the returned dict had no count in it at all. A run that lost 40 of 100 queries and a run that lost none look identical in the output. I think that matters here because these numbers get published: PR #5559 put this harness's results in the repo as a comparison against another provider.

Quick check with the current code, five queries in qrels, three results:

complete run: ndcg_cut_5 = 1.0
partial run:  ndcg_cut_5 = 1.0

What this changes

retrieval_metrics now also returns num_scored_queries and num_missing_queries. Generation metrics gets num_missing_queries (it already reported num_queries). Nothing else moves, no existing metric value changes.

I did not reuse the name num_queries for the scored count because beir_bench.py sets metrics["num_queries"] = len(queries) right after calling retrieval_metrics, meaning queries attempted. Returning a differently-meaning num_queries would have been silently overwritten there, so the new keys have their own names.

Tests

benchmarking/rag/lib/test_metrics_coverage.py covers the complete run, the partial run, and the no-results case, including the case where two runs score the same and only the counts tell them apart.

One honest note on how I ran them: evaluate will not import on my machine (a native library error), and metrics.py imports it at module load, so the test file skips there via importorskip. I verified the behaviour by loading metrics.py with evaluate and rouge_score stubbed, since retrieval_metrics uses neither. If someone with the benchmark requirements installed runs the file, that would be a useful second check.

Not included

The continue paths themselves are untouched, and so is zip(..., strict=False) in the turn handling. Skipping a failed conversation may well be the behaviour you want; this PR is only about making the skip visible in the output. Happy to look at either separately.

The benchmark runners skip a conversation when a query or an ingest
raises, and retrieval_metrics then averages over the qrels/results
intersection. So the dropped queries leave the denominator and the
output looks the same as a clean run. On the retrieval side there was
no count in the returned dict at all.

Adds num_scored_queries and num_missing_queries to retrieval_metrics,
and num_missing_queries to generation metrics. No existing number
changes. The names avoid num_queries because beir_bench sets that key
itself after the call, with a different meaning (queries attempted).
@arthi-arumugam-git arthi-arumugam-git changed the title Report how many queries the RAG benchmark actually scored fix(benchmarking): report how many queries the RAG benchmark actually scored Aug 19, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR improves the RAG benchmarking harness metrics output by explicitly reporting how many queries were actually scored (and how many were missing) so that partial/errored runs are visible in published results.

Changes:

  • Extend retrieval_metrics to return num_scored_queries and num_missing_queries, including the no-overlap case.
  • Extend answer_metrics to return num_missing_queries alongside existing generation metrics.
  • Add a pytest file covering complete, partial, and empty-results retrieval runs, ensuring equal scores can be distinguished by counts.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
benchmarking/rag/lib/metrics.py Adds scored/missing query counts to retrieval metrics and missing-query counts to answer metrics.
benchmarking/rag/lib/test_metrics_coverage.py Adds retrieval metrics coverage tests for complete/partial/no-results scenarios.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines 32 to +37
Returns:
Aggregated metrics dict, e.g. {"ndcg_cut_10": 0.45, "recall_10": 0.78, ...}
Aggregated metrics dict, e.g. {"ndcg_cut_10": 0.45, "recall_10": 0.78, ...},
plus "num_scored_queries" (queries the averages were taken over) and
"num_missing_queries" (queries in qrels that got no result, usually
because the run errored past them). Callers already set their own
"num_queries", so these use distinct names.
Comment on lines +17 to +22
import pytest

pytest.importorskip("pytrec_eval")
pytest.importorskip("evaluate")

from benchmarking.rag.lib.metrics import retrieval_metrics # noqa: E402
…urn types

Both from review. scripts/unit-tests.sh runs pytest against tests/unit
only, so a test under benchmarking/ was never collected: moved it there
and added the sys.path insert it needs, since benchmarking/ is not a
package. The return annotations still said dict[str, float] while the
functions now also return integer counts, so both are dict[str, float |
int] (answer_metrics already returned an int num_queries before this
PR, so that one was wrong already).
@arthi-arumugam-git

Copy link
Copy Markdown
Author

Both fixed, thanks:

Test placement. This was the important one. scripts/unit-tests.sh runs pytest against tests/unit/ only, so the file sat where nothing would ever collect it. Moved to tests/unit/test_rag_benchmark_metrics_coverage.py, with a small sys.path insert because benchmarking/ is not a package.

One caveat I should flag rather than hide: the test still needs pytrec_eval, which lives in benchmarking/rag/requirements.txt rather than the main dev deps, so it will skip via importorskip unless those are installed. That is better than never being collected, but if you would rather it actually ran in CI, I am happy to either add the dependency or split the count assertions into a version that does not import the metrics module at all.

Return types. Right, and it was already slightly wrong before this PR: answer_metrics has returned an integer num_queries for a while under a dict[str, float] annotation. Both are now dict[str, float | int]. I left the TypedDict idea alone since it would change more of the file than this PR should.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants