Files
Alpha/CHANGELOG.md
Martin 7e86d6cb04 fix: Calendar Agent - upsert, cleanup cancellati, schedule 30min
- ON CONFLICT DO UPDATE: aggiorna subject/category/detail/expires_at se evento cambia
- Cleanup step: DELETE eventi non più in HA nella finestra 7gg (eventi cancellati)
- Schedule: */30 * * * * (ogni 30 min, prima era 06:30 daily)
- Flusso: Parse GPT → Cleanup → Riemetti → Postgres Upsert → Aggrega → Telegram

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-21 13:53:10 +00:00

11 KiB
Raw Blame History

ALPHA_PROJECT — Changelog

Tutte le modifiche significative al progetto ALPHA_PROJECT sono documentate qui.


[2026-03-21] Calendar Agent — fix sincronizzazione e schedule

Problemi risolti

  • ON CONFLICT DO NOTHINGDO UPDATE: gli eventi modificati (orario, titolo) venivano ignorati. Ora vengono aggiornati in Postgres.
  • Cleanup eventi cancellati: aggiunto step 🗑️ Cleanup Cancellati che esegue DELETE FROM memory_facts WHERE source_ref NOT IN (UID attuali da HA) per la finestra 7 giorni. Se Martin cancella un meeting, sparisce da Postgres al prossimo run.
  • Schedule */30 * * * *: da cron 06:30 giornaliero a ogni 30 minuti — il calendario Postgres è sempre allineato alla source of truth (HA/Google Calendar).

Flusso aggiornato

... → 📋 Parse GPT → 🗑️ Cleanup Cancellati → 🔀 Riemetti → 💾 Upsert → 📦 → 📱

[2026-03-20] Calendar Agent — primo workflow Pompeo in produzione

Cosa è stato fatto

Primo agente Pompeo deployato e attivo su n8n: 📅 Pompeo — Calendar Agent [Schedule] (ID 4ZIEGck9n4l5qaDt).

Design

  • Sorgente dati: Home Assistant REST API usata come proxy Google Calendar — evita OAuth Google diretto in n8n e funziona per tutti i 25 calendari registrati in HA.
  • Calendari tracciati (12): Lavoro, Famiglia, Spazzatura, Pulizie, Formula 1, WEC, Inter, Compleanni, Varie, Festività Italia, Films (Radarr), Serie TV (Sonarr).
  • LLM enrichment: GPT-4.1 (via Copilot) classifica ogni evento: category, action_required, do_not_disturb, priority, behavioral_context, pompeo_note.
  • Dedup: memory_facts.source_ref = HA event UID; ON CONFLICT DO NOTHING su indice unico parziale.
  • Telegram briefing: ogni mattina alle 06:30, riepilogo eventi prossimi 7 giorni raggruppati per calendario.

Migrazioni DB applicate

  • ALTER TABLE memory_facts ADD COLUMN source_ref TEXT — colonna per ID esterno di dedup
  • CREATE UNIQUE INDEX memory_facts_dedup_idx ON memory_facts (user_id, source, source_ref) WHERE source_ref IS NOT NULL
  • CREATE INDEX idx_memory_facts_source_ref ON memory_facts (source_ref) WHERE source_ref IS NOT NULL

Credential n8n create

ID Nome Tipo
u0JCseXGnDG5hS9F Home Assistant API HTTP Header Auth
mRqzxhSboGscolqI Pompeo — PostgreSQL Postgres (pompeo/martin)

Flusso workflow

⏰ Schedule (06:30) → 📅 Range → 🔑 Token Copilot
  → 📋 Calendari (12 items) → 📡 HA Fetch (×12) → 🏷️ Estrai + Tag
  → 📝 Prompt (dedup) → 🤖 GPT-4.1 → 📋 Parse
  → 💾 Postgres Upsert (memory_facts) → 📦 Aggrega → 📱 Telegram

[2026-03-21] ADR — Message Broker: nessun broker dedicato

Decisione

Non verrà deployato un message broker dedicato (né NATS JetStream né Redis Streams). Il blackboard pattern viene implementato interamente su PostgreSQL + webhook n8n.

Ragionamento

