Un'analisi approfondita della memoria degli agenti: dalle liste Python ai file markdown, dalla ricerca vettoriale agli ibridi grafo-vettore, e infine, una soluzione pulita e open-source per tutto questo.

Un LLM è stateless per progettazione. Ogni chiamata API ricomincia da capo. La "memoria" che percepisci quando chatti con ChatGPT è un'illusione creata dal reinvio dell'intera cronologia della conversazione a ogni richiesta.
Questo trucco funziona per chat informali. Crolla nel momento in cui provi a costruire un agente reale.
Ecco 7 modalità di fallimento che emergono appena salti la memoria:
- Amnesia contestuale: l'agente chiede informazioni che gli hai già fornito
- Zero personalizzazione: ogni interazione sembra generica
- Fallimento in attività multi-step: lo stato intermedio scompare silenziosamente a metà attività
- Errori ripetuti: nessun ricordo episodico significa gli stessi errori, per sempre
- Nessun accumulo di conoscenza: ogni sessione parte da zero
- Allucinazioni da lacune: quando il contesto trabocca, il modello inventa
- Collasso dell'identità: nessuna continuità, nessuna fiducia
La risposta ovvia è "buttargli più contesto". Ecco perché finestre di token da 128K e 200K sembrano poter risolvere tutto.
Non è così.
La precisione cala oltre il 30% quando le informazioni rilevanti si trovano nel mezzo di un contesto lungo. Questo è il ben documentato effetto "perso nel mezzo".
Il contesto è un budget condiviso: prompt di sistema, documenti recuperati, cronologia della conversazione e output competono tutti per gli stessi token.
Anche a 100K token, l'assenza di persistenza, prioritarizzazione e salienza rende insufficiente la lunghezza grezza del contesto.

La memoria non significa stipare più testo nel prompt. Significa strutturare ciò che l'agente ricorda in modo che possa trovare ciò che conta.
Il quadro delle scienze cognitive che aiuta davvero
La formulazione di Lilian Weng del 2023 è diventata il framework predefinito:
Agente = LLM + Memoria + Pianificazione + Uso di Strumenti.
I quattro pilastri co-eguali.
La sua tassonomia attinge dalle scienze cognitive, dove la memoria umana si divide in tre sistemi:
- Memoria sensoriale cattura l'input percettivo grezzo e lo trattiene per una frazione di secondo. Solo le porzioni a cui presti attenzione vengono trasmesse.
- Memoria di lavoro è dove avviene il pensiero attivo. Contiene circa 7±2 elementi alla volta (scoperta di Miller del 1956). Perdi la concentrazione e i contenuti scompaiono.
- Memoria a lungo termine è un archivio durevole senza limiti pratici di capacità. Il recupero è il collo di bottiglia: puoi immagazzinare milioni di cose e comunque non riuscire a ricordare quella che ti serve.
Ciascuna corrisponde direttamente a un componente nelle architetture moderne degli agenti:

La memoria a lungo termine si divide ulteriormente:
- Episodica: eventi passati specifici ("martedì, il cluster PostgreSQL è andato giù")
- Semantica: fatti e concetti ("PostgreSQL è un database relazionale")
- Procedurale: competenze e flussi di lavoro ("quando un utente richiede un rimborso, prima controlla la data di acquisto")
Il ponte tra episodico e semantico è il consolidamento della memoria: eventi specifici ripetuti che si distillano in conoscenza generale. Un agente che nota "gli utenti preferiscono costantemente i riepiloghi esecutivi" in dozzine di interazioni dovrebbe trasformarlo in una regola riutilizzabile. Senza consolidamento, il tuo agente riproduce eventi individuali invece di imparare da essi.

