ADR-0009: reasoning-only → unscored + Pre-Flight + free max_tokens¶
- Status: akzeptiert · 2026-06-28
- Bereich: Judge
Kontext¶
Der erste echte Qual-Lauf via GUI (google/gemma-4-12b-qat, LM Studio) ergab 50/52 Antworten mit leerem content: Das Thinking-Modell schrieb alles ins reasoning-Feld und verbrauchte das knappe max_tokens-Budget (~400), bevor je sichtbarer content kam. Der Judge bewertete jede leere Antwort hart mit 1/5 — eine Scorecard aus lauter Einsen. Das ist ein Harness-Fehler, nicht das Modell: Die Ursache ist meist ein zu kleines max_tokens, also unser Test-Setup. Drei Kräfte/Constraints prägten die Entscheidung: (1) reasoning-only (Token-Starvation) und echt-leer (Modell gibt wirklich nichts aus) wurden zusammengeworfen; (2) der verschwendete Voll-Lauf fiel erst nach ~50 Cells (~30 Min) auf; (3) die Mess-Bedingung darf nie heimlich verfälscht werden — ein kleines Budget ist ein legitimes Test-Setup (kleine Maschine).
Entscheidung¶
Drei zusammenhängende Maßnahmen im Bewertungs-/Mess-Pfad:
- reasoning-only → unscored, nicht 1. Der
content_empty-Zweig injudge.py(score_response) wird überresp.reasoning_charsaufgeteilt: Beireasoning_chars > 0liefert der Judgeunscored=True,red_flag=False,score=0und dieREASONING_ONLY_RATIONALE("Nur Reasoning, kein sichtbarer Content … Budget zu klein oder Thinking aktiv … aus dem Mittel ausgenommen"). Beireasoning_chars == 0(echt-leer) bleibt es beiscore=1,unscored=False,EMPTY_RATIONALE("Als Assistenz-Antwort unbrauchbar");red_flagfolgt dortprompt.safety_critical.unscored-Verdicts fallen aus dem Mittel und aus der holistischen Master-Dimension-Evidenz. - Free
max_tokensals Eval-Default. Die eigentliche Lösung ist, das Limit gar nicht erst zu setzen:PackPrompt.max_tokensdefaultet aufNone→ keinmax_tokensan die API → der Server antwortet frei (kontextfenster-begrenzt). Inclient.OpenAIStreamClient.streamwirdmax_tokensnur gesetzt, wenn nichtNone. Das feste Budget lebt nur noch im Latenz-Runner (run_benchmark, überCell.max_tokens/config.max_tokens_for). Opt-in-Hebel bleiben:reasoning_headroom_tokens(extra Denk-Budget pro Modell, sichtbares Antwort-Budget bleibt der Cap) undextra_body(Thinking abschalten, generisch durchgereicht). - Pre-Flight-Smoke vor der Matrix.
preflight_models(touchstone/preflight.py) sendet vor dem Voll-Lauf einmal pro Modell einen kleinen realistischen Prompt (SMOKE_PROMPT= "Antworte in genau einem Satz: Was ist 2 + 2?") über dieselbestream_once-Bahn wie die Messung, mit dem großzügigsten Budget des Packs. Klassifikation:ok/reasoning_only/empty/error. Der Smoke warnt nur und wirft nie selbst; allein--strict-preflightbricht hart ab.
Erwogene Alternativen¶
- reasoning-only weiter als 1/5 bewerten — verworfen, weil unfair: Die leere Antwort entsteht durch unser zu kleines Budget, nicht durch Modellqualität; eine stille 1 verfälscht das Mittel.
- Budget automatisch aufblähen — verworfen (Nicht-Ziel/YAGNI): Ein kleines
max_tokensist ein legitimes Test-Setup; der Harness ändert die Mess-Bedingung nie ohne explizites Opt-in. - Engine-spezifischer Thinking-Aus-Code (
if engine == "lm-studio") — verworfen: Der Thinking-Schalter bleibt generischesextra_body, roh an die OpenAI-API durchgereicht; der Harness weiß nicht, wie eine Engine Thinking abschaltet. - Pre-Flight als Hard-Abort per Default — verworfen: Ein falsch-negativer Smoke (z.B. bewusst kleines Budget) darf keinen validen Lauf verhindern; daher Default = warnen, Hard-Abort nur via
--strict-preflight. - Eigener nicht-streaming Smoke-Pfad — verworfen: Der Pre-Flight nutzt denselben
stream_oncewie die Messung (eine Code-Bahn, gleiche Bedingung).
Auswirkungen¶
- Positiv: Thinking-Modelle werden fair vermessbar (free
max_tokenserwürgt sie gar nicht erst, der ursprüngliche Blocker entsteht nie), und der Trade-off ist transparent statt still als 1/5 verbucht. Das Denken bleibt bei leerem content viareasoning_texteinsehbar. Verschwendete 30-Minuten-Läufe werden per Frühwarnung vermieden. - Trade-off / Restgrenze:
reasoning_headroom_tokensist Platz zum Denken, keine harte Garantie auf sichtbaren Content — die OpenAI-API hat nur einmax_tokens. Der Pre-Flight nutzt bewusst das großzügigste Budget; enge Einzel-Budgets, die nur manche Zellen leer lassen, fängt erst die per-Zelle-unscored-Logik, nicht der Smoke. Resume überspringt den Pre-Flight. Ein Endpoint, der unbekanntesextra_bodymit 4xx ablehnt, schlägt schon im Pre-Flight auf (Warnung vor der Matrix).
Belege & Links¶
- Spec:
docs/superpowers/specs/2026-06-22-thinking-models-blocker-design.md· Code:touchstone/judge.py,touchstone/preflight.py,touchstone/client.py· Essay:docs/explanation/design-decisions.md· Tests:tests/test_judge.py,tests/test_preflight.py,tests/test_client.py - Hinweis: Der Judge-eigene Thinking-Aus-Schalter (
suppress_thinking,SUPPRESS_THINKING_BODYintouchstone/judge.py) gilt nur für Eval-Modelle bzw. das Judge-Backend, nicht für die hier behandelten Eval-Hebel — verwandte Thematik im Bereich Judge.