Das Programm erzeugt aus einem Optimierungstext in natürlicher Sprache ein gelöstes mathematisches Optimierungsmodell mit dazu passendem Python-Programm.
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
dschlueter 7459690bf5 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
benchmark refactor: Pipeline-Stufen in Ordner mit ihren Prompts 2026-09-17 12:11:51 +02:00
checks refactor: Pipeline-Stufen in Ordner mit ihren Prompts 2026-09-17 12:11:51 +02:00
core refactor: Pipeline-Stufen in Ordner mit ihren Prompts 2026-09-17 12:11:51 +02:00
data refactor: Pipeline-Stufen in Ordner mit ihren Prompts 2026-09-17 12:11:51 +02:00
modeling refactor: Pipeline-Stufen in Ordner mit ihren Prompts 2026-09-17 12:11:51 +02:00
reporting refactor: Pipeline-Stufen in Ordner mit ihren Prompts 2026-09-17 12:11:51 +02:00
solving refactor: Pipeline-Stufen in Ordner mit ihren Prompts 2026-09-17 12:11:51 +02:00
tasks refactor: Pipeline-Stufen in Ordner mit ihren Prompts 2026-09-17 12:11:51 +02:00
tests refactor: Pipeline-Stufen in Ordner mit ihren Prompts 2026-09-17 12:11:51 +02:00
understanding refactor: Pipeline-Stufen in Ordner mit ihren Prompts 2026-09-17 12:11:51 +02:00
.gitignore Erste öffentliche Fassung: Math Model Generator & Solver 2026-09-17 11:42:58 +02:00
LICENSE Erste öffentliche Fassung: Math Model Generator & Solver 2026-09-17 11:42:58 +02:00
pyproject.toml refactor: Pipeline-Stufen in Ordner mit ihren Prompts 2026-09-17 12:11:51 +02:00
README.md refactor: Pipeline-Stufen in Ordner mit ihren Prompts 2026-09-17 12:11:51 +02:00
requirements.txt Erste öffentliche Fassung: Math Model Generator & Solver 2026-09-17 11:42:58 +02:00
TextToModel.py refactor: Pipeline-Stufen in Ordner mit ihren Prompts 2026-09-17 12:11:51 +02:00

Math Model Generator & Solver

Übersetzt eine natürlichsprachige Optimierungsaufgabe (Deutsch) mit einem LLM in ein formales mathematisches Modell, prüft es, korrigiert Fehler und löst es tatsächlich mit einem passenden Python-Solver. Ergebnis ist ein nachvollziehbarer Report mit Formeln, Prüfbericht, Lösung und dem erzeugten Python-Code.

Aufgabentext  (+ optional beigelegte Daten: --daten kurse.csv)
   │                   ohne LLM: Profil je Datei – Zeilen, Spalten, Spannweiten, Lücken
   ▼
1. Verstehen ───────── LLM: Teilfragen a)/b)/…, alle Zahlen des Textes mit Fundstelle,
   │                   übergangene Zahlen mit Grund; ist es überhaupt eine Aufgabe?
   │                   ohne LLM geprüft: keine Zahl, keine Teilfrage übersehen
   ▼
2. Extraktion ──────── LLM: Problemklasse, Solver-Wahl, Indizes, Parameter,
   │                   Variablen, Zielfunktion, Restriktionen (LaTeX), Annahmen
   ▼
3. Konsistenz-Checks ─ ohne LLM: Solver passt zur Klasse? Einheiten? undeklarierte
   │                   Symbole? kamen die erfassten Zahlen im Modell an?
   ▼
4. LLM-Prüfung ─────── Zielfunktion = gefragte Größe? Annahmen textkonform?
   │        ▲
   ▼        │ Korrekturschleife (max. 2 Runden, beste Runde gewinnt)
5. Korrektur ┘
   │
   ▼
6. Solver ──────────── LLM erzeugt Python-Code, Ausführung in der Sandbox, Reparatur
   │                   bei Fehlern; Szenarien = Datenvarianten, ohne LLM nachgerechnet
   ▼
7. Abnahme ─────────── ohne LLM: jede Teilfrage beantwortet? jede Zahl der Antwort
   │                   belegt? Lösung zulässig? Tabellensummen richtig?
   ▼
Report (Markdown/HTML) + JSON + eigenständiges Solver-Skript

