From 70b668954ea49e71c5ca2fe7970cb26d49d18093 Mon Sep 17 00:00:00 2001 From: Martin Date: Sat, 21 Mar 2026 15:43:46 +0000 Subject: [PATCH] =?UTF-8?q?feat(db):=20schema=20v2=20=E2=80=94=20contacts,?= =?UTF-8?q?=20memory=5Ffacts=5Farchive,=20entity=5Frefs,=20pompeo=5Fnote?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - New table: contacts (multi-tenant person graph with aliases, details, GIN indexes) - New table: memory_facts_archive (expired facts cleanup destination) - memory_facts: added pompeo_note TEXT and entity_refs JSONB columns - Applied live to Patroni primary (postgres-1, namespace persistence, DB pompeo) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- CHANGELOG.md | 19 +++++++++++++ db/postgres.sql | 74 +++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 90 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ace0d9..be2e7a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,25 @@ Tutte le modifiche significative al progetto ALPHA_PROJECT sono documentate qui. --- +## [2026-03-21] Schema DB v2 — contacts, memory_facts_archive, entity_refs + +### Nuove tabelle + +- **`contacts`**: grafo di persone multi-tenant. Ogni riga modella una relazione `user_id → subject` con `relation`, `city`, `country`, `profession`, `aliases[]`, `born_year`, `details` (narrativa libera per LLM) e `metadata` JSONB. Traversabile ricorsivamente per inferire relazioni di secondo grado (es. Martin → zio Mujsi → figlio Euris → cugino di primo grado da parte di madre). Indici GIN su `subject` (trigram) e `aliases` per similarity search. +- **`memory_facts_archive`**: destinazione del cleanup settimanale dei fatti scaduti. Struttura identica a `memory_facts` + `archived_at` + `archive_reason` (`expired` | `superseded` | `merged`). I fatti archiviati vengono poi condensati in un episodio Qdrant settimanale. + +### Colonne aggiunte a `memory_facts` + +- **`pompeo_note TEXT`**: inner monologue dell'LLM al momento dell'insert — il "perché" del fatto (già in uso nel Calendar Agent, ora standardizzato su tutti i source). +- **`entity_refs JSONB`**: entità estratte dal fatto strutturato — `{people: [], places: [], products: [], amounts: []}`. Permette query SQL su persone/luoghi senza full-text scan (es. `entity_refs->'people' ? 'euris vruzhaj'`). + +### Applicato a + +- `alpha/db/postgres.sql` aggiornato (schema v2) +- Live su Patroni primary (`postgres-1`, namespace `persistence`, DB `pompeo`) + +--- + ## [2026-03-21] Actual Budget — Import Estratto Conto via Telegram ### Nuovi workflow diff --git a/db/postgres.sql b/db/postgres.sql index 9b78a8d..f9006c3 100644 --- a/db/postgres.sql +++ b/db/postgres.sql @@ -1,5 +1,5 @@ -- ============================================================================= --- ALPHA_PROJECT — Database "pompeo" — Schema iniziale +-- ALPHA_PROJECT — Database "pompeo" — Schema v2 -- ============================================================================= -- Applicare su: postgresql://martin@postgres.persistence.svc.cluster.local:5432/pompeo -- @@ -64,7 +64,9 @@ CREATE TABLE IF NOT EXISTS memory_facts ( action_text TEXT, created_at TIMESTAMP NOT NULL DEFAULT now(), expires_at TIMESTAMP, -- NULL = permanente - qdrant_id UUID -- FK logico → collection "episodes" + qdrant_id UUID, -- FK logico → collection "episodes" + pompeo_note TEXT, -- inner monologue dell'LLM al momento dell'insert + entity_refs JSONB -- entità estratte: {people, places, products, amounts} ); CREATE INDEX IF NOT EXISTS idx_memory_facts_user_source_cat @@ -207,8 +209,74 @@ INSERT INTO ha_sensor_config (pattern, user_id, group_name, description) VALUES ON CONFLICT DO NOTHING; +-- ============================================================================= +-- 7. CONTACTS +-- Grafo di persone multi-tenant. Ogni riga = una relazione (user_id → subject). +-- Traversabile dall'LLM: user_id=martin → cugino → euris, poi user_id=euris → padre → mujsi +-- → l'LLM inferisce che Mujsi è lo zio di Martin. +-- Il campo 'details' è narrativa libera ottimizzata per consumo LLM. +-- ============================================================================= +CREATE TABLE IF NOT EXISTS contacts ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + user_id TEXT NOT NULL, -- chi "possiede" questa relazione + subject TEXT NOT NULL, -- nome normalizzato del contatto + relation TEXT NOT NULL, -- 'cugino' | 'zio' | 'madre' | 'moglie' | 'amico' | ... + city TEXT, + country TEXT, + profession TEXT, + aliases TEXT[], -- soprannomi/varianti nome ['Eri', 'Mucho'] + born_year INT, + details TEXT, -- narrativa libera per l'LLM (storia, carattere, note) + metadata JSONB, -- extra strutturato: {email, phone, children, spouse, ...} + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), + UNIQUE (user_id, subject) +); + +CREATE INDEX IF NOT EXISTS idx_contacts_user + ON contacts(user_id); + +CREATE INDEX IF NOT EXISTS idx_contacts_subject_trgm + ON contacts USING gin(subject gin_trgm_ops); -- similarity search su nome + +CREATE INDEX IF NOT EXISTS idx_contacts_aliases + ON contacts USING gin(aliases); -- ricerca per alias/soprannome + + +-- ============================================================================= +-- 8. MEMORY_FACTS_ARCHIVE +-- Fatti scaduti spostati qui dal cleanup settimanale. +-- Struttura identica a memory_facts + campi archivio. +-- Un punto Qdrant "episodio settimanale" riassume il batch archiviato. +-- ============================================================================= +CREATE TABLE IF NOT EXISTS memory_facts_archive ( + id UUID NOT NULL, + user_id TEXT NOT NULL DEFAULT 'martin', + source TEXT NOT NULL, + source_ref TEXT, + category TEXT, + subject TEXT, + detail JSONB, + action_required BOOLEAN NOT NULL DEFAULT false, + action_text TEXT, + pompeo_note TEXT, + entity_refs JSONB, + qdrant_id UUID, + created_at TIMESTAMPTZ NOT NULL, + expires_at TIMESTAMPTZ, + archived_at TIMESTAMPTZ NOT NULL DEFAULT now(), + archive_reason TEXT NOT NULL DEFAULT 'expired' -- 'expired' | 'superseded' | 'merged' +); + +CREATE INDEX IF NOT EXISTS idx_mf_archive_user_date + ON memory_facts_archive(user_id, archived_at DESC); + +CREATE INDEX IF NOT EXISTS idx_mf_archive_source + ON memory_facts_archive(user_id, source, category); + + -- ============================================================================= -- Fine script -- ============================================================================= \echo '✅ Schema pompeo applicato correttamente.' -\echo ' Tabelle: user_profile, memory_facts, finance_documents, behavioral_context, agent_messages, ha_sensor_config' +\echo ' Tabelle: user_profile, memory_facts, memory_facts_archive, contacts, finance_documents, behavioral_context, agent_messages, ha_sensor_config'