21. Juli 2026

Fünf Kopien eines Plugins: Was mir eine chaotische Bereitstellung sagen wollte

Fünf Kopien eines Plugins: Was mir eine chaotische Bereitstellung sagen wollte

Letzte Woche suchte ich nach einem kleinen Bug und fand fünf Kopien derselben Software.

Die Software ist hermes-memory-pgvector, ein Open-Source-Plugin, das ich betreue. Es gibt einer Flotte von KI-Agenten gemeinsamen, dauerhaften Speicher auf PostgreSQL und pgvector — sodass das, was ein Agent lernt, die anderen abrufen können. Es ist auf PyPI veröffentlicht. Du kannst es mit pip installieren.

Und doch lief es auf meinem eigenen Server aus einem git-Checkout, der inmitten des Quellbaums eines anderen Projekts verborgen war, nur eine routinemäßige Aufräumaktion davon entfernt, gelöscht zu werden. Außerdem gab es eine vendorizierte Kopie in meinem Infrastruktur-Repo, die niemand nutzte, ein veraltetes Build-Verzeichnis, einen Paket-Cache und einen handgemachten Symlink, der erst Minuten zuvor erstellt worden war, um das ganze Konstrukt vor dem Einsturz zu bewahren.

Das ist die Art von Setup, die man sechs Monate später von sich selbst erbt und nicht sofort erklären kann.

Warum pip install nicht ausreichte

Hier ist der Teil, der das Chaos tatsächlich erklärt.

Das Host-Framework entdeckt Speicher-Plugins, indem es Verzeichnisse durchsucht. Es schaut in seinen eigenen gebündelten Plugins-Ordner und in einen Benutzer-Plugins-Ordner nach einem Unterverzeichnis mit einer __init__.py, die die richtige Klasse erwähnt. Das ist der gesamte Erkennungsmechanismus. Installierte Python-Pakete werden nie geprüft, und es gibt keine Entry-Point-Registrierung.

Also brachte pip install mein Plugin korrekt und genau in der richtigen Umgebung auf die Maschine — und das Framework konnte es nicht sehen. Perfekt installiert. Vollständig unsichtbar.

Vor diesem Hintergrund ist der naheliegende Schritt, eine Kopie dorthin zu legen, wo der Scanner wirklich hinschaut. Den Ordner ins Deployment-Repo kopieren. Neben dem Framework auschecken. Einen Symlink darauf setzen. Jede dieser Maßnahmen ist lokal betrachtet vernünftig, und zusammen führen sie dazu, dass man fünf Kopien hat und eine Bereitstellung, die niemand erklären kann.

Die Lösung: Nicht mehr gegen den Scanner arbeiten

Die Veröffentlichung, die ich diese Woche ausgeliefert habe, fügt einen einzigen Befehl hinzu, der nach der Installation einmal ausgeführt wird:

pip install hermes-memory-pgvector
hermes-pgvector install

Dieser zweite Befehl schreibt einen kleinen Shim in den Ordner, den der Scanner liest — ein paar Zeilen, deren einzige Aufgabe es ist, das echte, per pip installierte Paket zu importieren. Der Scanner findet ein Verzeichnis und ist zufrieden. Python löst den Import zur korrekt installierten Bibliothek auf.

Diese eine Ebene der Umleitung reduziert fünf Kopien auf eine. Upgrades werden zu einem pip-Upgrade und einem Neustart. Ein Rollback bedeutet, die vorherige Version festzupinnen. Nichts ist vendoriziert, nichts ist ausgecheckt, nichts driftet, und routinemäßige Aufräumarbeiten in jemand anderes Repository können meine Speicher-Schicht nicht länger mit sich reißen.

Was sonst noch dabei herauskam

Die Konsolidierung der Bereitstellung bedeutete, den Code noch einmal richtig zu lesen, und dabei tauchten mehrere echte Bugs auf, die es wert sind, benannt zu werden:

Eine Substring-Suche, die keine war. Beim Bearbeiten eines gespeicherten Speichers wurde dein Text in der Datenbank mit SQL LIKE abgeglichen, wobei % ein Platzhalter ist. Ein Speicher mit "revenue grew 15% YoY" konnte nicht zusammenhängende Zeilen treffen — und verändern. Backslashes hatten das gegenteilige Problem: Ein Windows-Dateipfad passte stillschweigend überhaupt nicht.

Eine Warteschlange, die Arbeit verlor und behauptete, fertig zu sein. Der Hintergrundschreiber nahm Schreibvorgänge an und ließ beim Herunterfahren, falls er gerade beschäftigt war, alles, was noch in der Warteschlange lag, wortlos fallen — ohne eine Zeile im Log.

Eine irreführende Fehlermeldung. Wenn man das Plugin auf ein Embedding-Modell mit der falschen Ausgabedimension zeigte, bekam man einen 404 von einem nicht zusammenhängenden Fallback statt "expected 768 dimensions, got 1024."

Nichts davon war exotisch. Es waren alles Dinge, die eine sorgfältige Lektüre findet und eine vorbeirauschende Test-Suite nicht.

Die Lektion, die ich immer wieder neu lerne

Wenn eine Bereitstellung chaotisch ist, sagt dieses Chaos meist etwas Wahres über das Design aus.

Die fünf Kopien waren kein Nachlässigkeit. Jede einzelne war ein vernünftiger Workaround. Sie waren ein Symptom, und das zugrunde liegende Problem war, dass mein Paket nicht so entdeckt werden konnte, wie es eigentlich installiert werden sollte. Die Workarounds nacheinander zu reparieren hätte nur ein aufgeräumteres Chaos erzeugt. Die Lücke in der Erkennung zu schließen machte die Workarounds überflüssig.

Wenn du etwas betreust, das Leute auf eine Weise installieren und auf eine andere bereitstellen, lohnt es sich, genau diese Lücke zu schließen. Sie versucht dir wahrscheinlich etwas zu sagen.


hermes-memory-pgvector steht unter BSD-3-Lizenz und ist auf GitHub und auf PyPI zu finden.