Sull’AsUS Ascent GX10 ho finalmente la configurazione che volevo: Qwen3.8 Flash Next in locale, con un servizio systemd che parte al boot e uno script per cambiare modello al volo. Questo post documenta l’installazione attuale e chiude il capitolo Qwen3.6.
Perché llama.cpp compilato a mano
Per sfruttare il GX10 servono leve fini — quantizzazione precisa, context molto grande, draft MTP per lo speculative decoding — e solo llama.cpp le espone tutte, senza strati intermedi.
git clone https://github.com/ggml-org/llama.cpp ~/llama.cpp
cmake -B build -DGGML_CUDA=ON -DCMAKE_BUILD_TYPE=Release
cmake --build build -j$(nproc)
~/llama.cpp/build/bin/llama-server --version
# version: 0.3.0-dev (build 10802, commit d1a92352c) — Linux aarch64
Nota per chi ha un GX10 (o qualunque Arm64): servono i CUDA per aarch64 e un build fatto in locale; i binari x86 non si eseguono nemmeno. La compilazione dura una ventina di minuti ma è un’operazione unica.
Il modello
Ho scelto una quantizzazione UD-IQ4_XS (il classico compromesso qualità/spazio delle quantizzazioni Unsloth Dynamic) più il draft MTP e il projection per la parte visiva:
| File | Cosa | Dimensione |
|---|---|---|
UD-IQ4_XS/...-00001-of-00003.gguf | modello principale (3 shard) | ~88 GB |
MTP/mtp-...-shared-Q8_0.gguf | draft MTP condivisibile | ~2,6 GB |
mmproj-F16.gguf | projection multimodale | ~863 MB |
Con 128 GB di RAM unificata del GX10 il modello carica comodo (a regime il sistema sta sui ~96 GB occupati) e avanza spazio per una context window generosa.
Lo script di avvio
Tutta la configurazione sta in ~/llama-server/scripts/start-qwen38:
#!/usr/bin/env bash
set -euo pipefail
MODEL="$HOME/models/Qwen3.8-Flash-Next-UD-IQ4_XS/UD-IQ4_XS/Qwen3.8-Flash-Next-UD-IQ4_XS-00001-of-00003.gguf"
DRAFT="$HOME/models/Qwen3.8-Flash-Next-UD-IQ4_XS/MTP/mtp-Qwen3.8-Flash-Next-shared-Q8_0.gguf"
MMPROJ="$HOME/models/Qwen3.8-Flash-Next-UD-IQ4_XS/mmproj-F16.gguf"
exec "$HOME/llama.cpp/build/bin/llama-server" \
--metrics \
-m "$MODEL" \
--mmproj "$MMPROJ" \
-md "$DRAFT" \
--spec-type draft-mtp \
--spec-draft-n-max 4 \
-c 256000 \
-np 1 \
--host 0.0.0.0 \
--port 30000
I pezzi che contano:
-c 256000→ context da 256k token. Con la RAM unificata del GX10 sta, ed è quello che fa la differenza su task lunghi e agenti.--spec-type draft-mtp --spec-draft-n-max 4→ speculative decoding col draft MTP: fino a 4 token di proposta per passo di verifica.-np 1→ una sola slot di inferenza: con un modello da 88 GB parallelizzare porta più problemi che benefici.--mmproj+--spec-type draft-mtp→ vision e MTP sono arrivati con le build recenti; nelle prime versioni dello script avevo una variante novision.--host 0.0.0.0 --port 30000→ API OpenAI-compatibile esposta sulla LAN, così anche il portatile la usa.--metrics→ metriche pronte per Prometheus/Grafana.
Esposizione all’avvio: systemd utente + linger
La parte che mi interessava di più: il modello deve essere su quando accendo la macchina, senza login e senza pensarci. Su un servizio utente bastano due pezzi.
~/.config/systemd/user/llama-qwen38.service:
[Unit]
Description=llama.cpp Qwen3.8 Flash Next Server
After=network-online.target
Wants=network-online.target
Conflicts=llama-qwen36.service
[Service]
Type=simple
ExecStart=%h/llama-server/scripts/start-qwen38
Restart=on-failure
RestartSec=10
[Install]
WantedBy=default.target
Restart=on-failure+RestartSec=10: se il server muore, systemd lo rimette su dopo 10 secondi.Conflicts=llama-qwen36.service: i due modelli condividono la porta 30000, non possono stare in piedi insieme.%h= home directory, così il file è portabile tra macchine.
Il trucco per far partire un servizio utente senza login è il linger:
loginctl enable-linger $USER # verifica: Linger=yes
systemctl --user enable --now llama-qwen38.service
Verifica rapida dopo un riavvio:
systemctl --user status llama-qwen38 --no-pager
curl -s http://127.0.0.1:30000/health
curl -s http://127.0.0.1:30000/v1/models | python3 -m json.tool
Cambiare modello al volo
Con due modelli installati e 128 GB di RAM contesi, ho scritto
~/bin/model-switch:
model-switch status # cosa è attivo adesso
model-switch qwen38 # passa a Qwen3.8
model-switch qwen36 # torna indietro
La logica, in breve: stoppa entrambi i servizi, aspetta che la porta si
liberi (fino a 60s), avvia il servizio giusto e aspetta che /health
risponda (fino a 180s) prima di dichiarare il successo. Senza queste due
attese si finisce per mandare richieste a un modello ancora in caricamento —
con 88 GB da mmap non è istantaneo.
Il precedente: Qwen3.6-35B-A3B
Prima dell’attuale configurazione, sul GX10 girava Qwen3.6-35B-A3B (MoE,
~3B di parametri attivi), sempre con llama.cpp ma in una forma più semplice:
un unico script start che leggeva un file di configurazione e scaricava il
modello direttamente da Hugging Face con -hf:
# com'era (configs/qwen3.6-35b.env, ormai rimosso)
MODEL="unsloth/Qwen3.6-35B-A3B-MTP-GGUF:UD-Q4_K_XL"
# → llama-server -hf "$MODEL" --ctx-size 262144 ...
Nel percorso avevo anche provato la variante NVIDIA NVFP4
(nvidia/Qwen3.6-35B-A3B-NVFP4), poi rimossa insieme alla cache Hugging Face
per liberare spazio.
Perché il cambio verso il 3.8:
- Qualità: il salto 3.6 → 3.8 si vede, soprattutto su codice e task lunghi.
- Gestione dei file: passare da
-hf(cache HF che cresce da sola) a GGUF scaricati e versionati sotto~/models/mi dà controllo su cosa occupa disco. - Configurazione esplicita: lo script con i flag scritti a mano batte l’env-file indirezionato, quando i flag da bilanciare sono tanti.
Il 35B MoE non era un cattivo modello — sugli scambi brevi è rapido e leggero,
e l’attivato da 3B lo rende ancora valido se servono risposte fulminee. Per
l’uso che ne faccio io (agenti, context enormi, editing di codice) il 3.8 in
IQ4_XS con MTP è un’altra categoria; per questo model-switch qwen36 resta
lì, pronto.
Cosa resta da fare
- Benchmark seri (tok/s in prefill e decode, con e senza MTP) su task realistici, invece delle impressioni
- Confronto energetico: GX10 vs la GPU desktop che usavo prima
- Dashboard Grafana con le metriche di
llama-server(--metricsè già attivo, manca il pannello) - Valutare se una context window più piccola libera RAM utile al resto
