talkingheadbench / src /envs /subenv2 /node6_grader.py
elix3r's picture
Upload folder using huggingface_hub
ab34aa7 verified
Raw
History Blame Contribute Delete
6.43 kB
"""
Node 6 (Grader): Dataset Health Grader β€” Sub-env 2.
This module implements the per-clip disposition grader that evaluates the
output of the Clip Disposition Recommender agent (Node 5).
``grade_clip_disposition`` scores a single clip decision. The Dataset Health
Grader (Node 6) aggregates scores across all clips and produces the
``DatasetHealthHandoff``.
Scoring contract
----------------
- Maximum achievable score: 0.80 (0.40 base + 0.20 fix quality + 0.20 reasoning)
- Override misuse subtracts up to 0.10 from the running total.
- Final score is clamped to [0.0, 1.0] β€” never negative.
- The ``defer`` branch checks ``ground_truth.disposition_ambiguity >= 0.5``;
deferring on an unambiguous case is penalised as avoidance.
- The override penalty applies **only** when ``override_decision == "applied"``.
"""
from __future__ import annotations
from src.schemas.ground_truth import GroundTruthClipAnnotation
from src.schemas.subenv2 import ClipDispositionAction
def grade_clip_disposition(
agent_action: ClipDispositionAction,
ground_truth: GroundTruthClipAnnotation,
) -> float:
"""Grade a single clip disposition recommendation (Node 5).
Evaluates four dimensions and returns a clamped composite score in
[0.0, 1.0]:
**1. Base disposition** (0.40 max)
- Correct disposition + confidence within 0.15 of ground truth β†’ +0.40
- Correct disposition but miscalibrated confidence β†’ +0.28
- ``"fix"`` when ground truth is ``"reject"`` β†’ +0.20
(partial credit: fix is better than blind accept)
- ``"defer"`` on an ambiguous case (ambiguity β‰₯ 0.5):
- with ``defer_reason`` β†’ +0.15
- without ``defer_reason`` β†’ +0.10
- ``"defer"`` on an unambiguous case (ambiguity < 0.5) β†’ βˆ’0.05
(avoidance penalty; clamped so total never goes below 0.0)
- ``"accept"`` when ground truth is ``"reject"`` β†’ +0.00
(worst case)
**2. Fix instruction quality** (0.20 max)
Applies only when ``disposition == "fix"`` and ``fix_instructions`` is
non-empty. Precision = fraction of agent steps present in
``ground_truth.valid_fix_steps``:
- precision β‰₯ 0.8 β†’ +0.20
- precision β‰₯ 0.5 β†’ +0.10
- precision < 0.5 β†’ +0.00
**3. Dataset impact reasoning** (0.20 max)
Checks how many of ``ground_truth.expected_reasoning_elements`` appear
(case-insensitive substring) in ``dataset_impact_reasoning``:
- β‰₯ 80 % matched β†’ +0.20
- β‰₯ 1 matched β†’ +0.10
- none matched β†’ +0.00
**4. Override misuse penalty** (applies only when ``override_decision == "applied"``)
- No ``override_justification`` provided β†’ βˆ’0.10
- Justification not in valid set β†’ βˆ’0.05
- Valid justification present β†’ no penalty
Args:
agent_action: The ``ClipDispositionAction`` produced by the Node 5 agent.
ground_truth: The ``GroundTruthClipAnnotation`` for this test case.
Returns:
A float in [0.0, 1.0] representing the per-clip grader score.
"""
score: float = 0.0
# ------------------------------------------------------------------
# 1. Base disposition (0.40 max)
# ------------------------------------------------------------------
if agent_action.disposition == ground_truth.disposition:
calibrated = abs(agent_action.confidence - ground_truth.confidence) < 0.15
score += 0.40 if calibrated else 0.28
elif agent_action.disposition == "fix" and ground_truth.disposition == "reject":
score += 0.20 # partial: fix is better than blind accept
elif agent_action.disposition == "defer":
if ground_truth.disposition_ambiguity >= 0.5:
# Ambiguous case β€” deferring is reasonable; reward more if documented
score += 0.15 if agent_action.defer_reason else 0.10
else:
# Unambiguous case β€” defer is avoidance; penalise
score = max(score - 0.05, 0.0)
elif agent_action.disposition == "accept" and ground_truth.disposition == "reject":
score += 0.00 # worst case β€” no credit
# ------------------------------------------------------------------
# 2. Fix instruction quality (0.20 max)
# ------------------------------------------------------------------
if agent_action.disposition == "fix" and agent_action.fix_instructions:
valid_steps = sum(
1
for step in agent_action.fix_instructions
if step in ground_truth.valid_fix_steps
)
fix_precision = valid_steps / len(agent_action.fix_instructions)
if fix_precision >= 0.8:
score += 0.20
elif fix_precision >= 0.5:
score += 0.10
# else: 0.00 β€” no addition
# ------------------------------------------------------------------
# 3. Dataset impact reasoning (0.20 max)
# ------------------------------------------------------------------
kw_elements = ground_truth.expected_reasoning_elements
agent_text = agent_action.dataset_impact_reasoning.lower()
matched = sum(1 for kw in kw_elements if kw in agent_text)
if matched >= len(kw_elements) * 0.8:
score += 0.20
elif matched >= 1:
score += 0.10
# else: 0.00 β€” no addition
# ------------------------------------------------------------------
# 4. Override misuse penalty
# Evaluate ONLY when override labels are actually annotated.
# ------------------------------------------------------------------
valid_override_justifications = ground_truth.valid_override_justifications
has_override_labels = (
isinstance(valid_override_justifications, list)
and len(valid_override_justifications) > 0
)
if has_override_labels and agent_action.override_decision == "applied":
if not agent_action.override_justification:
score -= 0.10
elif agent_action.override_justification not in valid_override_justifications:
score -= 0.05
# Valid justification present β†’ no penalty
# ------------------------------------------------------------------
# Clamp β€” score must never go below 0.0
# ------------------------------------------------------------------
return max(score, 0.0)