Unterstützte Problemklassen

Klasse Solver Typische Aufgaben
Lineare Programmierung (LP) SciPy linprog Mischungs-, einfache Produktionsprobleme
Gemischt-ganzzahlig (MILP) HiGHS (highspy) Zuordnung, Rucksack, Standortwahl, Rüst-/Fixkosten
Constraint Programming (CP) Google OR-Tools CP-SAT Schicht- und Personalplanung, logische Bedingungen
Quadratisch / Kegel (QP, SOCP) CVXPY Portfoliooptimierung (Varianz, Volatilität) mit kontinuierlichen Anteilen
Gemischt-ganzzahlig quadratisch (MIQP) SCIP (pyscipopt) Portfolio mit Kardinalität (mind./höchstens k Titel), Mindestgewicht nur für gewählte Titel, fixe Transaktionskosten
Nichtlinear, stetig (NLP) Ipopt (cyipopt) nichtlineare Ausbeute- oder Kostenfunktionen, Kurvenanpassung; mehrere Startpunkte, Streuung wird berichtet
Kürzeste Wege (Graph) NetworkX (Dijkstra) Wegewahl im Netz, Ersatz- und Erneuerungsprobleme; der Report zeichnet den Graphen und zeigt den Markierungsverlauf

Installation

Voraussetzungen: Python ≥ 3.10, Linux. Systempakete:

  • bubblewrap für die Sandbox des erzeugten Codes (Debian/Ubuntu: sudo apt install bubblewrap, siehe Sicherheitshinweis)
  • die Ipopt-Bibliothek für cyipopt (Debian/Ubuntu: sudo apt install coinor-libipopt-dev pkg-config), sofern pip kein fertiges Paket findet
python -m venv .venv
.venv/bin/pip install -r requirements.txt

API-Key als Umgebungsvariable setzen (nicht in Dateien speichern):

Anbieter Variable Modellnamen
OpenRouter (Standard) OPENROUTER_API_KEY mit /, z. B. deepseek/deepseek-v4.1-flash
Google Gemini GEMINI_API_KEY ohne /, z. B. gemini-2.5-flash

Der Anbieter wird am Modellnamen erkannt. Das kostenlose Gemini-Kontingent reicht nur für wenige Läufe pro Tag.

Benutzung

# Aufgabe aus Datei lösen, Report und Solver-Code speichern
.venv/bin/python TextToModel.py aufgabe.txt --report ausgabe/modell.html --code ausgabe/solve.py > ausgabe/ergebnis.json

# Aufgabe über stdin
echo "Ein Betrieb ..." | .venv/bin/python TextToModel.py --report ausgabe/modell.md

# nur modellieren und prüfen, nicht lösen; anderes Modell
.venv/bin/python TextToModel.py aufgabe.txt --no-solve --model deepseek/deepseek-v4-pro-0813

# Report nachträglich aus gespeichertem Ergebnis erzeugen (keine LLM-Kosten)
.venv/bin/python -m reporting.report ausgabe/ergebnis.json ausgabe/modell.md

# exportiertes Solver-Programm erneut ausführen
.venv/bin/python ausgabe/solve.py

# Variante rechnen: geänderte Eingabedaten, gleiches Programm, KEIN LLM-Aufruf
.venv/bin/python TextToModel.py --resolve ausgabe/ergebnis.json --data neue_daten.json > ausgabe/variante.json

# Aufgabe mit beigelegten Daten (CSV/Excel); das Profil geht in die Prompts, die Datei ins Programm
.venv/bin/python TextToModel.py aufgabe.md --daten kurse.csv --report ausgabe/modell.html

# nur die Solver-Stufe wiederholen: Modell und Prüfbericht bleiben, spart die Aufrufe davor
.venv/bin/python TextToModel.py --von solver ausgabe/ergebnis.json > ausgabe/neu.json

Der Ordner ausgabe/ ist in .gitignore eingetragen. Ohne Eingabe wird eine eingebaute Beispielaufgabe verwendet. Fortschritt und Kosten erscheinen auf stderr, das Ergebnis-JSON auf stdout.

