Skip to content

bench: judged answer-correctness is the primary quality axis - #7

Merged
hallelx2 merged 3 commits into
halleluyaholudele/hal-70-bench-treewalk-endpoint-and-title-pathfrom
halleluyaholudele/hal-322-bench-judged-answer-correctness
Jun 20, 2026
Merged

bench: judged answer-correctness is the primary quality axis#7
hallelx2 merged 3 commits into
halleluyaholudele/hal-70-bench-treewalk-endpoint-and-title-pathfrom
halleluyaholudele/hal-322-bench-judged-answer-correctness

Conversation

@hallelx2

@hallelx2 hallelx2 commented Jun 20, 2026

Copy link
Copy Markdown
Owner

What & why

The bench computed the LLM-as-judge verdict (answer_correct/faithful/answered) per question, wrote it to records.jsonl, then discarded it: the report ranked every system on F1@k span-overlap. An answer-first system (treewalk emits its own grounded answer, not the verbatim gold passage) scores near-0 on span-F1 even when its answer is correct — so treewalk looked like it scored ~0 in every judged run. The LLM judge ran, graded correctly, and the report threw the number away.

Changes (3 sequential commits)

  1. primary_quality(): rank on judged answer_correct when the judge ran (answer_correct -> abstention -> f1@k). Unjudged configs unchanged. + regression test.
  2. report.summarize() aggregates an answer block; new Answer correctness (LLM-as-judge) section in report.md + report.html; headline states what Quality means.
  3. financebench_glm_judged.yaml → canonical 3-way: treewalk vs vector_rag (local bge-small embeddings) vs bm25, 20 docs, GLM-4.6 judge.

Verified

  • 33 tests pass (32 existing + new).
  • Synthetic judged run: treewalk wins primary (0.83) with the lowest span-F1 (0.13); order treewalk > vector_rag > bm25 by judged answer-correctness.

Closes HAL-322

Summary by Sourcery

Prioritize LLM-judged answer correctness as the primary quality metric and surface it prominently in reports, while updating the canonical FinanceBench judged benchmark configuration.

New Features:

  • Add an answer correctness axis to report aggregation, including judged counts, correctness/faithfulness/answered rates, and judge cost.
  • Introduce markdown and HTML report sections describing LLM-as-judge answer correctness and clarifying the primary quality definition.

Enhancements:

  • Change primary_quality to prefer judged answer correctness over span-F1 and abstention when LLM judging is enabled.
  • Revise the FinanceBench GLM judged configuration to compare treewalk, vector_rag, and bm25 over a larger doc set with local embeddings and updated timeouts.

Tests:

  • Add a regression test ensuring primary_quality prefers judged answer correctness and preserves behavior for unjudged runs.

hallelx2 added 3 commits June 20, 2026 10:02
… ran

primary_quality() now prefers the LLM-judge verdict (answer_correct) over
span-overlap F1: answer_correct -> abstention -> f1@k. An answer-first system
(treewalk emits its own grounded answer, not the verbatim gold passage) scored
near-0 on span-F1 even when its answer was correct, so ranking on F1 buried it.
Unjudged configs fall back to f1@k unchanged. Adds a regression test.
summarize() now aggregates an answer block (judged_n, correct, faithful,
answered, judge_usd) that the runner already wrote to records.jsonl but the
report discarded. Adds an 'Answer correctness (LLM-as-judge)' section to both
report.md and report.html, and the headline now states what Quality means
(judged answer-correctness vs F1@k span-overlap).
… bm25)

financebench_glm_judged.yaml now runs vectorless_treewalk vs vector_rag (local
bge-small embeddings, no OpenAI key) vs bm25 over 20 docs, judged on GLM-4.6 —
the comparison the launch reports on. All LLM work stays on GLM via z.ai.
@sourcery-ai

sourcery-ai Bot commented Jun 20, 2026

Copy link
Copy Markdown

