← Indice documentazione Architettura › virtualizzazione dei modelli

Metnos

virtualizzazione dei modelli — LLM · embedding · VLM
Come si cambia il modello senza toccare il codice
Pubblico: chi vuole capire come Metnos sceglie e sostituisce i suoi modelli.

Lettura: 10 minuti.

Indice

  1. Il problema: il modello cablato nel codice
  2. La soluzione: chiedere un ruolo, non un modello
  3. I file di configurazione
  4. Segregazione: un solo punto da cui passare
  5. L'autonomia dell'embedding
  6. Il lignaggio: un sottoinsieme leggero
  7. Per andare più a fondo
  8. E per te, in pratica?

1. Il problema: il modello cablato nel codice

Metnos usa tre famiglie di modelli: un LLM che ragiona e pianifica, un embedder che trasforma testo e immagini in vettori per la ricerca semantica, e un VLM che guarda le foto e le descrive. La domanda di questo documento è semplice: quando voglio sostituire uno di questi modelli, cosa devo toccare?

Fino a poco tempo fa la risposta dipendeva dal modello. Per l'LLM esisteva già una buona soluzione: un piccolo strato (llm_router) leggeva da un file di configurazione quale modello servire, così cambiarlo voleva dire editare quel file. Per l'embedder e il VLM, invece, no: i vari pezzi del sistema importavano la classe concreta direttamente — bge_embedding qui, clip_embedding là — e l'indirizzo del VLM era scritto a mano nel codice.

Il difetto non è estetico, è pratico. Sostituire l'embedder voleva dire andare a cercare ogni punto del codice che lo nominava e cambiarlo uno per uno: il routing semantico, l'indicizzazione, due processi notturni, gli executor delle immagini. Dieci posti diversi, dieci occasioni di sbagliarne uno. E puntare l'embedding a un server esterno? Semplicemente non era previsto: avrebbe richiesto codice nuovo.

In una frase. Il modello era cablato: parte dell'implementazione, non della configurazione. Cambiarlo era un lavoro di codice, non un'impostazione. virt elimina questa differenza — porta embedding e VLM allo stesso livello di virtualizzazione che l'LLM aveva già.

2. La soluzione: chiedere un ruolo, non un modello

L'idea che sblocca tutto è un cambio di domanda. Invece di chiedere «dammi BGE-M3», il codice chiede «dammi l'embedder per il ruolo ‘testo’». Chi chiede non sa — e non gli interessa — quale modello risponde: sa solo a cosa gli serve. Tradurre il ruolo nel modello concreto è compito di un unico strato, il package runtime/virt/, che offre tre porte d'ingresso (in gergo, tre facciate):

FacciataChiedi un ruolo……e ti torna
virt.get_llm(role)"fast" / "middle" / "wise" / "frontier"il modello di linguaggio per quel tier (delega a llm_router)
virt.get_embedder(role)"text" oppure "image"l'embedder per quella modalità (BGE-M3 per il testo, SigLIP per le immagini)
virt.get_vlm(role)"default"la configurazione del VLM (provider, modello, indirizzo, limiti)

Si noti la natura dei ruoli. Per l'LLM sono livelli di capacità (fast per le risposte rapide, wise per il ragionamento difficile, frontier per il modello a pagamento usato solo quando serve). Per l'embedder sono modalità (testo o immagine), perché lì la distinzione utile non è quanto è potente il modello, ma che tipo di dato trasforma. Il ruolo è un’astrazione che si adatta al dominio.

from virt import get_embedder, get_llm, get_vlm