L'agente minimale, e cosa si rompe per primo
Togli i framework e un agente è un ciclo: percepisci, pensa, agisci.
1class Agent:2 """Agente AI minimale: percepisci, pensa, agisci"""3 def __init__(self):4 self.client = anthropic.Anthropic()5 self.model = "claude-sonnet-4-20250514"67 def run(self, user_input: str) -> str:8 response = self.client.messages.create(9 model=self.model,10 max_tokens=1024,11 messages=[{"role": "user", "content": user_input}],12 )13 return response.content[0].text
Digli "Ho 4 mele", poi chiedi "Ne ho mangiata una, quante ne rimangono?" e non ha idea di quali mele stai parlando. Ogni chiamata esiste in isolamento.
Livello 1: La lista Python
La prima soluzione a cui tutti ricorrono:
1class Agent:2 def __init__(self):3 self.client = anthropic.Anthropic()4 self.messages = [] # L'intera "memoria" è una lista56 def chat(self, user_input: str) -> str:7 self.messages.append({"role": "user", "content": user_input})8 response = self.client.messages.create(9 model="claude-sonnet-4-20250514",10 max_tokens=1024,11 messages=self.messages, # Cronologia completa inviata ogni volta12 )13 reply = response.content[0].text14 self.messages.append({"role": "assistant", "content": reply})15 return reply
Ora funziona il multi-turno. La domanda sulle mele viene risposta correttamente perché l'intera conversazione viene reinviata a ogni chiamata.
Due problemi emergono rapidamente:
- La lista cresce senza limiti. Intorno al turno 200, raggiungi il limite di contesto e i messaggi più vecchi vengono eliminati silenziosamente. Il nome dell'utente dal turno 1 scompare molto prima della battuta spensierata di ieri. Nessuna prioritarizzazione, solo ordine cronologico stretto.
- Tutto vive in RAM. Nel momento in cui il processo Python termina, il tuo agente non ha idea di chi tu sia.
Livello 2: File Markdown per la persistenza
La mossa successiva è scrivere la memoria su disco. Markdown è un adattamento naturale: leggibile dall'uomo, compatibile con Git, e l'agente può leggerlo come testo semplice. Claude Code usa esattamente questo schema con file CLAUDE.md e MEMORY.md.
1class MarkdownMemoryAgent:2 def __init__(self):3 self.client = anthropic.Anthropic()4 self.history_file = Path("memory/conversation_history.md")5 self.facts_file = Path("memory/known_facts.md")67 def save_to_disk(self, role: str, content: str) -> None:8 with open(self.history_file, "a") as f:9 f.write(f"### {role} at {datetime.now().isoformat()}\n{content}\n\n")1011 def load_history(self) -> str:12 if self.history_file.exists():13 return self.history_file.read_text()14 return ""1516 def chat(self, user_input: str) -> str:17 self.save_to_disk("user", user_input)18 history = self.load_history()19 response = self.client.messages.create(20 model="claude-sonnet-4-20250514",21 max_tokens=1024,22 system=f"Conversazione precedente:\n{history}",23 messages=[{"role": "user", "content": user_input}],24 )25 reply = response.content[0].text26 self.save_to_disk("assistant", reply)27 return reply
La persistenza è risolta. Riavvia lo script e la conversazione è ancora su disco. Potresti anche mantenere un file di fatti separato che l'agente estrae nel tempo:
1- Il nome dell'utente è Sarah2- Sarah gestisce il team backend in Acme Corp3- Acme Corp è un'azienda SaaS B2B4- Attualmente sta migrando il database di produzione in una nuova regione AWS
Puoi aprire il file in qualsiasi editor, vedere esattamente cosa sa l'agente e correggerlo a mano. Davvero utile per il prototyping.
Con 4 fatti, funziona perfettamente. Carica l'intero file nel contesto e l'LLM gestisce qualsiasi domanda su Sarah, la sua azienda o il suo settore.
Ora vai avanti veloce di tre mesi. Il tuo agente ha 2.000 fatti estratti e 200 log di conversazione. Sono 500K+ token di markdown su disco, e la tua finestra di contesto è 128K.
Non puoi più caricare tutto. Devi recuperare selettivamente solo i fatti rilevanti per la query corrente. Con file flat, la tua unica opzione è la ricerca per parole chiave:
1# L'utente chiede: "Qual è lo stato della nostra migrazione cloud?"2grep("migrazione cloud", facts_file)3# Restituisce: []4# Il fatto su disco dice "migrazione del database di produzione in una nuova regione AWS."5# Le parole "migrazione cloud" non compaiono da nessuna parte.67# L'utente chiede: "Quale team sta gestendo il lavoro sul database?"8grep("team database", facts_file)9# Restituisce: []10# Un fatto dice che Sarah "gestisce il team backend." Un altro dice che il team11# sta "migrando il database di produzione." Ma nessuna singola riga contiene12# sia "database" che "team" insieme.
A piccola scala, i file markdown funzionano. A scala reale, impongono il recupero per parole chiave, e le parole chiave non possono gestire sinonimi, parafrasi o connessioni tra fatti.
L'informazione è su disco. Ma non puoi caricarla tutta, e la ricerca per parole chiave è troppo fragile per trovare i pezzi giusti.
Se hai usato OpenClaw, hai visto questo scenario. Memorizza la memoria come file di checkpoint markdown, e dopo settimane di uso quotidiano, i fatti precedenti scivolano via silenziosamente mentre il contesto si accumula e viene compattato. L'archiviazione c'è. Il recupero no.
L'archiviazione senza recupero intelligente è una biblioteca senza catalogo.
Livello 3: La ricerca vettoriale e il muro che incontra
Aggiungi embeddings. Spezzetta il tuo markdown, incorpora i pezzi, cerca per similarità coseno. Ora "database" corrisponde a "PostgreSQL" perché i loro vettori vivono vicini nello spazio degli embedding. Il problema dei sinonimi si dissolve.
Poi incontri un nuovo muro. Considera questi tre fatti nel tuo DB vettoriale:
1- "Alice è la lead tecnica del Progetto Atlas"2- "Il Progetto Atlas usa PostgreSQL come archivio dati primario"3- "Il cluster PostgreSQL ha subito un'interruzione martedì"
L'utente chiede: "Il progetto di Alice è stato influenzato dall'interruzione di martedì?"
La query menziona Alice e l'interruzione di martedì, quindi la ricerca vettoriale classifica in alto il primo e il terzo fatto. Ma il ponte critico, "Il Progetto Atlas usa PostgreSQL", non menziona né Alice né martedì. È il pezzo di collegamento, ed è quello che non verrà in superficie.
Ogni fatto è un punto isolato nello spazio degli embedding. Il tessuto connettivo che li collega è invisibile ai vettori.

