Skip to content
docs

Documentazione tecnica

Come è fatta Déjà sotto il cofano. Estratto dal vault di progetto.

#Architettura

main.py avvia QApplication e 3 thread daemon coordinati da un unico stop_event:

  • t_captureloop screenshot + OCR ogni 5s
  • t_indexerembedding pendenti → tabelle vec ogni 10s
  • t_audiorecord mic+loopback, segmenti 30s, transcribe

La GUI Qt sta sul main thread. L'hotkey gira su un thread separato ed emette un signal che fa il toggle dell'overlay.

#Pipeline di cattura

capture.py
1shot = mss.grab(monitor) # multi-monitor aware
2jpeg = compress(shot, q=70) # resize 75%
3if sha256(jpeg) == last: skip # dedup
4text = tesseract(shot, "ita+eng")
5text = redact_pii(text) # cards, IBAN, tax ID, email, phone
6db.insert(screenshots, ts, app, jpeg, text)

#Ricerca semantica + esatta

search.py
1query text
2encode(normalize=True) # 768-dim
3sqlite-vec KNN:
4 WHERE emb MATCH vec_int8(?) ORDER BY distance LIMIT k
5score = 1.0 - distance # cosine
6thresholds: screenshot0.18, audio0.15
7PARALLEL: tokenized exact match (LIKE AND, fuzzy)
8merge dedup: exact firstscore descts desc

#AI & RAG

Endpoint OpenAI-compatibile. L'assistente decide quando interrogare la memoria via tool calling, poi cita i ricordi con marker [ss:ID] / [au:ID] resi come card inline.

ai_assistant.py
1def chat_stream(history, user_msg):
2 """yields (kind, content): text | tool_call | tool_result | error"""
3 # max 5 tool-calling iterations
4 # tools: search_memories, list_recent, list_by_date_range, memory_stats
5 # auto-trim history if estimated tokens > context budget
6 ...

#Stack tecnico

GUIPyQt6 — QSystemTrayIcon, QStackedWidget
DBSQLite WAL + sqlite-vec (int8 cosine, 768-dim)
Embeddingsparaphrase-multilingual-mpnet-base-v2 (768-dim)
STTfaster-whisper large-v3-turbo (CPU int8)
OCRTesseract (ita+eng)
Audiopyaudiowpatch (loopback, callback-based)
AI APIOpenAI SDK — Qwen3-235B / Kimi-K2.6, 128K ctx
VisionGemini 2.5 Flash (OpenAI-compat)
Hotkeykeyboard — ctrl+shift+d, ctrl+shift+a
Documentazione · Déjà