Option / Variable Bedeutung Standard
--report DATEI Report als .md (z. B. VS Code-Vorschau: Strg+Umschalt+V) oder .html (Browser, Formeln via MathJax) –
--code DATEI Solver-Programm als eigenständiges, dokumentiertes Python-Skript speichern –
--model / LLM_MODEL LLM-Modell deepseek/deepseek-v4.1-flash
--daten DATEI … beigelegte CSV-/Excel-Dateien; ihr Profil (Zeilen, Spalten, Spannweiten, Lücken) geht in die Prompts, die Dateien ins Arbeitsverzeichnis des Programms –
--von STUFE ERGEBNIS.JSON gespeichertes Ergebnis fortsetzen statt alles zu wiederholen: solver (nur Solver-Code neu) oder pruefung (ab Prüfung/Korrektur) –
--max-revisions / MAX_REVISIONS Korrekturrunden 2
--no-solve keinen Solver-Code erzeugen aus
SOLVER_RETRIES Reparaturversuche für fehlerhaften Solver-Code 2
INFEASIBLE_RECHECK Gegenprobe mit unabhängiger Modellierung bei „unlösbar“ (0 schaltet ab) 1
LLM_MAX_TOKENS Obergrenze je Antwort (OpenRouter) 32000
--max-restarts / MAX_RESTARTS komplette Neustarts, wenn das Modell nach allen Korrekturrunden fehlerhaft ist 1
OPENROUTER_MIN_THROUGHPUT bevorzugte Mindestgeschwindigkeit des OpenRouter-Anbieters (Tokens/s); Präferenz, kein harter Filter 75
LLM_TIMEOUT Timeout je LLM-Aufruf (s) 420
SOLVER_SANDBOX Isolierung des erzeugten Codes: auto (bubblewrap, wenn vorhanden), bwrap (Pflicht), aus auto
LLM_MODEL_EXTRACTION, LLM_MODEL_REVIEW, LLM_MODEL_REVISION, LLM_MODEL_SOLVER Modell einzelner Pipeline-Stufen, z. B. ein schnelles für den Solver-Code; verschiedene Anbieter sind erlaubt Modell aus --model

Solver-Programme

Das erzeugte Programm (--code sowie im HTML-/Markdown-Report) ist separat verwendbar und aufgebaut als:

  1. #!/usr/bin/env python3
  2. Docstring mit Aufgabentext, mathematischem Modell (Indizes, Parameter, Variablen, Zielfunktion, Restriktionen – mit Erläuterungen und Annahmen) und dem Algorithmus samt Begründung, warum er hier passt
  3. kommentierter Python-Code: Imports, Eingabedaten als Konstanten, Modellfunktion(en), main()
  4. if __name__ == "__main__": main()

Die Ausgabe ist lesbar – Status, Zielwert, verständliche Ergebnistabellen (z. B. je Aktie: Anteil, Rendite, Kosten, Beitrag zum Ziel, Summenzeile) und die Variablenwerte – und endet mit einer maschinenlesbaren RESULT_JSON-Zeile. Die Tabellen berechnet das Programm aus der tatsächlichen Lösung; die Pipeline prüft automatisch, dass die Beiträge sich zum Zielwert summieren, und warnt sonst. Der Report beginnt mit dem Ergebnis: ein Antwortsatz, die Kennzahlen, die Ergebnistabellen samt Diagramm (Balken je Objekt, Wasserfall für die Zielwert-Zerlegung aus Ertrag, Risiko und Kosten) und eine Tabelle der bindenden Restriktionen. Darunter folgen die Rohwerte nach Variablen gruppiert, danach Aufgabe, Modell, Prüfbericht und Programm. Die Diagramme sind reines SVG ohne externe Bibliothek und funktionieren offline, hell wie dunkel. Im HTML-Report lässt sich das Programm mit einem Klick auf das Kopier-Symbol in die Zwischenablage übernehmen. Das JSON-Ergebnis enthält weiterhin nur den rohen generierten Code.

Mit Datenanhängen (--daten): Das erzeugte Programm liest die Dateien über ihren Dateinamen aus dem Arbeitsverzeichnis. Ein mit --code exportiertes Programm braucht die Datendateien deshalb im selben Verzeichnis – sonst endet es mit FileNotFoundError. Kopiere sie mit:

.venv/bin/python TextToModel.py aufgabe.md --daten kurse.csv --code ausgabe/solve.py
cp kurse.csv ausgabe/ && (cd ausgabe && ../.venv/bin/python solve.py)

