Scarica tutti i modelli del router di llama.cpp senza riavviare

Memoria VRAM libera senza arrestare llama-server.

Indice

Modalità router di llama.cpp è uno dei cambiamenti più utili a llama-server degli ultimi anni. Fornisce finalmente agli operatori di LLM locali un’esperienza di gestione dei modelli vicina a quella che ci si aspetta da Ollama, mantenendo al contempo le prestazioni grezze e il controllo a basso livello che rendono llama.cpp meritevole di essere utilizzato in primo luogo.

Tuttavia, c’è un aspetto problematico: scaricare tutto non è un semplice pulsante magico nell’API HTTP.

Il router può elencare i modelli. Può caricare un modello. Può scaricare un modello. Può espellere il modello usato meno di recente quando viene raggiunto il limite --models-max. Ciò che non documenta attualmente come endpoint di primo livello è una chiamata universale scarica tutti i modelli ora.

laptop e desktop con llm

Questo non è un ostacolo insormontabile. Il modello corretto è semplice, esplicito e automatizzabile:

  1. Chiedi al router quali modelli esistono.
  2. Filtra i modelli il cui stato è loaded (caricato).
  3. Chiama /models/unload una volta per ogni modello caricato.

Questo è l’approccio che raccomando per flussi di lavoro LLM locali seri. È noioso, visibile e facile da debuggare. È esattamente ciò che vuoi quando il tuo obiettivo è liberare VRAM senza riavviare l’intero servizio di inferenza.

Cosa fa realmente la modalità router di llama.cpp

Nell’uso classico di llama-server, si avvia un server con un modello:

llama-server \
  --model ./models/qwen3-8b.gguf \
  --port 8080

La modalità router cambia questo paradigma. Invece di legare il server a un singolo file GGUF, il router diventa un coordinatore per più modelli. Può scoprire i modelli da una cache o da una directory dei modelli, caricarli su richiesta, instradare le richieste al modello corretto e scaricare i modelli quando necessario.

Un avvio tipico in modalità router appare così:

llama-server \
  --models-dir ./models \
  --models-max 4 \
  --port 8080

L’opzione importante qui è --models-max. Controlla quanti modelli possono essere caricati contemporaneamente. Se il limite viene raggiunto, llama.cpp può espellere il modello usato meno di recente. Questo è utile, ma non sostituisce un’operazione di scaricamento deliberata. L’espulsione LRU (Least Recently Used) è reattiva. Uno script di scaricamento è un controllo operativo.

Il mio parere personale: se esegui modelli locali per lavoro reale, dovresti trattare la modalità router come un gestore di processi di inferenza, non come un server chat giocattolo. Le operazioni di ciclo di vita esplicite sono importanti.

Gli endpoint di gestione dei modelli di cui hai bisogno

L’endpoint principale per la scoperta è:

curl -s http://localhost:8080/models | jq

Questo endpoint restituisce i modelli noti al router e il loro stato attuale del ciclo di vita. La struttura JSON esatta può variare leggermente tra le build, quindi ispeziona la tua risposta prima di scrivere automazioni.

Una forma di risposta comune è simile a questa:

{
  "data": [
    {
      "id": "qwen3-8b",
      "status": "loaded"
    },
    {
      "id": "llama-3.2-3b",
      "status": "unloaded"
    }
  ]
}

Per scaricare un modello, chiama:

curl -s -X POST http://localhost:8080/models/unload \
  -H "Content-Type: application/json" \
  -d '{"model":"qwen3-8b"}' \
  | jq

Questa è l’operazione primitiva. Tutto il resto in questo articolo si basa su di essa.

Non esiste un endpoint documentato per scaricare tutto

Questa è la parte che crea confusione.

Potresti aspettarti qualcosa del genere:

curl -X POST http://localhost:8080/models/unload-all

Non costruisci su questa assunzione. L’operazione documentata è per modello. Passi un identificatore del modello a /models/unload e llama.cpp scarica quel singolo modello.

Questo non è necessariamente un cattivo design API. Un’operazione per modello è più sicura. Fa decidere al chiamante cosa dovrebbe essere scaricato. Evita anche comportamenti di produzione sorprendenti in cui una richiesta amministrativa accidentale uccide ogni modello caldo utilizzato da altri client.

Per una workstation, una scorciatoia per scaricare tutto sarebbe conveniente. Per una scatola di inferenza multi-utente, i loop espliciti sono migliori.

Scarica prima un singolo modello

Prima di automatizzare qualsiasi cosa, testa l’esatto identificatore del modello che il tuo router si aspetta.

Prima elenca i modelli:

curl -s http://localhost:8080/models | jq

Scegli un modello caricato dall’output, poi scaricalo:

curl -s -X POST http://localhost:8080/models/unload \
  -H "Content-Type: application/json" \
  -d '{"model":"qwen3-8b"}' \
  | jq

Controlla di nuovo l’elenco dei modelli:

curl -s http://localhost:8080/models | jq

Se lo stato del modello cambia in unloaded (scaricato), il tuo endpoint, la porta e l’identificatore del modello sono corretti.

