Viele Agenten, eine Architektur: Was passiert, wenn zwei von ihnen dasselbe System ändern
Drei Agenten, drei Pull Requests, drei vernünftige Meinungen darüber, wo die Retry-Logik hingehört.
Einer packt den Retry in den HTTP-Client. Einer wickelt den Handler ein. Einer baut eine Queue dazu und leert sie. Lies jeden der drei für sich und du würdest ihn approven. Lies sie am selben Nachmittag und dir fällt auf: Das System macht jetzt an drei Stellen Retries, mit drei unterschiedlichen Backoff-Policies, und entschieden hat das niemand.
So sieht das Problem aus, sobald mehr als ein Agent gleichzeitig an einer Codebase arbeitet. Jeder Agent ist lokal korrekt. Die Inkohärenz ist global, und sie wird erst für die Person sichtbar, die als Letzte reviewt.
Warum die Rules-Datei das nicht entscheidet
Die Standardantwort ist eine Rules-Datei: CLAUDE.md, AGENTS.md, .cursor/rules. Wir haben darüber geschrieben, warum die veralten, und das Veralten ist hier das kleinere Problem. Das größere ist, dass eine Rules-Datei nicht entscheiden kann.
Sie ist Prosa. Zwei Agenten, denen man denselben Absatz gibt, produzieren zwei verschiedene Lesarten davon, beide vertretbar, und es gibt keinen Punkt, an dem sich diese Lesarten treffen. Sie lebt pro Repository, also steht eine Regel über eine Service-Grenze in einem Repo, während der Service auf der anderen Seite der Grenze in einem anderen liegt. Und sie hat keinen State: Sie kann nicht wissen, dass ein anderer Agent vor vierzig Minuten etwas vorgeschlagen hat, denn sie ist eine Datei, und Dateien wissen nichts.
Was du zum Entscheiden brauchst, ist nicht bessere Prosa. Es ist ein geteiltes Ding, das beide Agenten lesen und schreiben, das eine Entscheidung halten kann und das eine Meinungsverschiedenheit bemerken kann.
Was dir ein Modell gibt und ein Dokument nicht
Ein Architekturmodell sind Elemente und Beziehungen, die du abfragen kannst. Systeme, Container, Components, die Kanten dazwischen, und an diesen Kanten hängen die Dinge, die aus einem Design ein Design machen: die Entscheidung, die eine Grenze bewusst gemacht hat, der Owner, den man benachrichtigen muss, der Contract, auf den sich ein Consumer verlässt.
Daraus folgen drei Dinge, und jedes davon ist ein Mechanismus und keine Absicht.
Jeder Agent kann dieselben Bytes lesen. Die generate-context-Action schreibt aus dem Modell eine archyl.txt, standardmäßig in Markdown, optional automatisch ins Repository committet. Neun Agenten, die eine generierte Datei lesen, sind eine andere Situation als neun Agenten, die jeder für sich ein Prosadokument paraphrasieren. Das ist nicht clever. Das ist einfach geteilt.
Uneinigkeit lässt sich am Eingang abfangen. Die conformance-check-Action führt Architekturregeln gegen die Dateien aus, die ein Pull Request geändert hat, annotiert Verstöße inline und lässt den Check bei der Severity durchfallen, die du wählst. fail-on nimmt error, warning oder none. Wenn "Retries gehören in den Client" eine Regel ist und kein Satz, erfahren die beiden Agenten, die sie woanders hingepackt haben, das in der CI statt im Review.
Eine Entscheidung hat einen Ort zum Wohnen. ADRs hängen an den C4-Elementen, die sie einschränken. Der Grund, warum die Queue existiert, steht an der Queue und nicht in einem Slack-Thread vom März, den nie ein Agent gesehen hat.
Der Teil, den wir falsch hatten
Hier hörte das auf, ein Blogpost über eine nette Idee zu sein.
Agenten ändern das Modell nicht direkt. Sie öffnen einen Change Request: einen Vorschlag, der von einem Menschen reviewt und gemerged wird. Wenn ein Change Request angelegt wird, hält archyl fest, gegen welche Modellversion er gebaut wurde. Beim Merge wird die Version hochgezählt. Das ist genau die Maschinerie, die man für dieses Problem haben will.
Wir hatten die beiden nie verbunden.
Die Basisversion wurde beim Anlegen geschrieben und nirgends wieder gelesen. Was hieß, dass diese Abfolge funktionierte, leise und vollständig:
- Agent A und Agent B lesen beide das Modell. Beide sehen Version 7.
- A öffnet einen Change Request. B öffnet einen Change Request. Beide basieren auf Version 7.
- As Change Request merged. Das Modell ist jetzt auf Version 8.
- Bs Change Request merged. Er war gegen ein Modell geschrieben, das es nicht mehr gibt.
Keine Warnung, kein Konflikt, kein Vermerk in der History. Der zweite Satz Änderungen landet auf dem ersten, und wenn beide sich widersprechen, ist der Widerspruch jetzt die dokumentierte Architektur. Das ist ein Merge, dem man die Konflikterkennung ausgebaut hat, und es ist genau der Fehler, den die ganze "viele Agenten"-Geschichte verhindern soll.
Also haben wir es repariert. Einen Change Request zu mergen, dessen Basisversion nicht mehr zum Projekt passt, scheitert jetzt mit einem 409 Conflict und einer Meldung, die beide Versionen nennt:
architecture request is based on version 7 but the model is now at version 9;
rebase the request and merge again
Sicher macht das Ganze aber nicht der Vergleich. Zwei Merges, die im selben Moment ankommen, würden beide einen Vergleich bestehen und beide weiterlaufen. Was diese Lücke schließt, ist, das Hochzählen der Version selbst konditional zu machen: Der Merge bewegt das Modell nur dann weiter, wenn das Modell noch auf der Version steht, gegen die der Change Request gebaut wurde. Hat es sich schon bewegt, findet der Merge nichts zum Weiterbewegen, das Ganze rollt zurück, und keine einzige Änderung wird angewendet. Der Vergleich davor existiert nur, damit die Fehlermeldung dir sagen kann, wie weit du zurück bist.
409 statt 400 zählt mehr, als es aussieht. Ein Agent, der auf 400 retried, dreht sich ewig im Kreis, denn ein malformed Request bleibt malformed. 409 sagt das Gegenteil: Was du geschickt hast, war in Ordnung und hat aufgehört, anwendbar zu sein. Hol dir das aktuelle Modell und versuch es nochmal.
Was das immer noch nicht tut
Vier Grenzen, alle nachprüfbar.
Kein Agent merged irgendetwas. Es gibt kein MCP-Tool, das einen Change Request merged. Agenten schlagen vor; ein Mensch reviewt und merged. Das ist eine bewusste Grenze und wir haben nicht vor, sie zu entfernen, aber es heißt, dass die Schleife nicht vollständig automatisch ist und du nicht so designen solltest, als wäre sie es.
Die Konflikterkennung ist grob. Die Version gilt pro Projekt, nicht pro Element. Zwei Agenten, die wirklich unabhängige Ecken desselben Projekts anfassen, kollidieren trotzdem auf der Version. Das ist die sichere Richtung, in die man falsch liegen kann, und es ist falsch.
Das Context-Retrieval ist lexikalisch. find_relevant_context bewertet Elemente über Wortüberschneidung auf Namen, Beschreibungen, Tags und Pfaden. Es gibt kein Embedding und keine Synonym-Expansion, also bringt eine Aufgabe zum Thema "Checkout" keine Component namens OrderProcessor nach oben. Der Vorteil ist echt (deterministisch, keine Token-Kosten, kein Code, der irgendwohin geschickt wird), aber es ist Matching und kein Verstehen.
Regeln schreiben sich nicht von selbst. Alles oben setzt voraus, dass jemand "Retries gehören in den Client" als Conformance Rule formuliert hat. Ein leeres Regelset fängt nichts ab, egal wie viele Agenten laufen.
Was du diese Woche damit machst
Du musst nichts kaufen, um herauszufinden, wo du stehst.
Nimm die letzte Woche, in der dein Team mehr als einen von einem Agenten geschriebenen Pull Request gemerged hat. Lies sie zusammen statt nacheinander. Frag, ob zwei davon dieselbe Entscheidung unterschiedlich getroffen haben, und frag dann, was dir das in deinem jetzigen Setup gesagt hätte.
Wenn die Antwort "dem Reviewer ist es aufgefallen" lautet, funktioniert das bis zu dem Tag, an dem der Reviewer neun davon liest.
Change Requests, Conformance Rules und das C4-Modell sind Teil von archyl. Die GitHub Actions und die Agent Skills sind Open Source. Weiterlesen: warum deine Agenten eine Rules-Datei haben und kein Modell, wie Change Requests funktionieren und wie das Modell ehrlich gehalten wird.