Reviewer's Guide

Makes LLM-judge answer-correctness the primary quality metric when available, surfaces judged answer metrics in markdown/html reports, and updates the FinanceBench judged config to a canonical treewalk vs vector_rag vs bm25 comparison.

File-Level Changes

Change Details Files
Use LLM-judged answer correctness as the primary quality metric when present, with fallbacks for unjudged runs.
  • Extend primary_quality() to return answer_correct when available, ahead of abstention and span F1.
  • Keep existing behavior for unjudged runs by falling back to abstention then F1@k.
  • Add regression test to pin precedence and unjudged fallback behavior.
src/vectorless_bench/metrics/citation.py
tests/test_metrics.py
Expose LLM-judge answer metrics and definition of quality in markdown and HTML reports.
  • In summarize(), compute judged_n and aggregate answer_correct/answer_faithful/answered/judge_usd into an answer block per system.
  • In markdown report, state what "Quality" means depending on whether judged data exists, and add an Answer correctness (LLM-as-judge) section and table when judged.
  • In HTML report, mirror the judged answer section, highlight best correct score, and update the primary quality definition in the header subtitle.
src/vectorless_bench/report.py
Rework the FinanceBench judged config into a canonical treewalk vs vector_rag vs bm25 head-to-head using GLM-4.6 and local embeddings.
  • Add vector_rag to systems, configure it with an in-memory backend and local bge-small embeddings.
  • Increase limit to 20 docs and adjust timeouts for treewalk to support full run.
  • Document rationale and architecture-neutral judged answer axis extensively in comments, and configure bm25/vector_rag chunking to match.
configs/financebench_glm_judged.yaml

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai

coderabbitai Bot commented Jun 20, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 44116eb6-7723-4700-b3b5-023a322134bf

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch halleluyaholudele/hal-322-bench-judged-answer-correctness

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@sourcery-ai sourcery-ai Bot 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.

Hey - I've found 2 issues, and left some high level feedback:

  • In report.summarize, you now expose judged_n both at the top level and inside the answer block; consider keeping it in a single place (likely under answer) to avoid redundant state and reduce the chance of these drifting apart in future changes.
  • The judged flag logic (based on answer.judged_n) and the narrative explaining what ‘primary quality’ means are duplicated and slightly diverging between the markdown and HTML paths; it may be worth centralizing this logic/text so the two outputs stay in sync as the definition evolves.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `report.summarize`, you now expose `judged_n` both at the top level and inside the `answer` block; consider keeping it in a single place (likely under `answer`) to avoid redundant state and reduce the chance of these drifting apart in future changes.
- The `judged` flag logic (based on `answer.judged_n`) and the narrative explaining what ‘primary quality’ means are duplicated and slightly diverging between the markdown and HTML paths; it may be worth centralizing this logic/text so the two outputs stay in sync as the definition evolves.

## Individual Comments

