Dieses Thema: Agenten statt Serviceklassen, Teil 1 von 3 (die Idee). Teil 2 folgt am Freitag mit dem Stand nach fünf Tagen, Teil 3 mit dem Ergebnis. Alle Teile dieses Themas · Alle Vorhaben der Woche
Die Idee in einem Satz
Ein Geschäftsvorgang besteht heute aus Serviceklassen, die fest miteinander verdrahtet sind. Ich will diese Klassen durch kleine Agenten ersetzen, die auf Ereignisse hören, ihre Arbeit tun und ein neues Ereignis schreiben. Jeder Agent ist eine Markdown-Datei. Der Code darunter bleibt klein, deterministisch und trägt alles.
Woher der Gedanke kommt
Nehmen wir einen Vorgang, der in jedem Versicherungshaus existiert: Eine Schadenmeldung kommt herein. Danach passieren Dinge. Jemand prüft, ob die Meldung vollständig ist. Jemand fragt nach, wenn ein Bild fehlt. Jemand ordnet den Fall einer Sparte zu. Jemand bereitet ein Formular vor, jemand verschickt eine E-Mail, jemand legt eine Wiedervorlage an.
In Software sind das heute Klassen. Eine für die Prüfung, eine für die Nachfrage, eine für den Versand. Jede ruft die nächste, jede kennt die Struktur der anderen, jede Änderung an einer zieht Änderungen an den Nachbarn nach sich. Das funktioniert, ich habe es oft gebaut. Es ist nur teuer, wenn der Vorgang sich ändert, und Vorgänge ändern sich ständig.
Gleichzeitig arbeite ich seit über einem Jahr täglich mit KI-Agenten in der Softwareentwicklung. Dort sind Agenten inzwischen kleine Textdateien: eine Aufgabe, ein paar Regeln, eine Liste erlaubter Werkzeuge. Man legt eine Datei ab, und die Umgebung sorgt dafür, dass sie zur richtigen Zeit gerufen wird.
Die Frage, die mich seit Wochen beschäftigt: Warum bauen wir Geschäftsvorgänge dann noch aus Klassen?
Die Architektur, wie ich sie mir vorstelle
Vier Bausteine, mehr sollen es zunächst nicht werden.
Erstens der Event Store. Alles, was in einem Vorgang passiert, ist ein Ereignis mit Zeitstempel und Fallnummer: „Meldung eingegangen", „Bild fehlt", „Nachfrage vorbereitet", „Nachfrage verschickt". Ereignisse werden angehängt, nie geändert. Der Zustand eines Falls ist die Summe seiner Ereignisse. Das ist Event Sourcing, und ich baue seit Jahren so, weil es die einzige Struktur ist, in der drei Monate später jemand nachlesen kann, warum etwas passiert ist.
Zweitens die Agenten-Definitionen. Ein Ordner „Vorgänge", darin pro Agent eine Markdown-Datei. Im Kopf der Datei steht, wie er heißt, auf welche Ereignisse er hört, welche Ereignisse er schreiben darf, welche Werkzeuge er benutzen darf und welches Modell. Im Körper steht seine Aufgabe in Prosa: „Du bist der Erstbearbeiter. Wenn eine Meldung eingeht, prüfe Sparte, Vollständigkeit und Plausibilität. Fehlt etwas, schreibe genau eines der folgenden Ereignisse …" Mehr steht da nicht.
Drittens ein einziges generisches Werkzeug. Jeder Agent bekommt dieselbe Fähigkeit: Lies die Ereignisse zu diesem Fall. Kein Agent greift direkt auf Datenbanken, Systeme oder Dateien zu. Alles, was er wissen muss, steht in den Ereignissen, und alles, was er bewirkt, ist ein neues Ereignis. Wenn ein Ereignis eine Wirkung in der Außenwelt braucht, etwa eine E-Mail, dann tut das ein kleiner deterministischer Adapter, der auf genau dieses Ereignis hört. Der Agent verschickt nichts selbst.
Viertens die Engine. Ein kleines Stück Code, das auf jedes geschriebene Ereignis hört, in den Agenten-Definitionen nachschaut, wer sich für diesen Ereignistyp zuständig erklärt hat, und diesen Agenten mit der Fallnummer startet. Die Engine kennt keine Fachlichkeit. Sie kennt nur: Ereignis geschrieben, wer hört darauf, los.
Wie der Vorgang dann läuft
Eine Schadenmeldung kommt über ein Formular herein. Der Adapter am Formular schreibt „Meldung eingegangen" mit dem Inhalt der Meldung.
Die Engine sieht das Ereignis und findet den Erstbearbeiter. Der liest die Ereignisse des Falls, es ist bisher eines, und schreibt: „Fall geprüft: Kfz, Adresse vorhanden, Bild fehlt."
Die Engine sieht „Bild fehlt" und findet den Nachfrage-Agenten. Der liest den Fall, formuliert eine Nachfrage in der Sprache der Kundin, und schreibt: „Nachfrage vorbereitet", mit Text und Empfänger.
Die Engine sieht das und findet den Versand-Adapter, deterministischen Code. Der schickt die E-Mail und schreibt: „Nachfrage verschickt."
Wenn die Antwort mit dem Bild kommt, schreibt der Posteingangs-Adapter „Antwort eingegangen", und der Erstbearbeiter ist wieder dran.
Kein Agent kennt den anderen. Kein Agent weiß, was vor ihm oder nach ihm passiert. Jeder kennt nur die Ereignisse, auf die er hört, und die, die er schreiben darf.
Warum das die Erweiterung so leicht macht
Nehmen wir an, das Haus möchte zwischen „Fall geprüft" und „Nachfrage vorbereitet" eine Betrugsprüfung einziehen. Heute heißt das: Klasse schreiben, Aufrufkette ändern, Tests anpassen, ausliefern.
In diesem Modell heißt es: eine Markdown-Datei anlegen. „Du bist die Betrugsprüfung. Du hörst auf ‚Fall geprüft'. Du schreibst ‚Prüfung unauffällig' oder ‚Prüfung auffällig, Grund: …'." Und der Nachfrage-Agent hört ab jetzt auf „Prüfung unauffällig" statt auf „Fall geprüft", eine Zeile in seinem Kopf. Kein Wiring. Die Engine findet die neue Datei beim nächsten Ereignis.
Das ist der Punkt, an dem ich mir die Welt mache, wie sie mir gefällt: Die Fachlichkeit eines Vorgangs steht in Textdateien, die eine Fachabteilung lesen kann. Der Code darunter ist generisch und ändert sich fast nie.
Wo die eigentliche Schwierigkeit liegt
Ein Sprachmodell antwortet nie zweimal gleich. In einem Chat ist das egal. In einer Pipeline, in der der nächste Agent die Ausgabe des vorigen liest, ist es das zentrale Problem. Ich nenne es die Quantensprünge des Modells: dieselbe Frage, eine andere Struktur in der Antwort, und der nächste Schritt versteht sie nicht mehr.
Deshalb liegt fast die ganze Arbeit in einer Sache: dem Format der Ereignisse. Jeder Ereignistyp bekommt ein festes Schema. Was der Agent schreibt, wird gegen das Schema geprüft, bevor es in den Store darf. Passt es nicht, geht es mit den genauen Fehlern zurück an das Modell, bis zu zweimal. Passt es dann immer noch nicht, schreibt die Engine ein Ereignis „Agent gescheitert", und ein Mensch ist dran. Kein Ereignis ohne Schema, kein Schema-Verstoß im Store.
Dazu kommen drei Dinge, die ich aus anderen Projekten mitnehme: Jede Zahl und jedes Datum in einer Agentenantwort muss in den gelesenen Ereignissen vorkommen, sonst gilt sie als erfunden. Jeder Modellaufruf wird mit Kosten protokolliert, auch die gescheiterten. Und harte Obergrenzen pro Aufruf, weil mir ein degeneriertes Modell schon einmal zwanzigtausend Müll-Einträge in einen Store geflutet hat.
Was ich noch nicht weiß
Ehrlich aufgeschrieben, damit ich am Freitag dagegen prüfen kann:
Ob die Streuung der Antworten mit Schema und Reparaturschleife eng genug wird, dass ein Vorgang mit fünf Agenten in Folge zuverlässig durchläuft. Meine Vermutung: bei einfachen Ereignistypen ja, bei freien Texten wie der Nachfrage an die Kundin wird es schwer.
Wie die Engine mit zwei Agenten umgeht, die auf dasselbe Ereignis hören. Reihenfolge, Parallelität, Konflikte im Store. Ich habe eine Idee mit Versionsnummern pro Fall, aber noch keinen Code.
Ob eine Fachabteilung diese Dateien wirklich lesen und ändern kann, oder ob das ein Wunsch von mir ist. Das entscheidet sich erst, wenn ich jemandem eine hinlege.
Und was das kostet. Ein Vorgang mit fünf Agentenaufrufen auf einem eigenen Modell ist billig. Auf einer API summiert es sich. Ich messe es.
Was ich diese Woche baue
Den Event Store mit Fallnummer und Schema-Prüfung. Die Engine. Das eine Werkzeug. Drei Agenten: Erstbearbeiter, Nachfrage, Versand-Adapter. Und einen Prüfstand, der zwanzig erfundene Schadenmeldungen durch die Pipeline schickt und zählt, wie viele Antworten das Schema verfehlt haben, wie oft die Reparatur gegriffen hat und wie oft ein Mensch dran gewesen wäre.
Am Freitag steht hier Teil 2 mit genau diesen Zahlen. Wenn sie schlecht sind, stehen sie trotzdem hier.
Wenn Sie so einen Vorgang im Kopf haben, bei dem die Klassen längst im Weg stehen: zwei Sätze per Nachricht genügen mir.