"""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 from core.model_schema import AssumptionReview, CheckIssue, CheckSeverity, OptimizationModelSpecification REVIEW_PROMPT_FILE = Path(__file__).parent / "Pruef-Prompt.txt" def review_assumptions(session: LLMSession, task: str, spec: OptimizationModelSpecification) -> AssumptionReview: return session.create( AssumptionReview, [ {"role": "system", "content": REVIEW_PROMPT_FILE.read_text(encoding="utf-8")}, { "role": "user", "content": f"AUFGABENTEXT:\n{task}\n\nMODELLSPEZIFIKATION:\n{spec.model_dump_json(indent=2)}", }, ], 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)) 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)