Na semana passada fui à procura de um pequeno bug e encontrei cinco cópias do mesmo software.
O software é hermes-memory-pgvector, um plugin de código aberto que mantenho. Dá a uma frota de agentes de IA memória partilhada e duradoura em PostgreSQL e pgvector — para que o que um agente aprende, os outros possam recordar. É publicado no PyPI. Pode instalá-lo com pip.
E, no entanto, no meu próprio servidor, estava a correr a partir de um checkout do git escondido dentro da árvore de origem de outro projeto, a um passo de uma limpeza de rotina de ser apagado. Havia também uma cópia vendorizada no meu repositório de infraestrutura que nada utilizava, um diretório de compilação obsoleto, uma cache de pacotes e um symlink feito à mão poucos minutos antes para impedir que toda a estrutura colapsasse.
Esse é o tipo de configuração que herda de si próprio seis meses depois e não consegue justificar de imediato.
Porque é que pip install não era suficiente
Aqui está a parte que realmente explica a confusão.
O framework anfitrião descobre plugins de memória através de análise de diretórios. Procura dentro da sua própria pasta de plugins incluída e dentro de uma pasta de plugins do utilizador, um subdiretório que contenha um __init__.py que mencione a classe certa. Esse é todo o mecanismo de descoberta. Nunca inspeciona pacotes Python instalados e não existe registo de entry-point.
Assim, pip install colocou o meu plugin na máquina, corretamente, exatamente no ambiente certo — e o framework não o conseguia ver. Perfeitamente instalado. Completamente invisível.
Perante isso, a medida óbvia é colocar uma cópia onde o analisador vai procurar. Copiar a pasta para o repositório de implementação. Fazer checkout ao lado do framework. Criar um symlink. Cada uma dessas opções é uma decisão local razoável e, em conjunto, é assim que se acaba com cinco cópias e uma implementação que ninguém consegue explicar.
A correção: parar de lutar contra o analisador
A versão que lancei esta semana adiciona um comando, executado uma vez após a instalação:
pip install hermes-memory-pgvector
hermes-pgvector install
Esse segundo comando escreve uma pequena camada de adaptação na pasta que o analisador lê — algumas linhas cuja única função é importar o pacote real instalado via pip. O analisador encontra um diretório e fica satisfeito. O Python resolve o import para a biblioteca corretamente instalada.
Essa única camada intermédia reduz cinco cópias a uma. As atualizações passam a ser um upgrade via pip e um reinício. Reverter significa fixar a versão anterior. Nada é vendorizado, nada é feito checkout, nada deriva, e a manutenção de rotina no repositório de outra pessoa já não pode levar consigo a minha camada de memória.
O que mais resultou disso
Consolidar a implementação significou reler o código como deve ser, o que revelou vários bugs reais que vale a pena nomear:
Uma pesquisa por substring que não era uma. Editar uma memória armazenada comparava o seu texto com a base de dados usando SQL LIKE, onde % é um wildcard. Uma memória contendo "revenue grew 15% YoY" podia corresponder — e modificar — linhas não relacionadas. As barras invertidas tinham o problema oposto: um caminho de ficheiro do Windows não correspondia silenciosamente a nada.
Uma fila que descartava trabalho enquanto afirmava estar a terminar. O escritor em segundo plano aceitava escritas e depois, no encerramento, se por acaso estivesse ocupado, abandonava tudo o que ainda estava enfileirado sem uma palavra nos logs.
Um erro enganador. Aponte o plugin para um modelo de embeddings com o tamanho de saída errado e receberia um 404 de um fallback sem relação, em vez de "esperadas 768 dimensões, obtidas 1024."
Nada disto era exótico. Eram todas coisas que uma leitura cuidadosa deteta e um conjunto de testes que passa não deteta.
A lição que continuo a reaprender
Quando uma implementação é uma confusão, a confusão está normalmente a dizer algo verdadeiro sobre o design.
As cinco cópias não eram descuido. Cada uma era uma solução alternativa racional. Eram um sintoma, e o problema subjacente era que o meu pacote não podia ser descoberto da forma como se destinava a ser instalado. Corrigir as soluções alternativas uma a uma teria produzido uma confusão mais arrumada. Corrigir a falha na descoberta tornou as soluções alternativas desnecessárias.
Se mantém algo que as pessoas instalam de uma forma e implementam de outra, vale a pena fechar essa lacuna. Provavelmente está a tentar dizer-lhe alguma coisa.
hermes-memory-pgvector tem licença BSD-3, e está no GitHub e no PyPI.
