- Python 100%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
Die Solver-Stufe erzeugt langen Python-Code plus Reasoning. Bei komplexen Aufgaben (Portfoliomanagement: 4× je ~26k Reasoning-Tokens am 32k-Limit abgeschnitten, $0,28 für nichts) reichte das globale Limit nicht. DeepSeek V4.1 Flash unterstützt bis 393k Output-Tokens (OpenRouter). Neu: LLM_MAX_TOKENS_SOLVER (Default 65536, 0 = globales Limit gilt auch für den Solver). Wirkt nur für die Stufen 'Solver-Code' und 'Solver-Reparatur'. CLI-Option --max-tokens-solver. 438 Tests grün. |
||
| benchmark | ||
| checks | ||
| core | ||
| data | ||
| docs | ||
| modeling | ||
| reporting | ||
| solving | ||
| tasks | ||
| tests | ||
| understanding | ||
| .gitignore | ||
| BEDIENUNGSANLEITUNG.md | ||
| LICENSE | ||
| pyproject.toml | ||
| README.md | ||
| requirements.txt | ||
| TextToModel.py | ||
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,
│ understanding/ übergangene Zahlen mit Grund; keine Optimierungsaufgabe
│ (z. B. reine Gleichung) ⇒ abgelehnt, Ende
│ ohne LLM geprüft: keine Zahl, keine Teilfrage übersehen
▼
2. Extraktion ──────── LLM: Problemklasse, Solver-Wahl, Indizes, Parameter,
│ modeling/ Variablen, Zielfunktion, Restriktionen (LaTeX), Annahmen
▼
3. Konsistenz-Checks ─ ohne LLM: Solver passt zur Klasse? Einheiten? undeklarierte
│ checks/ Symbole? kamen die erfassten Zahlen im Modell an?
▼
4. LLM-Prüfung ─────── Zielfunktion = gefragte Größe? Annahmen textkonform?
│ ▲ checks/
▼ │ Korrekturschleife (max. 2 Runden, beste Runde gewinnt)
5. Korrektur ┘ modeling/
│
▼
6. Solver ──────────── LLM erzeugt Python-Code, Ausführung in der Sandbox, Reparatur
│ solving/ bei Fehlern; Szenarien = Datenvarianten, ohne LLM nachgerechnet
▼
7. Abnahme ─────────── ohne LLM: jede Teilfrage beantwortet? jede Zahl und Einheit
│ solving/ der Antwort belegt? Lösung zulässig? Eingabedaten ausgewiesen?
│ jedes Szenario mit Ergebnis?
▼
Report (Markdown/HTML) + JSON + eigenständiges Solver-Skript reporting/
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:
bubblewrapfü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
Ausführliche Schritt-für-Schritt-Anleitung – Aufgaben formulieren, Ergebnis lesen, Weiterarbeiten ohne LLM-Kosten, Fehlerbehebung: BEDIENUNGSANLEITUNG.md.
# Aufgabe aus Datei lösen: output/ergebnis.json, .html (Report) und .py (Solver-Programm)
.venv/bin/python TextToModel.py aufgabe.txt --ausgabe output/ergebnis
# alle Optionen mit Defaults und Beispielen
.venv/bin/python TextToModel.py --help
# Aufgabe über stdin
echo "Ein Betrieb ..." | .venv/bin/python TextToModel.py --report output/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 output/ergebnis.json output/modell.md
# exportiertes Solver-Programm erneut ausführen
.venv/bin/python output/solve.py
# Variante rechnen: geänderte Eingabedaten, gleiches Programm, KEIN LLM-Aufruf
.venv/bin/python TextToModel.py --resolve output/ergebnis.json --data neue_daten.json -o output/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 output/modell.html
# nur die Solver-Stufe wiederholen: Modell und Prüfbericht bleiben, spart die Aufrufe davor
.venv/bin/python TextToModel.py --von solver output/ergebnis.json -o output/neu.json
Exit-Codes: 0 Ergebnis zuverlässig, 1 Eingabefehler (Datei, Abbildung, gespeichertes Ergebnis), 2 Ergebnis vorhanden, aber unzuverlässig, 64 falscher Aufruf, 3 abgelehnt – der Text ist keine Optimierungsaufgabe (es wird weder modelliert noch gerechnet; Report und JSON nennen den Grund). Der Ordner output/ ist in .gitignore eingetragen. Ohne Eingabe wird eine eingebaute Beispielaufgabe verwendet. Fortschritt und Kosten erscheinen auf stderr; das Ergebnis-JSON geht mit -o/--ausgabe in eine Datei, sonst auf eine umgeleitete Standardausgabe – im Terminal erscheint stattdessen eine Kurzfassung. Umgebungsvariablen wie SOLVER_TIMEOUT haben gleichnamige Optionen (--solver-timeout), die Vorrang haben.
| 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-flash-latest (Alias, derzeit 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 |
--understand-retries / UNDERSTAND_RETRIES |
Nachfassen, wenn die Verstehensstufe Zahlen oder Teilfragen übersehen hat | 1 |
--solver-retries / SOLVER_RETRIES |
Reparaturversuche für fehlerhaften Solver-Code oder nicht ausgewiesene Eingabedaten | 2 |
--infeasible-recheck / INFEASIBLE_RECHECK |
Gegenprobe mit unabhängiger Modellierung bei „unlösbar“ (0 schaltet ab) | 1 |
--max-tokens / 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 |
OPENROUTER_SWITCH_BELOW |
liefert ein Aufruf (ab 1000 Tokens) weniger Tokens/s, wird der Anbieter für den Rest des Laufs gemieden; 0 schaltet das ab |
wie OPENROUTER_MIN_THROUGHPUT (75) |
--llm-timeout / LLM_TIMEOUT |
Timeout je LLM-Aufruf (s) | 420 |
--reasoning / LLM_REASONING_EFFORT |
Denkaufwand des Modells über OpenRouter: low, medium oder high |
Modellvorgabe |
--solver-timeout / SOLVER_TIMEOUT |
Zeitlimit je Programmlauf in Sekunden; zugleich das Zeitbudget, das der Solver-Prompt dem Programm nennt | 60 |
--sandbox / SOLVER_SANDBOX |
Isolierung des erzeugten Codes: auto (bubblewrap, wenn vorhanden), bwrap (Pflicht), aus |
auto |
LLM_MODEL_UNDERSTANDING, 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:
#!/usr/bin/env python3- 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
- kommentierter Python-Code: Imports, alle Eingabezahlen im Wörterbuch
DATA(austauschbar, siehe--resolve), Modellfunktion(en),main() 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 und erklärt, bevor er Tabellen zeigt: Antwortsatz → Antwort je Teilfrage → Status und Kennzahlen → Warnkästen → „Wie das Modell die Aufgabe sieht“ (Erklärung in Alltagssprache) → Graphenzeichnung (bei Wegeproblemen) → Ergebnistabellen samt Diagramm (Balken je Objekt, Wasserfall für die Zielwert-Zerlegung) und bindende Restriktionen → verglichene Konfigurationen → Sensitivität → Stabilität über Startpunkte → Rohwerte nach Variablen gruppiert. Danach folgen beigelegte Daten, „Was in der Aufgabe steht“ (Teilfragen und Zahlen), 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 output/solve.py
cp kurse.csv output/ && (cd output && ../.venv/bin/python solve.py)
Wie verlässlich ist das?
Das LLM kann Fehler machen – deshalb gibt es mehrere Sicherungen:
- Nur echte Aufgaben: Verlangt der Text keine Entscheidung – etwa eine Gleichung, die nur ausgerechnet werden soll –, wird er abgelehnt, statt ein Optimierungsmodell zu erfinden.
- 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
infeasiblegemeldet, 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.
- Abbildungen werden gelesen und gegengeprüft: Enthält die Aufgabe Bilder (docling-Markdown, eingebettet oder als Datei), überträgt die Verstehensstufe sie in Text; eine zweite, unabhängige Lesung wird Zahl für Zahl verglichen. Alle weiteren Stufen rechnen mit der Übertragung, der Report zeigt Original und Übertragung nebeneinander.
- Selbst erzeugte Daten sind gekennzeichnet: Erzeugt das Programm Eingabedaten (Zufallszahlen, synthetischer Markt, verkleinertes Universum), muss das in
data_assumptionsstehen, 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. Hat ein Szenario kein Ergebnis und ist nicht als unlösbar belegt (beobachtet: Zeitlimit abgelaufen, gemeldet als „keine Lösung“), folgt eine Reparatur und notfalls die Markierung „unzuverlässig“.
- Keine erfundenen Einheiten: Eine Einheit hinter einer Zahl im Antworttext muss im Aufgabentext oder in beigelegten Daten vorkommen (nach Größenart: „Tausend Euro“ deckt „€“); neutrale Geld- und Mengeneinheiten (GE, ME) nur, wenn der Text von Kosten bzw. Mengen spricht. Sonst folgt eine Reparatur.
- Keine erfundene Bedeutung (Prompt-Regel, nur teilweise wirksam): Nennt der Text keine Bedeutung – etwa bei einem reinen Formelsatz –, soll das Modell mathematisch erklären statt ein Produktionsproblem zu erfinden. Gemessen wirkt das nur halb: Bedeutungswörter („Kosten“, „Ressourcen“) tauchen weiter auf; automatisch geprüft werden nur die Einheiten.
- 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). Dazu kommen 4 bewusst schlecht gestellte Fälle „ehrlich scheitern“ (ehrlich_*: Nicht-Optimierungsaufgaben, fehlende Daten); bewertet wird dort, ob die Pipeline ablehnt, Annahmen ausweist oder das Ergebnis als unzuverlässig kennzeichnet. 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. Ergebnisse landen als Tabelle auf stdout und im Detail in benchmark/results/<Zeitstempel>.json; jede Zeile hält Commit und eine Kennung der Prompts fest, damit Messungen vergleichbar bleiben.
.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) |
Erweitern
Einige Stellen müssen gemeinsam geändert werden – Vollständigkeitstests schlagen fehl, wenn etwas fehlt:
- Neuer Solver oder Problemtyp: Enum in
core/model_schema.py, Routing-Regel inmodeling/Routing-und-Extraktions-Prompt.txt,ALLOWED_STACKSinchecks/model_checks.py, API-Abschnitt insolving/Solver-Prompt.txt(API vorher real testen und intests/test_solver_stage.pyabsichern),ALLOWED_MODULESinsolving/solver_stage.py,ALGORITHMS/CLASS_FITinsolving/script_export.py,requirements.txt. - Neues Prüfurteil oder Prüffeld:
core/model_schema.py,checks/Pruef-Prompt.txt,tests/test_review.py. - Feldbeschreibungen in
core/model_schema.pygehen als Anweisung an das LLM – eine Änderung dort wirkt wie eine Prompt-Änderung. - Prüfkennungen: Endet eine Prompt-Regel mit
[→ NAME], muss es einen Check-Code oder eine Prüffunktion dieses Namens geben (Testtest_every_checker_marker_in_the_prompts_exists). - Keine Datei
instructor.pyanlegen – sie überschattet das gleichnamige Paket.
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.