Wie verlässlich ist das?

Das LLM kann Fehler machen – deshalb gibt es mehrere Sicherungen:

  • Zielgrößen-Prüfung: erkennt z. B., wenn die Varianz statt der gefragten Volatilität minimiert wird.
  • Annahmen-Prüfung: Unklarheiten im Text müssen als Annahme ausgewiesen werden und werden gegen den Text geprüft (z. B. „wenn A, dann B“ ist keine Äquivalenz).
  • Zulässigkeitsprüfung ohne LLM: Die gefundene Lösung wird in alle Restriktionen eingesetzt, Schranken und Ganzzahligkeit geprüft und abgeglichen, ob jede Restriktion des Modells im Programm vorkommt. Verletzungen lösen eine Reparatur aus – Solver-Toleranzen (beobachtet: 8 Cent über dem Budget) fallen damit auf.
  • Unlösbare Aufgaben werden als infeasible gemeldet, nicht durch Weglassen von Bedingungen „passend gemacht“ – aber auch nicht ungeprüft geglaubt: Eine zweite, unabhängige Modellierung bestätigt oder widerlegt den Status.
  • Beste Korrekturrunde: Verschlechtert eine Korrektur das Modell, wird die frühere Fassung verwendet.
  • Sensitivität: Für bindende Restriktionen rechnet das Programm mit verschobener rechter Seite neu; der Report zeigt, was eine Lockerung je Einheit bringt, und bei ganzzahligen Modellen den Preis der Ganzzahligkeit. Unplausible Richtungen werden gemeldet.
  • Neustart bei Fremdproblem: Gelegentlich modelliert das LLM ein anderes als das gestellte Problem. Passt die Zielgröße auch nach allen Korrekturrunden nicht, wird die Modellierung komplett neu gestartet.
  • Jede Teilfrage beantwortet: Der Aufgabentext wird vor dem Modellieren in seine Teilfragen zerlegt (a, b, …); das Programm liefert je Teilfrage einen Antwortsatz. Fehlt eine Antwort, gilt die Aufgabe als nicht erfüllt – auch wenn der Zielwert stimmt.
  • Jede Zahl der Antwort belegt: Zahlen im Antworttext müssen aus dem Ergebnis oder der Aufgabe stammen (Rundungen und Einheitenumrechnungen werden akzeptiert). Ein Vergleichswert, der in keiner Tabelle steht, fällt auf.
  • Jede Zahl der Aufgabe erfasst: Zahlen des Aufgabentexts müssen als Fakt mit Fundstelle oder mit Begründung als irrelevant ausgewiesen sein – sonst betreten sie die Pipeline erst im Solver-Code, hinter allen Prüfungen.
  • Selbst erzeugte Daten sind gekennzeichnet: Erzeugt das Programm Eingabedaten (Zufallszahlen, synthetischer Markt, verkleinertes Universum), muss das in data_assumptions stehen, nicht nur im Fließtext – sonst folgt eine Reparatur und notfalls die Markierung „unzuverlässig“.
  • Szenarien nachgerechnet: Vergleicht die Aufgabe mehrere Konfigurationen, wird jede durch erneutes Ausführen des Programms bestätigt – ohne LLM.
  • Sandbox: Der erzeugte Code läuft ohne Netzzugang, mit nur lesbarem Dateisystem und beschreibbar allein dem Arbeitsverzeichnis des Laufs.
  • Kein falsches „optimal“: Bleibt das Modell fehlerhaft, wird das Ergebnis als unzuverlässig markiert – bei falscher Zielgröße ohne Solver-Lauf, mit Warnkasten im Report und Exit-Code 2.

Trotzdem gilt: Ergebnisse vor Verwendung prüfen – der Report zeigt dafür Modell, Annahmen, Prüfbericht und Code.

Benchmark

benchmark/cases/ enthält 19 Aufgaben mit unabhängig verifiziertem Optimum (Brute Force bzw. zweiter Solver) – von kleinen LPs bis zu Aufgaben mit eingebauten Fallen (Rüstkosten/Mindestlos, Wenn-dann-Regel als Implikation, Volatilität statt Varianz, nichtkonvexes NLP mit globalem Optimum, beweisbar unlösbare Mischung, Losgrößen als kürzester Weg). Gemessen wird der Zielwert und, strenger, ob die Aufgabe erfüllt ist: jede Teilfrage beantwortet, jede Zahl der Antwort belegt. Dateien im Fallverzeichnis außer task.txt/expected.json werden als Datenanhang übergeben.