Al momento della progettazione iniziale, il broker era necessario per disaccoppiare gli agenti dall'Arbiter. Con l'introduzione della tabella agent_messages nel database pompeo, questo obiettivo è già raggiunto:

Agente n8n  →  INSERT agent_messages  (arbiter_decision = NULL)
Arbiter     →  SELECT WHERE arbiter_decision IS NULL  (polling a cron)
            →  UPDATE arbiter_decision = 'notify' | 'defer' | 'discard'

Il flusso high-priority (bypass immediato dell'Arbiter) viene gestito con una chiamata diretta al webhook n8n dell'Arbiter da parte dell'agente — zero infrastruttura aggiuntiva.

Alternative valutate

Opzione Esito Motivazione
agent_messages su PostgreSQL Adottata Già deployata, persistente, queryabile, audit log gratuito
Redis Streams ⏸ Rimandato Già in cluster, valutabile se volume cresce
NATS JetStream Scartato Nuovo componente da operare, overkill per il volume attuale (pochi msg/ora) e per il caso d'uso single-household

Impatto su README.md

La sezione "Message Broker (Blackboard Pattern)" rimane valida concettualmente. Il campo agent e il message schema definiti nel README vengono rispettati nella tabella agent_messages — cambia solo il mezzo di trasporto (Postgres invece di NATS/Redis).


[2026-03-21] PostgreSQL — Database "pompeo" e schema ALPHA_PROJECT

Overview

Creato il database pompeo sul cluster Patroni (namespace persistence) e applicato lo schema iniziale per la memoria strutturata di Pompeo. Seconda milestone della Phase 0 — Infrastructure Bootstrap.


Modifica manifest Patroni

Aggiunto pompeo: martin nella sezione databases di infra/cluster/persistence/patroni/postgres.yaml. Il database è stato creato automaticamente dallo Zalando Operator senza downtime sugli altri database.

Script DDL idempotente disponibile in: alpha/db/postgres.sql


Design decision — Multi-tenancy anche in PostgreSQL

Coerentemente con la scelta adottata per Qdrant, tutte le tabelle includono il campo user_id TEXT NOT NULL DEFAULT 'martin'. I valori 'martin' e 'shared' sono seedati in user_profile come utenti iniziali del sistema.

Aggiungere un nuovo utente in futuro non richiede modifiche allo schema — è sufficiente inserire una riga in user_profile e usare il nuovo user_id negli INSERT.


Design decision — agent_messages come blackboard persistente

La tabella agent_messages implementa il blackboard pattern del message broker: ogni agente n8n inserisce le proprie osservazioni con arbiter_decision = NULL (pending). Il Proactive Arbiter legge i messaggi in coda, decide (notify / defer / discard) e aggiorna arbiter_decision, arbiter_reason e processed_at.

Rispetto a usare solo NATS/Redis come broker, questo approccio garantisce un audit log permanente di tutte le osservazioni e decisioni, interrogabile via SQL per debug, tuning e analisi storiche.


Schema creato

5 tabelle nel database pompeo:

Tabella Ruolo
user_profile Preferenze statiche per utente (lingua, timezone, stile notifiche, quiet hours). Seed: martin, shared
memory_facts Fatti episodici prodotti da tutti gli agenti, con TTL (expires_at) e riferimento al punto Qdrant (qdrant_id)
finance_documents Documenti finanziari strutturati: bollette, fatture, cedolini. Include raw_text per embedding
behavioral_context Contesto IoT/comportamentale per l'Arbiter: DND, home presence, tipo evento
agent_messages Blackboard del message broker — osservazioni agenti + decisioni Arbiter

15 index totali:

Index Tabella Tipo
idx_memory_facts_user_source_cat memory_facts (user_id, source, category)
idx_memory_facts_expires memory_facts (expires_at) WHERE NOT NULL
idx_memory_facts_action memory_facts (user_id, action_required) WHERE true
idx_finance_docs_user_date finance_documents (user_id, doc_date DESC)
idx_finance_docs_correspondent finance_documents (user_id, correspondent)
idx_behavioral_ctx_user_time behavioral_context (user_id, start_at, end_at)
idx_behavioral_ctx_dnd behavioral_context (user_id, do_not_disturb) WHERE true
idx_agent_msgs_pending agent_messages (user_id, priority, created_at) WHERE pending
idx_agent_msgs_agent_type agent_messages (agent, event_type, created_at)
idx_agent_msgs_expires agent_messages (expires_at) WHERE pending AND NOT NULL

Phase 0 — Stato aggiornato

  • Deploy Qdrant sul cluster 2026-03-21
  • Collections Qdrant con multi-tenancy user_id 2026-03-21
  • Payload indexes Qdrant 2026-03-21
  • Database pompeo + schema PostgreSQL 2026-03-21
  • Verify embedding endpoint via Copilot (text-embedding-3-small)
  • Migrazione a Ollama nomic-embed-text (quando LLM server è online)

[2026-03-21] Qdrant — Deploy e setup collections (Phase 0)

Overview

Completato il deploy di Qdrant v1.17.0 sul cluster Kubernetes (namespace persistence) e la creazione delle collections per la memoria semantica di Pompeo. Questa è la prima milestone della Phase 0 — Infrastructure Bootstrap.


Deploy infrastruttura

Qdrant deployato via Helm chart ufficiale (qdrant/qdrant) nel namespace persistence, coerente con il pattern infrastrutturale esistente (Longhorn storage, Sealed Secrets, ServiceMonitor Prometheus).

Risorse create:

Risorsa Dettaglio
StatefulSet qdrant 1/1 pod Running, image qdrant/qdrant:v1.17.0
PVC qdrant-storage-qdrant-0 20Gi Longhorn RWO
Service qdrant ClusterIP — porte 6333 (REST), 6334 (gRPC), 6335 (p2p)
SealedSecret qdrant-api-secret API key cifrata, namespace persistence
ServiceMonitor qdrant Prometheus scraping su :6333/metrics, label release: monitoring

Endpoint interno: qdrant.persistence.svc.cluster.local:6333

Manifest in: infra/cluster/persistence/qdrant/


Design decision — Multi-tenancy collections (Opzione B)

Problema affrontato: nominare le collections martin_episodes, martin_knowledge, martin_preferences avrebbe vincolato Pompeo ad essere esclusivamente un assistente personale singolo, rendendo impossibile — senza migration — estendere il sistema ad altri membri della famiglia in futuro.

Scelta adottata: architettura multi-tenant con 3 collection condivise e isolamento via campo user_id nel payload di ogni punto vettoriale.

episodes      ← user_id: "martin" | "shared" | <futuri utenti>
knowledge     ← user_id: "martin" | "shared" | <futuri utenti>
preferences   ← user_id: "martin" | "shared" | <futuri utenti>

Il valore "shared" è riservato a dati della casa/famiglia visibili a tutti gli utenti (es. calendario condiviso, documenti di casa, finanze comuni). Le query n8n usano un filtro should: [user_id=martin, user_id=shared] per recuperare sia il contesto personale che quello condiviso.

Vantaggi: aggiungere un nuovo utente domani non richiede alcuna modifica infrastrutturale — solo includere il nuovo user_id negli upsert e nelle query.


Collections create

Tutte e 3 le collections sono operative (status green):

Collection Contenuto
episodes Fatti episodici con timestamp (email, IoT, calendario, conversazioni)
knowledge Documenti, note Outline, newsletter, knowledge base
preferences Preferenze, abitudini e pattern comportamentali per utente

Payload schema comune (5 index su ogni collection):

Campo Tipo Scopo
user_id keyword Filtro multi-tenant ("martin", "shared")
source keyword Origine del dato ("email", "calendar", "iot", "paperless", …)
category keyword Dominio semantico ("finance", "work", "personal", …)
date datetime Timestamp del fatto — filtrabile per range
action_required bool Flag per il Proactive Arbiter

Dimensione vettori: 1536 (compatibile con text-embedding-3-small via GitHub Copilot — bootstrap phase). Da rivedere alla migrazione verso nomic-embed-text su Ollama.


Phase 0 — Stato al momento del deploy Qdrant

  • Deploy Qdrant sul cluster
  • Creazione collections con multi-tenancy user_id
  • Payload indexes: user_id, source, category, date, action_required
  • Run PostgreSQL migrations su Patroni completato nella sessione stessa