Ich habe im Juli einen Artikel über Determinismus bei lokalen Modellen veröffentlicht. Darin steht der Satz: „Alle 198 Ausgaben liegen vollständig vor, damit man mir widersprechen kann.“
Das stimmt nicht mehr. Der zweite Rechner aus jenem Vergleich wurde Anfang August für eine Neuinstallation plattgemacht. Die Ausgaben lagen dort. Es gab nie einen Download-Link, weil ich davon ausging, dass „liegt bei mir“ reicht.
Ein Benchmark, dessen Belege niemand nachprüfen kann, ist am Ende eine Behauptung mit Tabelle daneben. Mir ist das erst aufgefallen, als ich die Dateien für einen neuen Vergleich heraussuchen wollte und feststellte, dass es sie nicht mehr gibt.
Der zweite Fund war schlimmer
Beim Aufräumen fiel mir auf, dass mein aktueller Prüfstand dasselbe tut. Er lässt Modelle eine interaktive Simulation als einzelne HTML-Datei bauen, prüft das Ergebnis maschinell — Syntax, Animationsschleife, ob bestimmte physikalische Größen im Code vorkommen — schreibt eine Zeile Messwerte in ein Protokoll, und wirft die erzeugte Datei weg.
Was dabei herauskam, sah nach einem klaren Ergebnis aus: ein Modell bestand 5 von 5 Durchläufen, ein anderes nur 2 von 5. Beim Nachrechnen an den Rohdaten stellte sich heraus: das schwächere Modell hatte in 10 von 10 Durchläufen syntaktisch gültigen, animierten Code geliefert. Der gesamte Unterschied hing an einer Suche nach Wörtern im Quelltext. Wer die Auftriebskraft F_b nennt statt buoyancy, fällt durch.
Ob genau das passiert ist, kann ich nicht mehr feststellen. Die Dateien sind weg.
Ein Bewertungssystem, das das bewertete Ding wegwirft, macht sein Urteil unwiderlegbar — und damit wertlos.
Der Deckel, der wie Unfähigkeit aussieht
Es gab noch einen dritten Fund, und der hat mich am meisten geärgert, weil er so einfach zu vermeiden gewesen wäre. Der Prüfstand gibt jedem Modell ein Token-Budget vor. Ich hatte 8192 gesetzt, was großzügig wirkte. Aus diesem Budget wird aber nicht nur die Antwort bezahlt, sondern auch das Nachdenken davor — bei Modellen, die vor dem Schreiben erst überlegen, ist das der größere Posten.
Ein Modell im Testfeld denkt besonders ausführlich. In einem Durchlauf kam es auf knapp 21.000 Zeichen Gedankengang, bevor überhaupt die erste Zeile Code entstand. In drei von fünf Durchläufen lief es exakt ins Limit: die Datei brach mitten im Satz ab, war dadurch kein gültiges JavaScript mehr, und meine Auswertung notierte pflichtschuldig null Merkmale, ungültige Syntax, durchgefallen.
Das Modell landete in der Tabelle bei 2 von 5 und sah aus wie der schwächste Teilnehmer. Als ich die Durchläufe nachrechnete, in denen es zu Ende schreiben durfte, standen dort 2 von 2 bestanden, beide mit voller Merkmalsabdeckung — die höchste im ganzen Feld, höher als das Modell, das die Tabelle angeführt hatte.
Wer am gründlichsten nachdenkt, verliert. Das ist die genaue Umkehrung dessen, was der Test messen sollte. Und es steht nirgends im Ergebnis: „durchgefallen“ und „abgeschnitten“ sahen in meinem Protokoll identisch aus. Seitdem gilt: ein Lauf, der ins Token-Limit rennt, ist ungültig und fliegt aus dem Mittelwert. Er ist kein schlechtes Ergebnis, er ist gar keins.
Was ich stattdessen gebaut habe
Drei Anforderungen, in dieser Reihenfolge:
- Jedes Artefakt wird aufgehoben — die erzeugte Datei, die vollständige Antwort des Modells, die Denkspur, der exakte Prompt, die Einzelnoten der Bewertung und eine Prüfsumme.
- Es muss im Browser laufen. Eine interaktive Simulation als Screenshot zu zeigen, ist eine Bankrotterklärung. Man muss sie anklicken können.
- Der Link muss stabil bleiben. Inhaltsadressierte Ablage: gleiche Datei, gleicher Schlüssel.
Punkt zwei ist der unangenehme. Denn er heißt: ungeprüfter, maschinell erzeugter JavaScript-Code soll im Browser meiner Leser ausgeführt werden.
Wörter zählen ist nicht Physik prüfen
Die Merkmalsprüfung suchte im Quelltext nach Begriffen: kommt „buoyancy“ vor, kommt „drag“ vor, kommt „groundwater“ vor. Damit ein Modell die Liste nicht einfach als Kommentar abschreiben kann, entferne ich vorher alle Kommentare und Zeichenketten. Das war gut gemeint und hat einen Nebeneffekt, den ich zu spät gesehen habe: übrig bleibt der reine Code, und geprüft wird damit das Vokabular der Variablennamen.
Bei einem Modell fehlte der Begriff „buoyancy“ in neun von zehn Durchläufen. Neun von zehn. Wenn eine Fähigkeit wirklich fehlt, schwankt das; ein Ausfall von neun zu zehn auf genau einem Wort sieht nicht nach Unvermögen aus, sondern danach, dass jemand die Auftriebskraft konsequent anders nennt. F_b zum Beispiel, wie es in der Physik üblich ist. Oder lift.
Ob das der Grund war, kann ich nicht sagen. Die Dateien waren zu dem Zeitpunkt schon weg. Genau an dieser Stelle wurde aus einem Ärgernis eine Konsequenz: eine Bewertung, deren Gegenstand man nicht mehr ansehen kann, lässt sich weder widerlegen noch verteidigen.
Die Prüfung selbst habe ich trotzdem behalten, nur anders gewichtet. Ein Begriff zählt jetzt zwei Punkte, wenn er in einer Zeile steht, in der auch gerechnet wird — eine Zuweisung, eine Multiplikation, ein Aufruf einer mathematischen Funktion. Steht er nur da, gibt es einen Punkt. Das trennt „hat den Auftrieb implementiert“ von „hat eine Variable so genannt“, ohne auf ein bestimmtes Wort zu bestehen.
Warum das keine Kleinigkeit ist
Niemand liest zehn generierte Dateien durch. Bei hundert erst recht nicht. Es wäre auch am Ziel vorbei: Ich will ja gerade zeigen, was herauskam, und nicht die drei hübschesten aussuchen.
Läge dieser Code auf derselben Herkunft wie das Blog, könnte ein generiertes Skript die Sitzungsdaten der Besucher lesen. Nicht weil ein Modell bösartig wäre, sondern weil niemand hinsieht. Drei Schichten, alle nötig:
- Eigene Subdomain, eigener Vhost, serverseitige Skriptsprachen abgeschaltet.
- Eine Inhaltsrichtlinie mit
connect-src 'none'. Das ist die entscheidende Zeile: selbst ein generiertesfetch()kommt nicht nach draußen.script-src 'unsafe-inline'lässt sich nicht vermeiden, weil die Artefakte selbst Inline-Skripte sind. Deshalb die eigene Subdomain. - Im Artikel ein Rahmen mit
sandbox="allow-scripts"— ohneallow-same-origin. Diese Kombination gibt dem Rahmen eine undurchsichtige Herkunft: das Skript läuft und animiert, erreicht aber weder Cookies noch Speicher noch die Elternseite. Beide Angaben zusammen heben den Schutz auf. Das ist die Falle, in die man einmal tappt.
Drei Dinge, die schiefgingen
Alle drei sahen nach Erfolg aus, bis ich nachgemessen habe.
Der Objektspeicher startete nicht, meldete aber keinen Fehler. Das Containerabbild reicht Konfigurationsdateien anders durch als erwartet; die Zugangsdaten waren für den Hauptprozess nicht lesbar. Der Dienst starb mit einem fatal, der Container startete neu, viermal. Kurios: ein Testzugriff in den Container konnte die Datei problemlos lesen. Wer nur das prüft, hält alles für in Ordnung.
Die Schutzrichtlinie fehlte ausgerechnet dort, wo sie zählt. Der Webserver setzte sie im äußeren Block korrekt. Für die Artefakte selbst hatte ich einen eigenen Block, der nur die Zwischenspeicherung regelte. add_header ist aber nicht additiv: ein solcher Eintrag im inneren Block ersetzt alle geerbten. Die Konfiguration las sich einwandfrei. Die Antwort auf die Artefakt-Datei enthielt keine Richtlinie. Aufgefallen ist es nur, weil ich die Kopfzeilen einer echten Datei abgerufen habe statt die Konfiguration zu lesen.
Der Speicher hatte eine unsichtbare Obergrenze. Die Voreinstellung erlaubt acht Ablageeinheiten. Das reicht, bis es nicht mehr reicht — und dann bricht jeder Schreibvorgang ab. Denselben Fehler hatte ich zwei Tage zuvor an anderer Stelle gesehen. Er steht jetzt als Kommentar in der Konfigurationsdatei.
Die Regel, die ich daraus mitnehme
Ein bestandener Test sagt wenig, solange daneben kein Gegentest steht, von dem man weiß, dass er durchfallen muss. Ohne den zweiten prüft man nur, ob überhaupt etwas passiert.
Deshalb hat der Prüfstand jetzt zwei handgeschriebene Kontrolldateien. Eine muss die volle Punktzahl bekommen. Die andere enthält alle gesuchten Fachbegriffe — aber nur im Kommentar — und muss durchfallen. Laufen beide vor jeder Messung durch, ist bewiesen, dass die Bewertung Code liest und nicht Kommentare.
Beim ersten Lauf ergab das 16 von 20 Punkten für die gute Datei und 1 von 20 für die schlechte. Erst danach durfte ein Modell antreten.
Was das kostet
Der Speicher ist kleiner, als man denkt. Ein Artefakt liegt im Median bei 15 Kilobyte, das größte im bisherigen Feld bei 40. Ein kompletter Lauf über dreißig Ziehungen kommt auf rund eine halbe Megabyte. Tausend solcher Läufe wären knapp 500 Megabyte — für ein Archiv, das jeden veröffentlichten Vergleich belegt, ist das nichts.
Ich hatte zwischendurch überlegt, ob das einen eigenen verteilten Objektspeicher rechtfertigt, und die Antwort war zuerst ein klares Nein. Bei der Menge wäre es Überbau. Dass am Ende doch einer läuft, hat einen anderen Grund: den Zugriffsvertrag. Wenn mehrere Rechner Artefakte schreiben sollen, ist ein Endpunkt mit Zugangsdaten sauberer als ein Netzlaufwerk plus Kopierschritt. Dazu kommt die Inhaltsadressierung: Dateien, die in mehreren Läufen identisch sind — dieselben Bilder, dieselben Vorlagen —, landen unter demselben Schlüssel und liegen nur einmal.
Der wirkliche Aufwand steckt woanders. Er steckt darin, jeden Schritt einmal falsch zu machen und es zu merken. Drei der oben beschriebenen Fehler haben zusammen mehr Zeit gekostet als der gesamte Rest des Aufbaus, und alle drei hatten dieselbe Form: eine Konfiguration, die richtig aussah, und ein Ergebnis, das niemand nachgesehen hat.
Warum ich das überhaupt aufschreibe
Es gibt eine bequeme Version dieses Textes. In ihr hätte ich den Prüfstand gebaut, die Zahlen gezeigt und die drei Fehler weggelassen, weil sie ja behoben sind. Die Version hätte besser ausgesehen und wäre weniger wert gewesen. Der interessante Teil an einer Messung ist selten das Ergebnis, sondern die Frage, woran man gemerkt hätte, dass sie falsch ist.
Bei allen drei Fehlern lautet die Antwort gleich: an nichts, wenn man nur hinschaut, wo man ohnehin hinschaut. Der Objektspeicher meldete sich als laufend. Die Webserver-Konfiguration las sich richtig. Der Speicherdeckel stand nirgends. In allen drei Fällen hat erst ein Aufruf von außen die Lücke gezeigt — eine echte Datei abrufen und die Kopfzeilen ansehen, statt die Konfiguration zu lesen; den Dienst befragen, statt seinen Status abzufragen. Das klingt banal, bis man merkt, wie oft man das Gegenteil tut, weil es schneller geht.
Dieselbe Sache in klein: Ich habe für den Prüfstand eine Kontrolldatei geschrieben, die durchfallen muss. Sie enthält alle gesuchten Fachbegriffe, aber ausschließlich in einem Kommentar, dazu einen absichtlichen Syntaxfehler. Wenn die Bewertung ihr Punkte gibt, liest sie Kommentare statt Code, und dann ist jede Zahl daneben wertlos. Zusammen mit einer zweiten Datei, die die volle Punktzahl bekommen muss, laufen beide vor jeder Messung durch. Beim ersten Einsatz standen 16 von 20 für die gute und 1 von 20 für die schlechte Datei. Erst danach durfte ein Modell antreten — und ich wusste, dass das Ergebnis wenigstens gemessen und nicht geraten ist.
Der Aufwand dafür war ein Nachmittag. Der Aufwand, den mich das Fehlen dieser Kontrolle gekostet hat, waren drei falsche Behauptungen in einem Text, den ich fast so veröffentlicht hätte.
Was das für die alten Artikel heißt
Die Zahlen bleiben stehen. Was nicht stehen bleibt, ist der Satz, die Ausgaben lägen vollständig vor. Ich lasse ihn nicht still verschwinden — er bekommt eine datierte Korrektur.
Ab dem nächsten Lauf liegt jedes Artefakt öffentlich, anklickbar, mit Prompt und vollständiger Antwort daneben. Wer mir widersprechen will, soll es können, ohne mich fragen zu müssen.