Questo non è un caso limite. È la forma normale delle domande del mondo reale. La conoscenza aziendale è intrinsecamente relazionale: le persone appartengono a team, i team possiedono progetti, i progetti dipendono da sistemi, i sistemi hanno incidenti. Qualsiasi domanda che attraversa due o più salti supera ciò che il recupero vettoriale piatto può rispondere.
La matrice delle capacità
Ogni livello risolve il dolore precedente ma ne rivela uno più profondo:

Hai bisogno di persistenza, comprensione semantica e ragionamento relazionale in un unico livello di memoria.
Costruire questo da solo significa incollare insieme un database vettoriale, un database a grafo, un archivio relazionale, un estrattore di entità, una pipeline di deduplicazione e un sistema di pesatura degli archi. Sono settimane di lavoro sull'infrastruttura prima di scrivere una singola riga di logica dell'agente.
Ho usato una soluzione che colma questa lacuna in modo pulito. È completamente open-source, gestisce tutti e tre i paradigmi di archiviazione sotto lo stesso tetto, e puoi farlo funzionare in pochi minuti. Parliamo di Cognee.
Cognee: tre archivi, un motore, quattro chiamate
Cognee è un motore di conoscenza open-source costruito per la memoria degli agenti. Combina la ricerca vettoriale con grafi di conoscenza e un livello di provenienza relazionale in un unico sistema.
L'intera superficie API è di quattro chiamate asincrone:
1import cognee23await cognee.add("Il tuo documento qui") # Ingerisci qualsiasi cosa4await cognee.cognify() # Costruisci grafo di conoscenza + embeddings5await cognee.memify() # Auto-migliora la memoria6await cognee.search("La tua query") # Recupera con ragionamento
Dietro queste quattro chiamate si trova un'architettura a tre archivi.

