200 Zeilen Python, ein lokales Modell — und ein kaputter Docker-Dienst, der sich selbst repariert.

Bis hierhin ging es um Messwerte. Teil 4 endete mit der Frage, was die Kiste eigentlich kann, wenn man sie arbeiten lässt. Jetzt bekommt sie eine Aufgabe, die ich sonst selbst erledigen müsste.

Vorweg, damit die Erwartung stimmt: Es gab zwei Fehlschläge, bevor es klappte. Die stehen hier mit drin, weil sie mehr erklären als der Erfolg.


Ein Agent ist eine Schleife, kein Framework

Man kann sich Agenten aus fertigen Bausteinen zusammensetzen. Ich wollte einmal verstehen, was darunter passiert, und habe deshalb selbst gebaut: rund 200 Zeilen Python, nur Standardbibliothek, keine einzige zusätzliche Abhängigkeit.

Das Prinzip ist erstaunlich schlicht:

→ Das Programm schickt den Auftrag ans Modell
→ Das Modell antwortet entweder mit Text — oder mit der Bitte, ein Werkzeug zu benutzen
→ Das Programm führt das Werkzeug aus und schickt das Ergebnis zurück
→ Von vorn, bis das Modell fertig ist oder das Rundenlimit greift

Der entscheidende Satz dabei: Das Modell fasst nie selbst etwas an. Es bittet nur. Ob eine Datei wirklich geschrieben oder ein Befehl wirklich ausgeführt wird, entscheidet das Programm. Daraus folgt eine Regel, die ich für die wichtigste dieses Projekts halte: Sicherheit gehört in den Rahmen, nicht in den Prompt. Ein Modell zu bitten, brav zu sein, ist keine Absicherung. Ein Pfad-Käfig, der es gar nicht erst aus dem Arbeitsverzeichnis lässt, ist einer.

Die Werkzeuge waren: Dateien auflisten, lesen, ändern — und Shell-Befehle ausführen. Dazu ein paar Schutzmaßnahmen: Ausgabelänge deckeln, Rückfragen vor schreibenden Aktionen, und ältere Werkzeug-Ausgaben eindampfen, damit der Kontext nicht überläuft.

Der einzige Unterschied zur Vorlage aus dem Netz: Statt einer Cloud-Schnittstelle zeigt mein Harness auf die eigene Karte. Das ändert kaum Code — und macht einen großen Unterschied im Kopf. Lokal kostet jede Schleife nichts. Man kann den Agenten dreißig Runden drehen lassen, ohne auf eine Abrechnung zu schielen.


Der erste Lauf

Zum Aufwärmen ein eingebauter Rechenfehler in einem kleinen Python-Skript: Bei der Umrechnung von Celsius in Fahrenheit stand eine 31, wo eine 32 hingehört.

Der Agent brauchte vier Schritte: Verzeichnis ansehen, Datei lesen, die Zahl korrigieren, das Skript ausführen. Ergebnis: 212 Grad Fahrenheit für 100 Grad Celsius — richtig. Kein Fehlversuch, kein Herumraten.

Das ist noch keine Kunst. Interessant wurde es, als drei verschiedene Modelle dieselbe Aufgabe bekamen.


Der Dreikampf — und der Absturz des Schnellsten

Bei der leichten Aufgabe bestanden alle drei. Das 14B war mit sieben Sekunden am schnellsten, das 35B löste sie am elegantesten mit nur drei Schritten, das 27B lieferte den saubersten Abschlussbericht.

Also eine schwerere Aufgabe: ein Warenkorb über mehrere Dateien, mit einer Funktion, die komplett fehlte und aus den vorhandenen Testfällen erschlossen werden musste.

→ 27B — der Präzise: sieben Schritte, 31 Sekunden, fehlerfrei. Erst alle Dateien gelesen, dann ein einziger sitzender Eingriff. Die fehlende Funktion hat es korrekt allein aus den Tests abgeleitet.
→ 35B — der Ingenieur: elf Schritte, 30 Sekunden, grün. Als Einziges testgetrieben: erst die Tests laufen lassen, um die Fehler zu sehen, dann reparieren. Eine falsche Annahme unterwegs — am fehlschlagenden Test selbst bemerkt und korrigiert.
→ 14B — der Hektiker: siebzehn Schritte bis zum Rundenlimit, 50 Sekunden, gescheitert.

Der tödliche Moment beim kleinsten Modell ist der lehrreichste des ganzen Projekts. Mein Änderungswerkzeug hatte eine Bequemlichkeit eingebaut: Ein leerer Suchtext bedeutete „lege die Datei neu an". Das Modell benutzte einen leeren Suchtext auf eine bestehende Datei — und überschrieb damit die komplette Klasse. Danach zehn Runden Flickschusterei auf Trümmern.

Das war mein Fehler, nicht seiner. Anlegen und Ändern in einem Werkzeug zusammenzufassen war die Falle; ein größeres Modell tappt seltener hinein, aber die Falle gehörte da nie hin. Seitdem sind es zwei getrennte Werkzeuge.


