- README.md: contesto ALPHA_PROJECT, architettura multi-agent, stack infrastrutturale - CHANGELOG.md: documenta deploy Qdrant v1.17.0 e creazione database pompeo - db/postgres.sql: schema DDL database pompeo (user_profile, memory_facts, finance_documents, behavioral_context, agent_messages) con multi-tenancy user_id - db/qdrant.sh: script per creazione/ripristino collections Qdrant (episodes, knowledge, preferences) con payload indexes Design decisions: - Multi-tenancy via user_id su Qdrant e PostgreSQL (estendibile a nuovi utenti senza modifiche infrastrutturali) - agent_messages come blackboard persistente per il Proactive Arbiter Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
17 KiB
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 |
| Qdrant | Vector store for semantic/episodic memory — qdrant.persistence.svc.cluster.local:6333 |
| NATS / Redis Streams | Message broker between agents (to be chosen and deployed) |
| Authentik | SSO / IAM (OIDC) |
| Home Assistant | IoT hub — device tracking, automations, sensors |
| 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 publish observations to a central message queue (NATS JetStream or Redis Streams — TBD). The Proactive Arbiter consumes the queue, batches low-priority messages, and immediately processes high-priority ones.
Message schema (all agents must conform):
{
"agent": "mail",
"priority": "low|high",
"event_type": "new_fact|reminder|alert|behavioral_observation",
"subject": "brief description",
"detail": {},
"source_ref": "optional reference to postgres record or external ID",
"timestamp": "ISO8601",
"expires_at": "ISO8601 or null"
}
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.
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 Bolletta [Email] (vbzQ3fgUalOPdcOq)
- Triggered by webhook from Daily Digest (payload includes
email_id) - Downloads the PDF attachment from Gmail API
- Fetches Paperless metadata (correspondents, document types, tags, storage paths, similar existing documents)
- Calls GPT-4.1 to infer Paperless metadata (correspondent, doc type, tags, storage path, filename, date)
- Uploads PDF to Paperless, polls task status, patches metadata on the created document
- Sends Telegram confirmation
📄 Paperless — Upload Documento [Telegram] (ZX5rLSETg6Xcymps)
- Triggered by Telegram bot (user sends a PDF with caption starting with "Documento")
- Downloads file from Telegram
- Sends to FileWizard OCR microservice (async job), polls for result
- Same GPT-4.1 metadata inference pipeline as above
- Uploads to Paperless (filename = original filename without extension), patches metadata
- Sends Telegram confirmation with link to document
- Cleans up FileWizard: deletes processed files, then clears job history
Common pattern across all three: 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.
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 |
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,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
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
- Poll Google Calendar (all relevant calendars)
- Persist upcoming events to Postgres (
memory_facts+behavioral_contextfor leisure events) - Weekly cluster embedding (chunk per week, not per event)
- Dedup recurring events: embed only first occurrence, store rest in Postgres only
-
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
- Home Assistant webhook → Node-RED → n8n
- Device presence tracking →
behavioral_context - Pattern recognition via Qdrant similarity on historical episodes (e.g. "Tuesday evening, outside, laptop on")
-
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
- Choose and deploy broker: NATS JetStream (preferred — lightweight, native Kubernetes) or Redis Streams
- Define final message schema (draft above, to be validated)
- Implement Proactive Arbiter n8n workflow:
- Adaptive schedule (morning briefing, midday, evening recap)
- Consume queue batch → LLM correlation prompt → structured
notify/defer/discardoutput - High-priority bypass path
- 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