Dev

Dein Watchdog killt den beschäftigten Agenten und vertraut dem festgefahrenen

Ein Health-Check, der nur beobachtet, ob ein langlaufender Agent noch existiert, liegt in beiden schweren Fällen falsch: Er killt einen gesunden Agenten in dem Moment, in dem der still genug wird, um abwesend zu wirken, und winkt einen eingefrorenen durch, dessen Prozess weiter läuft. Die Reparatur ist keine schärfere Probe, sondern ein anderes Signal, die dauerhafte Spur, die ein Agent nur durch das Tun der Arbeit hinterlässt und die ein festgefahrener nicht weiter verlängert, weil das Verlängern die Arbeit ist.

Damit ein langlaufender Agent dir nicht stillschweigend wegstirbt, ist das Naheliegende zu prüfen, ob er überhaupt noch da ist. Läuft der Prozess. Hängt die Session noch dran. Ein kleiner Watchdog wacht auf einem Timer auf, prüft, dass das Bewachte existiert, und startet neu, was verschwunden ist. So beaufsichtigen wir Daemons seit Jahrzehnten, und für einen Daemon trifft das nah genug zu: Ein Webserver, der nicht läuft, ist ein Webserver, der unten ist, und dazwischen gibt es nichts Feines. Richte denselben Watchdog auf einen langlaufenden autonomen Agenten, und die Prüfung hört leise auf zu bedeuten, was du glaubst, denn Präsenz und Gesundheit sind auseinandergefallen, und der Watchdog misst genau die, die dich nicht interessiert.

Nimm zuerst die Richtung, die weh tut. Ein Präsenz-Check ist nur so gut wie seine Fähigkeit, das zu beobachten, was er prüft, und einen laufenden Agenten zu beobachten ist überraschend leicht falsch zu machen. Ich habe zugesehen, wie ein Watchdog eine Reihe gesunder Agenten für tot erklärte und begann, sie zu killen, aus zwei rein mechanischen Gründen, von denen keiner etwas mit den Agenten zu tun hatte. Der erste war eine Scope-Grenze: Die Agenten liefen in einem Terminal-Multiplexer, dessen Session-Liste pro Benutzer von einem Server geführt wird, den nur die eigenen Werkzeuge dieses Benutzers abfragen können, und die Prüfung fragte als der falsche Benutzer, bekam eine leere Liste zurück und las die Leere als Tod. Der zweite war subtiler: Die Prüfung suchte den Agenten über den Namen seines Prozesses, aber der Agent war hinter einem kleinen Shell-Wrapper gestartet worden, also war der Prozess, den der Watchdog fand, die Shell und nicht der Agent, und ein lebendiger Agent las sich als ein abwesender. Beides sind Fehler des Beobachters, nicht des Beobachteten, und ein Health-Check, der sich darin irren kann, ob der Patient überhaupt im Raum ist, wird auf einem lang genug laufenden Timer einem das Messer setzen, der nur dort stand, wo das Licht nicht hinreichte.

Das ist keine neue Gefahr, nur ein neuer Ort, ihr zu begegnen. Wer schon Dienste unter Kubernetes betrieben hat, ist von derselben Kante von der anderen Seite gebissen worden: Eine Liveness-Probe, die Prüfung, die einen Container neu startet, wenn er nicht mehr antwortet, zu aggressiv eingestellt, startet einen Container neu, der nie ungesund war, nur beschäftigt. Unter Last ist der Prozess zu ausgelastet, um die Probe innerhalb ihrer Frist zu beantworten, die Probe verbucht genug aufeinanderfolgende Fehlschläge, um ihn für tot zu erklären, der Orchestrator killt und startet ihn neu, und das Killen wälzt seine Last auf seine Nachbarn ab, die jetzt anfangen, ihre eigenen Probes zu verpassen, sodass ein Cluster, das nur langsam war, in eine Welle von Neustarts kippen kann, die die Überlast schlimmer macht, bevor sie sich löst. Die Lehre, die die Ops-Welt daraus zog, war nicht, am Timeout zu drehen und weiterzumachen. Sie lautet, dass eine Probe, die misst, ob ein Prozess gerade jetzt antworten kann, etwas anderes misst als, ob er seine Arbeit tut, und dass das Verwechseln der beiden den Monitor zum Ausfall werden lässt. Ein Agent, der sich durch eine lange Berechnung mahlt, ist genau der beschäftigte Container. Die Strecke, in der er still und schwer zu beobachten wird, ist nicht das Versagen. Sie ist die Arbeit.

