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>
This commit is contained in:
parent
b74445b8d6
commit
7459690bf5
1 changed files with 315 additions and 279 deletions
134
TextToModel.py
134
TextToModel.py
|
|
@ -5,10 +5,10 @@ import sys
|
|||
from pathlib import Path
|
||||
from typing import List
|
||||
|
||||
from llm import DEFAULT_MODEL, LLMSession
|
||||
import data_profile
|
||||
from model_checks import check_spec, check_understanding, has_errors
|
||||
from model_schema import (
|
||||
from checks.model_checks import check_spec, check_understanding, has_errors
|
||||
from checks.review import needs_work, review_assumptions, select_best
|
||||
from core.llm import DEFAULT_MODEL, LLMSession
|
||||
from core.model_schema import (
|
||||
AssumptionReview,
|
||||
SolverStatus,
|
||||
CheckIssue,
|
||||
|
|
@ -17,14 +17,13 @@ from model_schema import (
|
|||
PipelineResult,
|
||||
TaskUnderstanding,
|
||||
)
|
||||
from report import write_report
|
||||
from script_export import build_solver_script
|
||||
from solver_stage import check_feasibility, check_multistart, check_sensitivity, run_solver_code, solve
|
||||
from data import data_profile
|
||||
from modeling.extraction import revise_model, text_to_model
|
||||
from reporting.report import write_report
|
||||
from solving.script_export import build_solver_script
|
||||
from solving.solver_stage import check_feasibility, check_multistart, check_sensitivity, run_solver_code, solve
|
||||
from understanding.understand import understand_task
|
||||
|
||||
PROMPT_DIR = Path(__file__).parent
|
||||
ROUTING_PROMPT_FILE = PROMPT_DIR / "Routing-und-Extraktions-Prompt.txt"
|
||||
REVIEW_PROMPT_FILE = PROMPT_DIR / "Pruef-Prompt.txt"
|
||||
UNDERSTAND_PROMPT_FILE = PROMPT_DIR / "Verstehen-Prompt.txt"
|
||||
MAX_REVISIONS = int(os.environ.get("MAX_REVISIONS", "2"))
|
||||
SOLVER_RETRIES = int(os.environ.get("SOLVER_RETRIES", "2"))
|
||||
MAX_RESTARTS = int(os.environ.get("MAX_RESTARTS", "1"))
|
||||
|
|
@ -48,119 +47,6 @@ def log(msg: str) -> None:
|
|||
print(msg, file=sys.stderr)
|
||||
|
||||
|
||||
def understand_task(session: LLMSession, task: str, issues: List[CheckIssue] = (),
|
||||
data_prompt: str = "") -> TaskUnderstanding:
|
||||
"""Stufe 0: erfassen, was im Text steht – Teilfragen, Zahlen, Machbarkeit. Noch kein Modell."""
|
||||
nachtrag = ""
|
||||
if issues:
|
||||
nachtrag = ("\n\nDEIN VORIGER VERSUCH WAR UNVOLLSTÄNDIG:\n"
|
||||
+ "\n".join(f"- [{i.code}] {i.message}" for i in issues)
|
||||
+ "\nErfasse die fehlenden Punkte, behalte den Rest bei.")
|
||||
return session.create(
|
||||
TaskUnderstanding,
|
||||
[
|
||||
{"role": "system", "content": UNDERSTAND_PROMPT_FILE.read_text(encoding="utf-8")},
|
||||
{"role": "user", "content": f"AUFGABENTEXT:\n{task}\n\n{data_prompt}{nachtrag}".strip()},
|
||||
],
|
||||
step="Verstehen",
|
||||
)
|
||||
|
||||
|
||||
def text_to_model(session: LLMSession, task: str,
|
||||
understanding: TaskUnderstanding | None = None,
|
||||
data_prompt: str = "") -> OptimizationModelSpecification:
|
||||
inhalt = task if not data_prompt else f"{task}\n\n{data_prompt}"
|
||||
if understanding is not None:
|
||||
# Die Verstehensstufe hat Teilfragen und Zahlen schon erfasst – die Extraktion muss den Text
|
||||
# nicht noch einmal danach absuchen, sondern baut darauf auf.
|
||||
inhalt += ("\n\nBEREITS ERFASSTES VERSTÄNDNIS (Teilfragen und Zahlen; übernimm die Werte aus facts "
|
||||
"in extracted_values, statt sie neu zu suchen):\n" + understanding.model_dump_json(indent=2))
|
||||
return session.create(
|
||||
OptimizationModelSpecification,
|
||||
[
|
||||
{"role": "system", "content": ROUTING_PROMPT_FILE.read_text(encoding="utf-8")},
|
||||
{"role": "user", "content": inhalt},
|
||||
],
|
||||
step="Extraktion",
|
||||
)
|
||||
|
||||
|
||||
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",
|
||||
)
|
||||
|
||||
|
||||
def revise_model(
|
||||
session: LLMSession, task: str, spec: OptimizationModelSpecification, review: AssumptionReview,
|
||||
issues: List[CheckIssue] = (),
|
||||
) -> OptimizationModelSpecification:
|
||||
errors = [i for i in issues if i.severity == CheckSeverity.ERROR]
|
||||
check_text = "\n".join(f"- [{i.code}] {i.message}" for i in errors) or "keine"
|
||||
# Warnungen lösen für sich keine Korrekturrunde aus; findet aber ohnehin eine statt,
|
||||
# sollen sie mit behoben werden – sonst erreicht z. B. unerklärter Fachjargon das LLM nie.
|
||||
hinweise = [i for i in issues if i.severity == CheckSeverity.WARNING]
|
||||
hinweis_text = "\n".join(f"- [{i.code}] {i.message}" for i in hinweise) or "keine"
|
||||
return session.create(
|
||||
OptimizationModelSpecification,
|
||||
[
|
||||
{"role": "system", "content": ROUTING_PROMPT_FILE.read_text(encoding="utf-8")},
|
||||
{
|
||||
"role": "user",
|
||||
"content": (
|
||||
f"AUFGABENTEXT:\n{task}\n\n"
|
||||
f"BISHERIGE MODELLSPEZIFIKATION:\n{spec.model_dump_json(indent=2)}\n\n"
|
||||
f"PRÜFBERICHT:\n{review.model_dump_json(indent=2)}\n\n"
|
||||
f"FEHLER DER AUTOMATISCHEN KONSISTENZPRÜFUNG:\n{check_text}\n\n"
|
||||
f"HINWEISE DER AUTOMATISCHEN PRÜFUNG (kein Fehler, aber zu verbessern):\n{hinweis_text}\n\n"
|
||||
"Überarbeite die Modellspezifikation: Setze alle Korrekturen aus dem Prüfbericht um, "
|
||||
"behebe alle Fehler der Konsistenzprüfung, passe die betroffenen Restriktionen, Variablen "
|
||||
"und Parameter an und weise fehlende Annahmen in open_assumptions aus. "
|
||||
"Arbeite auch die Hinweise ab, soweit das Modell dadurch nicht schlechter wird – "
|
||||
"unerklärte Fachbegriffe gehören in executive_summary, nicht weggelassen. "
|
||||
"Behalte korrekte Teile unverändert bei."
|
||||
),
|
||||
},
|
||||
],
|
||||
step="Korrektur",
|
||||
)
|
||||
|
||||
|
||||
# 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)
|
||||
|
||||
|
||||
def _understand(session, task: str, label: str, data_prompt: str = "") -> tuple:
|
||||
"""Verstehensstufe samt Prüfung; ein Nachfassen, wenn Zahlen oder Teilfragen fehlen."""
|
||||
if UNDERSTAND_RETRIES < 0:
|
||||
|
|
|
|||
Loading…
Reference in a new issue