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 5st_indexerembedding pendenti → tabelle vec ogni 10st_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 aware2jpeg = compress(shot, q=70) # resize 75%3if sha256(jpeg) == last: skip # dedup4text = tesseract(shot, "ita+eng")5text = redact_pii(text) # cards, IBAN, tax ID, email, phone6db.insert(screenshots, ts, app, jpeg, text)#Ricerca semantica + esatta
search.py
1query text2 → encode(normalize=True) # 768-dim3 → sqlite-vec KNN:4 WHERE emb MATCH vec_int8(?) ORDER BY distance LIMIT k5 → score = 1.0 - distance # cosine6 → thresholds: screenshot ≥ 0.18, audio ≥ 0.157 ‖ PARALLEL: tokenized exact match (LIKE AND, fuzzy)8 → merge dedup: exact first → score desc → ts 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 iterations4 # tools: search_memories, list_recent, list_by_date_range, memory_stats5 # auto-trim history if estimated tokens > context budget6 ...#Stack tecnico
| GUI | PyQt6 — QSystemTrayIcon, QStackedWidget |
| DB | SQLite WAL + sqlite-vec (int8 cosine, 768-dim) |
| Embeddings | paraphrase-multilingual-mpnet-base-v2 (768-dim) |
| STT | faster-whisper large-v3-turbo (CPU int8) |
| OCR | Tesseract (ita+eng) |
| Audio | pyaudiowpatch (loopback, callback-based) |
| AI API | OpenAI SDK — Qwen3-235B / Kimi-K2.6, 128K ctx |
| Vision | Gemini 2.5 Flash (OpenAI-compat) |
| Hotkey | keyboard — ctrl+shift+d, ctrl+shift+a |