KI & Werkzeuge

Zwei Agenten, ein Kanal

Ein kleines Werkzeug, das zwei Coding-Agenten auf zwei Rechnern dasselbe Projekt bauen lässt, ohne dass sie unbemerkt auseinanderlaufen. So ist es entstanden: das Design, die Annahme, bei der ich mir sicher war und mich irrte, und die Fehler, die nur ein Live-Test finden konnte.

Ramazan Yavuz
Ramazan Yavuz ·
Zwei Agenten, ein Kanal

Zwei Menschen bauen dasselbe Projekt, jeder mit einem KI-Coding-Agenten auf dem eigenen Rechner. Die Agenten sehen einander nicht. Der eine entscheidet, dass die Auth-Schicht ein bestimmtes Verfahren nutzt; der andere baut eine Stunde später und hundert Kilometer entfernt gegen ein anderes. Niemand merkt es, bis die Branches zusammentreffen und der Merge ein Schlamassel ist. Ich wollte einen Weg, dass die beiden Agenten einander beim Arbeiten ehrlich halten, dass solches Auseinanderlaufen am Anfang einer Runde sichtbar wird statt erst beim Merge. Dieses Werkzeug ist agent-doublethink, und so ist es entstanden, samt der Stelle, an der ich mir bei etwas sicher war und mich irrte.


Die erste Entscheidung war die wichtigste, und es ging darum, was man nicht bauen sollte. Der naheliegende Pitch ist "ein MCP, das erkennt, wenn dein Partner abweicht, und euch beide ausgerichtet hält." Aber Erkennung ist ein Urteil: zu entscheiden, ob dein Partner abgedriftet ist, heißt, dass ein Modell seine Entscheidungen gegen den gemeinsamen Plan liest und sich eine Meinung bildet. Eine Leitung kann das nicht, und in dem Moment, in dem man dieses Urteil in den Nachrichten-Broker legt, hat man die eine Eigenschaft zerstört, auf die es ankommt, nämlich dass der Broker den Verkehr nicht lesen kann. Also habe ich die Idee in zwei Teile geteilt und sie strikt getrennt gehalten. Der Transport ist dumm und blind: er trägt versiegelte Nachrichten zwischen den beiden Agenten und sieht nie hinein. Das Urteil lebt in den Agenten selbst, dort, wo die Intelligenz ohnehin sitzt. Das Werkzeug transportiert ein "Abweichungs-Flag"; es entscheidet nie eines. Automatische Abweichungserkennung als Funktion zu verkaufen, hieße, etwas zu verkaufen, das das Design ehrlicherweise nicht kann.

Der Transport selbst musste nicht erfunden werden. Er setzt auf doublethink auf, einen kleinen Broker, den ich schon gebaut hatte und dessen ganzer Sinn Ende-zu-Ende-verschlüsselte Kanäle sind, die der Betreiber nicht lesen kann. agent-doublethink ist ein dünner Client darauf: jeder Agent führt ihn lokal aus, die beiden teilen einen privaten Kanal, und sie tauschen beim Arbeiten kurze strukturierte Beats aus, einen Plan, eine Entscheidung, einen Blocker, eine Frage, ein Abweichungs-Flag, und sonst nichts. Die geschlossene Liste ist Absicht. Man kann keine freie Erzählung posten, denn ein Koordinationskanal, der Geplauder trägt, ist ein Koordinationskanal, den niemand liest.

xkcd 1810: Chat Systems
Die Fragmentierung, die ein gemeinsamer Kanal zwei Agenten ersparen soll.Comic: xkcd 1810, „Chat Systems“ von Randall Munroe (CC BY-NC 2.5).

Dann hatte ich etwas falsch, und der einzige Grund, warum ich es gemerkt habe, ist, dass jemand widersprach und mich bat zu prüfen, statt anzunehmen. Das Ziel war, dass es mit jedem Coding-Agenten funktioniert, nicht nur mit einem. Mein Instinkt sagte, der schöne Teil, eine einzeilige Zusammenfassung des Partnerzustands, die automatisch am Anfang jeder Runde eingespielt wird, funktioniere nur in einem Agenten, weil das der einzige war, von dem ich wusste, dass er den richtigen Hook hat, und die anderen müssten auf manuelles Nachsehen zurückfallen. Ich habe das als Einschränkung notiert. Dann habe ich tatsächlich nachgesehen. Alle drei Agenten, auf die ich abzielte, unterstützen sowohl eine Ein-Befehl-Registrierung des Werkzeugs als auch einen Hook pro Runde, der eine Zeile Kontext einspielen kann, bevor das Modell denkt. Einer bringt sogar einen Befehl mit, um die Hooks der anderen zu importieren. Die "Einschränkung" war, dass ich die Dokumentation nicht gelesen hatte. Die automatische Zusammenfassung funktioniert überall; ein Agent nennt den Hook nur anders. Ich habe die falsche Behauptung gelöscht und die richtige geschrieben. Die Lektion ist alt, und ich lerne sie oft neu: eine selbstsicher ausgesprochene Annahme ist trotzdem eine Annahme.


