Files
workspace/docs/enanch_memorie_v2_mem.md
Roberto Musso e08fa429d1 Update app name
2026-04-08 23:27:57 +02:00

17 KiB

Enhanced Memory V2 - Analisi e Progettazione

Stato: FASE 2 - Analisi e Proposta completata


1. DECISIONI PRESE

Domanda Risposta
Privacy Backend PUÒ processare plaintext in-memory per estrazione. NO persistenza plaintext
SaaS vs In-House Solo in-house. Nessuna dipendenza Supermemory API
Feature target Graph Memory, Contradiction Resolution, Forgetting/Decay, User Profiles, LLM Episode Summarization
Scala utenti < 100 (early stage)
Budget LLM Minimizzare. Preferire modelli piccoli/euristiche dove possibile
Semantic search NON richiesta in questa fase (keyword fallback accettato per ora)

2. IMPLEMENTAZIONE ATTUALE (AdiuvAI)

Architettura Memoria (MemGPT-style, 4 livelli)

Livello Tabella Stato
Core memory_core Funzionante - key/value preferenze
Associativa memory_associative Parziale - keyword fallback, no semantic
Episodica memory_episodic Funzionante - troncamento 200 char
Proattiva memory_proactive Schema vuoto, nessuna logica

Punti di Forza

  • E2E encryption per-user (Fernet)
  • 9 tool agente per memoria
  • Context injection automatica pre-LLM
  • Episodi auto-salvati post-conversazione

Gap vs Supermemory (ciò che manca)

  1. Nessuna estrazione automatica di fatti dalle conversazioni
  2. Nessuna relazione tra memorie (graph UPDATE/EXTEND/DERIVE)
  3. Nessun forgetting/decay temporale
  4. Nessuna risoluzione contraddizioni
  5. Nessun user profile auto-generato
  6. Episodi troncati a 200 char senza summarization
  7. Proactive memory non implementata

IL PROBLEMA CENTRALE: context selection cieca

Il metodo _load_associative() fa:

SELECT * FROM memory_associative
WHERE user_id = ?
ORDER BY updated_at DESC    ordina per DATA, non per rilevanza
LIMIT 5

Il messaggio dell'utente NON viene usato per filtrare. Ritorna i 5 fatti più recenti, anche se totalmente irrilevanti alla domanda. Lo stesso per episodic (ultimi 10 per data).


3. COME SUPERMEMORY SALVA I DATI (non usa MD!)

Chiarimento: Supermemory NON salva file MD

Supermemory accetta MD come formato di input (insieme a PDF, JSON, codice, ecc.), ma i dati vengono trasformati e salvati in PostgreSQL + vector embeddings (Cloudflare AI).

Schema di storage Supermemory (da codice sorgente)

Documents table (Drizzle ORM + Postgres)
├── id, customId, orgId, userId
├── content         ← raw content originale
├── title, summary  ← generati da LLM
├── type            ← 'text' | 'web' | 'pdf' | 'md' | ecc.
├── status          ← 'queued' | 'extracting' | 'chunking' | 'embedding' | 'done'
├── metadata        ← JSON key/value filtrabile
├── tokenCount, wordCount, chunkCount
├── summaryEmbedding (vector)
├── containerTags[] ← namespace isolation (user_id, project_id)
└── createdAt, updatedAt

MemoryEntries table (fatti estratti)
├── id, memory      ← il fatto estratto ("Mario preferisce risposte concise")
├── version         ← numero di versione del fatto
├── context
│   ├── parents[]   ← [{relation: 'updates'|'extends'|'derives', memory, version}]
│   └── children[]  ← [{relation, memory, version}]
├── similarity      ← score di ricerca
├── metadata
├── sourceDocumentId ← da quale documento è stato estratto
└── updatedAt

Come AdiuvAI salva i dati (confronto)

memory_core (PostgreSQL)
├── key, value_encrypted  ← Fernet AES-128
└── user_id

memory_associative (PostgreSQL + pgvector)
├── content_encrypted     ← cifrato
├── embedding (1536-dim)  ← colonna presente ma MAI usata per search
├── entity_type, entity_id
└── user_id

memory_episodic (PostgreSQL)
├── summary_encrypted     ← "User: [200 char]\nAssistant: [200 char]"
├── session_id
└── user_id

memory_proactive (PostgreSQL)
├── pattern_encrypted, confidence ← schema vuoto, nessun dato
└── user_id

Differenza chiave nel salvataggio

Supermemory AdiuvAI
Cosa salva Fatti strutturati estratti da LLM Testo grezzo cifrato
Relazioni Graph con UPDATE/EXTEND/DERIVE + versioning Nessuna relazione
Embeddings Generati e usati attivamente per search Colonna presente ma inutilizzata
Encryption Nessuna (plaintext) Fernet per-user
Processing Pipeline: Extract → Chunk → Embed → Index Store diretto senza processing