Se non funziona, non indovinare. Ispeziona il JSON. Alias del router, nomi dei file GGUF e ID dei modelli spesso non sono la stessa stringa.

Scarica tutti i modelli caricati con curl e jq

Una volta che lo scaricamento di un singolo modello funziona, il pattern per scaricare tutto è solo un loop shell.

Usa questo quando la tua risposta /models ha .data[].id e .data[].status:

curl -s http://localhost:8080/models \
| jq -r '.data[] | select(.status == "loaded") | .id' \
| while IFS= read -r model; do
    echo "Scaricamento: $model"
    curl -s -X POST http://localhost:8080/models/unload \
      -H "Content-Type: application/json" \
      -d "{\"model\":\"$model\"}" \
      | jq
  done

Questo è l’intero trucco. Non è glamour, ma è la forma giusta per un’operazione amministrativa:

  • Scarica solo i modelli che sono realmente caricati.
  • Stampa cosa sta facendo.
  • Fallisce modello per modello invece di nascondere tutto dietro un’azione opaca.
  • Funziona da cron, hook systemd, SSH o job CI.

Uno script riutilizzabile per l’uso in produzione

Per qualsiasi cosa che esegui più di due volte, smetti di incollare one-liner. Salva uno script.

Crea llama-router-unload-all.sh:

#!/usr/bin/env bash
set -euo pipefail

LLAMA_SERVER_URL="${LLAMA_SERVER_URL:-http://localhost:8080}"

models_json="$(curl -fsS "$LLAMA_SERVER_URL/models")"

loaded_models="$(printf '%s' "$models_json" \
  | jq -r '.data[] | select(.status == "loaded") | .id')"

if [ -z "$loaded_models" ]; then
  echo "Nessun modello caricato trovato."
  exit 0
fi

printf '%s\n' "$loaded_models" | while IFS= read -r model; do
  [ -z "$model" ] && continue

  echo "Scaricamento: $model"

  curl -fsS -X POST "$LLAMA_SERVER_URL/models/unload" \
    -H "Content-Type: application/json" \
    -d "{\"model\":\"$model\"}" \
    | jq

done

echo "Fatto. Stato attuale dei modelli:"
curl -fsS "$LLAMA_SERVER_URL/models" | jq

Rendilo eseguibile:

chmod +x llama-router-unload-all.sh

Eseguilo contro il server locale predefinito:

./llama-router-unload-all.sh

Eseguilo contro un altro host:

LLAMA_SERVER_URL=http://192.168.1.50:8080 ./llama-router-unload-all.sh

Questa è la versione che manterrei effettivamente in una directory degli strumenti. Usa curl -f in modo che gli errori HTTP falliscano lo script, e ti permette di sovrascrivere l’URL del server senza modificare il file.

Adattare lo script alla tua struttura JSON

Non dare per scontato che ogni build di llama.cpp restituirà sempre gli stessi campi esatti. La modalità router è ancora in evoluzione, e la tua build potrebbe esporre una struttura JSON leggermente diversa.

Inizia ispezionando la risposta:

curl -s http://localhost:8080/models | jq

Lo script usa questo filtro:

jq -r '.data[] | select(.status == "loaded") | .id'

Se il tuo identificatore del modello è in .name, cambialo in:

jq -r '.data[] | select(.status == "loaded") | .name'

Se il tuo campo di stato usa un altro valore, adatta il filtro di conseguenza. Il principio è ciò che conta: seleziona i modelli caricati, estrai l’identificatore accettato da /models/unload, poi chiama unload per ciascuno.

Perché i modelli potrebbero ricaricarsi dopo che li hai scaricati

Questa è la fonte più comune di confusione.

La modalità router supporta il caricamento su richiesta. Se un client invia una richiesta di completamento chat per un modello che è attualmente scaricato, il router potrebbe caricarlo di nuovo automaticamente.

Ciò significa che questa sequenza è possibile:

  1. Scarichi ogni modello.
  2. Open WebUI, uno script di test o un agente invia una richiesta.
  3. llama.cpp carica di nuovo il modello richiesto.
  4. Pensi che lo scaricamento abbia fallito, ma non è così.

La soluzione è operativa, non tecnica. Fermi prima il traffico dei client se il tuo obiettivo è mantenere la VRAM libera.

Per esempio:

  • Ferma gli script di benchmark.
  • Metti in pausa agenti e job cron.
  • Chiudi o disconnetti le sessioni di Open WebUI.
  • Disabilita i health check che accidentalmente eseguono richieste di modello reali.

Lo scaricamento non è un firewall. Se i client continuano a chiedere modelli, la modalità router sta facendo il suo lavoro servendoli.

Open WebUI e il pulsante Eject (Espelli)

Open WebUI può integrarsi con il supporto di scaricamento modelli di llama.cpp. Quando il provider è configurato come llama.cpp, Open WebUI può mostrare lo stato dei modelli caricati e esporre un’azione Eject (Espelli) per gli amministratori.