Perché tre archivi e non uno solo?
Ogni archivio cattura una dimensione della conoscenza che gli altri non possono:
- Archivio relazionale → provenienza: da dove provengono i dati, quando sono stati ingeriti, chi ha accesso
- Archivio vettoriale → semantica: cosa significa il contenuto, a cosa è simile
- Archivio a grafo → relazioni: come le entità sono collegate, cosa causa cosa, chi riporta a chi
Appiattisci uno qualsiasi di questi e perdi informazioni che contano per la precisione del recupero.
Lo stack predefinito è SQLite + LanceDB + Kuzu, completamente incorporato e basato su file. pip install cognee più una chiave API LLM e sei operativo.
Niente Docker, niente servizi esterni.
Per la produzione, sostituisci SQLite con Postgres, LanceDB con Qdrant/Pinecone/pgvector e Kuzu con Neo4j/FalkorDB/Neptune.
Stessa API a quattro chiamate in entrambi i casi.
Cosa fa effettivamente cognify?
cognee.cognify() esegue una pipeline multi-stadio che converte il testo grezzo in conoscenza strutturata e interconnessa:
- Classificazione dei documenti per tipo e dominio
- Controllo dei permessi per il controllo degli accessi multi-tenant
- Estrazione di chunk che rispetta la struttura dei paragrafi (non tagli a dimensione fissa)
- Estrazione di entità e relazioni tramite LLM, con deduplicazione automatica tramite hashing del contenuto
- Generazione di riepiloghi per un recupero efficiente
- Indicizzazione duale nell'archivio vettoriale (embeddings) e nell'archivio a grafo (archi)
Il passo di deduplicazione è più importante di quanto sembri. Se la stessa entità appare in 50 documenti, Cognee la fonde in un singolo nodo del grafo con 50 archi in entrata. Il tuo agente non vede più "Alice" come 50 sconosciuti diversi. E la pipeline è incrementale per impostazione predefinita: solo i file nuovi o aggiornati vengono rielaborati.

Ogni nodo del grafo ha un embedding corrispondente. Questa doppia rappresentazione è il trucco principale: entra attraverso i vettori (trova contenuto semanticamente simile) ed esci attraverso il grafo (segui le relazioni verso entità collegate), o viceversa. Questo è ciò che rende possibili le query multi-salto senza sacrificare la ricerca semantica.
Memify: memoria che impara
memify() è ciò che distingue Cognee da qualsiasi strumento di "ingerisci e cerca". Esegue un passaggio di ottimizzazione ispirato al RL sul grafo:
- Rafforzamento dei percorsi utili che hanno portato a un buon recupero
- Potatura dei nodi obsoleti che non sono stati toccati
- Auto-sintonizzazione dei pesi degli archi in base all'uso reale
- Aggiunta di fatti derivati identificando relazioni implicite
Il grafo di un agente di supporto clienti rafforza naturalmente i percorsi attraverso i documenti di prodotto e le politiche di rimborso, mentre lascia decadere gli archi HR raramente interrogati. Il grafo sviluppa il proprio senso di rilevanza nel tempo.

Quattordici modalità di recupero
Cognee offre 14 modalità di ricerca. Quelle a cui ricorrerai effettivamente:

