Sistema ML end-to-end per gestire e cercare le mie lesson learned testuali: raccolta, tagging, ricerca e suggerimenti intelligenti.
Ogni volta che imparo qualcosa (da ChatGPT, da libri, da esperimenti), LeLe Manager diventa il mio archivio centrale:
- aggiungo una lesson con testo + metadati (data, fonte, topic, importanza);
- posso cercare per testo libero, tag, periodo;
- posso vedere lesson simili o correlate;
- nel tempo il sistema impara a classificare e suggerire in autonomia.
- CI:
ruff check .+pytest(GitHub Actions, Python 3.12) - Security:
pip-audit+bandit(GitHub Actions) - pre-commit: whitespace/end-of-file,
check-yaml,ruff
- Milestone v1.2 (stabilizzazione): milestone/1
- Milestone v2 (future/esperimenti): milestone/2
- Documento completo:
ROADMAP.md - Changelog:
CHANGELOG.md
- 📥 Raccolta veloce delle lesson learned via CLI e API.
- 🏷️ Tagging e metadati: data, fonte, topic, importanza, tag liberi.
- 🔍 Ricerca full-text e per filtri (topic, periodo, fonte).
- 🤝 Similarità: suggerimento di lesson correlate a quella che sto scrivendo.
- 🧠 In prospettiva: classificazione automatica per tema/cluster e ranking per importanza.
- Python 3.12 (CI) — testato anche con 3.13
pandas/numpyper analisi datiscikit-learnper ML classico (classificatori, KNN/similarity, ecc.)- (opzionale) piccolo MLP per migliorare embedding/scoring
- FastAPI + Uvicorn per esporre API HTTP
- Storage: JSONL / SQLite (a seconda della fase del progetto)
Clona il repository e crea un ambiente virtuale:
git clone git@github.com:gcomneno/lele-manager.git
cd lele-manager
python -m venv .venv
source .venv/bin/activate # su Windows: .venv\Scripts\activate
pip install -e .[dev]Alcuni comandi base già disponibili:
# Converti un CSV di lesson in JSON
python -m lele_manager.cli.csv2json samples/input.csv samples/output.json
# Monitora una directory (es. data/) per nuovi file
python -m lele_manager.cli.file_watcher data
# Importa LeLe da una directory Markdown con frontmatter YAML nel vault personale
python -m lele_manager.cli.import_from_dir \
"$LELE_VAULT_DIR" \
data/lessons.jsonl \
--on-duplicate overwrite \
--default-source note \
--default-importance 3 \
--write-missing-frontmatterAggiungere una lesson:
python -m lele_manager.cli.add_lesson \
--text "Con layout src/ devo configurare PYTHONPATH o usare un conftest per pytest." \
--source chatgpt \
--topic python \
--importance 4 \
--tags "python,pytest,tooling"Campi principali:
text: contenuto testuale della lessonsource: origine (chatgpt,libro,esperimento, ecc.)topic: macro-tema (es.python,ml,linux,writing)importance: scala numerica (es. 1–5)tags: lista di tag separati da virgola
Elencare le lesson:
python -m lele_manager.cli.list_lessons --limit 10Oltre al salvataggio diretto via CLI, LeLe Manager supporta un vault di file Markdown con frontmatter YAML.
L’idea:
- scrivi e organizzi le LeLe come file
.mdnella tua cartella (es.~/LeLeVaulto/home/utente/Uploads/LeLe-Vault); - uno script (
import_from_dir) li legge, normalizza i metadati e generadata/lessons.jsonl; - l’ID della LeLe vive nel frontmatter, non nel path → puoi correggere e spostare i file senza perdere l’identità.
LeLeVault/
python/
2025-11-20.pytest-src-layout.md
cpp/
2025-11-20.cin-vs-getline.md
linux/
2025-11-20.rsync-dry-run-backup.md
writing/
2025-11-22.show-dont-tell.md
Convenzioni (soft):
- directory = topic principale (
python,cpp,linux,writing, …); - filename =
YYYY-MM-DD.slug.md(senza underscore, usa.e-).
Ogni LeLe può avere in testa un frontmatter YAML:
---
id: cpp/2025-11-20.cin-vs-getline
topic: cpp
source: libro
importance: 4
tags: [cpp, io, stringhe]
date: 2025-11-20
title: "LL-5 — std::cin vs std::getline"
---Campi supportati (tutti opzionali, tranne id che viene generato se manca):
id(str) → identità stabile della LeLe- se non presente, viene derivato dal path relativo, es.
cpp/2025-11-20.cin-vs-getline; - una volta generato, conviene non toccarlo a mano.
- se non presente, viene derivato dal path relativo, es.
topic(str) → se manca, può essere dedotto dal nome della directory (python,cpp, etc.) o da--default-topic.source(str) → es.chatgpt,libro,esperimento,note.importance(int) → tipicamente 1–5.tags(lista o stringa) → es.["python", "pytest", "tooling"]oppure"python, pytest, tooling".date(str, ISO-like) → es.2025-11-20. Se mancante, può essere dedotta dal filenameYYYY-MM-DD.slug.md.title(str) → titolo umano della LeLe (opzionale).
Internamente LeLe Manager calcola anche un frontmatter_hash (hash del solo frontmatter) utile per debug/versioning, ma l’identità resta sempre id.
Per costruire data/lessons.jsonl a partire dalla cartella del vault:
python -m lele_manager.cli.import_from_dir \
"$LELE_VAULT_DIR" \
data/lessons.jsonl \
--on-duplicate overwrite \
--default-source note \
--default-importance 3 \
--write-missing-frontmatterCosa fa:
- scandisce ricorsivamente
$LELE_VAULT_DIRalla ricerca di.md; - per ogni file:
- legge frontmatter YAML + body;
- se manca
id, lo genera dal path (topic/YYYY-MM-DD.slug) e, con--write-missing-frontmatter, lo scrive nel file; - deduce
topic(frontmatter →--default-topic→ nome directory); - normalizza
tags,importance,date; - calcola un
frontmatter_hash(solo metadati);
- crea in RAM una mappa
id → record; - scrive da zero
data/lessons.jsonlcon una riga per ogniidunico.
L’identità delle LeLe è l’id nel frontmatter.
Se durante l’import compaiono più file con lo stesso id, il comportamento si controlla con:
--on-duplicate overwrite(default) → l’ultimo file letto vince;--on-duplicate skip→ la prima occorrenza vince, le successive vengono ignorate;--on-duplicate error→ il comando fallisce appena trova unidduplicato.
-
Scrivi/organizzi le LeLe nel vault Markdown (
$LELE_VAULT_DIR). -
Lanci l’import:
python -m lele_manager.cli.import_from_dir \ "$LELE_VAULT_DIR" \ data/lessons.jsonl \ --on-duplicate overwrite \ --write-missing-frontmatter -
Alleni il topic model:
python -m lele_manager.cli.train_topic_model \ --input data/lessons.jsonl \ --output models/topic_model.joblib \ --overwrite
-
Esplori l’archivio con la similarità:
python -m lele_manager.cli.suggest_similar \ --input data/lessons.jsonl \ --model models/topic_model.joblib \ --text "Quando uso std::cin >> su una string, l'input viene troncato agli spazi" \ --top-k 5 \ --min-score 0.1
LeLe Manager include una prima infrastruttura ML testuale.
Funzione interna:
train_topic_model(df)
Caratteristiche:
- TF-IDF (unigrammi + bigrammi) sul testo delle lesson.
LogisticRegressionper predire il campotopic.
Classe:
LessonFeatureExtractor
Produce una matrice di feature combinando:
- TF-IDF del testo (
text); - meta-feature numeriche:
- lunghezza in caratteri,
- numero di parole,
importance(se presente).
Questo estrattore è usato sia per la classificazione di topic sia per l’indice di similarità.
Classe:
LessonSimilarityIndex.from_lessons(...)/from_topic_pipeline(...)
Metodo principale:
most_similar(query_text, top_k)→ restituisce gli ID delle lesson più simili e il relativo score (coseno).
Uso previsto:
- raccomandare lesson correlate quando ne aggiungo una nuova;
- in futuro, auto-proporre topic/cluster a partire dal testo.
Per addestrare il topic model a partire dal tuo archivio JSONL:
python -m lele_manager.cli.train_topic_model \
--input data/lessons.jsonl \
--output models/topic_model.joblib \
--overwriteRequisiti del file data/lessons.jsonl:
- formato JSONL (una lesson per riga),
- colonne minime:
text: testo della lesson,topic: label di training (stringa).
Esempio di riga:
{"id": "89c6bca8-941b-4a93-a7ca-a35e584ae5ec",
"text": "Con layout src/ devo gestire PYTHONPATH o usare un conftest per pytest.",
"topic": "python",
"source": "chatgpt",
"importance": 4,
"tags": ["python", "pytest", "tooling"]}L’output è una pipeline sklearn completa (feature + modello) salvata in:
models/topic_model.joblib
Nota: via API è disponibile anche POST /similar per confrontare testo libero senza ID.
python -m lele_manager.cli.suggest_similar \
--input data/lessons.jsonl \
--model models/topic_model.joblib \
--text "Con layout src/ devo configurare PYTHONPATH o usare un conftest per pytest." \
--top-k 5 \
--min-score 0.1Se nel dataset hai una colonna id (UUID, string o int), puoi usare una lesson come query:
python -m lele_manager.cli.suggest_similar \
--input data/lessons.jsonl \
--model models/topic_model.joblib \
--from-id "89c6bca8-941b-4a93-a7ca-a35e584ae5ec" \
--id-column id \
--top-k 5 \
--min-score 0.1L’output mostra:
- ID della lesson,
- score di similarità,
- anteprima del testo.
LeLe Manager non è mission-critical, ma l’obiettivo è non far uscire la scimmia senza casco.
Workflow .github/workflows/security.yml che gira su push/PR + scan settimanale:
pip-auditper vulnerabilità sulle dipendenze Python.banditper analisi statica del codice sottosrc/.
File .pre-commit-config.yaml con hook:
- cleanup di base (spazi a fine riga, newline finale),
check-yamlper non rompere i workflow,ruffper lint/fix del codice Python.
Attivazione locale:
pip install pre-commit
pre-commit install- I file reali delle lesson learned vivono in
data/. - I modelli allenati vivono in
models/. data/emodels/sono esclusi dal versioning (vedi.gitignore).
Risultato: l’archivio personale e i modelli restano fuori dal repo pubblico.
Script “tasto F5” completo:
- importa le LeLe dal vault Markdown (
$LELE_VAULT_DIR) indata/lessons.jsonl; - riallena il topic model in
models/topic_model.joblib; - avvia LeLe API in dev con Uvicorn (
--reload).
Uso tipico:
cd ~/Progetti/lele-manager
export LELE_VAULT_DIR=/home/utente/Uploads/LeLe-Vault # adattare al proprio path
./scripts/lele-api-refresh.shScript leggero per avviare solo le API FastAPI (senza re-importare né riallenare):
cd ~/Progetti/lele-manager
./scripts/lele-api-dev.shFa:
- individua la root del progetto;
- attiva
.venv(se manca segnala come crearla); - controlla che
uvicornsia installato nella venv; - avvia il server su
http://127.0.0.1:8000con--reload.
LeLe Manager espone un’API HTTP (FastAPI) sopra il motore interno:
- lettura e ricerca delle LeLe,
- training del topic model,
- similarità tra lesson.
Modalità “giro completo” (vault → JSONL → modello → API):
./scripts/lele-api-refresh.shModalità “solo API” (dataset e modello già pronti):
./scripts/lele-api-dev.shGET /health→ stato rapido del servizio (dati/modello presenti).GET /lessons→ lista/ricerca delle LeLe (query param base).GET /lessons/{id}→ dettaglio di una LeLe.GET /lessons/{id}/similar→ LeLe simili a quella indicata.POST /train/topic→ (ri)allena il topic model a partire dadata/lessons.jsonl.POST /lessons/search→ ricerca avanzata con payload JSON (testo + filtri).POST /similar→ suggerisce LeLe simili a partire da testo libero (senzalesson_id).
LeLe Manager segue il versioning semantico (SemVer):
- MAJOR: cambiamenti incompatibili nelle API/nei formati (es. 1.x → 2.0.0).
- MINOR: nuove funzionalità retro-compatibili (es. 1.0.0 → 1.1.0).
- PATCH: bugfix o piccoli miglioramenti interni (es. 1.0.0 → 1.0.1).
Per l’uso quotidiano:
- lavori normalmente sul branch
main; - quando uno stato è “buono per un lungo periodo”, viene taggato (
vX.Y.Z) e usato come release; - il primo stato considerato “strumento 1.0 stabile” è quello con:
- import dal vault Markdown (
import_from_dir), - dataset JSONL (
data/lessons.jsonl), - topic model + similarità (
models/topic_model.joblib), - API FastAPI (
/lessons,/lessons/search,/lessons/{id}/similar, ecc.), - client CLI
lelefunzionante, - tests
pytestverdi.
- import dal vault Markdown (
Esempio di creazione di una release dall’ultimo commit stabile:
git tag -a v1.0.0 -m "LeLe Manager 1.0.0 – primo rilascio stabile"
git push origin v1.0.0-
Scrivo una nuova LeLe nel vault, es.
~/Uploads/LeLe-Vault/git/2025-12-05.architettura-locale-remoto.mdcon frontmatter YAML (topic: git,importance: 5, ecc.). -
Lancio:
./scripts/lele-api-refresh.sh
che fa in sequenza:
- import dal vault →
data/lessons.jsonl, - training del topic model →
models/topic_model.joblib, - avvio di Uvicorn con LeLe API su
http://127.0.0.1:8000.
- import dal vault →
-
Cerco dal client CLI:
lele search git --topic git --limit 5
-
Se voglio le LeLe più simili a quella che ho appena scritto:
lele similar "git/2025-12-05.architettura-locale-remoto" --top-k 5
-
Modifico il contenuto di una LeLe già esistente nel vault (stesso
idnel frontmatter YAML). -
Rilancio:
./scripts/lele-api-refresh.sh
-
Il comando:
- riscrive il JSONL,
- riallena il topic model,
- riavvia l’API.
-
Da qui in poi:
/lessonsrestituisce il nuovo testo,/lessons/{id}/similarelele similarlavorano sul contenuto aggiornato.
-
Avvio LeLe API in una shell:
cd ~/Progetti/lele-manager ./scripts/lele-api-dev.sh
-
Dal progetto esterno, interrogo le API, ad esempio:
curl -s "http://127.0.0.1:8000/lessons/search" \ -H "Content-Type: application/json" \ -d '{"q": "git", "topic_in": ["git"], "limit": 5}'
-
Oppure, sempre dall’altro repo, uso direttamente il client CLI
lele(se è nel PATH, o viapython -m lele_manager.cli.lele).
See CONTRIBUTING.md.
Oltre agli script tecnici (python -m lele_manager.cli.*), è disponibile un client CLI più ergonomico:
lele --helpQuery da testo libero (via API POST /similar):
lele suggest --text "Quando uso std::cin >> su una string, l'input viene troncato agli spazi"Da file:
lele suggest --file note.mdDa stdin:
cat note.md | lele suggestModalità watch (ogni 2 secondi):
lele suggest --watch note.md --every 2Opzioni principali:
--top-k→ numero massimo risultati (default 5)--min-score→ soglia minima di similarità (default 0.1)--json→ output JSON grezzo
Questo comando usa l’API locale (default http://127.0.0.1:8000).
Assicurati che il server sia attivo (./scripts/lele-api-dev.sh).