Sotto il cofano, quell’azione chiama l’API di scaricamento di Open WebUI, che a sua volta chiama l’endpoint /models/unload di llama.cpp sulla connessione configurata.

Questo è bello per l’operazione manuale, ma manterrei comunque lo script shell. Un pulsante UI è conveniente. Uno script è auditabile, ripetibile e utilizzabile su una macchina headless alle 2 del mattino.

Quando usare lo scaricamento totale

Scaricare ogni modello caricato è utile quando vuoi:

  • Liberare la memoria GPU prima di avviare un modello più grande.
  • Resetare una macchina di sviluppo senza riavviare llama-server.
  • Preparare per un run di benchmark con uno stato di memoria pulito.
  • Drenare i carichi di lavoro di inferenza locali prima della manutenzione.
  • Riprendersi da una sessione confusa in cui troppo modelli sono stati riscaldati.

Non è lo strumento giusto quando utenti attivi dipendono da modelli caldi. In quel caso, ottimizza --models-max, usa l’instradamento deliberato e lascia che l’espulsione LRU faccia parte del lavoro. Se hai bisogno di uno scaricamento basato su timeout più intelligente con controllo del ciclo di vita per modello, llama-swap è un proxy costruito appositamente che sovrappone esattamente quello su qualsiasi configurazione llama-server.

La mia regola è semplice: usa LRU per la pressione normale, usa lo scaricamento esplicito per l’intento dell’operatore.

Risoluzione dei problemi

L’endpoint dei modelli restituisce 404

Potresti non essere in esecuzione su una build con supporto router, o potresti stare chiamando la porta sbagliata.

Controlla il processo del server e le opzioni disponibili:

llama-server --help | grep -i models

Poi testa entrambi gli endpoint:

curl -s http://localhost:8080/models | jq
curl -s http://localhost:8080/v1/models | jq

L’endpoint /v1/models è l’elenco dei modelli compatibile con OpenAI. L’endpoint /models è l’endpoint di gestione dei modelli del router. Sono correlati, ma non sono la stessa cosa.

jq non è installato

Installalo prima di scriptare il parsing JSON.

Su Ubuntu o Debian:

sudo apt-get update
sudo apt-get install jq

Su macOS con Homebrew:

brew install jq

La chiamata di scaricamento restituisce un errore

La maggior parte dei fallimenti deriva dal passaggio dell’identificatore del modello sbagliato. Usa l’identificatore esatto restituito da /models, non il nome del file che pensi dovrebbe funzionare.

Controlla anche se il nome del tuo modello contiene virgolette, slash o spazi. Lo script sopra gestisce bene le stringhe normali, ma nomi insoliti potrebbero richiedere una costruzione JSON più attenta.

Per la massima sicurezza, puoi costruire il corpo POST con jq:

jq -n --arg model "$model" '{model: $model}'

Un loop di scaricamento più difensivo userebbe quel corpo invece di JSON escapato a mano.

La VRAM non viene liberata immediatamente

Prima conferma che lo stato del modello è cambiato. Poi controlla se un’altra richiesta l’ha ricaricato. Ricorda anche che gli strumenti di memoria GPU possono essere in ritardo o riportare il comportamento dell’allocatore piuttosto che l’intento dell’applicazione a livello istantaneo.

Il test pratico è semplice: fermi il traffico, scarichi i modelli, elenchi lo stato dei modelli, poi ispezioni la memoria GPU. Per l’uso misurato della VRAM attraverso dimensioni dei modelli e finestre di contesto su llama.cpp, i benchmark llama.cpp su 16 GB VRAM forniscono figure concrete contro cui verificare la sanità mentale.

Una versione più sicura del corpo JSON

Se i tuoi identificatori dei modelli contengono caratteri insoliti, usa jq per generare il corpo della richiesta JSON:

curl -s http://localhost:8080/models \
| jq -r '.data[] | select(.status == "loaded") | .id' \
| while IFS= read -r model; do
    echo "Scaricamento: $model"
    body="$(jq -n --arg model "$model" '{model: $model}')"
    curl -s -X POST http://localhost:8080/models/unload \
      -H "Content-Type: application/json" \
      -d "$body" \
      | jq
  done

Questa è la versione da usare se i tuoi modelli sono denominati con identificatori in stile repository, alias personalizzati o percorsi.

Considerazioni finali

La modalità router di llama.cpp è un grande passo avanti per le operazioni LLM locali. Ti dà caricamento dinamico, switching di modelli ed espulsione consapevole della memoria senza rinunciare alla directness di llama-server.

Ma non aspettare un endpoint unload-all perfetto. La soluzione pulita esiste già: elenca i modelli caricati e scaricali uno per uno.

Quel pattern è esplicito. È automatizzabile. Funziona via SSH. Si integra bene con Open WebUI. E, più importante, libera la VRAM senza riavviare il router.

Per l’infrastruttura AI locale, è esattamente il tipo di superficie di controllo noiosa che vuoi.

Iscriviti

Ricevi nuovi articoli su sistemi, infrastruttura e ingegneria AI.