Quando una richiesta arriva a Metnos, prima di scomodare il pianificatore LLM si cerca di riconoscerla. Se l'abbiamo già vista e abbiamo già deciso bene come rispondere, la rieseguiamo così com'è e torniamo all'utente in mezzo secondo invece che in dieci. Niente modelli linguistici nel percorso critico, solo memoria.
Il pianificatore di Metnos è un Qwen 3.6 35B-A3B locale: pensa
bene ma ci mette circa dodici secondi a decidere il primo passo di
un turno. Per molte richieste questa attesa è spropositata.
«Che ora è» non ha bisogno di un LLM: ha bisogno di
get_now. Anche «scaricami questa pagina e
descrivila in due righe» non ha bisogno del pianificatore se è
una richiesta che facciamo spesso e di cui sappiamo già la
sequenza: get_urls e poi describe_entries.
Da qui la domanda: come riconosciamo che una richiesta è già nota? E come ci accorgiamo che quasi nota è abbastanza? La risposta è il fast-path introvertivo, organizzato in due livelli — ciascuno coglie un grado diverso di certezza, e tutti convergono allo stesso esito: l'assistente esegue la sequenza giusta senza pensarci due volte.
| Livello | Cosa riconosce | Come | Costo per ogni hit |
|---|---|---|---|
| L0 | Una richiesta già risolta con successo, identica o semanticamente molto vicina | Hash esatto (0a) + cosine BGE-M3 (0b) | < 5 ms (hash) / < 150 ms (cosine) |
| L1 | Un intent appartenente a un cluster confermato dall'utente tramite feedback positivo | Intent hash + cluster_id lookup | ~30 ms |
Il flusso è rigorosamente sequenziale: prima si tenta L0, se non matcha si tenta L1. Se entrambi mancano, si arriva al pianificatore LLM (Engine v2) come sempre.
fastpath.py)
Il primo livello vive in runtime/engine/fastpath.py e gestisce
un database SQLite (fastpaths.sqlite) di piani già eseguiti.
Le entry si producono in automatico: ogni volta che un turno
si completa con successo (piano pieno dal motore, hit L1, o promozione da
cosine 0b), Metnos registra la query canonica, il suo hash, l'embedding
BGE-M3, il piano completo (framework) e l'intent (verbo + oggetto).
Non serve nessuna approvazione: le catene sono fatte di executor già
vagliati e testati.
La ricerca avviene in due fasi:
/admin/praxis.undo_last_turn e
get_inputs non generano fastpath, perché la loro semantica
dipende dal contesto del turno.since_iso="2026-06-11") non viene registrato, perché
il replay in un giorno diverso eseguirebbe una finestra temporale congelata.
Le date relative (time_window="today") restano cacheabili,
perché il replay le ri-risolve correttamente.autopath.py)
Il secondo livello vive in runtime/engine/autopath.py e opera
su una logica diversa: non ripete la stessa query, ma generalizza
a un cluster di intent confermato dal feedback positivo dell'utente.
Quando l'utente dà un feedback «✓», Metnos registra il framework del turno, il suo hash e il cluster semantico (intent hash + cluster ID). Dopo un numero minimo di conferme (configurabile, default 1) sullo stesso framework hash e cluster, il piano diventa una autopath riusabile: la prossima volta che arriva un intent nello stesso cluster, il piano viene rieseguito senza passare dal pianificatore.
I fastpath L0 invecchiano e muoiono in modo deterministico, senza alcun
intervento LLM. Il job notturno task_state_reaper applica tre
regole di invecchiamento e quattro condizioni di morte.
| Regola | Criterio | Default | Env |
|---|---|---|---|
| Mai riusato | Creato da oltre N giorni ma mai servito una seconda volta | 14 giorni | METNOS_FASTPATH_GRACE_DAYS |
| Stantio | Ultimo uso oltre N giorni fa | 30 giorni | METNOS_FASTPATH_STALE_DAYS |
| Cap LRU | Totale entry superiore al tetto; le meno recenti vengono potate | 500 | METNOS_FASTPATH_MAX |
| Codice | Causa | Eredità |
|---|---|---|
| C1 | Un tool nel piano non esiste più nel catalogo (ritirato, rinominato, archiviato). Il replay fallirebbe. | No |
| C2 provenienza | Il fastpath è stato promosso a executor sintetico (vedi §9) e quell'executor è ora nel catalogo. | Sì |
| C2 nome | Esiste un executor con nome {verbo}_{oggetto} corrispondente all'intent, ma nessun tool del piano appartiene a quella famiglia. Il fastpath oscurerebbe l'executor. | Sì |
| C2 prefilter | Per i piani multi-step: il prefilter di routing deterministico sulla query canonica indica che un singolo executor copre l'intent (anche con nome diverso). | Sì |
Quando un fastpath muore per superamento (C2), i suoi conteggi d'uso
(n_uses) vengono trasferiti all'executor erede tramite il
sistema di aging degli executor. La domanda accumulata non va persa.
Riconoscere la richiesta è metà del lavoro. L'altra
metà è ricostruire i valori concreti degli argomenti:
quali path, quali URL, quale data, quale soglia. Metnos ha un
estrattore deterministico (args_extractor.py) che
lavora a regole:
https://...), path (~/... o
/..., con la scorciatoia «home» che diventa
~/), email, numeri, estensioni di file («file
PDF» diventa *.pdf), date
(oggi/ieri/domani/dopodomani in italiano e inglese, mappate a formato
ISO), finestre temporali («questa settimana», «ultime
24 ore», «last 7 days»).
I parametri del fast-path sono controllati da variabili d'ambiente
METNOS_*. Un file TOML
(~/.config/metnos/runtime.toml) stabilisce valori
persistenti; il default codificato nel modulo è l'ultima rete
di sicurezza.
| Variabile | Default | Significato |
|---|---|---|
METNOS_FASTPATH_STALE_DAYS | 30 | Giorni calendario dopo cui una entry non usata viene potata |
METNOS_FASTPATH_GRACE_DAYS | 14 | Giorni di grazia per entry mai riusate |
METNOS_FASTPATH_MAX | 500 | Tetto massimo righe (LRU) |
| Variabile | Default | Significato |
|---|---|---|
METNOS_AUTOPATH_MIN_OBS | 1 | Osservazioni positive minime per promuovere un autopath |
METNOS_AUTOPATH_TTL_ANTI | 2592000 (30 gg) | Durata di un'anti-autopath in secondi |
METNOS_AUTOPATH_TTL_REPEAT | 3600 (1 h) | Finestra soft per feedback ripetuti |
| Variabile | Default | Significato |
|---|---|---|
METNOS_FP_PROMOTE_MIN_CLUSTER | 3 | Fastpath distinti minimo nel cluster |
METNOS_FP_PROMOTE_MIN_USES | 15 | Uso cumulato minimo |
METNOS_FP_PROMOTE_MIN_AGE_DAYS | 30 | Età minima del cluster |
METNOS_FP_PROMOTE_MAX_PER_NIGHT | 3 | Massimo emissioni nuove per notte |
METNOS_FASTPATH_AUTOPROMOTE | off | Abilita auto-promozione Tier 2 (senza approvazione) |
Quando un gruppo di fastpath L0 ricorrenti condivide la stessa struttura
di piano (framework hash) e lo stesso intent, un job notturno
(task_fastpath_promotion) li valuta come candidati per
diventare un executor sintetico completo. La promozione è
cluster-based, mai per singola istanza: servono almeno
3 fastpath distinti, 15 utilizzi cumulati e 30 giorni di età.
Solo i pattern multi-step vengono promossi: quelli a step singolo hanno
già il loro executor, e il valore del fastpath lì è
saltare l'LLM, non il piano.
/admin/proposals e nell'hub unificato. L'approvazione scrive
un marker synt_pending/ che avvia la pipeline di sintesi
completa (5 stadi + test + firma + installazione).
Ogni candidato emesso registra gli ID e gli hash canonici dei fastpath
d'origine in una tabella promotions. Quando l'executor entra
nel catalogo, la morte C2 per provenienza è esatta: il legame
fra il fastpath originale e il suo erede è registrato, non
inferito dal nome.
© 2026 Roberto Brunialti · documentazione Metnos