refactor: Pipeline-Stufen in Ordner mit ihren Prompts
Im Wurzelverzeichnis liegt nur noch TextToModel.py (Ablauf und CLI).
Jede Stufe liegt mit Code und Prompt in einem eigenen Ordner: core,
understanding, modeling, checks, solving, reporting, data, benchmark;
Tests unter tests/, Aufgaben unter tasks/. understand_task,
text_to_model/revise_model und review_assumptions/needs_work/select_best
sind aus TextToModel.py zu ihren Prompts gewandert, inhaltlich
unveraendert. Prompt-Kennung des Benchmarks bleibt 93744636095a.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 12:11:51 +02:00
|
|
|
|
"""Stufe 4 der Pipeline: LLM-Prüfung von Zielgröße und Annahmen; Entscheidung über Korrekturrunden."""
|
|
|
|
|
|
from pathlib import Path
|
|
|
|
|
|
from typing import List
|
|
|
|
|
|
|
|
|
|
|
|
from checks.model_checks import has_errors
|
|
|
|
|
|
from core.llm import LLMSession
|
2026-09-17 16:28:39 +02:00
|
|
|
|
from core.model_schema import AssumptionReview, AssumptionVerdict, CheckIssue, CheckSeverity, OptimizationModelSpecification
|
refactor: Pipeline-Stufen in Ordner mit ihren Prompts
Im Wurzelverzeichnis liegt nur noch TextToModel.py (Ablauf und CLI).
Jede Stufe liegt mit Code und Prompt in einem eigenen Ordner: core,
understanding, modeling, checks, solving, reporting, data, benchmark;
Tests unter tests/, Aufgaben unter tasks/. understand_task,
text_to_model/revise_model und review_assumptions/needs_work/select_best
sind aus TextToModel.py zu ihren Prompts gewandert, inhaltlich
unveraendert. Prompt-Kennung des Benchmarks bleibt 93744636095a.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 12:11:51 +02:00
|
|
|
|
|
|
|
|
|
|
REVIEW_PROMPT_FILE = Path(__file__).parent / "Pruef-Prompt.txt"
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-09-18 13:22:53 +02:00
|
|
|
|
def review_assumptions(session: LLMSession, task: str, spec: OptimizationModelSpecification,
|
|
|
|
|
|
data_prompt: str = "") -> AssumptionReview:
|
|
|
|
|
|
# Mit beigelegten Daten gehört ihr Profil zum Aufgabentext – sonst beurteilt die Prüfung Annahmen
|
|
|
|
|
|
# über Spalten, die sie nie gesehen hat.
|
|
|
|
|
|
aufgabe = f"{task}\n\n{data_prompt}" if data_prompt else task
|
refactor: Pipeline-Stufen in Ordner mit ihren Prompts
Im Wurzelverzeichnis liegt nur noch TextToModel.py (Ablauf und CLI).
Jede Stufe liegt mit Code und Prompt in einem eigenen Ordner: core,
understanding, modeling, checks, solving, reporting, data, benchmark;
Tests unter tests/, Aufgaben unter tasks/. understand_task,
text_to_model/revise_model und review_assumptions/needs_work/select_best
sind aus TextToModel.py zu ihren Prompts gewandert, inhaltlich
unveraendert. Prompt-Kennung des Benchmarks bleibt 93744636095a.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 12:11:51 +02:00
|
|
|
|
return session.create(
|
|
|
|
|
|
AssumptionReview,
|
|
|
|
|
|
[
|
|
|
|
|
|
{"role": "system", "content": REVIEW_PROMPT_FILE.read_text(encoding="utf-8")},
|
|
|
|
|
|
{
|
|
|
|
|
|
"role": "user",
|
2026-09-18 13:22:53 +02:00
|
|
|
|
"content": f"AUFGABENTEXT:\n{aufgabe}\n\nMODELLSPEZIFIKATION:\n{spec.model_dump_json(indent=2)}",
|
refactor: Pipeline-Stufen in Ordner mit ihren Prompts
Im Wurzelverzeichnis liegt nur noch TextToModel.py (Ablauf und CLI).
Jede Stufe liegt mit Code und Prompt in einem eigenen Ordner: core,
understanding, modeling, checks, solving, reporting, data, benchmark;
Tests unter tests/, Aufgaben unter tasks/. understand_task,
text_to_model/revise_model und review_assumptions/needs_work/select_best
sind aus TextToModel.py zu ihren Prompts gewandert, inhaltlich
unveraendert. Prompt-Kennung des Benchmarks bleibt 93744636095a.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 12:11:51 +02:00
|
|
|
|
},
|
|
|
|
|
|
],
|
|
|
|
|
|
step="Prüfung",
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Warnungen, die eine Korrekturrunde wert sind: keine Fehler (ein richtiges Ergebnis bleibt zuverlässig),
|
|
|
|
|
|
# aber in einer Runde billig zu beheben. Unerklärte Fachbegriffe kosten den Leser sonst das Verständnis.
|
|
|
|
|
|
REVISION_WORTHY_WARNINGS = {"UNEXPLAINED_JARGON", "UNUSED_FACTS"}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def needs_work(review: AssumptionReview, issues: List[CheckIssue]) -> bool:
|
|
|
|
|
|
"""Rechtfertigt der Stand eine Korrekturrunde?"""
|
|
|
|
|
|
return (review.needs_revision or has_errors(issues)
|
|
|
|
|
|
or any(i.code in REVISION_WORTHY_WARNINGS for i in issues))
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-09-17 16:28:39 +02:00
|
|
|
|
def revision_reasons(review: AssumptionReview, issues: List[CheckIssue]) -> List[str]:
|
|
|
|
|
|
"""Warum eine Korrekturrunde nötig ist – fürs Log.
|
|
|
|
|
|
|
|
|
|
|
|
Bisher stand dort nur „Probleme gefunden“; welche Beanstandung eine Runde auslöste, ließ sich im
|
|
|
|
|
|
Nachhinein nicht mehr feststellen (beobachtet an wagner_whitin_dijkstra, 20260917-160618).
|
|
|
|
|
|
"""
|
|
|
|
|
|
gruende = []
|
|
|
|
|
|
oc = review.objective_check
|
|
|
|
|
|
if not oc.matches:
|
|
|
|
|
|
gruende.append(f"Zielgröße passt nicht: {oc.explanation[:160]}")
|
|
|
|
|
|
if not oc.units_consistent:
|
|
|
|
|
|
gruende.append(f"Einheiten der Zielfunktion uneinheitlich: {oc.unit_explanation[:160]}")
|
|
|
|
|
|
faulty = {AssumptionVerdict.CONTRADICTS_TASK, AssumptionVerdict.STRICTER_THAN_TASK, AssumptionVerdict.WEAKER_THAN_TASK}
|
|
|
|
|
|
for check in review.checks:
|
|
|
|
|
|
if check.verdict in faulty:
|
|
|
|
|
|
gruende.append(f"Annahme {check.verdict.value}: {check.unclear_point[:120]}")
|
|
|
|
|
|
for fehlend in review.missing_assumptions:
|
|
|
|
|
|
gruende.append(f"fehlende Annahme: {fehlend.unclear_point[:120]}")
|
|
|
|
|
|
for issue in issues:
|
|
|
|
|
|
if issue.severity == CheckSeverity.ERROR or issue.code in REVISION_WORTHY_WARNINGS:
|
|
|
|
|
|
gruende.append(f"[{issue.code}] {issue.message[:120]}")
|
|
|
|
|
|
return gruende
|
|
|
|
|
|
|
|
|
|
|
|
|
refactor: Pipeline-Stufen in Ordner mit ihren Prompts
Im Wurzelverzeichnis liegt nur noch TextToModel.py (Ablauf und CLI).
Jede Stufe liegt mit Code und Prompt in einem eigenen Ordner: core,
understanding, modeling, checks, solving, reporting, data, benchmark;
Tests unter tests/, Aufgaben unter tasks/. understand_task,
text_to_model/revise_model und review_assumptions/needs_work/select_best
sind aus TextToModel.py zu ihren Prompts gewandert, inhaltlich
unveraendert. Prompt-Kennung des Benchmarks bleibt 93744636095a.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 12:11:51 +02:00
|
|
|
|
def select_best(candidates: List[tuple]) -> int:
|
|
|
|
|
|
"""Index der besten (spec, issues, review)-Runde.
|
|
|
|
|
|
|
|
|
|
|
|
Rangfolge: passende Zielgröße vor unpassender (sonst womöglich Fremdproblem), keine Check-Errors vor Errors,
|
|
|
|
|
|
Prüfung ohne Beanstandung vor Beanstandung, weniger Errors;
|
|
|
|
|
|
bei Gleichstand gewinnt die spätere Runde. Verhindert, dass eine Korrekturrunde, die das Modell
|
|
|
|
|
|
verschlechtert (z. B. halluziniertes Fremdproblem), die bessere frühere Fassung ersetzt.
|
|
|
|
|
|
"""
|
|
|
|
|
|
def score(index):
|
|
|
|
|
|
_, issues, review = candidates[index]
|
|
|
|
|
|
errors = sum(1 for i in issues if i.severity == CheckSeverity.ERROR)
|
|
|
|
|
|
return (not review.objective_check.matches, errors > 0, review.needs_revision, errors, -index)
|
|
|
|
|
|
|
|
|
|
|
|
return min(range(len(candidates)), key=score)
|