ALPHA_PROJECT — System Context for GitHub Copilot
Who I am
I am a Cloud Architect with 6 years of Big4 consulting experience (currently at manager level at EY), working daily on Azure, Dynamics 365 / Dataverse, .NET, and enterprise integration patterns. I run a production-grade homelab on Kubernetes at home, and I am building ALPHA_PROJECT as a personal initiative in my spare time (evenings and weekends).
What ALPHA_PROJECT is
ALPHA_PROJECT is a proactive, multi-agent personal AI assistant built entirely on self-hosted infrastructure. It is not a chatbot. It is an autonomous system that:
- Monitors my digital life (email, calendar, home automation, finances, infrastructure)
- Maintains a persistent, structured memory of facts, habits, and preferences
- Takes initiative to notify me of relevant events, correlations, and pending actions
- Interacts with me via voice (Amazon Echo / Alexa custom skill named "Pompeo") and Telegram
- Runs local LLMs on dedicated hardware — no cloud AI inference (except GitHub Copilot completions, available via EY license at zero cost)
The assistant is named Pompeo (the Alexa skill wake word).
Infrastructure
LLM Server (new, dedicated node — outside the Kubernetes cluster)
- CPU: AMD Ryzen 5 4500
- RAM: 16 GB DDR4
- GPU: NVIDIA GeForce RTX 3060 (16 GB VRAM)
- Runtime: Ollama (API-compatible with OpenAI)
- Primary model: Qwen2.5-14B-Instruct Q4_K_M (fits entirely in VRAM, no offload)
- Secondary model: Qwen2.5-Coder-14B-Instruct Q4_K_M (for code-related tasks)
- Embedding model: TBD — to be served via Ollama (e.g.
nomic-embed-text) - Constraint: zero RAM offload — all models must fit entirely in 16 GB VRAM
Kubernetes Homelab Cluster
Production-grade self-hosted stack. Key components relevant to ALPHA_PROJECT:
| Component | Role |
|---|---|
| n8n | Primary orchestrator and workflow engine for all agents |
| Node-RED | Event-driven automation, Home Assistant bridge |
| Patroni / PostgreSQL | Persistent structured memory store — postgres.persistence.svc.cluster.local:5432/pompeo |
| Qdrant | Vector store for semantic/episodic memory — qdrant.persistence.svc.cluster.local:6333 |
| Home Assistant | IoT hub — device tracking, automations, sensors, Google Calendar proxy |
| MikroTik | Network — VLANs, firewall rules, device presence detection |
| Paperless-ngx | Document archive (docs.mt-home.uk) |
| Actual Budget | Personal finance |
| Mealie | Meal planning / recipes |
| Immich | Photo library |
| Outline | Internal wiki / knowledge base |
| Radarr / Sonarr | Media management |
| Jenkins | CI/CD |
| AdGuard | DNS filtering |
| WireGuard | VPN |
| Minio | S3-compatible object storage |
| Longhorn | Distributed block storage |
| Velero | Disaster recovery / backup |
External Services (in use)
- Gmail — primary email
- Google Calendar — calendar (multiple calendars: Work, Family, Formula 1, WEC, Inter, Birthdays, Tasks, Pulizie, Spazzatura, Festività Italia, Varie)
- Amazon Echo — voice interface for Pompeo
- AWS Lambda — bridge between Alexa skill and n8n webhook
- Telegram — notifications, logging, manual document upload
- GitHub Copilot (GPT-4.1 via
api.githubcopilot.com) — LLM completions at zero cost (EY license)
Internal Services / Custom
orchestrator.mt-home.uk— n8n instancedocs.mt-home.uk— Paperless-ngxfilewizard.home.svc.cluster.local:8000— custom OCR microservice (async, job-based API)
Architecture Overview
Multi-Agent Design
ALPHA_PROJECT uses specialized agents, each responsible for a specific data domain. All agents are implemented as n8n workflows.
| Agent | Trigger | Responsibility |
|---|---|---|
| Mail Agent | Cron every 15-30 min | Read Gmail, classify emails, extract facts, detect invoices/bills |
| Finance Agent | Triggered by Mail Agent or Telegram | Process PDF invoices/bills, archive to Paperless, persist to memory |
| Calendar Agent | Cron + on-demand | Read Google Calendar, detect upcoming events, cross-reference with other agents |
| Infrastructure Agent | Cron + alert webhooks | Monitor Kubernetes cluster health, disk usage, failed jobs |
| IoT Agent | Event-driven (Home Assistant webhooks) | Monitor device presence, home state, learn behavioral patterns |
| Newsletter Agent | Cron morning | Digest newsletters, extract relevant articles |
| Proactive Arbiter | Cron (adaptive frequency) + high-priority queue messages | Consume agent outputs, correlate, decide what to notify |
Message Broker (Blackboard Pattern)
Agents do not call each other directly. They write observations to the agent_messages table in PostgreSQL (blackboard pattern). The Proactive Arbiter polls this table, batches low-priority messages, and immediately processes high-priority ones. High-urgency events trigger a direct n8n webhook call bypassing the queue.
ADR: No dedicated message broker — Postgres is sufficient for the expected message volume and avoids operational overhead. Revisit if throughput exceeds 1k messages/day.
Memory Architecture
Three layers of persistence:
1. Structured memory — PostgreSQL (Patroni)
Episodic facts, finance records, reminders, behavioral observations. Fast, queryable, expirable.
-- Generic episodic facts
CREATE TABLE memory_facts (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
source TEXT NOT NULL, -- 'email', 'calendar', 'iot', 'paperless', ...
category TEXT, -- 'finance', 'personal', 'work', 'health', ...
subject TEXT,
detail JSONB, -- flexible per-source payload
action_required BOOLEAN DEFAULT false,
action_text TEXT,
created_at TIMESTAMP DEFAULT now(),
expires_at TIMESTAMP, -- facts have a TTL
qdrant_id UUID -- FK to vector store
);
-- Finance documents (frequent structured queries)
CREATE TABLE finance_documents (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
paperless_doc_id INT,
correspondent TEXT,
amount NUMERIC(10,2),
currency TEXT DEFAULT 'EUR',
doc_date DATE,
doc_type TEXT,
tags TEXT[],
created_at TIMESTAMP DEFAULT now()
);
-- Behavioral context (used by IoT agent and Arbiter)
CREATE TABLE behavioral_context (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
event_type TEXT, -- 'sport_event', 'dog_walk', 'work_session', ...
start_at TIMESTAMP,
end_at TIMESTAMP,
do_not_disturb BOOLEAN DEFAULT false,
home_presence_expected BOOLEAN,
notes TEXT
);
2. Semantic memory — Qdrant — qdrant.persistence.svc.cluster.local:6333
Vector embeddings for similarity search. Three collections with multi-tenant design: isolation via user_id payload field ("martin", "shared", future users).
| Collection | Content |
|---|---|
episodes |
Conversations, episodic facts with timestamp |
knowledge |
Documents, Outline notes, newsletters, knowledge base |
preferences |
Preferences, habits, behavioral patterns |
Each Qdrant point includes a metadata payload for pre-filtering (user_id, source, date, category, action_required) to avoid full-scan similarity searches.
3. Profile memory — PostgreSQL (static table)
User preferences, fixed facts, communication style. Updated manually or via explicit agent action.
IoT Agent — Design Notes
Data Source: Home Assistant
Home Assistant (http://10.30.20.100:8123, HA OS 2026.3.2, Alzano Lombardo BG) is the primary hub for physical-world context. It aggregates Google Pixel 10, Pixel Watch 4, smart home devices, and 25 Google Calendars.
Person allowlist (permanent by design — person.ajada_tahiraj is explicitly excluded):
| Person | Entity | Notes |
|---|---|---|
| Martin Tahiraj | person.martin_tahiraj |
✅ Tracked |
| Ajada Tahiraj | person.ajada_tahiraj |
❌ Excluded (sister — privacy) |
Key sensors for Martin:
| Sensor | Entity ID | Signal |
|---|---|---|
| Activity (Google) | sensor.pixel_10_detected_activity |
still / walking / running / in_vehicle |
| Geocoded location | sensor.pixel_10_geocoded_location |
Human-readable street address |
| EY laptop | device_tracker.ey_hp |
Router tracker — online = laptop on home WiFi |
| Spotify | media_player.spotify_martin |
Current track, playing/paused |
| Sleep duration | sensor.pixel_10_sleep_duration |
Pixel Watch 4 |
| Next alarm | sensor.pixel_10_next_alarm |
Scheduled wake-up |
| Work Profile | binary_sensor.pixel_10_work_profile |
Android Work Profile active |
| Screen on | binary_sensor.pixel_10_interactive |
Phone screen on/off |
| Do Not Disturb | binary_sensor.pixel_10_do_not_disturb |
DND mode |
| Daily steps | sensor.pixel_10_daily_steps |
Pixel Watch 4 |
| Heart rate | sensor.pixel_10_heart_rate |
Pixel Watch 4 |
| GPS Zone | person.martin_tahiraj |
home / not_home / zone name |
Room presence sensors (PIR-based) are considered unreliable — excluded for now.
Sensor Allowlist — ha_sensor_config
Instead of hardcoded rules, the IoT Agent uses a dynamic allowlist stored in Postgres. Sensors are matched by regex pattern, allowing glob-style additions:
CREATE TABLE ha_sensor_config (
id SERIAL PRIMARY KEY,
pattern TEXT NOT NULL, -- regex pattern, e.g. 'sensor\.pixel_10_.*'
user_id TEXT NOT NULL,
group_name TEXT NOT NULL, -- 'mobile_device' | 'work_presence' | 'entertainment' | ...
description TEXT,
active BOOLEAN NOT NULL DEFAULT true
);
-- Seed entries
INSERT INTO ha_sensor_config (pattern, user_id, group_name, description) VALUES
('sensor\.pixel_10_.*', 'martin', 'mobile_device', 'All Pixel 10 sensors'),
('device_tracker\.ey_hp', 'martin', 'work_presence', 'EY Laptop router tracker'),
('media_player\.spotify_martin', 'martin', 'entertainment', 'Spotify'),
('binary_sensor\.pixel_10_.*', 'martin', 'mobile_device', 'Pixel 10 binary sensors'),
('person\.martin_tahiraj', 'martin', 'presence', 'Martin GPS zone state');
This allows adding new sensors (e.g. sensor.pixel_watch_.*) without workflow changes.
Activity State Machine (LLM-based — no fixed rules)
The IoT Agent sends a snapshot of all allowlisted sensor values to GPT-4.1 and asks it to infer the current activity label and confidence. No if/else rules are coded — the LLM performs inference.
Example LLM output:
{
"activity": "home_working",
"confidence": 0.92,
"do_not_disturb": true,
"location": "home",
"notes": "Laptop EY online, work profile attivo, orario lavorativo 09-18"
}
Activity labels: sleeping, home_relaxing, home_working, commuting, at_office, out_errands, out_with_dog, exercising, traveling, unknown.
Three-Layer Data Flow
| Layer | Trigger | Frequency | Output |
|---|---|---|---|
| Webhook | HA automation (zone change, motion) | Event-driven | Immediate agent_messages entry |
| Polling | n8n cron | Every 20 min | Sensor snapshot → LLM → behavioral_context |
| Daily cron | n8n cron midnight | Once/day | Day summary → Qdrant episodes embedding |
Historical Bootstrap
One-time job: last 12 months of HA sensor history → daily LLM summaries → Qdrant episodes.
- Source: HA History API (
/api/history/period/{start}?filter_entity_id=...) - Output: one Qdrant point per day per user, with full behavioral context
Confidence-Gated Clarification
When activity inference confidence < 0.6, or when Pompeo detects a potential life change (new employer from emails, travel pattern, etc.), it asks Martin directly via Telegram:
"Ciao Martin, sto notando email di Avanade — lavori ancora per EY o sei passato lì? 🤔"
Pompeo updates user_profile or memory_facts with the confirmed fact and adjusts its confidence threshold.
Calendar Agent — Design Notes
Design Decisions
- Data source: Google Calendar events fetched via Home Assistant REST API (
/api/calendars/{entity_id}?start=&end=) — HA proxies all 25 calendars and removes the need for a direct Google OAuth credential in n8n. - Dedup:
memory_facts.source_refstores the HA event UID;ON CONFLICT (user_id, source, source_ref) WHERE source_ref IS NOT NULL DO NOTHINGprevents duplicates. - LLM enrichment: GPT-4.1 classifies each event in batch (category, action_required, do_not_disturb, priority, behavioral_context, pompeo_note).
- No Qdrant embedding yet (Phase 2): individual events go to Postgres only; a weekly aggregated embedding will be added later.
Calendars Tracked
| Calendar | Entity ID | Category | User |
|---|---|---|---|
| Lavoro | calendar.calendar |
work | martin |
| Famiglia | calendar.famiglia |
personal | martin |
| Spazzatura | calendar.spazzatura |
chores | martin |
| Pulizie | calendar.pulizie |
chores | martin |
| Formula 1 | calendar.formula_1 |
leisure | martin |
| WEC | calendar.lm_wec_fia_world_endurance_championship |
leisure | martin |
| Inter | calendar.inter_calendar |
leisure | martin |
| Compleanni | calendar.birthdays |
social | martin |
| Varie | calendar.varie |
misc | martin |
| Festività Italia | calendar.festivita_in_italia |
holiday | shared |
| Films (Radarr) | calendar.films |
leisure | martin |
| Serie TV (Sonarr) | calendar.serie_tv |
leisure | martin |
n8n Workflow
📅 Pompeo — Calendar Agent [Schedule] — ID 4ZIEGck9n4l5qaDt
⏰ Schedule (06:30) → 📅 Imposta Range → 🔑 Token Copilot
→ 📋 Prepara Calendari (12 items)
→ 📡 HA Fetch (×12, one per calendar)
→ 🏷️ Estrai ed Etichetta (tagged events, flat)
→ 📝 Prepara Prompt (dedup + LLM prompt)
→ 🤖 GPT-4.1 (batch classify all events)
→ 📋 Parse Risposta
→ 💾 Postgres Upsert (memory_facts, per event, ON CONFLICT DO NOTHING)
→ 📦 Aggrega → ✍️ Prepara Messaggio → 📱 Telegram Briefing
Embedding Strategy
- Embeddings are generated via Ollama (
nomic-embed-textor equivalent) once the LLM server is online - During bootstrap phase: embeddings generated via GitHub Copilot (
text-embedding-3-smallatapi.githubcopilot.com/embeddings) — same token acquisition pattern already in use - Never embed raw content — always embed LLM-generated summaries + extracted entities
Proactive Notification Logic
The Arbiter runs on an adaptive schedule:
| Time slot | Frequency | Behavior |
|---|---|---|
| 23:00–07:00 | Never | Silence |
| 07:00–09:00 | Once | Morning briefing (calendar, reminders, pending actions) |
| 09:00–19:00 | Every 2-3h | Only high-priority or correlated events |
| 19:00–22:00 | Once | Evening recap + next day preview |
High-priority queue messages bypass the schedule and trigger immediate notification.
Notification is sent via Amazon Echo / Pompeo (TTS) for voice, and Telegram for logging. Every Arbiter decision (notify / discard / defer) is logged to a dedicated Telegram audit channel.
Voice Interface (Pompeo)
- Amazon Echo → Alexa Custom Skill → AWS Lambda (bridge) → n8n webhook → Ollama (Qwen2.5-14B) → TTS response back to Echo
- Wake phrase: "Pompeo"
- Lambda is intentionally thin — it only translates the Alexa request format to the n8n webhook payload and returns the TTS response
Existing n8n Workflows (already in production)
📬 Gmail — Daily Digest [Schedule] (1lIKvVJQIcva30YM)
- Runs every 3 hours (+ test webhook)
- Fetches unread emails from the last 3 hours
- Calls GPT-4.1 (via Copilot) to classify each email: category, sentiment, labels, action_required, whether it has a Paperless-relevant PDF attachment
- Applies Gmail labels, marks as read, trashes spam
- If a bill/invoice PDF is detected → triggers the Upload Bolletta webhook
- Sends a digest report to Telegram
📄 Paperless — Upload Documento [Multi] (GBPFFq8rmbdFrNn9) ✅ Active
Replaces the two retired workflows below. Single core pipeline with two entry points:
- Trigger 1 — Telegram: PDF sent to bot with caption starting with "Documento" → downloads file
- Trigger 2 — Webhook (
POST /webhook/paperless-upload): called by Daily Digest with{email_id, attachment_id, filename, hint, from}→ downloads attachment from Gmail API
Both paths converge:
- FileWizard OCR (async, polls job) → GPT-4.1 metadata inference → Paperless upload → PATCH metadata → Telegram confirmation → FileWizard cleanup
- Dedup: if Paperless returns "duplicate", patches the existing document's metadata instead
🔴 Retired (deactivated):
vbzQ3fgUalOPdcOq— Paperless — Upload Bolletta [Email]ZX5rLSETg6Xcymps— Paperless — Upload Documento [Telegram]
💰 Actual — Import Estratto Conto [Telegram] (qtvB3r0cgejyCxUp) ✅ Active
Imports bank CSV statements (Banca Sella format) into Actual Budget via Telegram upload.
- Trigger: Telegram bot, send CSV file with caption starting with "Estratto"
- Fetches token Copilot → downloads CSV binary from Telegram
- Parses CSV: skips SALDO rows, extracts transactions with
Id. XXXXXXXXXas dedup key - Calls Actual HTTP API to fetch existing payees, categories, and transactions since
min_date - Deduplicates: skips transactions already present (by
imported_id = banca-sella-{Id}) - Splits remaining transactions into batches of 30
- Per batch: GPT-4.1 classifies each transaction (payee, category, notes) → creates missing payees/categories on Actual → imports transactions
- Final Telegram report: imported count, skipped, new payees/categories created
- After report: marks Google Task "Actual - Estratto conto" in list "Finanze" as completed (non-blocking)
CSV format (Banca Sella): ; separator, dd/mm/yyyy dates, . decimal, Row 1 = header, Row 2 = SALDO FINALE (skip), Last row = SALDO INIZIALE (skip).
⏰ Actual — Reminder Estratto Conto [Schedule] (w0oJ1i6sESvaB5W1) ✅ Active
- Daily cron at 09:00 → checks Google Tasks for "Actual - Estratto conto" in "Finanze" list
- If task exists and is not completed (due date ≤ today or no due date) → sends Telegram reminder
- Reminder instructs to upload CSV via Telegram with caption
Estratto [mese]
Common pattern across Paperless + Actual workflows: GitHub Copilot token is obtained fresh at each run (GET https://api.github.com/copilot_internal/v2/token), then used for POST https://api.githubcopilot.com/chat/completions with model gpt-4.1.
🎬 Pompeo — Jellyfin Playback Agent [Webhook] (AyrKWvboPldzZPsM) ✅ Active
Webhook-based — triggered in real time by the Jellyfin Webhook plugin on every PlaybackStart / PlaybackStop event.
- Webhook path:
jellyfin-playback(viahttps://orchestrator.mt-home.uk/webhook/jellyfin-playback) - Normalizes the Jellyfin payload (body is a JSON-encoded string →
JSON.parserequired) - On PlaybackStart: INSERT into
behavioral_context(event_type=watching_media,do_not_disturb=true, metadata innotesJSONB:item,device,item_type) - On PlaybackStop: UPDATE
behavioral_context→ setend_at=now(),do_not_disturb=false - Both events write to
agent_messages(subject:▶️ {title} ({device})or⏹️ …) - User filter: only processes events for
UserId = 42369255a7c64917a28fc26d4c7f8265(Martin) - Jellyfin stop behavior:
PlaybackStopfires only on player close, NOT on pause
Known quirks fixed:
- Jellyfin webhook plugin sends body as
$json.body(JSON string) — mustJSON.parse()before reading fields - Real Jellyfin field names:
Name(notItemName),DeviceName/Client,UserId(no dashes)
🎬 Pompeo — Media Library Sync [Schedule] (o3uM1xDLTAKw4D6E) ✅ Active
Weekly cron — every Sunday at 03:00.
- Fetches all movies from Radarr (
/radarr/api/v3/movie) and all series from Sonarr (/sonarr/api/v3/series) - Merges into unified list:
{type, title, year, genres, status: available|missing|monitored|unmonitored, source, source_id} - GPT-4.1 analysis: extracts
top_genres,preferred_types,library_stats,taste_summary,notable_patterns - Postgres upsert:
memory_facts(source=media_library, source_ref=media_preferences_summary, expires +7d) - Per-item loop: for each movie/series → Ollama
nomic-embed-text(768-dim) → Qdrant upsert intomedia_preferencescollection- Qdrant payload:
{title, year, type, genres, status, source, source_id, expires_at (+6 months)}
- Qdrant payload:
Internal endpoints used:
http://radarr.media.svc.cluster.local:7878/radarr/api/v3/movie?apikey=922d1405ab1147019d98a2997d941765http://sonarr.media.svc.cluster.local:8989/sonarr/api/v3/series?apikey=22140655993a4ff6bf12314813ec6982http://ollama.ai.svc.cluster.local:11434/api/embeddings(model:nomic-embed-text)http://qdrant.persistence.svc.cluster.local:6333(api-key: sealed secretqdrant-api-secret)
🎞️ Pompeo — Jellyfin Watch History Sync [Schedule] (K07e4PPANXDkmQsr) ✅ Active
Daily cron — every day at 04:00.
- Fetches last 100 played/partially-played items for Martin from Jellyfin API
- Endpoint:
/Users/42369255a7c64917a28fc26d4c7f8265/Items?Recursive=true&IncludeItemTypes=Movie,Episode&SortBy=DatePlayed&SortOrder=Descending&Limit=100 - Auth:
Authorization: MediaBrowser Token="d153606c1ca54574a20d2b40fcf1b02e"(Pompeo API key)
- Endpoint:
- Filters items with
PlayCount > 0andLastPlayedDatewithin 90 days - GPT-4.1 analysis: extracts
recent_favorites,preferred_genres,watch_patterns,completion_rate,notes - Postgres upsert:
memory_facts(source=jellyfin, source_ref=watch_history_summary, expires +30d) - Skips silently if no played items found (IF node guards the LLM call)
Jellyfin credentials:
- API Token (app: Pompeo):
d153606c1ca54574a20d2b40fcf1b02e— created viaPOST /Auth/Keys?app=Pompeowith admin session - Martin UserId:
42369255a7c64917a28fc26d4c7f8265(from Jellyfin SQLite DB / webhook payload) - Admin user
adminwith password__Montecarlo00!(local auth, SSO via Authentik is separate)
📅 Pompeo — Calendar Agent [Schedule] (4ZIEGck9n4l5qaDt) ✅ Active
Runs every morning at 06:30 (and on-demand via manual trigger).
- Fetches events for the next 7 days from 12 Google Calendars via Home Assistant REST API (calendar proxy — no Google OAuth needed in n8n)
- Tags each event with calendar name, category, user_id
- GPT-4.1 batch classification: category, action_required, do_not_disturb, priority, behavioral_context, pompeo_note
- Postgres upsert →
memory_facts(source=calendar, source_ref=HA event UID, dedup ON CONFLICT DO NOTHING) - Telegram briefing: daily grouped summary sent to the notification channel
Calendars: Lavoro, Famiglia, Spazzatura, Pulizie, Formula 1, WEC, Inter, Compleanni, Varie, Festività Italia, Films (Radarr), Serie TV (Sonarr).
n8n Credentials (IDs)
| ID | Name | Type |
|---|---|---|
qvOikS6IF0H5khr8 |
Gmail OAuth2 | OAuth2 |
uTXHLqcCJxbOvqN3 |
Telegram account | Telegram API |
vBwUxlzKrX3oDHyN |
GitHub Copilot OAuth Token | HTTP Header Auth |
uvGjLbrN5yQTQIzv |
Paperless-NGX API | HTTP Header Auth |
ZIVFNgI3esCKuYXc |
Google Calendar account | Google Calendar OAuth2 (also used for Tasks API) |
u0JCseXGnDG5hS9F |
Home Assistant API | HTTP Header Auth (long-lived HA token) |
mRqzxhSboGscolqI |
Pompeo — PostgreSQL | Postgres (database: pompeo, user: martin) |
u0JCseXGnDG5hS9F |
Home Assistant API | HTTP Header Auth (long-lived HA token) |
Qdrant Collections
| Collection | Dimensions | Distance | Model | Used by |
|---|---|---|---|---|
media_preferences |
768 | Cosine | nomic-embed-text (Ollama) |
Media Library Sync (B1) |
Qdrant API key: sealed secret qdrant-api-secret in namespace persistence → __Montecarlo00!
Coding Conventions
- n8n workflows: nodes named in Italian, descriptive emoji prefixes on trigger nodes
- Workflow naming:
{icon} {App} — {Azione} {Tipo} [{Sorgente}](e.g.📄 Paperless — Upload Documento [Telegram]) - HTTP nodes: always use
predefinedCredentialTypefor authenticated services already configured in n8n credentials - GPT body: use
contentType: "raw"+rawContentType: "application/json"+JSON.stringify({...})inline expression — neverspecifyBody: string - LLM output parsing: always defensive — handle missing
choices, malformed JSON, empty responses gracefully - Copilot token: always fetched fresh per workflow run, never cached across executions
- Binary fields: Telegram node
file.getwithdownload: truestores binary in field nameddata(notattachment) - Postgres: use UUID primary keys with
gen_random_uuid(), JSONB for flexible payloads, always includecreated_at - Qdrant upsert: always include full metadata payload for filtering; use
message_id/thread_id/doc_idas logical dedup keys
TO-DO
Phase 0 — Infrastructure Bootstrap (prerequisite for everything)
Deploy Qdrant on the Kubernetes cluster✅ 2026-03-21- Collections:
episodes,knowledge,preferences(multi-tenant viauser_idpayload field) - Payload indexes:
user_id,source,category,date,action_required - Endpoint:
qdrant.persistence.svc.cluster.local:6333
- Collections:
Run PostgreSQL migrations on Patroni✅ 2026-03-21- Database
pompeocreato (Zalando Operator) - Tabelle:
user_profile,memory_facts(+source_ref+ dedup index),finance_documents,behavioral_context,agent_messages - Multi-tenancy: campo
user_idsu tutte le tabelle, seedmartin+shared - Script DDL:
alpha/db/postgres.sql
- Database
- Verify embedding endpoint via Copilot (
text-embedding-3-small) as bootstrap fallback Plan migration to local Ollama embedding model once LLM server is online✅ Active —nomic-embed-textviahttp://ollama.ai.svc.cluster.local:11434(768-dim, multilingual)- Create
ha_sensor_configtable in Postgres and seed initial sensor patterns
Phase 1 — Memory Integration into Existing Workflows
-
Daily Digest: after
Parse risposta GPT-4.1, add:- Postgres INSERT into
memory_facts(source=email, category, subject, detail JSONB, action_required, expires_at) - Embedding generation (Copilot endpoint) → Qdrant upsert into
episodes(user_id=martin) - Thread dedup: use
thread_idas logical key, update existing Qdrant point if thread already exists
- Postgres INSERT into
-
Upload Bolletta + Upload Documento (Telegram): after
Paperless - Patch Metadati, add:- Postgres INSERT into
finance_documents(correspondent, amount, doc_date, doc_type, tags, paperless_doc_id) - Postgres INSERT into
memory_facts(source=paperless, category=finance, cross-reference) - Embedding of OCR text chunks → Qdrant upsert into
knowledge(user_id=martin)
- Postgres INSERT into
Phase 2 — New Agents
-
Calendar Agent✅ 2026-03-20 —4ZIEGck9n4l5qaDt- 12 calendari Google via HA proxy, fetch next 7 days
- GPT-4.1 batch classification →
memory_facts(dedup by HA event UID) - Telegram daily briefing at 06:30
- Phase 2: add weekly Qdrant embedding for semantic retrieval
-
Jellyfin Playback Agent✅ 2026-03-21 —AyrKWvboPldzZPsM- Webhook: PlaybackStart →
behavioral_contextINSERT (do_not_disturb=true), PlaybackStop → UPDATE (end_at,do_not_disturb=false) agent_messagespopulated with▶️/⏹️+ title + device- User filter: Martin only (UserId
42369255…)
- Webhook: PlaybackStart →
-
Media Library Sync (B1)✅ 2026-03-21 —o3uM1xDLTAKw4D6E- Weekly (Sunday 03:00): Radarr + Sonarr → GPT-4.1 taste analysis →
memory_facts+ Qdrantmedia_preferences(768-dim, nomic-embed-text)
- Weekly (Sunday 03:00): Radarr + Sonarr → GPT-4.1 taste analysis →
-
Jellyfin Watch History Sync (B2)✅ 2026-03-21 —K07e4PPANXDkmQsr- Daily (04:00): Jellyfin play history (90d window) → GPT-4.1 pattern analysis →
memory_facts
- Daily (04:00): Jellyfin play history (90d window) → GPT-4.1 pattern analysis →
-
Finance Agent (extend beyond Paperless)
- Read Actual Budget export or API
- Persist transactions, monthly summaries to
finance_documents - Trend analysis prompt for periodic financial summary
-
Infrastructure Agent
- Webhook receiver for Kubernetes/Longhorn/Minio alerts
- Cron-based cluster health check (disk, pod status, backup freshness)
- Publishes to message broker with
priority: highfor critical alerts
-
IoT Agent — design complete, implementation pending
- Sensor allowlist via
ha_sensor_configPostgres table (regex-based) - No fixed rules: GPT-4.1 infers activity label + confidence from sensor snapshot
- Three layers: webhook (events) + polling 20min (behavioral_context) + daily cron (Qdrant episodes)
- Historical bootstrap: 12 months HA history → daily LLM summaries → Qdrant
episodes - Confidence-gated clarification: ask Martin via Telegram if confidence < 0.6
- Sensor allowlist via
-
Newsletter Agent
- Separate Gmail label for newsletters (excluded from Daily Digest main flow)
- Morning cron: summarize + extract relevant articles →
knowledge
Phase 3 — Message Broker + Proactive Arbiter
- Implement Proactive Arbiter n8n workflow:
- Adaptive schedule (morning briefing, midday, evening recap)
- Consume
agent_messagesbatch → LLM correlation prompt → structurednotify/defer/discardoutput - High-priority bypass path (direct webhook)
- All decisions logged to Telegram audit channel
- Implement correlation logic: detect when 2+ agents report related events (e.g. IoT presence + calendar event + open reminder)
Phase 4 — Voice Interface (Pompeo)
- Create Alexa Custom Skill ("Pompeo")
- AWS Lambda bridge (thin translator: Alexa request → n8n webhook → TTS response)
- n8n webhook handler: receive transcribed text → prepend memory context → Ollama inference → return TTS string
- TTS response pipeline back to Echo
- Proactive push: Arbiter → Lambda → Echo notification (Alexa proactive events API)
Phase 5 — Generalization and Backlog
- OCR on email attachments in Daily Digest: generalize the ingest pipeline to extract text from any PDF attachment (not just bills), using FileWizard OCR — produce richer embeddings and enable full-text retrieval on any emailed document
- Flusso Cedolino (payslip pipeline):
- Trigger: Gmail label
Lavoro/Cedolinoor Telegram upload - PDF → FileWizard OCR → GPT-4.1 metadata extraction (month, gross, net, deductions)
- Paperless upload with tag
Cedolino - Persist structured data to
finance_documents(custom fields for payslip) - Trend embedding in
knowledgefor finance agent queries
- Trigger: Gmail label
- Behavioral habit modeling: aggregate
behavioral_contextrecords over time, generate periodic "habit summary" embeddings inpreferences - Outline → Qdrant pipeline: sync selected Outline documents into
knowledgeon edit/publish event - Chrome browsing history ingestion (privacy-filtered): evaluate browser extension or local export → embedding pipeline for interest/preference modeling
- "Posti e persone" graph: structured contact/location model in Postgres, populated from email senders, calendar attendees, Home Assistant presence data
- Local embedding model: migrate from Copilot
text-embedding-3-smallto Ollama-served model (e.g.nomic-embed-text) once LLM server is stable