La settimana scorsa sono andato a cercare un piccolo bug e ho trovato cinque copie dello stesso software.
Il software è hermes-memory-pgvector, un plugin open-source che mantengo. Offre a una flotta di agenti AI una memoria condivisa e duratura su PostgreSQL e pgvector — così ciò che impara un agente, gli altri possono ricordarlo. È pubblicato su PyPI. Puoi installarlo con pip.
Eppure sul mio server era in esecuzione da un checkout git nascosto dentro il codice sorgente di un altro progetto, a una pulizia di routine dal venire cancellato. C'era anche una copia vendorizzata nel mio repo di infrastruttura che non usava nessuno, una directory di build obsoleta, una cache di pacchetto e un symlink creato a mano pochi minuti prima per evitare che l'intero assetto crollasse.
È il tipo di configurazione che erediti da te stesso sei mesi dopo e che non riesci a giustificare subito.
Perché pip install non era sufficiente
Ecco la parte che spiega davvero il disastro.
Il framework host scopre i plugin di memoria scansionando le directory. Cerca nella propria cartella dei plugin inclusi e in una cartella dei plugin dell'utente, una sottodirectory che contenga un __init__.py che menzioni la classe giusta. Questo è l'intero meccanismo di individuazione. Non esamina mai i pacchetti Python installati e non c'è alcuna registrazione di entry point.
Quindi pip install aveva messo il mio plugin sulla macchina, correttamente, esattamente nell'ambiente giusto — e il framework non riusciva a vederlo. Perfettamente installato. Completamente invisibile.
Di fronte a ciò, la mossa ovvia è mettere una copia dove lo scanner guarderà davvero. Copiare la cartella nel repo di distribuzione. Fare il checkout accanto al framework. Creare un symlink. Ognuna di queste è una decisione locale ragionevole, e tutte insieme sono il modo in cui si finisce con cinque copie e una distribuzione che nessuno sa spiegare.
La soluzione: smettere di combattere lo scanner
La release che ho distribuito questa settimana aggiunge un comando, da eseguire una volta dopo l'installazione:
pip install hermes-memory-pgvector
hermes-pgvector install
Quel secondo comando scrive un piccolo shim nella cartella che lo scanner legge — poche righe il cui unico compito è importare il vero pacchetto installato con pip. Lo scanner trova una directory e si accontenta. Python risolve l'import nel libreria correttamente installata.
Quell'unica indirezione fa collassare cinque copie in una sola. Gli aggiornamenti diventano un pip upgrade e un riavvio. Fare rollback significa fissare la versione precedente. Nulla è vendorizzato, nulla è in checkout, nulla deriva, e la manutenzione ordinaria nel repository di qualcun altro non può più portarsi via con sé il mio livello di memoria.
Cos'altro ne è emerso
Consolidare la distribuzione ha significato rileggere il codice per davvero, e questo ha fatto emergere diversi bug reali che vale la pena nominare:
Una ricerca di sottostringa che non lo era. Modificare una memoria salvata confrontava il tuo testo con il database usando SQL LIKE, dove % è un wildcard. Una memoria che contenesse "revenue grew 15% YoY" poteva corrispondere — e modificare — righe non correlate. Le barre rovesciate avevano il problema opposto: un percorso file di Windows non corrispondeva silenziosamente a nulla.
Una coda che scartava lavoro fingendo di aver finito. Lo scrittore in background accettava le scritture e poi, in shutdown, se capitava che fosse occupato, abbandonava tutto ciò che era ancora in coda senza una parola nei log.
Un errore fuorviante. Punta il plugin a un modello di embedding con la dimensione di output sbagliata e ricevevi un 404 da un fallback non correlato, invece di "expected 768 dimensions, got 1024.".
Niente di tutto questo era esotico. Erano tutte cose che una lettura attenta trova e una suite di test che passa non vede.
La lezione che continuo a reimparare
Quando una distribuzione è un disastro, il disastro di solito sta dicendo qualcosa di vero sul design.
Le cinque copie non erano negligenza. Ognuna era un workaround razionale. Erano un sintomo, e il problema di fondo era che il mio pacchetto non poteva essere individuato nel modo in cui era pensato per essere installato. Sistemare i workaround uno alla volta avrebbe prodotto un disordine più ordinato. Sistemare il divario di individuazione ha reso inutili i workaround.
Se mantieni qualcosa che le persone installano in un modo e distribuiscono in un altro, vale la pena chiudere quel divario. Probabilmente sta cercando di dirti qualcosa.
hermes-memory-pgvector è con licenza BSD-3, e si trova su GitHub e su PyPI.
