fix: Index-Platzhalter im Namensvertrag, englische Einheiten, fett gesetzte Teilfragen

- Routing-Prompt: Restriktionsnamen als Muster beschrieben statt kopierbarer Beispiele,
  kein Indexbuchstabe im identifier (Weiter_ab_Woche_k löste in 3/3 Läufen eine
  unnötige Solver-Reparatur aus)
- check_constraint_coverage: identifier …_k passt zu …_2, nur ohne direkten Treffer
  (nonneg_x/nonneg_y bleiben getrennt)
- check_answer_units: englische Einheitenwörter (tons, hours, euros …); vorher strich
  eine Reparatur die richtige Einheit aus einer Antwort auf eine englische Aufgabe
- QUESTION_PATTERN erkennt **Teil 1:** Text

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Dieter Schlüter 2026-09-17 16:50:27 +02:00
commit 915c79c1c8

View file

@ -432,8 +432,18 @@ def check_constraint_coverage(constraints: list, spec: OptimizationModelSpecific
identifiers = [c.identifier for c in spec.constraints]
reported = [c.name for c in constraints]
def direkt(name: str, identifier: str) -> bool:
return name == identifier or name.startswith(identifier + "_") or name.startswith(identifier + "[")
def platzhalter(name: str, identifier: str) -> bool:
# Index-Platzhalter im identifier („Weiter_ab_Woche_k“) gegen den ausgeschriebenen Index („…_2“) –
# beobachtet in allen drei Läufen 20260917-164603, jedes Mal eine unnötige Solver-Reparatur.
# Nur ohne direkten Treffer: „nonneg_x“/„nonneg_y“ sind echte Namen, kein Platzhalter.
basis = re.fullmatch(r"(.+)_[a-z]", identifier)
return bool(basis) and re.fullmatch(re.escape(basis.group(1)) + r"_[\w\[\]]+", name) is not None
def zuordnung(name: str):
treffer = [i for i in identifiers if name == i or name.startswith(i + "_") or name.startswith(i + "[")]
treffer = [i for i in identifiers if direkt(name, i)] or [i for i in identifiers if platzhalter(name, i)]
return max(treffer, key=len) if treffer else None
zugeordnet = {name: zuordnung(name) for name in reported}
@ -884,14 +894,19 @@ def check_answers(run, understanding, task: str = "", data_summary: str = "") ->
# Einheiten nach Größenart: gedeckt ist eine Einheit, wenn der Text irgendeine derselben Art nennt
# ("Tausend Euro" deckt "€", "Stunden" deckt "min"). Prozent fehlt bewusst – es ist keine Einheit, die
# man erfinden kann, sondern eine Schreibweise für Anteile.
# Englische Wörter gehören dazu: beobachtet (lp_klein auf Englisch) galt „1 Tonne“ als erfunden, weil die
# Aufgabe „tons“ schreibt – eine Reparatur strich daraufhin die richtige Einheit aus der Antwort.
UNIT_FAMILIES = {
"Geld": ("€", "EUR", "Euro", "$", "USD", "Dollar", "CHF", "Cent", "ct", "GE", "Geldeinheit", "Geldeinheiten"),
"Menge": ("Stück", "Stk", "ME", "Mengeneinheit", "Mengeneinheiten"),
"Geld": ("€", "EUR", "Euro", "Euros", "euros", "$", "USD", "Dollar", "Dollars", "dollars", "CHF", "Cent", "Cents",
"cents", "ct", "GE", "Geldeinheit", "Geldeinheiten"),
"Menge": ("Stück", "Stk", "ME", "Mengeneinheit", "Mengeneinheiten", "pieces", "pcs"),
"Zeit": ("h", "Std", "Stunde", "Stunden", "min", "Minute", "Minuten", "s", "Sekunden", "Tag", "Tage", "Tagen",
"Woche", "Wochen", "Monat", "Monate", "Monaten", "Jahr", "Jahre", "Jahren"),
"Masse": ("g", "kg", "t", "Tonne", "Tonnen"),
"Länge": ("mm", "cm", "m", "km", "Meter", "Kilometer"),
"Volumen": ("l", "Liter", "hl", "m³"),
"Woche", "Wochen", "Monat", "Monate", "Monaten", "Jahr", "Jahre", "Jahren",
"hour", "hours", "minute", "minutes", "seconds", "day", "days", "week", "weeks", "month", "months",
"year", "years"),
"Masse": ("g", "kg", "t", "Tonne", "Tonnen", "ton", "tons", "tonne", "tonnes", "kilogram", "kilograms"),
"Länge": ("mm", "cm", "m", "km", "Meter", "Kilometer", "meter", "meters", "metre", "metres", "kilometers"),
"Volumen": ("l", "Liter", "hl", "m³", "liter", "liters", "litre", "litres"),
}
_UNIT_OF = {einheit: art for art, einheiten in UNIT_FAMILIES.items() for einheit in einheiten}
_UNIT_ALT = "|".join(sorted(map(re.escape, _UNIT_OF), key=len, reverse=True))