### Comment 1
<location path="src/vectorless_bench/metrics/citation.py" line_range="94-95" />
<code_context>
+    2. **Correct abstention** for no-answer questions (when unjudged).
+    3. **F1@k** span-overlap retrieval quality (the unjudged default).
+    """
+    if "answer_correct" in metrics:
+        return metrics["answer_correct"]
     if "abstained" in metrics:
         return metrics["abstained"]
</code_context>
<issue_to_address>
**issue (bug_risk):** Primary quality now mixes LLM-judge scores with F1/abstention within the same aggregate when only some rows are judged.

With this precedence, when only some queries are LLM-judged (e.g., first repeat only), `primary_quality` will use `answer_correct` for judged rows but fall back to `abstained`/`f1@k` for others. Since `summarize()` averages `quality['primary']` over all ok rows, this makes the efficiency frontier and `quality_per_1k_usd` combine different definitions of “quality” into a single scalar, which conflicts with the docs that describe primary quality as judged answer-correctness when `judged` is true.

Consider making primary quality consistently judged-only once answer-correctness is enabled. For example:
- Require that all rows in judged runs have `answer_correct`, or
- In `summarize()`, when `judged_n > 0`, compute primary quality as the mean of `answer_correct` over `judged` rows only, and don’t fall back to span-F1 for unjudged rows.

This keeps the primary quality axis semantically consistent and aligned with the documentation.
</issue_to_address>

### Comment 2
<location path="src/vectorless_bench/report.py" line_range="163-172" />
<code_context>
     lines.append(f"Run: `{run_dir.name}` · k={k} · see `manifest.json` for full config.\n")

+    judged = any(r["answer"]["judged_n"] > 0 for r in results.values())
+    quality_def = (
+        "LLM-judged answer-correctness (the candidate answer vs the FinanceBench "
+        "gold answer, graded by the judge model)" if judged
</code_context>
<issue_to_address>
**issue (bug_risk):** HTML description of primary quality may not match the actual scalar used in the frontier when judged is partially populated.

Right now `judged` is true as soon as any row has `judged_n > 0`, but `primary_quality()` still falls back to abstention/F1 for unjudged rows. That means `quality['primary']` for a “judged” run can mix judge scores with span-F1/abstention for the same system while the HTML still labels the axis as pure LLM-judged correctness.

To keep the frontier semantics clear, consider either (a) defining `quality['primary']` to only use fully judged rows (or requiring all rows be judged in “judged” runs), and/or (b) tightening the `judged` condition (e.g., only when all systems/rows are judged) so the label reflects the actual metric.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +94 to +95
if "answer_correct" in metrics:
return metrics["answer_correct"]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

issue (bug_risk): Primary quality now mixes LLM-judge scores with F1/abstention within the same aggregate when only some rows are judged.

With this precedence, when only some queries are LLM-judged (e.g., first repeat only), primary_quality will use answer_correct for judged rows but fall back to abstained/f1@k for others. Since summarize() averages quality['primary'] over all ok rows, this makes the efficiency frontier and quality_per_1k_usd combine different definitions of “quality” into a single scalar, which conflicts with the docs that describe primary quality as judged answer-correctness when judged is true.

Consider making primary quality consistently judged-only once answer-correctness is enabled. For example:

  • Require that all rows in judged runs have answer_correct, or
  • In summarize(), when judged_n > 0, compute primary quality as the mean of answer_correct over judged rows only, and don’t fall back to span-F1 for unjudged rows.

This keeps the primary quality axis semantically consistent and aligned with the documentation.

Comment on lines +163 to +172
quality_def = (
"LLM-judged answer-correctness (the candidate answer vs the FinanceBench "
"gold answer, graded by the judge model)" if judged
else "F1@k span-overlap for answerable questions / correct abstention for "
"no-answer questions"
)

lines.append("## Efficiency frontier (the headline)\n")
lines.append("Quality is meaningless without its price. `quality_per_1k_usd` = "
lines.append(f"**Quality = {quality_def}.** "
"Quality is meaningless without its price. `quality_per_1k_usd` = "

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

issue (bug_risk): HTML description of primary quality may not match the actual scalar used in the frontier when judged is partially populated.

Right now judged is true as soon as any row has judged_n > 0, but primary_quality() still falls back to abstention/F1 for unjudged rows. That means quality['primary'] for a “judged” run can mix judge scores with span-F1/abstention for the same system while the HTML still labels the axis as pure LLM-judged correctness.

To keep the frontier semantics clear, consider either (a) defining quality['primary'] to only use fully judged rows (or requiring all rows be judged in “judged” runs), and/or (b) tightening the judged condition (e.g., only when all systems/rows are judged) so the label reflects the actual metric.

@hallelx2
hallelx2 merged commit c54851e into halleluyaholudele/hal-70-bench-treewalk-endpoint-and-title-path Jun 20, 2026
2 checks passed
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.

1 participant