Costruire un agente reale con la memoria di Cognee
Ecco lo schema completo per collegare Cognee al ciclo percepisci-pensa-agisci:
1import cognee2from cognee import SearchType34class CogneeMemoryAgent:5 """Agente con memoria persistente ibrida grafo-vettore."""67 def __init__(self, session_id: str = "default"):8 self.llm_client = OpenAI()9 self.session_id = session_id1011 async def ingest(self, text: str, dataset: str = "main"):12 await cognee.add(text, dataset)13 await cognee.cognify([dataset])1415 async def recall(self, query: str) -> str:16 results = await cognee.search(17 query_text=query,18 query_type=SearchType.GRAPH_COMPLETION,19 session_id=self.session_id,20 )21 return results[0] if results else ""2223 async def chat(self, user_input: str) -> str:24 context = await self.recall(user_input)25 messages = [26 {"role": "system", "content": "Sei utile. Usa il contesto della memoria."},27 {"role": "system", "content": f"Contesto della memoria:\n{context}"},28 {"role": "user", "content": user_input},29 ]30 response = self.llm_client.chat.completions.create(31 model="gpt-4o-mini", messages=messages32 )33 reply = response.choices[0].message.content34 await cognee.add(35 f"Utente: {user_input}\nAssistente: {reply}",36 "conversazioni"37 )38 await cognee.cognify(["conversazioni"])39 return reply
Il ciclo della memoria: ingerisci, estrai, archivia, recupera, rispondi, archivia di nuovo. Ogni turno arricchisce il grafo di conoscenza, e l'elaborazione incrementale significa che paghi solo per indicizzare nuovi contenuti.
La memoria di sessione gestisce automaticamente la risoluzione dei pronomi:
1await cognee.search(query_text="Dove vive Alice?", session_id="conv_1")2await cognee.search(query_text="Cosa fa per lavoro?", session_id="conv_1")3# "lei" si risolve in Alice dal contesto della sessione
Il multi-tenancy è integrato a livello di grafo con permessi per dataset (lettura, scrittura, eliminazione, condivisione). Non separazione di namespace, ma isolamento effettivo a livello di grafo.
Il percorso pratico da seguire
Se stai costruendo un agente oggi, la vera domanda di partenza è: "cosa deve ricordare il mio agente, e che tipo di domande risponderà?"
Se le tue query necessitano solo di ricerca per similarità ("trova conversazioni come questa"), la memoria solo vettoriale funziona. Nel momento in cui le query attraversano i confini delle entità ("Il progetto di Alice è stato influenzato dall'interruzione di martedì?"), hai bisogno di traversal del grafo.
Puoi cablare insieme archivi vettoriali, a grafo e relazionali separati da solo. I team che seguono questa strada di solito bruciano settimane sull'infrastruttura per un livello di memoria che ancora non impara dal proprio utilizzo.
Cognee riduce tutto a quattro chiamate API. Le impostazioni predefinite incorporate ti fanno funzionare in pochi minuti. Backend intercambiabili (Postgres, Qdrant, Neo4j) ti portano in produzione senza cambiare il codice del tuo agente.
L'intelligenza richiede struttura, non solo archiviazione. I tre paradigmi di archiviazione (relazionale, vettoriale, a grafo) non sono opzioni in competizione. Sono livelli complementari dello stesso sistema di memoria. Trattarli in questo modo è ciò che trasforma un wrapper LLM stateless in qualcosa che impara davvero.
Qual è la prossima cosa che vorresti che il tuo agente ricordasse domani e che ha dimenticato oggi? Inizia da lì.
👉 Scopri Cognee su GitHub →, dagli una stella e prova a collegarlo al tuo prossimo agente.
Quattro chiamate asincrone, un pip install, e sei operativo.
Questo è tutto!
Se ti è piaciuto leggere questo:
Trovami →@akshay_pachaar ✔️
Ogni giorno, condivido tutorial e approfondimenti su AI, Machine Learning e le migliori pratiche di vibe coding.





