Skip to content

feat: add could-not-evaluate status so non-gradable cases are excluded from scores - #357

Open
pdebjyot wants to merge 1 commit into
strands-agents:mainfrom
pdebjyot:feat/could-not-evaluate-status
Open

feat: add could-not-evaluate status so non-gradable cases are excluded from scores#357
pdebjyot wants to merge 1 commit into
strands-agents:mainfrom
pdebjyot:feat/could-not-evaluate-status

Conversation

@pdebjyot

Copy link
Copy Markdown
Contributor

What

Adds an EvaluationOutput.status field so an evaluator can signal that it could not produce a real verdict for a case, and excludes those non-gradable outputs from every score/pass aggregate. Implements the design in #346.

status is one of:

  • "graded" (default) — score/test_pass are real verdicts and count toward aggregates.
  • "could_not_evaluate" — the evaluator tried but couldn't grade the case (preconditions not met, missing data, harness error). Excluded from aggregates.
  • "informational" — surfaces content for human review; never counts toward a numeric aggregate.

The default is "graded", so existing evaluators, consumers, and previously-saved reports are unaffected.

Why

Today, when an evaluator fails — a judge context-window overflow, missing data, unmet preconditions — the case is recorded as score=0, test_pass=False and that 0 is thrown into overall_score. A correct agent gets silently punished because the judge couldn't score the case, not because the agent was wrong. A capability failure of the harness is reported as a quality failure of the agent, with no way to tell the two apart.

How

  • types/evaluation.py — add GRADED / COULD_NOT_EVALUATE / INFORMATIONAL constants (plain str, not an Enum, so new values can be added without a breaking change) and the status field on EvaluationOutput (Literal[...], default graded).
  • evaluators/evaluator.py_default_aggregator averages graded outputs only. When every output is non-graded there is nothing to score, so it returns a non-failure sentinel with the evaluator's own reason string.
  • experiment.py — roll per-output statuses up to an evaluator-level status; tag both error-isolation paths (RetryError and generic Exception) as could_not_evaluate; compute overall_score from graded rows only.
  • types/evaluation_report.pyflatten() excludes non-graded case rows from overall_score; rows with no status key default to graded so legacy reports load identically.

Raw per-case scores / test_passes lists are left untouched — only the aggregate changes, so anything reading individual rows is unaffected.

Behavior change (illustration)

A correct agent, one evaluator grading it 1.0, and one evaluator that raises (simulating a judge context-window overflow):

evaluator score status
GoodEvaluator 1.0 graded
OverflowEvaluator 0.0 could_not_evaluate
  • Before: overall_score = 0.500 (naive average punishes the correct agent).
  • After: overall_score = 1.000 (reflects only what was gradable).

Tests

18 tests across the three layers:

  • Aggregator (_default_aggregator): graded-only average, could_not_evaluate/informational exclusion, all-skipped non-failure, empty-list unchanged.
  • Report (flatten()): non-graded row excluded, missing-status defaults to graded, all-non-graded → 0.
  • EvaluationOutput.status default + settable.
  • Experiment layer: error-isolation path tags could_not_evaluate; mixed-status run excludes the non-graded rows from overall_score.

Full suite: 1753 passed (baseline + net-new tests, no regressions). ruff check / ruff format --check clean.

Closes #346

…d from scores

Adds an EvaluationOutput.status field ("graded" | "could_not_evaluate" |
"informational", default "graded") so an evaluator can signal that it could
not produce a real verdict for a case. Non-graded outputs are excluded from
every score/pass aggregate instead of being recorded as a score-0 quality
failure.

- types/evaluation.py: add GRADED/COULD_NOT_EVALUATE/INFORMATIONAL constants
  and the status field (plain str + Literal, default graded for back-compat).
- evaluators/evaluator.py: _default_aggregator averages graded outputs only;
  all-skipped returns a non-failure sentinel.
- experiment.py: roll per-output statuses up per evaluator, tag the two
  error-isolation paths (RetryError, generic Exception) could_not_evaluate,
  and compute overall_score from graded rows only.
- types/evaluation_report.py: flatten() excludes non-graded case rows from
  overall_score; rows without a status default to graded.

Raw per-case scores/test_passes lists are left untouched; only aggregates
change. Adds tests at the aggregator, report, and experiment layers.

Closes strands-agents#346
@pdebjyot
pdebjyot requested a review from a team as a code owner August 10, 2026 13:56
@pdebjyot
pdebjyot requested a review from Unshure August 10, 2026 13:56
@github-actions github-actions Bot added enhancement New feature or request area-evaluators Evaluators: output, trajectory, tool use, interactions, and LLM-as-judge quality metrics area-core Core eval framework: Case, Experiment, task handler, evaluation data stores labels Aug 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-core Core eval framework: Case, Experiment, task handler, evaluation data stores area-evaluators Evaluators: output, trajectory, tool use, interactions, and LLM-as-judge quality metrics enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEATURE] Could-Not-Evaluate verdict for evaluators that cannot score a case

1 participant