Skip to content

fix(evals): ground judge verdicts, don't trust ungrounded met=true (#99) - #102

Merged
qdang46 merged 3 commits into
mainfrom
fix/judge-grounding
Sep 4, 2026
Merged

fix(evals): ground judge verdicts, don't trust ungrounded met=true (#99)#102
qdang46 merged 3 commits into
mainfrom
fix/judge-grounding

Conversation

@qdang46

@qdang46 qdang46 commented Sep 4, 2026

Copy link
Copy Markdown
Collaborator

Closes #99.

Problem

The LLM-judge path (llm_bool, goals_met, constraint_respected, and whole-conversation judge_run) had no independent check on the model's own met/relevant self-labels:

  1. A criterion could be marked met: true with no evidence cited at all — parse_judgment_payload accepted it as-is.
  2. apply_relevancy unconditionally promoted a fail verdict to pass whenever the model self-labeled the failing criteria as relevant: false, with no verification that the self-label was correct.

This is exactly how a topically-related-but-wrong agent reply slips through, e.g.:

  • Agent should re-ask: "Could you say company name again, please?"
  • Agent actually said: "Thanks, Jane Doe. I'm ready to hear more details about your company and the contact person—go ahead when you're ready!"

A judge that reads the second reply as "on-topic enough" can mark the criterion met without ever quoting evidence, or excuse it as "not relevant to this criterion" — either path currently produces a silent pass.

Fix

  • evals/types.py::parse_judgment_payload: a criterion reported met=True with blank/missing evidence is flipped to met=False, and the judgment is marked needs_human_review=True. An ungrounded "met" claim is not evidence of a met criterion.
  • evals/relevancy.py::apply_relevancy: keep the fail→pass promotion when all relevant criteria are met (it's usually correct), but now also sets needs_human_review=True instead of trusting the model's own relevant=False self-label blindly.

This doesn't require a schema/prompt change — it's a pure grounding check on the judge's existing output shape.

Tests

  • tests/test_evals_judge.py: added test_parse_judgment_ungrounded_met_criterion_is_not_trusted, test_parse_judgment_grounded_met_criterion_is_trusted, test_relevancy_promotion_flags_human_review.
  • uv run pytest -q → 683 passed.

…99)

The LLM-judge path (llm_bool/goals_met/constraint_respected assert
types + judge_run) let a criterion's met=true stand even when the
judge cited no transcript evidence for it, and unconditionally
promoted a fail→pass whenever the model self-labeled the failing
criteria as "irrelevant" — neither self-label is independently
verified. Together these let a topically-related-but-wrong agent
reply (e.g. "I'm ready to hear more about your company..." instead of
re-asking for the missing company name) slip through as a pass.

- evals/types.py::parse_judgment_payload: when a criterion is reported
  met=True with blank/missing evidence, flip it to met=False and mark
  the whole judgment needs_human_review — an ungrounded "met" claim is
  not trustworthy.
- evals/relevancy.py::apply_relevancy: keep the fail→pass promotion
  when all relevant criteria are met (usually correct), but flag
  needs_human_review instead of trusting the model's own
  relevant=False self-label blindly.

Tests: added grounding/promotion coverage to tests/test_evals_judge.py.
Full suite (uv run pytest -q) → 683 passed.
…uild

Same pre-existing main-branch build break as in #101 (introduced by the
#97 merge); needed here too since this branch was cut from main before
that fix landed.
Same pre-existing #97 merge damage as on fix/turn-split-merge — this
branch was cut from main before that fix landed.
@qdang46
qdang46 merged commit 2253cdd into main Sep 4, 2026
7 checks passed
@qdang46
qdang46 deleted the fix/judge-grounding branch September 4, 2026 09:07
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.

Hallucination detector misses topically-related but wrong answers

2 participants