feat: Benchmarkklasse „ehrlich scheitern“ – bewertet Verhalten statt Zielwert
expected.json kann statt objective_value eine Liste "behavior" nennen (rejected | unreliable | assumed_data), eines genügt. Treffer = Verhalten gezeigt, erfüllt = zusätzlich alle Teilfragen beantwortet (außer bei Ablehnung). Bei erwarteter Ablehnung wird kein Routing bewertet. Vier Fälle: reine Gleichung, reine Rechenaufgabe (-> abgelehnt), fehlende Kursdaten, fehlender Kundenbedarf (-> Annahme ausgewiesen oder unzuverlässig). Erste Messung 20260918-134339: 3/4; der fehlende Bedarf wird im Text parametrisch richtig behandelt, maschinell aber als „optimal 270, zuverlässig“ ohne ausgewiesene Annahme gemeldet. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
6ecfdf6ae0
commit
d6bad28cb2
1 changed files with 151 additions and 9 deletions
|
|
@ -1,6 +1,7 @@
|
|||
"""run_pipeline ohne LLM: Neustart bei Fremdproblem und Kennzeichnung unzuverlässiger Ergebnisse."""
|
||||
from benchmark.benchmark import result_correct
|
||||
from core.model_schema import (
|
||||
Answer,
|
||||
AssumptionReview,
|
||||
LLMUsage,
|
||||
OptimizationModelSpecification,
|
||||
|
|
@ -736,3 +737,71 @@ def test_untracked_files_do_not_mark_the_measurement_dirty(monkeypatch):
|
|||
benchmark.prompt_state.cache_clear()
|
||||
assert stand["dirty"] is False
|
||||
assert any("--untracked-files=no" in befehl for befehl in aufrufe)
|
||||
|
||||
|
||||
# --- Benchmarkfälle „ehrlich scheitern“ (expected["behavior"]) ---
|
||||
|
||||
def _ergebnisse():
|
||||
"""Drei Ausgänge derselben Pipeline: abgelehnt, mit ausgewiesenen Annahmen gelöst, unzuverlässig."""
|
||||
abgelehnt = run_pipeline("4x - 7 = 25", session=FakeSession([], [], understandings=[KEINE_AUFGABE]))
|
||||
mit_annahme = run_pipeline("Aufgabe", session=FakeSession(
|
||||
[GOOD], [OK], [SolverCode(code=STRUCTURED_CODE, data_assumptions=["Bedarf G3 = 0 Kisten (nicht genannt)"])]),
|
||||
max_revisions=0, max_restarts=0)
|
||||
unzuverlaessig = run_pipeline("Aufgabe", session=FakeSession([HALLUCINATED] * 3, [MISMATCH] * 3),
|
||||
max_revisions=2, max_restarts=0)
|
||||
return abgelehnt, mit_annahme, unzuverlaessig
|
||||
|
||||
|
||||
def test_observed_behaviors():
|
||||
from benchmark.benchmark import observed_behaviors
|
||||
abgelehnt, mit_annahme, unzuverlaessig = _ergebnisse()
|
||||
assert observed_behaviors(abgelehnt) == {"rejected"}
|
||||
assert observed_behaviors(mit_annahme) == {"assumed_data"}
|
||||
assert observed_behaviors(unzuverlaessig) == {"unreliable"}
|
||||
|
||||
|
||||
def test_honest_failure_cases_are_scored_by_behavior(monkeypatch):
|
||||
from benchmark import benchmark
|
||||
abgelehnt, mit_annahme, unzuverlaessig = _ergebnisse()
|
||||
fehlende_daten = {"behavior": ["assumed_data", "unreliable"]}
|
||||
for ergebnis, expected, treffer in [
|
||||
(abgelehnt, {"behavior": ["rejected"]}, True),
|
||||
(mit_annahme, {"behavior": ["rejected"]}, False), # Gleichung modelliert statt abgelehnt
|
||||
(mit_annahme, fehlende_daten, True), # Annahme ausgewiesen
|
||||
(unzuverlaessig, fehlende_daten, True), # als unzuverlässig gekennzeichnet
|
||||
(abgelehnt, fehlende_daten, False), # lösbare Aufgabe abgelehnt
|
||||
]:
|
||||
monkeypatch.setattr(benchmark, "run_pipeline", lambda *a, **k: ergebnis)
|
||||
zeile = benchmark.run_case({"name": "ehrlich", "task": "-", "expected": expected}, "fake", 1)
|
||||
assert zeile["objective_ok"] is treffer, (zeile["behavior"], expected)
|
||||
# mit ausgewiesener Annahme und beantworteter Teilfrage ist die Aufgabe erfüllt
|
||||
monkeypatch.setattr(benchmark, "run_pipeline", lambda *a, **k: mit_annahme)
|
||||
mit_annahme.solver_run.answers = [Answer(question_id="1", text="Kosten 9 bei angenommenem Bedarf.")]
|
||||
mit_annahme.solver_run.warnings = []
|
||||
assert benchmark.run_case({"name": "e", "task": "-", "expected": fehlende_daten}, "fake", 1)["task_ok"] is True
|
||||
|
||||
|
||||
def test_unknown_behavior_is_refused():
|
||||
import pytest
|
||||
from benchmark.benchmark import behavior_matches
|
||||
abgelehnt = _ergebnisse()[0]
|
||||
with pytest.raises(ValueError, match="Unbekanntes Verhalten"):
|
||||
behavior_matches(abgelehnt, {"behavior": ["ehrlich"]})
|
||||
|
||||
|
||||
def test_honest_failure_cases_are_well_formed():
|
||||
from benchmark.benchmark import BEHAVIORS, load_cases
|
||||
faelle = [f for f in load_cases() if "behavior" in f["expected"]]
|
||||
assert len(faelle) >= 4
|
||||
for fall in faelle:
|
||||
assert fall["expected"]["behavior"] and set(fall["expected"]["behavior"]) <= set(BEHAVIORS)
|
||||
assert "objective_value" not in fall["expected"] # kein Zielwert: bewertet wird das Verhalten
|
||||
|
||||
|
||||
def test_expected_rejection_has_no_routing_to_score(monkeypatch):
|
||||
from benchmark import benchmark
|
||||
abgelehnt = _ergebnisse()[0]
|
||||
monkeypatch.setattr(benchmark, "run_pipeline", lambda *a, **k: abgelehnt)
|
||||
erwartet = benchmark.run_case({"name": "g", "task": "-", "expected": {"behavior": ["rejected"]}}, "fake", 1)
|
||||
falsch = benchmark.run_case({"name": "lp", "task": "-", "expected": {"objective_value": 9, "tolerance": 1e-6}}, "fake", 1)
|
||||
assert erwartet["class_ok"] is None and falsch["class_ok"] is False # eine unerwartete Ablehnung bleibt ein Fehler
|
||||
|
|
|
|||
Loading…
Reference in a new issue