4. SUPERMEMORY - Cosa Prendere Come Ispirazione

NON integriamo Supermemory SaaS. Ci ispiriamo al design per implementare in-house.

Concetti da adottare

  1. Relazioni tra memorie: UPDATE (sostituisce), EXTEND (arricchisce), DERIVE (inferisce)
  2. isLatest flag: tracciamento della versione corrente di un fatto
  3. Automatic forgetting: fatti temporali con expires_at, episodi che decadono
  4. User Profile duale: static (fatti stabili) + dynamic (attività recente)
  5. Fact extraction post-conversazione: LLM estrae fatti strutturati dopo ogni chat

Concetti da NON adottare (non rilevanti)

  • Connectors (Google Drive, Gmail, etc.) — AdiuvAI è un'app desktop, non un aggregatore
  • Multi-modal extraction (PDF, video) — fuori scope
  • Hybrid RAG+Memory search — non richiesto ora

4. ANALISI COSTI/BENEFICI - OPZIONI

OPZIONE SCARTATA: Supermemory SaaS Integration

Dettaglio
Costo $0-19/mo per <100 utenti (Free/Pro)
Pro Implementazione rapida, SOTA benchmarks
Contro Fatali Viola privacy (plaintext obbligatorio), vendor lock-in, latenza API esterna, architettura Cloudflare Workers non self-hostabile facilmente
Verdetto SCARTATA — incompatibile con zero-trust e preferenza in-house

OPZIONE SCELTA: Enhancement In-House Ispirato a Supermemory

Approccio: evoluzione incrementale dell'architettura esistente in 4 fasi.


5. PIANO DI IMPLEMENTAZIONE PROPOSTO

FASE 1 — Memory Graph + Contradiction Resolution

Effort: ~3-5 giorni | Costo LLM extra: ~$0.002/conversazione (GPT-4o-mini)

Cosa cambia nel DB:

  • Nuova tabella memory_fact (sostituisce progressivamente memory_associative)
    memory_fact:
      id, user_id
      content_encrypted    -- il fatto estratto, cifrato
      category             -- 'preference' | 'fact' | 'episode' | 'goal' | 'relationship'
      entity_type          -- a cosa si riferisce: 'user' | 'project' | 'task' | 'person'
      entity_id            -- opzionale, FK
      is_latest            -- boolean, come Supermemory
      superseded_by_id     -- FK → memory_fact (relazione UPDATE)
      extends_id           -- FK → memory_fact (relazione EXTEND)
      derived_from_ids     -- JSON array di FK (relazione DERIVE)
      confidence           -- 0.0-1.0
      source               -- 'extracted' | 'explicit' | 'inferred'
      expires_at           -- nullable, per fatti temporali
      last_accessed_at     -- per decay scoring
      created_at, updated_at
    

Come funziona:

  1. Post-conversazione, GPT-4o-mini riceve transcript (ultimi 2000 char) + prompt strutturato
  2. LLM estrae JSON array di fatti: [{content, category, entity_type, is_temporal, expires_at}]
  3. Per ogni fatto estratto, sistema verifica con fatti esistenti (keyword match su decrypt in-memory)
  4. Se contraddizione trovata → vecchio fatto is_latest=false, superseded_by_id=nuovo
  5. Se arricchimento → nuovo fatto ha extends_id=vecchio
  6. Tutto cifrato prima di persistenza

Stima costi LLM (GPT-4o-mini @ $0.15/1M input, $0.60/1M output):

  • Input: ~2500 tokens/conversazione (transcript + system prompt)
  • Output: ~300 tokens (JSON fatti estratti)
  • Costo: $0.0006/conversazione → **$0.06 per 100 conversazioni/giorno**

FASE 2 — Automatic Forgetting + Decay

Effort: ~1-2 giorni | Costo LLM extra: $0 (puro heuristico)

Meccanismi:

  1. TTL-based expiry: fatti con expires_at vengono ignorati dopo la data
  2. Access decay: last_accessed_at + scoring formula: score = confidence * (1 / (1 + days_since_access * 0.05))
  3. Background cleanup (cron/periodic): soft-delete fatti con score < 0.1 da >30 giorni
  4. Episodic consolidation: dopo N episodi per sessione, consolida in un singolo summary

Nessun costo LLM — pura logica temporale e scoring matematico.


FASE 3 — User Profile Auto-Generato

Effort: ~2-3 giorni | Costo LLM extra: ~$0.001/aggiornamento

