Torna al Blog

Il mio agente continuava a perdere fatti che aveva già salvato

2026-06-184 min read

AIdaemon funziona interamente sulla mia macchina, su modelli open-source come Gemma 4, senza chiamate a API cloud. Salva fatti su di me mentre ci parlo, poi li cerca quando glielo chiedo. Salvarli funzionava già. Trovarli era ciò che continuava a deludermi. Chiedevo qualcosa che sapevo fosse memorizzato e ottenevo risultati inutili, o due fatti sull'argomento giusto ma che mancavano il punto.

La ricerca si basa sul significato. Ogni fatto diventa un vettore quando viene salvato, la mia domanda diventa un vettore quando chiedo, e classifica i fatti in base alla vicinanza dei due. Il problema è che vicino non è uguale a giusto. Una domanda richiama ciò che ne condivide il soggetto, quindi alcuni fatti prolissi sullo stesso argomento mettono in ombra quello breve che contiene effettivamente la risposta. Su una query che continuava a fallire, ho controllato dove si trovasse la risposta. Circa al 30° posto. La ricerca ha letto i primi pochi e si è fermata molto prima di arrivarci.

Perché il succo non è abbastanza

Quella prima ricerca utilizza un bi-encoder, un piccolo modello chiamato all-MiniLM-L6-v2. Codifica ogni fatto da solo quando lo salvi, e la tua query da sola quando cerchi, poi confronta i due. Non li vede mai fianco a fianco. Questo è il motivo per cui è veloce. Incorpori tutto una volta e una ricerca è solo matematica economica sull'archivio, nessuna chiamata al modello quando chiedi.

È anche il motivo per cui l'ordine è sbagliato. Un bi-encoder coglie l'argomento generale e inciampa sulla corrispondenza precisa. Chiedi di una cosa e fa emergere qualsiasi cosa nello stesso quartiere mentre il fatto che volevi affonda. È abbastanza economico da eseguire su ogni fatto che hai, motivo per cui ci si rivolge ad esso quando tutto deve risiedere sul tuo hardware. Semplicemente non è abbastanza attento da classificarli bene. La maggior parte delle volte va bene. Quando ho bisogno di una risposta specifica, fallisce.

Lasciare che un secondo modello legga davvero

La soluzione è un reranker. È un cross-encoder, quindi legge la mia domanda e un fatto candidato insieme, nello stesso passaggio, e valuta quanto bene quel fatto risponde alla domanda. Dove il bi-encoder confrontava due riassunti costruiti separatamente, questo legge la coppia. Migliore domanda, migliore risposta. Lo svantaggio è la velocità. È un'esecuzione di un modello per fatto, quindi non puoi puntarlo su un intero archivio di memoria e aspettare.

Il modo per aggirare questo problema è vecchio, noioso e funziona. Recupera prima, riordina poi. Lascia che il modello economico raccolga un'ampia pila di fatti potenzialmente pertinenti, quindi impiega quello costoso solo su quella breve pila.

In AIdaemon, il bi-encoder estrae i primi 50 candidati con un cutoff lasco di 0.22. Lo mantengo più lasco del 0.30 che uso per la memoria che viene inserita nei prompt da sola, perché i sinonimi ottengono punteggi bassi e un cutoff stretto scarterebbe il fatto giusto prima che il reranker lo veda. La fase uno non deve essere corretta. Deve solo far atterrare la risposta da qualche parte nei 50. Quindi il cross-encoder rilegge tutti i 50 rispetto alla domanda e li riordina. Il fatto bloccato al 30° posto viene finalmente letto alle sue condizioni, salta in alto e torna indietro. Non ho cambiato nulla su come vengono memorizzati i fatti, solo su come vengono ordinati all'uscita.

// fase 1: il bi-encoder lancia una rete ampia (coseno su tutti i fatti attivi)
let mut pool = cosine_rank(&query_vec, &facts, MIN_SCORE); // 0.22
pool.truncate(CANDIDATE_POOL); // 50

// fase 2: il cross-encoder rilegge ogni coppia (query, fatto) e riordina
let docs: Vec<String> = pool.iter().map(|c| c.text()).collect();
let ranked = reranker.rerank(&query, docs, false, None)?;

Il reranker viene eseguito sulla stessa macchina di tutto il resto, tramite il crate fastembed come modello ONNX, senza un'API di rerank da chiamare. Sto usando Jina Reranker v2 Base Multilingual, e ho scelto il multilinguismo di proposito, poiché le mie note ad AIdaemon passano dall'inglese allo spagnolo, e un reranker solo in inglese inciamperebbe esattamente sui fatti che mi interessano di più ottenere correttamente.

Mantenerlo economico

Un secondo modello è una seconda cosa che può rompersi, quindi rimane al guinzaglio corto. Si carica solo la prima volta che una ricerca ne ha bisogno, perché il download non è piccolo. Se il caricamento fallisce, la ricerca torna all'ordine coseno semplice che usava prima. Nulla si rompe, diventa solo meno preciso. E si attiva solo quando chiedo esplicitamente all'agente di cercare qualcosa. La memoria che viene piegata in ogni prompt da sola segue ancora il percorso economico. Eseguire 50 fatti tramite un cross-encoder è ragionevole una volta, quando ho posto una domanda. Ad ogni messaggio sarebbe uno spreco.

Recupera-poi-riordina non è nuovo. I motori di ricerca si basano su di esso da anni. Si scopre che si adatta bene anche alla memoria degli agenti. Il primo modello raccoglie un'ampia pila, il secondo legge quella pila correttamente, e quella seconda lettura costa quasi nulla, cinquanta brevi stringhe attraverso un modello. È il motivo per cui AIdaemon ora mi restituisce il fatto che ha salvato invece di tornare vuoto quando chiedo.

Rimani Aggiornato

Ricevi gli ultimi articoli e approfondimenti direttamente nella tua casella di posta.

Unsubscribe anytime. No spam, ever.