Connesso · memoria volatile

Memoria

I quattro livelli

Il sistema ha quattro livelli di memoria, indipendenti fra loro. Tenerli separati è ciò che permette di far crescere ciascuno senza rompere gli altri.

LivelloDove viveDurataA cosa serve
Stato del grafo (checkpoint)tabelle di LangGraphper threadriprendere l'esecuzione da dove era rimasta
Storico messaggiliagraph.messagesper threadmostrare la conversazione, analizzarla, esportarla
Riassuntoliagraph.thread_summariesper threadtenere corto il contesto nelle chat lunghe
Memoria a lungo termineliagraph.memoriesper utentericordare fatti oltre la singola conversazione

Il collante è il thread_id: è lo stesso identificativo per il checkpointer di LangGraph e per le nostre tabelle.


1. Stato del grafo (breve termine)

Modulo: app/memory/checkpointer.py.

Ad ogni passo LangGraph serializza l'intero stato e lo associa al thread. Serve a:

  • continuità: il messaggio successivo riprende con tutto il contesto;
  • time travel: si può rileggere e ripartire da un checkpoint precedente;
  • human-in-the-loop: una run può essere sospesa e ripresa dopo un'approvazione (interrupt).

Con DATABASE_URL configurata si usa AsyncPostgresSaver, che crea da sé le proprie tabelle nello schema public al primo avvio. Senza database si usa InMemorySaver: stessa interfaccia, nessuna persistenza.

Con Supabase usa la connection string del pooler in modalità session (porta 5432). In modalità transaction i prepared statement non sopravvivono fra le transazioni; per questo il pool viene aperto con prepare_threshold=None.

2. Storico dei messaggi

Modulo: app/memory/threads.py.

I checkpoint sono ottimizzati per l'esecuzione, non per la lettura. Per la UI e per le analisi serve una forma interrogabile: chi ha detto cosa, quale agente ha risposto, in quale run. Da qui nascono l'elenco delle conversazioni e la colonna "Richiesta" nella tabella delle esecuzioni.

3. Riassunto progressivo

Modulo: app/memory/summarizer.py.

Gli ultimi MEMORY_WINDOW messaggi (default 20) restano testuali. Tutto ciò che li precede viene condensato in un riassunto aggiornato di volta in volta con il modello economico (FAST_MODEL), e reso disponibile agli agenti come parte del contesto.

L'aggiornamento avviene dopo aver risposto all'utente, in un task in background: comprimere non deve aggiungere latenza percepita.

Disattivabile con MEMORY_SUMMARY_ENABLED=false.

4. Memoria a lungo termine

Modulo: app/memory/long_term.py.

Fatti che devono valere anche fra un mese: preferenze, ruolo, vincoli, progetti. Sono gli agenti a decidere cosa memorizzare, tramite due tool:

  • remember_fact(content, importance) — salva un fatto;
  • recall_facts(query) — cerca fra i fatti salvati.

user_id e thread_id non sono parametri del tool: vengono letti dalla RunnableConfig della run. Il modello non può quindi scrivere o leggere nella memoria di un altro utente, nemmeno se glielo si chiede esplicitamente.

Oltre ai tool, ad ogni turno le memorie pertinenti al messaggio vengono recuperate e iniettate nel contesto: l'agente le ha davanti anche senza chiedere.

Kind usati dal sistema:

KindChi lo scriveA cosa serve
factremember_factfatti generici
ruleRelationship, onboardingregole relazionali durature
kpiRelationshipKPI emozionali
platformRelationship, onboardingcome presentarsi su un canale
nudge_outcomeworkflow sollecitocosa ha funzionato / no
client_setupworkflow onboardingconfigurazione comunicazione del creditore
team_policyworkflow onboardingvincoli operativi del team agentico

Ricerca: perché non ci sono embedding

Il recupero usa la similarità trigram di Postgres (pg_trgm). Motivi:

  • zero infrastruttura aggiuntiva e zero costi di embedding;
  • risultati ispezionabili con una query SQL;
  • su poche migliaia di fatti brevi funziona bene.

Quando servirà la ricerca semantica, il punto da cambiare è uno solo: la query dentro recall().

-- 1. estensione e colonna
create extension if not exists vector;
alter table liagraph.memories add column embedding vector(1024);
create index on liagraph.memories using hnsw (embedding vector_cosine_ops);
# 2. in recall(): sostituire similarity(content, $2) con la distanza coseno
#    order by embedding <=> $2::vector
#    calcolando l'embedding con Amazon Titan Embeddings via Bedrock.

Il resto del sistema non cambia: remember, recall e i tool mantengono la stessa firma.


Il contesto che arriva agli agenti

services.chat.build_context compone il testo iniettato nei prompt:

Riassunto della conversazione finora:
<riassunto progressivo>

Cosa sai già dell'utente (memoria a lungo termine):
- lavora su un framework multi-agent chiamato LiaGraph
- preferisce risposte concise in italiano

Ogni agente lo riceve nella parte finale del proprio system prompt (agents/builder.py), dopo l'identità comune e le istruzioni specifiche.


Regolazioni

VariabileDefaultEffetto
MEMORY_WINDOW20messaggi tenuti testuali prima della compressione
MEMORY_SUMMARY_ENABLEDtrueattiva il riassunto progressivo
LONG_TERM_MEMORY_ENABLEDtrueattiva memorizzazione e recupero dei fatti
FAST_MODELclaude-haiku-3-5modello usato per i riassunti

Ispezionare e correggere la memoria

Un sistema che ricorda senza che si possa vedere cosa ricorda è ingestibile. Gli endpoint /api/memories permettono di elencare, cercare, aggiungere a mano e cancellare i fatti memorizzati.

curl "http://localhost:8000/api/memories?user_id=anonymous"
curl "http://localhost:8000/api/memories/search?q=preferenze&user_id=anonymous"
curl -X DELETE "http://localhost:8000/api/memories/<id>"