Come funziona:

  1. Nuova mini-tabella user_profile:
    user_profile:
      user_id (PK)
      static_encrypted   -- JSON: fatti stabili (nome, ruolo, preferenze durature)
      dynamic_encrypted  -- JSON: attività recente (ultimi 3-5 topic, task in corso)
      updated_at
    
  2. Dopo ogni estrazione fatti (Fase 1), profilo viene aggiornato:
    • static: fatti con category IN ('preference', 'fact', 'relationship') e confidence > 0.7
    • dynamic: ultimi 5 fatti con category = 'goal' o episodi recenti
  3. Profilo iniettato nel system prompt a OGNI conversazione (prima del context attuale)
  4. Aggiornamento trigger: post-extraction, batch di fatti nuovi → GPT-4o-mini "aggiorna profilo"

Stima costi:

  • Input: ~1000 tokens (profilo attuale + nuovi fatti)
  • Output: ~500 tokens (profilo aggiornato)
  • Costo: ~$0.0005/aggiornamento → trascurabile

Alternativa zero-costo LLM: il profilo static è calcolato come aggregazione diretta dei fatti con is_latest=true + alta confidence. Il dynamic è gli ultimi N episodi. Nessuna LLM call, solo query SQL. Meno elegante ma $0.


FASE 4 — LLM Episode Summarization

Effort: ~1-2 giorni | Costo LLM extra: ~$0.001/episodio

Cosa cambia:

  • Il campo summary_encrypted in memory_episodic passa da troncamento 200 char a summary LLM
  • GPT-4o-mini genera un riassunto strutturato: {topic, user_intent, outcome, key_facts_mentioned}
  • Async: non blocca la risposta. Avviene dopo che la risposta è già stata inviata al client

Stima costi:

  • Input: ~1500 tokens (conversazione completa)
  • Output: ~200 tokens (summary strutturato)
  • Costo: $0.0004/episodio → **$0.04 per 100 conversazioni/giorno**

6. RIEPILOGO COSTI TOTALI

Costi LLM aggiuntivi stimati (< 100 utenti, ~100 conversazioni/giorno)

Fase Costo/conv Costo/giorno Costo/mese
Fase 1 (Fact Extraction) $0.0006 $0.06 ~$1.80
Fase 2 (Forgetting) $0 $0 $0
Fase 3 (User Profile) $0-0.0005 $0-0.05 $0-1.50
Fase 4 (Episode Summary) $0.0004 $0.04 ~$1.20
TOTALE ~$0.001-0.002 ~$0.10-0.15 ~$3-4.50

Confronto con Supermemory SaaS

In-House Supermemory Free Supermemory Pro
Costo/mese ~$3-4.50 (LLM) $0 (ma limiti) $19/mo
Privacy E2E mantenuta Plaintext obbligatorio Plaintext obbligatorio
Limiti Solo LLM rate limits 1M tokens, 10K search 3M tokens, 100K search
Personalizzazione Totale Nessuna Nessuna
Vendor lock-in Zero Alto Alto

Verdetto: l'implementazione in-house costa meno di $5/mese, mantiene la privacy, e offre personalizzazione totale.


7. MATRICE BENEFICI

Feature Impatto UX Effort Priorità
Fact Extraction + Graph ALTO - l'AI ricorda tutto automaticamente Medio P0
Contradiction Resolution ALTO - niente informazioni obsolete Basso (incluso in P0) P0
Automatic Forgetting MEDIO - meno noise nel context Basso P1
User Profile ALTO - personalizzazione immediata Medio P1
Episode Summarization MEDIO - recall migliore Basso P2

8. ANALISI CRITICA: COME SUPERMEMORY INIETTA IL CONTESTO

Il flusso Supermemory (Python SDK)

from supermemory import Supermemory
client = Supermemory()  # richiede SUPERMEMORY_API_KEY

# ── PRE-LLM: recupera profilo + memorie rilevanti ──
profile = client.profile(
    container_tag="user_123",       # = user_id di AdiuvAI
    q="What sneakers should I buy?" # = il messaggio dell'utente
)

# profile.profile.static  → ["Senior engineer at Acme", "Prefers dark mode"]
# profile.profile.dynamic → ["Working on auth migration"]
# profile.search_results  → memorie rilevanti per la query

# ── Assemblaggio system prompt ──
context = f"""Static profile:
{chr(10).join(profile.profile.static)}

Dynamic profile:
{chr(10).join(profile.profile.dynamic)}

Relevant memories:
{chr(10).join(r.get("memory","") for r in profile.search_results.results)}"""

messages = [{"role": "system", "content": f"User context:\n{context}"}, *conversation]
# → passa al tuo LLM

# ── POST-LLM: salva conversazione (Supermemory estrae fatti automaticamente) ──
client.add(
    content="\n".join(f"{m['role']}: {m['content']}" for m in conversation),
    container_tag="user_123",
)