Der Praxistest: ein kaputter Dienst

Jetzt die echte Aufgabe. Auf dem Server sollte der Agent über die Verwaltungs-Schnittstelle einen Dokumenten-Dienst als Docker-Stack anlegen. Das gelang — nur startete der Container nicht, sondern lief in eine Neustartschleife.

Der Grund war eine dieser Kleinigkeiten, an denen man selbst auch hängenbleibt: Der Dienst verlangt seit einer neueren Version zwingend einen geheimen Schlüssel in der Konfiguration. Fehlt er, bricht er beim Start ab.

Der erste Reparaturversuch scheiterte — am Kontextüberlauf. Der Agent lud vollständige API-Antworten in seinen Speicher, und bei einem lokalen Modell mit begrenztem Fenster ist der schnell voll. Mitten in der Arbeit war Schluss.

Also zwei Verbesserungen am Rahmen: ältere Ausgaben eindampfen, und ein Hinweis im System-Prompt, große Ausgaben gezielt zu filtern statt komplett zu laden. Dann der zweite Lauf.

Vier Schritte:

→ Die letzten dreißig Zeilen des Container-Protokolls abrufen — und den Konfigurationsfehler daraus erkennen
→ Die Stack-Definition über die API holen, diesmal gezielt gefiltert statt am Stück
→ Einen 64 Zeichen langen Schlüssel erzeugen, in die Konfiguration einbauen, den Stack neu ausrollen
→ Neunzig Sekunden warten, dann Containerzustand und Weboberfläche prüfen

Ergebnis: Der Dienst lief, meldete sich als gesund, die Weboberfläche antwortete mit der Weiterleitung zur Anmeldeseite. Ich habe unabhängig nachgeprüft — es stimmte.

Zwei Details, die mich mehr beeindruckt haben als die Reparatur selbst. Erstens hat der Agent den erzeugten Schlüssel nie im Klartext ausgegeben — im Protokoll steht nur der Erzeugungsbefehl. Zweitens hat er von sich aus gewartet, bevor er prüfte. Er wusste, dass ein frisch gestarteter Dienst Zeit braucht. Diese Geduld hätte ich einem lokalen Modell auf einer 16-GB-Karte nicht zugetraut.

Das große Modell schaffte dieselbe Aufgabe übrigens auch — und legte noch etwas drauf: Im Auftrag stand eine veraltete Nummer für den Stack. Statt sie stur zu befolgen, hat es über die Schnittstelle nachgesehen, den richtigen gefunden und dort repariert. Eine falsche Anweisung erkannt und korrigiert — die reifste Leistung des Abends.

Das kleinste Modell scheiterte auch hier. Es wartete minutenlang auf einen Dienst, den es nie gestartet hatte: Es verifizierte, bevor es baute.


Was ich gelernt habe

Das schnellste Modell ist der langsamste Agent. Auf dem Papier war das 14B mit Abstand vorn — in echter Arbeit hat es Dateien zerstört, API-Antworten nie geprüft und am Ende Maßnahmen angekündigt, statt sie auszuführen. Rohes Tempo sagt über Agententauglichkeit nichts aus.

Der Rahmen ist die halbe Miete, das Modell die andere. Zum Vergleich habe ich dieselbe Aufgabe mit einem ausgereiften Werkzeugkasten laufen lassen: 25 statt 31 Sekunden bei gleichem Modell, also rund 20 Prozent. Spürbar, aber nicht entscheidend. Aus einem hektischen Modell macht auch der beste Rahmen keinen sorgfältigen Agenten.

Kleine Modelle wiederholen Fehlschläge stur. In einem Fehllauf feuerte der Agent achtmal denselben fehlerhaften API-Aufruf ab, bis das Rundenlimit ihn stoppte. Eine Wiederholungserkennung gehört in jeden ernsthaften Rahmen.

Und die Regel, die alles zusammenhält: Geheimnisse und Sicherheit gehören in den Rahmen. Nicht in den Prompt, nicht ins Vertrauen.


Was das praktisch bedeutet

Auf einer Grafikkarte für ein paar hundert Euro, in einem ausgemusterten Arbeitsrechner, hat ein lokales Modell einen kaputten Dienst diagnostiziert, über eine Schnittstelle repariert und sein Ergebnis selbst überprüft. Ohne Cloud, ohne Schlüssel zu einem fremden Anbieter, ohne dass ein einziges Byte das Haus verlassen hat.

Das ist keine Demo für ein Video, sondern die Arbeit eines Abends — mit zwei Fehlschlägen unterwegs, die dazugehören. Genau deshalb glaube ich, dass der Ansatz trägt.

Im letzten Teil geht es um die Frage, die Kunden mir dazu stellen: Wann lohnt sich das? Die Antwort hat weniger mit Kosten zu tun, als man denkt — und mehr damit, welche Daten das Haus nicht verlassen dürfen.