get_embedder("text").embed_texts([...])     # vettori del testo, ruolo "text"
get_llm("middle").chat(system, user).text   # risposta del LLM, tier "middle"
get_vlm()                                    # spec del VLM (ruolo "default")
Il principio. Il chiamante esprime un bisogno (un ruolo), non una scelta (un modello). La scelta vive in un solo posto. È lo stesso patto che regge già l'LLM da tempo: virt lo estende a tutte e tre le famiglie.
i consumatori le tre facciate — chiedono un RUOLO i modelli concreti routing semantico indicizzazione · processi notturni executor delle immagini il motore (pianificazione) get_embedder(role) role = "text" | "image" get_llm(role) role = fast | middle | wise | frontier get_vlm(role) role = "default" i file di configurazione (TOML) llm_tiers.toml · embedding_tiers.toml · vlm_tiers.toml cambiare modello = editare QUI (default coprono la realtà) BGE-M3 (testo) · SigLIP (img) ONNX in-process llama-server :8080 (Qwen) Qwen3-VL :8081 (on-demand) legge
Figura 1 — Le tre facciate. A sinistra i consumatori chiedono un ruolo; al centro le facciate di virt traducono quel ruolo leggendo i file di configurazione TOML; a destra i modelli concreti. Nessun consumatore nomina più un modello: lo fa, in un solo posto, la configurazione.

3. I file di configurazione

La traduzione da ruolo a modello vive in tre file TOML, uno per famiglia, nella cartella di configurazione dell'utente:

Ogni file ha sezioni piatte, una per ruolo. Ecco com'è fatto davvero quello dell'embedding: due righe per dire «al testo serve BGE, alle immagini SigLIP».

[text]
provider = "bge"        # BGE-M3, 1024 dimensioni
# Per puntare a un embedder REMOTO:
#   provider = "http"
#   base_url = "http://host:port"

[image]
provider = "siglip"     # SigLIP, 768 dimensioni, testo+immagine

Quello del VLM è altrettanto piccolo — una sola sezione [default] con il provider, il modello, l'indirizzo del server e i limiti dell'immagine:

[default]
provider   = "llamacpp"               # server OpenAI-compat multimodale
model      = "qwen3vl-2b"
base_url   = "http://127.0.0.1:8081"
timeout_s  = 60
max_edge   = 1024                     # ridimensiona il lato lungo
max_tokens = 512
Il principio guida: cambiare modello = editare il TOML, non il codice. Vuoi un altro embedder per il testo? Cambi provider in [text]. Vuoi spostare il VLM su un'altra macchina? Cambi base_url in [default]. Nessuna riga di Python da toccare, nessun executor da ri-firmare.
I file sono opzionali. Se mancano, Metnos usa dei valori predefiniti incorporati nel codice (in virt) che riproducono esattamente la realtà attuale: BGE per il testo, SigLIP per le immagini, Qwen3-VL su :8081. I TOML servono solo quando si vuole deviare dal default. Editarli sovrascrive il valore di partenza, riga per riga; ciò che non si tocca resta com'era.

4. Segregazione: un solo punto da cui passare

Il vero guadagno non è solo la comodità del TOML: è la segregazione. Prima del cambiamento, dieci punti del codice importavano l'embedder concreto. Dopo, nessun consumatore importa più bge_embedding o clip_embedding direttamente: passano tutti per virt.get_embedder. La conoscenza di «quale modello» è stata raccolta in un unico imbuto.

 PrimaDopo
Chi nomina il modelloogni consumatore (routing, indici, notturni, executor immagini)solo la configurazione, letta da virt
Per cambiare modellocercare e modificare N punti del codiceeditare una riga del TOML
Endpoint remotonon previsto (servirebbe codice nuovo)provider = "http" + base_url

Da questo discende una libertà che prima non c'era: poiché tutti chiedono l'embedder allo stesso sportello, si può sostituire l'implementazione sotto i loro piedi senza che se ne accorgano. In particolare, l'embedder del testo può diventare un servizio remoto — un server dietro un indirizzo HTTP — semplicemente scrivendo provider = "http" e l'indirizzo nel TOML. È la controparte, per l'embedding, di quel «puntare un tier a un endpoint» che il LLM faceva già.

L'unico pezzo di codice nuovo. Le classi locali esistenti (BGE, SigLIP) sapevano già rispondere alle domande giuste, quindi virt le restituisce così come sono. L'unica implementazione aggiunta è HttpEmbedder: l'embedder remoto, che parla a un server compatibile con l'API OpenAI. Tutto il resto è instradamento.

5. L'autonomia dell'embedding