Cosa succede sotto: client.add() → HTTPS → Supermemory cloud

  1. Supermemory riceve il plaintext completo della conversazione
  2. Il loro LLM estrae fatti, preferenze, entità
  3. Costruisce relazioni graph (UPDATE/EXTEND/DERIVE) con fatti esistenti
  4. Aggiorna il profilo utente (static + dynamic)
  5. Applica forgetting su fatti temporali scaduti

Come si confronta con il tuo enrich_context():

Aspetto AdiuvAI (attuale) Supermemory
Dove vive la logica memory_middleware.py nel tuo backend Cloud di terzi
Come recupera contesto 4 query SQL → decrypt in-memory 1 HTTPS call client.profile()
Qualità contesto Raw key/value + troncamenti 200 char Fatti strutturati + profilo curato
Chi estrae fatti Nessuno (o l'utente via tool) LLM automatico su ogni add()
Latenza retrieval ~5-15ms (DB locale) ~50-200ms (HTTPS)
Latenza storage ~2ms (INSERT SQL) ~200-500ms (HTTPS + LLM extraction)
Privacy Plaintext solo in-memory, cifrato a riposo Plaintext permanente su server terzi

9. VALUTAZIONE CRITICA ONESTA

BENEFICI REALI dell'integrazione Supermemory

  1. Extraction automatica — Non dover fare nulla: client.add(conversation) e i fatti vengono estratti. Risparmi ~3-5 giorni dev della Fase 1.

  2. Contradiction resolution SOTA — Il loro graph engine è #1 sui benchmark. Implementarlo in-house richiede un LLM prompt ben ingegnerizzato + logica di matching.

  3. User Profiles pronticlient.profile() restituisce static+dynamic in ~50ms. In-house devi costruire la logica di aggregazione.

  4. Temporal forgetting — Gestiscono scadenza e noise filtering. In-house è semplice (TTL + cron) ma loro lo fanno meglio con LLM.

PROBLEMI CRITICI dell'integrazione

  1. PRIVACY DISTRUTTA — Il punto più grave. Tutto il modello E2E di AdiuvAI si basa su: "il backend non persiste mai plaintext". Supermemory riceve e tiene tutti i dati utente in chiaro. Per un'app che vende privacy, è un dealbreaker.

  2. LATENZA AGGIUNTA — Ogni conversazione aggiunge:

    • +50-200ms PRE-LLM (profile fetch via HTTPS)
    • +200-500ms POST-LLM (add + extraction)
    • vs. ~5-15ms totali con DB locale
    • Su connessione instabile: timeout → memoria persa
  3. SINGLE POINT OF FAILURE — Se supermemory.ai è down, la tua app perde TUTTA la memoria. Non ha fallback locale. Le tue 4 tabelle PostgreSQL attuali sono resilienti.

  4. VENDOR LOCK-IN — I fatti estratti vivono nel loro cloud. Se chiudono, cambi pricing, o limiti free tier → migrazione dolorosa. Con la soluzione in-house hai ownership totale.

  5. COSTI CHE SCALANO MALE — Free tier: 1M tokens/mese = ~250 conversazioni medie. Con 100 utenti attivi:

    • ~30 conv/utente/mese = 3000 conv = ~12M tokens → Scale plan $399/mo
    • In-house: $3-5/mo per le stesse 3000 conv con GPT-4o-mini
  6. ARCHITETTURA OVERHAUL — Devi:

    • Rimuovere/sostituire le 4 tabelle memory
    • Riscrivere i 9 tool dell'agente per usare l'SDK
    • Rimuovere la logica di encryption
    • Cambiare il contratto WebSocket (se i tool memory cambiano)
    • Effort paradossale: più lavoro per integrare che per migliorare in-house
  7. NON SELF-HOSTABILE — Il repo GitHub è MIT ma il core è Cloudflare Workers + KV + Postgres. Self-hosting richiede Cloudflare infra o riscrittura significativa.

BILANCIO FINALE

Pro Peso
Extraction + Graph + Forgetting gratis ★★★★
User profiles automatici ★★★
Zero dev effort per le feature memory ★★★
Totale Pro 10/15
Contro Peso
Privacy distrutta (dealbreaker per il brand) ★★★★★
Vendor lock-in su funzionalità core ★★★★
Costi $399/mo a regime vs $5/mo in-house ★★★★
Latenza +200-700ms per conversazione ★★★
Single point of failure ★★★
Totale Contro 19/25

Verdetto: l'integrazione Supermemory SaaS è netta-negativa per AdiuvAI. I benefici (extraction, graph, profiles) sono replicabili in-house a costo inferiore, senza sacrificare privacy, ownership e resilienza.


10. PROSSIMI PASSI

  • Approvazione piano di miglioramento in-house (4 fasi)
  • Design schema migration per memory_fact e user_profile
  • Implementazione Fase 1 (fact extraction + graph)
  • Test extraction con conversazioni reali
  • Implementazione Fasi 2-4 incrementalmente