Crea agenti che non dimenticano mai

@akshay_pachaar
INGLESE3 mesi fa · 13 apr 2026
625K
1.9K
305
46
4.1K

TL;DR

Un approfondimento tecnico sui sistemi di memoria per agenti AI, dall'evoluzione delle semplici liste al motore open-source graph-vector di Cognee per un ragionamento relazionale e persistente.

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.

Akshay 🚀 - inline image

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:

  1. Amnesia contestuale: l'agente chiede informazioni che gli hai già fornito
  2. Zero personalizzazione: ogni interazione sembra generica
  3. Fallimento in attività multi-step: lo stato intermedio scompare silenziosamente a metà attività
  4. Errori ripetuti: nessun ricordo episodico significa gli stessi errori, per sempre
  5. Nessun accumulo di conoscenza: ogni sessione parte da zero
  6. Allucinazioni da lacune: quando il contesto trabocca, il modello inventa
  7. 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.

Akshay 🚀 - inline image

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:

Akshay 🚀 - inline image

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.

Akshay 🚀 - inline image

L'agente minimale, e cosa si rompe per primo

Togli i framework e un agente è un ciclo: percepisci, pensa, agisci.

python
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"
6
7 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:

python
1class Agent:
2 def __init__(self):
3 self.client = anthropic.Anthropic()
4 self.messages = [] # L'intera "memoria" è una lista
5
6 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 volta
12 )
13 reply = response.content[0].text
14 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.

python
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")
6
7 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")
10
11 def load_history(self) -> str:
12 if self.history_file.exists():
13 return self.history_file.read_text()
14 return ""
15
16 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].text
26 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:

text
1- Il nome dell'utente è Sarah
2- Sarah gestisce il team backend in Acme Corp
3- Acme Corp è un'azienda SaaS B2B
4- 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:

python
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.
6
7# 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 team
11# sta "migrando il database di produzione." Ma nessuna singola riga contiene
12# 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:

text
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.

Akshay 🚀 - inline image

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:

Akshay 🚀 - inline image

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:

python
1import cognee
2
3await cognee.add("Il tuo documento qui") # Ingerisci qualsiasi cosa
4await cognee.cognify() # Costruisci grafo di conoscenza + embeddings
5await cognee.memify() # Auto-migliora la memoria
6await cognee.search("La tua query") # Recupera con ragionamento

Dietro queste quattro chiamate si trova un'architettura a tre archivi.

Akshay 🚀 - inline image

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:

  1. Classificazione dei documenti per tipo e dominio
  2. Controllo dei permessi per il controllo degli accessi multi-tenant
  3. Estrazione di chunk che rispetta la struttura dei paragrafi (non tagli a dimensione fissa)
  4. Estrazione di entità e relazioni tramite LLM, con deduplicazione automatica tramite hashing del contenuto
  5. Generazione di riepiloghi per un recupero efficiente
  6. 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.

Akshay 🚀 - inline image

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.

Akshay 🚀 - inline image

Quattordici modalità di recupero

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

Akshay 🚀 - inline image

Costruire un agente reale con la memoria di Cognee

Ecco lo schema completo per collegare Cognee al ciclo percepisci-pensa-agisci:

python
1import cognee
2from cognee import SearchType
3
4class CogneeMemoryAgent:
5 """Agente con memoria persistente ibrida grafo-vettore."""
6
7 def __init__(self, session_id: str = "default"):
8 self.llm_client = OpenAI()
9 self.session_id = session_id
10
11 async def ingest(self, text: str, dataset: str = "main"):
12 await cognee.add(text, dataset)
13 await cognee.cognify([dataset])
14
15 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 ""
22
23 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=messages
32 )
33 reply = response.choices[0].message.content
34 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:

python
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.

Salva con un clic

Leggi in profondità gli articoli virali con l’AI di YouMind

Salva la fonte, fai domande mirate, riassumi l’argomentazione e trasforma un articolo virale in note riutilizzabili in un unico spazio di lavoro AI.

Scopri YouMind
Per i creator

Trasforma il tuo Markdown in un articolo 𝕏 pulito

Quando pubblichi i tuoi testi lunghi, formattare immagini, tabelle e blocchi di codice per 𝕏 è una seccatura. YouMind trasforma un'intera bozza Markdown in un articolo 𝕏 pulito e pronto da pubblicare.

Prova Markdown verso 𝕏

Altri pattern da decodificare

Articoli virali recenti

Esplora altri articoli virali