C'è un secondo motivo, più profondo, per cui questo riordino conta. Storicamente l'embedding del testo passava — almeno concettualmente — per una struttura esterna condivisa. Riportandolo dentro virt è emerso (e si è reso esplicito) che in realtà gli embedder girano già dentro il processo di Metnos: sono modelli ONNX caricati in-process, senza dipendere da alcuna struttura esterna.

FamigliaModelloDove gira
embedding testoBGE-M3 (1024 dim.)ONNX in-process — nessun server, nessuna dipendenza esterna
embedding immaginiSigLIP (768 dim.)ONNX in-process — lo stesso modello vettorizza testo e immagine
LLM (testo)Qwenendpoint llama-server su :8080
VLM (immagini)Qwen3-VLserver su :8081, acceso su richiesta durante l'indicizzazione

La distinzione è importante. L'embedding — il cuore della ricerca semantica, quella che gira a ogni richiesta — è ora autonomo: vive nel processo, senza chiamare nulla fuori. L'LLM e il VLM restano invece dei server a parte (con il VLM acceso solo quando serve davvero, durante l'indicizzazione delle foto), ma anche per loro «quale modello» e «a quale indirizzo» è ormai una voce di configurazione, non una costante nel codice.

Perché ti riguarda. Autonomia dell'embedding significa che la parte di Metnos usata più spesso — capire cosa intendi e cercare nei tuoi dati — non dipende da servizi esterni né da connessioni: gira sulla tua macchina, dentro il programma stesso.

6. Il lignaggio: un sottoinsieme leggero

Da dove viene la forma di virt? È modellata su un pattern già collaudato altrove: dichiarare i contratti — cosa deve saper fare un embedder, cosa un LLM — come Protocol (in Python, un'interfaccia che descrive i metodi attesi senza imporre un'eredità). Ma virt ne prende solo l'osso, restando volutamente un sottoinsieme leggero.

Il pattern di partenza, più ricco, prevede un registro e l'iniezione delle dipendenze: un'infrastruttura che costruisce e distribuisce gli oggetti su richiesta. virt butta via tutto questo. Tiene due cose sole:

Perché ci si può permettere tanta semplicità? Per un motivo preciso: le classi locali che già esistono — quella di BGE, quella di SigLIP, quella del provider llama — rispettano già quei Protocol senza modifiche. Espongono i metodi attesi così come sono. Non serve scrivere alcun adattatore: la factory le restituisce direttamente. Un registro, qui, sarebbe peso morto.

La regola di casa. «Codice deterministico, semplice e lineare prima di tutto». Un registro con iniezione delle dipendenze risolverebbe un problema che Metnos non ha (decine di implementazioni intercambiabili scelte a runtime). Tre facciate e una funzione che legge un file fanno lo stesso lavoro con una frazione delle parti in movimento — ed è lo stesso disegno, già in produzione, del router del LLM.

7. Per andare più a fondo

Per capire…Leggi
i tre tier del LLM, gli alias e il frontier opt-inmultilang (sezione sui tier)
dove l'embedding entra nella scelta degli strumenti (vicinanza semantica)fastpath e autopath
il VLM al lavoro: come si descrivono le foto durante l'indicizzazioneexecutor
il confine fra skill e backend (un altro asse di sostituibilità)skill & backend

8. E per te, in pratica?

E per te, in pratica? Quasi sempre: niente. I valori predefiniti coprono l'uso normale e Metnos sceglie i modelli giusti da solo. Questa macchineria si fa notare in un solo caso — quando vuoi cambiare un modello: provare un embedder più nuovo, spostare il VLM su un'altra macchina, o appoggiarti a un server esterno. In quel momento la differenza è netta: apri un file di testo, cambi una riga, salvi. Non c'è codice da modificare, niente da ricompilare, niente da ri-firmare.
Il lato positivo. Metnos nasce per restare il più possibile locale: il cervello che usi ogni giorno gira sulla tua macchina. La ricerca semantica — l'embedding — è ormai del tutto autonoma, dentro il processo, senza dipendenze. E quando tu decidi di fare diversamente — un modello più potente, un server remoto, una soluzione su misura — la porta è aperta: basta una voce di configurazione. Il controllo resta tuo.

Metnos — virtualizzazione dei modelli, chiedi un ruolo, non un modello