Jetzt die andere Richtung, die schlimmer ist, weil sie ohne einen Laut versagt. Repariere den Präsenz-Check vollständig, gib ihm den richtigen Benutzer und den richtigen Prozess, und er beantwortet immer noch die falsche Frage, denn ein Agent kann vollständig präsent und vollständig festgefahren sein. Die offenen Claude-Code-Meldungen lesen sich wie ein Feldführer zu genau dieser Form. Eine Session verklemmt sich zwischen zwei Tool-Aufrufen und bringt erst später ein Stream-Idle-Timeout mit einer bereits erhaltenen Teilantwort hervor (Issue #46987). Der Stream eines Subagenten stockt still, während seine Verbindung bestehen bleibt und null Bytes ankommen, und die Hauptsession läuft unberührt weiter (Issue #79292). Ein Einfrieren bleibt unbemerkt, weil die tote Verbindung darunter nie als tot erkannt wird (Issue #88178). Was jede dieser Meldungen beschreibt, ist kein Prozess, der umgefallen ist, sondern eine Arbeitseinheit, die aufgehört hat voranzukommen, während der Prozess drumherum, die Session, der Socket, alle einwandfrei oben bleiben. Die Lesart, die ich daraus ziehe und die die Meldungen selbst nicht machen, ist diese: Präsenz war nie ein Beleg für Fortschritt, und es war der Fortschritt, den du wissen musstest. Eine Prüfung, die nur sehen kann, ob der Prozess existiert, kann einen angestrengt denkenden Agenten nicht von einem für immer festgefahrenen unterscheiden, denn auf der Ebene existiert-er sind die beiden identisch.

Kubernetes, einmal verbrannt, ging den halben Weg zur Antwort, und die Hälfte, die es erreichte, lohnt sich mitzunehmen. Es teilte die eine Frage in zwei: Liveness, soll dieser Container neu gestartet werden, und Readiness, soll ihm gerade jetzt Arbeit geschickt werden. Die Readiness zu verfehlen zieht einen Container aus der Rotation, die ihm Traffic zuführt, ohne ihn zu killen, was der richtige Reflex ist, weil er einen Moment der Nicht-Antwort von einem Todesurteil trennt. Es bietet sogar eine Probe, die einen Befehl im Container ausführt, statt einen Endpunkt anzupingen, die also einen Marker lesen kann, den die Anwendung im Arbeiten berührt, statt die Anwendung um eine Antwort zu bitten, und das ist bereits, leise, eine Fortschrittsprüfung. Die Supervisor-Welt kennt den Zug seit Jahren: Ein systemd-Dienst kann so gebaut werden, dass er aus seiner eigenen Arbeitsschleife heraus schlägt oder für sein Schweigen gekillt wird, und Frameworks für lange Tasks killen einen Task, der sich nicht innerhalb einer Frist mit Fortschritt gemeldet hat. Die Form der Lösung ist also alt. Was keine dieser älteren Formen geklärt hat und was ein autonomer Agent ans Licht zwingt, ist, woraus der Beat gemacht sein soll, denn in jeder von ihnen ist der Beat immer noch der Prozess, der sagt, mir geht es gut, und ein Prozess, der sagt, mir geht es gut, ist der eine Sensor, dem du am wenigsten trauen kannst.

Die Lösung ist also kein besserer Präsenz-Check, und sie ist auch nicht bloß Präsenz gegen Fortschritt zu tauschen, denn ein Fortschritts-Beat, den der Agent zu senden wählt, ist ein Präsenz-Check mit besseren Manieren: Er kann feuern, während nichts gelandet ist, und er kann von selbst ausfallen, während die Arbeit bestens weiterläuft. Der Zug, der Gewicht hat, ist, den Agenten überhaupt keinen Fortschritt mehr melden zu lassen und stattdessen den Fortschritt an der Spur abzulesen, die die Arbeit hinterlässt, ob der Agent kooperiert oder nicht. Mach zu dem, was du überwachst, denselben dauerhaften Schreibvorgang, der die Arbeit festhält: die Queue-Zeile, die erst in ihren nächsten Zustand umspringt, wenn ein Schritt fertig ist, die Zeile, die nur deshalb in einem append-only-Log landet, weil eine Aktion abgeschlossen wurde und ihr Ergebnis jetzt auf der Platte liegt. Dann kann ein Beat nicht existieren, ohne dass die Arbeit existiert, denn der Beat ist der Fußabdruck der Arbeit selbst und keine Nachricht über sie. Und lies den Fußabdruck auf Richtung, nicht auf Bewegung: Der Cursor muss monoton durch eine begrenzte Menge Arbeit vorrücken, denn ein Agent, der in einer Retry-Schleife feststeckt, denselben Schritt scheitern lässt und Versuch um Versuch anhängt, wirft die geschäftigst aussehende Spur im System ab, und ein Monitor, der Aktivität zählt, würde sie für das Gesündeste auf dem Brett halten. Eine Spur, die wächst, ohne voranzukommen, ist der Festgefahrene im Kostüm. Was du beobachtest, ist die Position, die sich bewegt, nicht das Log, das länger wird.

Hier verdient sich das Argument seinen Unterhalt, und es ist der Punkt, an dem ein benachbartes Argument aufhörte, der Fall dafür, eine Flotte von Agenten als verteiltes System zu behandeln: Jenes sagte, nimm einem Agenten sein Wort über den Erfolg einer fertigen Einheit nicht ab, sondern prüf stattdessen das Artefakt, das er erzeugt hat. Das ist derselbe Instinkt, auf eine andere Zeitform gerichtet. Dort ist die Einheit fertig und du verifizierst sie einmal gegen das, was sie geschaffen hat; hier läuft die Einheit noch und du liest ihre Lebendigkeit fortlaufend aus dem noch in Bewegung befindlichen Artefakt. Eine abschließende Prüfung gegen einen Puls. Und der Puls beantwortet den Einwand, dem die Fertige-Einheit-Fassung nie begegnen musste, nämlich dass ein laufender Agent, gefragt, wie es ihm geht, aus Reflex lügt, munter, und am meisten, wenn er festgefahren ist. Du fragst ihn nie. Du beobachtest den Cursor, den er nur durch das Beenden eines Schritts vorrücken kann, und du behandelst den still werdenden Cursor als das Signal. Der Zug geht vom Wort des Agenten zur Spur des Agenten, und sobald du ihn gemacht hast, ist das Selbstauskunfts-Problem schlicht nicht mehr im Raum, denn du hast aufgehört, dem Agenten irgendetwas aufs Wort zu glauben.

Nichts davon erkauft einen so sauberen Sieg, wie es gern hätte, und die ehrliche Fassung sagt das laut. Ein Fußabdruck landet, wenn ein Schritt endet, nicht während er läuft, also schrumpft das in Wahrheit nur das Fenster der Mehrdeutigkeit vom ganzen Leben des Prozesses auf einen einzigen Schritt, und innerhalb eines wirklich langen Schritts, einer vierzigminütigen Berechnung, eines langsamen Modellaufrufs, ist die Spur so kalt wie in einer Leiche. Der Betreiber ist zurück beim Wählen einer Frist, die länger ist als der längste legitime Schritt, was das Beschäftigter-Container-Problem im besser sitzenden Mantel ist, und der einzige echte Ausweg ist, die Schritte klein genug zu machen, oder einen langen Schritt unterwegs eine Teilmarkierung setzen zu lassen, sodass gesundes Schweigen die Frist nie überdauert. Genau deshalb darf ein ausgebliebener Beat nicht direkt an ein Killen verdrahtet werden. Borg dir die andere Hälfte dessen, was Kubernetes gelernt hat: Ein stehengebliebener Cursor sollte den Agenten zuerst als 'degraded' markieren und aufhören, ihm neue Arbeit zu geben, so wie eine fehlgeschlagene Readiness-Prüfung einen Pod aus der Rotation zieht, und erst dann zum Killen eskalieren, wenn das Schweigen jeden Schritt überdauert, der noch ehrlich sein könnte. Eine Prüfung, die von einem ausgebliebenen Beat direkt zum Messer läuft, ist bloß die Beschäftigter-Container-Kaskade, neu gebaut auf einer klügeren Achse.

Es gibt ein letztes Versagen, und es ist das erste, das wiederkehrt. Der Guard, der die Spur liest, ist selbst ein Beobachter, und ein Beobachter kann am Beobachten scheitern: Er kann auf den falschen Pfad schauen, die Berechtigung zum Lesen des Logs nicht haben, einer Datei folgen, die unter ihm wegrotiert wurde. Wenn das passiert, lernt er nichts, und das ganze Schema wird dann davon entschieden, als was nichts gemeldet wird. Ich habe einen Guard gesehen, dessen eigene Unfähigkeit, seine Prüfung zu Ende zu bringen, still mit der gewöhnlichen Ruhe verrührt und nach oben als alles in Ordnung gemeldet wurde: Der Guard war blind, und blind erschien als grün. Das ist der schlimmste Zustand, in dem das System sein kann, schlimmer als jede der beiden Arten von Agenten-Versagen, denn es ist der eine, der eine Entwarnung einreicht. Ein Monitor, der die Spur nicht lesen kann, muss das sagen, laut, und immer weiter sagen. Sein Schweigen darf nie dedupliziert oder in das allgemeine Summen guter Nachrichten gemittelt werden, denn ein Sensor, der genau in dem Moment, in dem er offline geht, alles in Ordnung meldet, ist keine Sicherheitsvorrichtung, sondern eine Lampe, die auf Grün geht, wenn jemand das Kabel durchschneidet. Die Regel, die das ganze Schema ehrlich hält, ist die Regel, um die der einleitende Vorfall schon bat: Dass ein Monitor selbst nicht sieht, ist nie nichts, es ist das Lauteste auf dem Brett.

Die Intelligenz im Prozess war hier nie das, was auf dem Spiel stand, und nach einem klügeren Agenten zu greifen hätte nichts davon berührt. Falsch war die Frage, die an der Grenze gestellt wurde. Präsenz ist billig zu prüfen und fast immer das Falsche zu prüfen, denn die zwei Momente, in denen ein langlaufender Agent am meisten tot wirkt, die tiefe Stille harter Arbeit und die tiefe Stille des Festgefahrenseins, sind für alles, was auf bloße Existenz achtet, nicht zu unterscheiden. Beobachte Präsenz, und du wirst zuverlässig den beschäftigten killen und dem festgefahrenen vertrauen, was genau verkehrt herum ist. Beobachte stattdessen die Spur, die Fährte, die ein Agent nur durch Bewegung hinterlässt, und die beiden fallen endlich auseinander: Der arbeitende Agent ist der, der noch eine Marke hinterlässt, und der festgefahrene ist die Stille, die du endlich sehen kannst.

Diskussion

Hier gibt es keine Kommentarspalte. Diskussionen laufen auf X.

Max Nardit

Max Nardit

@mnardit

Weitere Artikel

Dein geforkter Subagent weiß bereits zu viel

Die Historie eines Agenten enthält jede Sackgasse, in die er gelaufen ist, und nichts darin markiert, welche davon er inzwischen aufgegeben hat. Delegiere diese Historie an eine Teilaufgabe, und die verworfenen Vermutungen kommen mit demselben Gesicht an wie die gesicherten Fakten. Warum ein sauberer Start meist gewinnt, wann ein geschriebener Auftrag die Vererbung schlägt, und wann die Arbeit ein lebendiges Ding ist, das kein Auftrag fassen kann.

Deine Instruktionsdateien sind Adressierung, keine Fragmentierung

Jede Agent-Instruktionsdatei in einen gemeinsamen Standard zu verschmelzen sieht aus, als würde man überflüssige Wiederholung löschen, und fürs Projekt-Setup ist es das auch wirklich. Aber das meiste, was diese Dokumente enthalten, ist keine verstreute Kopie einer Sache; es sind getrennte Adressen, sortiert danach, für wen eine Zeile gilt und wer sie schreiben darf. Eine dieser Grenzen ist zugleich eine Vertrauensgrenze, weshalb die aufgeräumte Lösung einer universellen Datei stillschweigend Repository-Text die Autorität deiner eigenen stehenden Regeln verleiht.

Dein Multi-Agenten-System ist ein verteiltes System

Die Fehler, die man den Agenten anlastet (viele laufen auf dieselbe falsche Antwort zusammen, eine Meinungsverschiedenheit verhärtet sich zu Sabotage, ein Erfolg wird für Arbeit gemeldet, die nie lief), sind keine Lücken, die ein stärkeres Modell schließt. Es sind die ältesten Fehler im verteilten Rechnen, und sie lösen sich, indem man die Schicht zwischen den Agenten baut, nicht durch mehr Intelligenz in jedem einzelnen.