.venv/bin/python -m benchmark.benchmark --models deepseek/deepseek-v4.1-flash deepseek/deepseek-v4-flash-0731 --runs 2 --jobs 3
.venv/bin/python -m benchmark.benchmark --cases hard_schichtplan_woche --runs 3

Letzter Stand (September 2026, DeepSeek V4.1-Flash über OpenRouter, je 2 Läufe): 38 von 38 Läufen über alle 19 Fälle mit richtigem Zielwert und erfüllter Aufgabe, etwa 0,05–0,15 $ und 3–10 Minuten je Lauf. Die Fallmenge ist damit gesättigt; die Grenzen der Pipeline zeigen sich eher an schlecht gestellten Aufgaben (fehlende Daten, mehrdeutige Angaben) – Beispiele dafür liegen in tasks/. Hinweis: Drei Fälle wurden nach dieser Messung sprachlich neu gefasst (gleiche Zahlen und Optima).

Tests

.venv/bin/python -m pytest -q                               # offline, ohne API-Kosten
.venv/bin/python -m pytest -q tests/test_solver_stage.py -k cvxpy # einzelner Test

Projektstruktur

Im Wurzelverzeichnis liegt nur das Hauptprogramm; jede Pipeline-Stufe liegt mit ihrem Code und ihrem Prompt in einem eigenen Ordner.

Ordner / Datei Inhalt
TextToModel.py CLI und Ablauf der Pipeline (run_pipeline, Neustart, Gegenprobe, Fortsetzen, Neurechnen)
core/ model_schema.py (Pydantic-Schemas aller Zwischenergebnisse), llm.py (LLM-Zugriff über OpenRouter/Gemini, Wiederholungen, Kostenerfassung)
understanding/ Stufe Verstehen: Teilfragen und Zahlen des Aufgabentexts erfassen (understand.py, Verstehen-Prompt.txt)
modeling/ Stufen Extraktion und Korrektur: Modellspezifikation erzeugen und überarbeiten (extraction.py, Routing-und-Extraktions-Prompt.txt)
checks/ Prüfungen: deterministische Konsistenz-Checks (model_checks.py) und LLM-Prüfung von Zielgröße und Annahmen (review.py, Pruef-Prompt.txt)
solving/ Solver-Code erzeugen, prüfen, in der Sandbox ausführen (solver_stage.py, Solver-Prompt.txt); eigenständiges Solver-Programm (script_export.py)
reporting/ Report als Markdown/HTML, auch als CLI (report.py); Diagramme und Graphenzeichnung als Inline-SVG (charts.py)
data/ Profil beigelegter CSV-/Excel-Dateien, ohne LLM (data_profile.py)
benchmark/ Benchmark (benchmark.py) und Referenzaufgaben (cases/)
tasks/ weitere Aufgaben zum Ausprobieren (u. a. mit fehlenden Daten)
tests/ Offline-Tests (pytest, Konfiguration in pyproject.toml)

Sicherheitshinweis

Der Solver-Code wird vom LLM erzeugt. Er läuft in einer bubblewrap-Sandbox: kein Netzzugang, das Dateisystem nur lesbar, beschreibbar allein das Temp-Verzeichnis des jeweiligen Laufs, HOME zeigt ebenfalls dorthin. Davor greifen weiterhin eine Import-Whitelist (nur Mathe- und Solver-Bibliotheken), das Verbot von open/eval/exec und ein Timeout.

Voraussetzung ist ein installiertes bubblewrap (Debian/Ubuntu: sudo apt install bubblewrap). Fehlt es, läuft der Code ungeschützt mit deinen Benutzerrechten – dann nur eigene, vertrauenswürdige Aufgabentexte verwenden. Mit SOLVER_SANDBOX=bwrap wird die Sandbox zur Pflicht: ohne bubblewrap wird der Lauf abgelehnt statt ungeschützt ausgeführt.

.venv/bin/python -m pytest -q -k sandbox   # prüft, dass Schreiben außerhalb des Laufs scheitert

Lizenz

MIT – siehe LICENSE.