Das Rollenproblem sah schwerer aus, als es war. Die beiden Agenten nutzen ein symmetrisches Verfahren: es gibt eine Rolle A und eine Rolle B, und wenn beide dieselbe wählen, verschlüsseln sie mit demselben Schlüssel und können einander nicht lesen, sie schweigen sich einfach an. Die Rollen müssen also zuverlässig zugewiesen werden, ohne Münzwurf und ohne manuellen Schritt. Die Antwort steckte im Verhalten des Brokers selbst. Einen Kanal zu erstellen gelingt entweder, weil man der Erste ist, oder kommt mit der Auskunft zurück, dass er schon existiert. Diese Auskunft ist atomar: wenn beide Agenten im selben Moment versuchen, bekommt genau einer "erstellt" und der andere "existiert bereits". Wer den Kanal erstellt, ist also A, wer ihn schon vorfindet, ist B, und ein gleichzeitiges Rennen erzeugt trotzdem genau eines von beiden. Die Agenten ermitteln ihre eigenen Rollen aus einer Tatsache, die der Broker ihnen ohnehin schon mitteilte. Kein neuer Mechanismus, kein Raten.


Die Fehler, die zählten, waren die, die ich nicht durch Lesen des Codes sehen konnte, sondern nur, indem ich es gegen den echten Broker über das echte Internet laufen ließ. Als ich das Ganze zum ersten Mal verkabelt hatte und Agent A eine Nachricht posten und Agent B seinen Posteingang prüfen ließ, war Bs Posteingang leer. Der Code sah korrekt aus. Der Broker, so stellte sich heraus, weist jede Nachricht ab, die nicht in seine spezifische Hüllen-Form verpackt ist, und ich hatte die versiegelten Bytes roh gesendet. Die Lösung war, jeden versiegelten Beat so zu verpacken, wie der Broker es erwartet, genau wie es der Referenz-Client tut, ein Detail, das man nur findet, indem man gegen etwas vergleicht, das funktioniert. Der zweite Fehler war subtiler: selbst bei fließenden Nachrichten kam der Posteingang manchmal leer zurück, weil das Lesen mit einem Hintergrund-Leser im Wettlauf stand, der die nachgeladenen Nachrichten noch nicht fertig eingefaltet hatte. Ich habe das Rennen durch ein deterministisches Leeren ersetzt: alles holen, was der Broker hat, einfalten, dann lesen. Beide Fehler sind die Art, die ein Unit-Test mit einem Fake-Broker fröhlich besteht und ein echter Zwei-Rechner-Umlauf in Sekunden aufdeckt. Ich vertraue dem grünen Haken weniger als der Sache, die tatsächlich funktioniert.

Es gab am Ende noch einen kleineren, peinlicheren. Die Veröffentlichung ging raus, das Skript sagte, es habe gepusht, und ich habe trotzdem nachgesehen: der Push war still gescheitert, weil mein lokaler Branch nach dem alten Standard hieß und die Gegenseite den neuen erwartete. Es war überhaupt nichts veröffentlicht worden. Eine Minute später bauten die Release-Binaries mit der Version im Dateinamen, was den stabilen Download-Link kaputtmachte, den die Installationsanweisung den Agenten nannte. Beides fiel auf, weil ich das Live-Ergebnis prüfte, statt der Erfolgsmeldung zu glauben. "Es sagt, es hat geklappt" ist nicht dasselbe wie "es hat geklappt", und bei den Schritten, die einem Nutzer gegenüberstehen, ist dieser Unterschied die ganze Arbeit.


Was ausgeliefert wurde, ist bewusst klein. Eine einzelne statische Binärdatei, keine Laufzeit zu installieren, die jeder der drei Agenten einrichten kann, indem man ihm eine einzige URL gibt: er liest den Installations-Prompt, lädt sich selbst herunter, registriert sich als Werkzeug in der eigenen Konfiguration des Agenten, installiert den Hook pro Runde, fragt den Nutzer nach den Kanaldetails und verbindet sich. Die Aufbewahrung steht standardmäßig auf dem öffentlichen Maximum, und nur wenn du länger willst, fragt er nach deinem eigenen Broker und einem Schlüssel. Der gemeinsame Plan als Referenz lebt in einer schlichten Datei, die du ins Repo committest; der Kanal trägt die Deltas dagegen und ist ein gleitendes Fenster, kein Archiv, was klar gesagt statt verschwiegen wird. Das Urteil, ob dein Partner abdriftet, bleibt bei dir, dem Menschen und dem Modell; das Werkzeug macht seinen Zustand nur günstig sichtbar.

Der Installations-Prompt, die vier Werkzeuge und die ehrlichen Grenzen stehen auf der agent-doublethink-Projektseite. Der vollständige Quellcode liegt auf GitHub, und es ist auf doublethink aufgebaut.

Das ist das Projekt, so wie es gebaut wurde: eine Entscheidung darüber, was nicht zu bauen ist, eine Annahme, die ich geprüft und korrigiert habe, ein Rollenproblem, gelöst durch eine Tatsache, die der Broker schon kannte, und eine Handvoll Fehler, die nur ein Live-Lauf zutage förderte. agent-doublethink ist Open Source, wird als einzelne Binärdatei ausgeliefert und trägt einen schlichten Haftungsausschluss, denn ein Werkzeug, das zwischen der Arbeit zweier Menschen und ihrem privaten Verkehr sitzt, sollte ehrlich sein, dass es die Arbeit einer einzelnen Person ist, geprüft, aber nicht unfehlbar. Das Nützlichste, was ich beim Bauen getan habe, war, Meldungen, die "fertig" sagten, nicht mehr zu glauben und nachzusehen.