Cluster di smartphone Android per l'esecuzione di modelli LLM open source, integrato con VS Code + Continue come agente di sviluppo AI.
- Panoramica del Progetto
- Architettura del Sistema
- Requisiti Hardware Minimi
- Requisiti Software
- Dispositivi Consigliati
- Cablaggio e Alimentazione
- Installazione — Telefoni
- Installazione — Server Orchestrator (PC/Raspberry Pi)
- Configurazione del Cluster
- Integrazione con VS Code + Continue
- Modelli LLM Consigliati
- Monitoraggio e Dashboard
- Script di Avvio e Gestione
- Espansione del Cluster
- Troubleshooting
- Benchmark e Performance
Questo progetto consente di costruire un cluster di smartphone Android riciclati o economici che eseguono modelli LLM open source (Llama, Phi, Mistral, Gemma ecc.) tramite llama.cpp (compilato per ARM). Il cluster espone un'API compatibile OpenAI che può essere consumata da VS Code + Continue come backend AI per assistenza al codice, completamento, refactoring e agent loop.
- 💰 Basso Costo
- ⚡ CPU ARM efficienti nel consumo energetico
- 🔧 Completamente offline e privato
- 📈 Scalabile aggiungendo nuovi nodi
- 🔄 Compatibile con qualsiasi tool che supporta OpenAI API
[Telefoni Android (nodi LLM)]
|
[Switch USB / LAN]
|
[Orchestrator Node (PC / RPi 5)]
├── Load Balancer (Nginx / HAProxy)
├── API Gateway (compatibile OpenAI)
├── Prometheus + Grafana (monitoring)
└── [VS Code + Continue Extension]
| Componente | Ruolo |
|---|---|
| Nodi Android | Eseguono llama.cpp server via Termux |
| Orchestrator | Load balancing, routing, API unificata |
| llama.cpp server | Server HTTP compatibile OpenAI su ogni nodo |
| Continue Extension | Interfaccia VS Code verso il cluster |
| Prometheus/Grafana | Monitoraggio risorse e performance |
192.168.1.0/24
├── 192.168.1.10 → Orchestrator (PC/RPi)
├── 192.168.1.20 → Nodo Android #1
├── 192.168.1.21 → Nodo Android #2
├── 192.168.1.22 → Nodo Android #3
└── 192.168.1.2x → Nodo Android #N
| Parametro | Minimo | Consigliato |
|---|---|---|
| RAM | 6 GB | 8–12 GB |
| CPU | Snapdragon 7xx / Dimensity 1000 | Snapdragon 8 Gen 1+ / Dimensity 9000+ |
| Storage | 64 GB UFS 2.1 | 128 GB UFS 3.1 |
| Android | 10 | 12+ |
| Batteria | 4000 mAh | Non rilevante (sempre in carica) |
| Componente | Minimo | Consigliato |
|---|---|---|
| CPU | Quad-core 2 GHz | x86_64 6-core+ / RPi 5 |
| RAM | 4 GB | 8–16 GB |
| Storage | 32 GB SSD | 128 GB SSD |
| OS | Ubuntu 22.04 / Debian 12 | Ubuntu 22.04 LTS |
| Rete | 100 Mbit | Gigabit |
3× telefoni Android di recupero (es. Xiaomi Redmi Note 10 Pro)
1× Raspberry Pi 5 (4 GB) oppure vecchio PC come orchestrator
1× Switch di rete 8 porte (o router con porte libere)
1× USB Hub alimentato 10 porte
Cavi USB-C per alimentazione
Cavi Ethernet + adattatori USB-C/Ethernet per i telefoni
- Termux (da F-Droid, NON dal Play Store)
- Termux:API add-on
- OpenSSH (in Termux)
- llama.cpp (compilato da sorgente in Termux)
- Python 3 + script di avvio automatico
- Ubuntu 22.04 LTS (o Debian 12)
- Docker + Docker Compose
- Nginx (load balancer)
- Prometheus + Grafana (monitoring)
- Python 3.11+ (script di controllo cluster)
- VS Code + estensione Continue
| Modello | RAM | SoC | Note |
|---|---|---|---|
| Xiaomi Redmi Note 10 Pro | 6–8 GB | Snapdragon 732G | Ottimo rapporto qualità/prezzo |
| Samsung Galaxy A52s | 6–8 GB | Snapdragon 778G | Molto diffuso, economico |
| OnePlus Nord 2 | 8–12 GB | Dimensity 1200 | Eccellente performance |
| Poco X3 Pro | 6–8 GB | Snapdragon 860 | Veloce, economico |
| Motorola Edge 20 | 6 GB | Snapdragon 778G | Compatto |
| Modello | RAM | SoC | Note |
|---|---|---|---|
| Xiaomi 11T | 8 GB | Dimensity 1200 | Eccellente per LLM |
| OnePlus 9 | 8–12 GB | Snapdragon 888 | Molto potente |
| Samsung Galaxy S21 | 8 GB | Exynos 2100 / SD 888 | Ottima stabilità |
| Pixel 6 | 8 GB | Google Tensor G1 | Buon supporto Android |
| Poco F3 | 6–8 GB | Snapdragon 870 | Top performance/prezzo |
Consiglio: Per iniziare, 3× Poco X3 Pro o Redmi Note 10 Pro usati. per un cluster funzionante per modelli 3B–7B quantizzati.
[220V AC]
|
[UPS 650VA] (opzionale ma consigliato)
|
┌──────────────┴──────────────┐
│ │
[USB Hub Alimentato] [Router/Switch]
(10 porte, 60W+) (Gigabit)
│ │
├── USB-C → Telefono 1 ├── ETH → Orchestrator
├── USB-C → Telefono 2 ├── USB-C/ETH → Tel. 1
├── USB-C → Telefono 3 ├── USB-C/ETH → Tel. 2
└── USB-C → Orchestrator └── USB-C/ETH → Tel. 3
| Componente | Quantità | Prezzo Est. | Note |
|---|---|---|---|
| USB Hub alimentato 10p (60W) | 1 | ~30€ | Es. Anker A8310 o simile |
| Cavi USB-C 90° (1m) | N+1 | ~5€ cad. | Uno per telefono |
| Adattatori USB-C → Ethernet | N | ~10€ cad. | Per connessione LAN |
| Cavi Ethernet Cat6 (1m) | N+1 | ~3€ cad. | |
| Switch 8 porte Gigabit | 1 | ~20–30€ | Es. TP-Link TL-SG108 |
| Supporti/rack per telefoni | 1 | ~10–20€ | Stampabili 3D o acquistati |
| UPS 650VA | 1 (opt.) | ~60–80€ | Per stabilità |
Ogni telefono consuma ~5–15W sotto carico. Per 6 telefoni: max ~90W. Un hub da 60W è sufficiente per tenere in carica telefoni già carichi durante l'uso. NON rimuovere mai le batterie — fungono da UPS locale.
I telefoni sotto carico LLM si scaldano (60–80°C). Soluzioni:
- Posizionare i telefoni verticalmente con spazio tra di loro
- Mini ventole USB (5V) puntate verso i dispositivi
- Evitare custodie durante l'uso nel cluster
- Monitorare la temperatura con
termux-battery-status
- Abilita Opzioni sviluppatore: Impostazioni → Info telefono → Tocca 7 volte su "Numero build"
- Abilita Debug USB: Impostazioni → Opzioni sviluppatore → Debug USB
- Abilita Installazione da fonti sconosciute per F-Droid
# Sul PC con ADB connesso al telefono via USB
adb install termux.apk
adb install termux-api.apkOppure scarica manualmente da: https://f-droid.org/packages/com.termux/
Apri Termux sul telefono ed esegui:
# Aggiorna pacchetti
pkg update && pkg upgrade -y
# Installa dipendenze essenziali
pkg install -y git cmake clang python openssh wget curl nano htop
# Installa build tools per llama.cpp
pkg install -y binutils make
# Abilita storage access
termux-setup-storagecd ~
git clone https://github.com/ggerganov/llama.cpp
cd llama.cpp
# Compila con ottimizzazioni ARM NEON
cmake -B build \
-DLLAMA_NATIVE=ON \
-DLLAMA_NEON=ON \
-DCMAKE_BUILD_TYPE=Release
cmake --build build --config Release -j$(nproc)
# Verifica compilazione
./build/bin/llama-server --version⏱ La compilazione richiede 15–40 minuti a seconda del dispositivo.
mkdir -p ~/models
cd ~/models
# Esempio: Phi-3 Mini (3.8B) — ottimo per telefoni con 6GB RAM
wget https://huggingface.co/microsoft/Phi-3-mini-4k-instruct-gguf/resolve/main/Phi-3-mini-4k-instruct-q4.gguf
# Alternativa: Llama 3.2 3B
wget https://huggingface.co/bartowski/Llama-3.2-3B-Instruct-GGUF/resolve/main/Llama-3.2-3B-Instruct-Q4_K_M.gguf# Genera chiave SSH
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519 -N ""
# Avvia server SSH (porta 8022 di default in Termux)
sshd
# Mostra indirizzo IP del telefono
ip addr show | grep 'inet ' | grep -v 127.0.0.1Dal PC orchestrator, aggiungi la chiave:
ssh-copy-id -p 8022 user@192.168.1.2XCrea il file ~/start_llm_server.sh:
#!/data/data/com.termux/files/usr/bin/bash
MODEL_PATH="$HOME/models/Phi-3-mini-4k-instruct-q4.gguf"
PORT=8080
HOST="0.0.0.0"
THREADS=$(nproc)
CTX_SIZE=4096
GPU_LAYERS=0 # Aumenta se hai GPU Vulkan supportata
# Calcola layers in base alla RAM disponibile
TOTAL_MEM=$(free -m | awk '/^Mem:/{print $2}')
if [ "$TOTAL_MEM" -gt 10000 ]; then
N_PARALLEL=4
elif [ "$TOTAL_MEM" -gt 7000 ]; then
N_PARALLEL=2
else
N_PARALLEL=1
fi
echo "[$(date)] Starting llama.cpp server on port $PORT"
echo "[$(date)] Model: $MODEL_PATH"
echo "[$(date)] Threads: $THREADS | Parallel: $N_PARALLEL | CTX: $CTX_SIZE"
# Imposta priorità processo
renice -n -10 $$
exec "$HOME/llama.cpp/build/bin/llama-server" \
--model "$MODEL_PATH" \
--host "$HOST" \
--port "$PORT" \
--threads "$THREADS" \
--ctx-size "$CTX_SIZE" \
--n-predict 512 \
--parallel "$N_PARALLEL" \
--cont-batching \
--flash-attn \
--log-disable \
2>&1 | tee -a "$HOME/llm_server.log"chmod +x ~/start_llm_server.shInstalla Termux:Boot da F-Droid, poi:
mkdir -p ~/.termux/boot
cat > ~/.termux/boot/start_cluster_node.sh << 'EOF'
#!/data/data/com.termux/files/usr/bin/bash
# Attendi connessione di rete
sleep 30
# Avvia SSH
sshd
# Avvia server LLM
bash ~/start_llm_server.sh &
EOF
chmod +x ~/.termux/boot/start_cluster_node.sh# Ubuntu 22.04 LTS
sudo apt update && sudo apt upgrade -y
sudo apt install -y git python3 python3-pip python3-venv \
nginx docker.io docker-compose curl wget htop net-tools
# Aggiungi utente a gruppo docker
sudo usermod -aG docker $USER
newgrp dockergit clone https://github.com/YOUR_USERNAME/android-llm-cluster
cd android-llm-cluster
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txtEdita config/cluster.yaml con gli IP dei tuoi telefoni:
cluster:
name: "android-llm-cluster"
orchestrator:
host: "192.168.1.10"
api_port: 11434
dashboard_port: 3000
nodes:
- id: "node-01"
host: "192.168.1.20"
port: 8080
ssh_port: 8022
model: "phi-3-mini"
ram_gb: 8
enabled: true
- id: "node-02"
host: "192.168.1.21"
port: 8080
ssh_port: 8022
model: "llama-3.2-3b"
ram_gb: 8
enabled: true
- id: "node-03"
host: "192.168.1.22"
port: 8080
ssh_port: 8022
model: "phi-3-mini"
ram_gb: 6
enabled: true
load_balancing:
strategy: "least_loaded" # round_robin | least_loaded | random
health_check_interval: 30 # secondi
timeout: 120 # secondi per risposta LLM
models:
phi-3-mini:
context_length: 4096
max_tokens: 512
llama-3.2-3b:
context_length: 4096
max_tokens: 512cd android-llm-cluster
docker-compose up -dQuesto avvia:
- API Gateway (porta 11434) — compatibile OpenAI
- Prometheus (porta 9090)
- Grafana (porta 3000)
- Cluster Manager (daemon Python)
android-llm-cluster/
├── README.md
├── config/
│ ├── cluster.yaml # Configurazione nodi
│ └── nginx.conf # Load balancer config
├── scripts/
│ ├── cluster_manager.py # Daemon orchestrator
│ ├── health_check.py # Monitor nodi
│ ├── deploy_node.sh # Deploy automatico su un nodo
│ ├── start_cluster.sh # Avvio cluster completo
│ └── stop_cluster.sh # Arresto cluster
├── monitoring/
│ ├── prometheus.yml
│ └── grafana/
│ └── dashboard.json
├── docker-compose.yml
└── requirements.txt
- Apri VS Code
- Estensioni (Ctrl+Shift+X)
- Cerca "Continue" → Installa (by Continue Dev)
Apri ~/.continue/config.json (o tramite palette comandi: "Continue: Open Config"):
{
"models": [
{
"title": "Android Cluster — Phi-3 Mini",
"provider": "openai",
"model": "phi-3-mini",
"apiBase": "http://192.168.1.10:11434/v1",
"apiKey": "not-required",
"contextLength": 4096,
"completionOptions": {
"temperature": 0.1,
"maxTokens": 512
}
},
{
"title": "Android Cluster — Llama 3.2 3B",
"provider": "openai",
"model": "llama-3.2-3b",
"apiBase": "http://192.168.1.10:11434/v1",
"apiKey": "not-required",
"contextLength": 4096
}
],
"tabAutocompleteModel": {
"title": "Android Cluster — Autocomplete",
"provider": "openai",
"model": "phi-3-mini",
"apiBase": "http://192.168.1.10:11434/v1",
"apiKey": "not-required"
},
"embeddingsProvider": {
"provider": "openai",
"model": "nomic-embed-text",
"apiBase": "http://192.168.1.10:11434/v1",
"apiKey": "not-required"
},
"contextProviders": [
{ "name": "code" },
{ "name": "docs" },
{ "name": "diff" },
{ "name": "terminal" },
{ "name": "problems" },
{ "name": "folder" },
{ "name": "codebase" }
],
"slashCommands": [
{
"name": "edit",
"description": "Edit highlighted code"
},
{
"name": "comment",
"description": "Write comments for the highlighted code"
},
{
"name": "tests",
"description": "Write unit tests"
}
]
}| Shortcut | Funzione |
|---|---|
Ctrl+L |
Apri chat Continue |
Ctrl+Shift+L |
Aggiungi selezione al contesto |
Ctrl+I |
Inline edit |
Tab |
Accetta autocomplete |
| Modello | Quantizzazione | Dimensione | RAM Necessaria |
|---|---|---|---|
| Phi-3 Mini 3.8B | Q4_K_M | ~2.3 GB | ~3.5 GB |
| Llama 3.2 3B | Q4_K_M | ~1.9 GB | ~3.0 GB |
| Gemma 2B | Q4_K_M | ~1.5 GB | ~2.5 GB |
| TinyLlama 1.1B | Q4_K_M | ~0.7 GB | ~1.5 GB |
| Modello | Quantizzazione | Dimensione | RAM Necessaria |
|---|---|---|---|
| Phi-3 Small 7B | Q4_K_M | ~4.1 GB | ~5.5 GB |
| Llama 3.1 8B | Q4_K_M | ~4.7 GB | ~6.5 GB |
| Mistral 7B v0.3 | Q4_K_M | ~4.1 GB | ~5.5 GB |
| CodeLlama 7B | Q4_K_M | ~3.8 GB | ~5.0 GB |
| Modello | Quantizzazione | Dimensione | RAM Necessaria |
|---|---|---|---|
| Phi-3 Medium 14B | Q4_K_M | ~8.1 GB | ~10 GB |
| Llama 3.1 8B | Q8_0 | ~8.5 GB | ~10 GB |
| DeepSeek Coder 6.7B | Q5_K_M | ~4.5 GB | ~6 GB |
Consiglio per sviluppo: Usa Phi-3 Mini o CodeLlama 7B per code completion. Sono ottimizzati per il codice e funzionano bene anche con quantizzazioni aggressive.
| Servizio | URL | Credenziali |
|---|---|---|
| Grafana | http://192.168.1.10:3000 | admin / admin (cambia al primo accesso) |
| Prometheus | http://192.168.1.10:9090 | — |
| Cluster API | http://192.168.1.10:11434 | — |
| Cluster Status | http://192.168.1.10:11434/cluster/status | — |
- Per nodo: CPU%, RAM%, temperatura, token/s, richieste attive, latenza
- Cluster: Throughput totale, distribuzione carico, disponibilità nodi, errori
- Modello: Token generati, lunghezza prompt, TTFT (Time To First Token)
# Status rapido del cluster
./scripts/cluster_manager.py status
# Log in tempo reale di un nodo
ssh -p 8022 user@192.168.1.20 "tail -f ~/llm_server.log"
# Benchmark velocità
./scripts/benchmark.sh --node all --prompt "Write a Python function to sort a list"
# Temperatura tutti i nodi
./scripts/health_check.py --tempsTutti gli script si trovano nella cartella scripts/. Vedi i file allegati per il codice completo.
| Script | Descrizione |
|---|---|
start_cluster.sh |
Avvia tutti i nodi e l'orchestrator |
stop_cluster.sh |
Arresta tutto in modo ordinato |
deploy_node.sh |
Configura un nuovo telefono da zero |
cluster_manager.py |
Daemon orchestrator con load balancing |
health_check.py |
Monitor continuo dei nodi |
benchmark.sh |
Test performance token/s |
add_node.sh |
Aggiunge un nuovo nodo al cluster live |
# 1. Prepara il telefono (Step 1-6 sezione Installazione Telefoni)
# 2. Esegui lo script di deploy automatico
./scripts/deploy_node.sh --ip 192.168.1.23 --model phi-3-mini
# 3. Aggiungi alla configurazione
echo "
- id: node-04
host: 192.168.1.23
port: 8080
ssh_port: 8022
model: phi-3-mini
ram_gb: 8
enabled: true" >> config/cluster.yaml
# 4. Ricarica la configurazione senza downtime
./scripts/cluster_manager.py reload| Nodi | RAM Totale | Modelli Paralleli | Token/s Totali (est.) |
|---|---|---|---|
| 3 | 24 GB | 3 | ~45–90 |
| 6 | 48 GB | 6 | ~90–180 |
| 10 | 80 GB | 10 | ~150–300 |
| 20 | 160 GB | 20 | ~300–600 |
I token/s variano enormemente in base al modello, quantizzazione e SoC. Questi valori sono stime per Phi-3 Mini Q4 su Snapdragon 7xx.
# Verifica connettività
ping 192.168.1.20
# Verifica porta SSH
nc -zv 192.168.1.20 8022
# Controlla lo stato del server LLM
ssh -p 8022 user@192.168.1.20 "ps aux | grep llama"
# Riavvia il server sul nodo
ssh -p 8022 user@192.168.1.20 "pkill llama-server; sleep 2; bash ~/start_llm_server.sh &"- Riduci
--ctx-size(da 4096 a 2048) - Usa un modello più piccolo o quantizzazione più aggressiva (Q3_K_M)
- Riduci
--parallela 1 - Chiudi altre app in background
- Il telefono potrebbe essere in throttling termico → riduci il carico, aggiungi raffreddamento
- Verifica che la batteria non sia al di sotto del 20% (modalità risparmio energetico)
- Controlla che
--threadssia impostato al numero corretto di core
# Verifica che l'API gateway sia up
curl http://192.168.1.10:11434/v1/models
# Testa direttamente un nodo
curl http://192.168.1.20:8080/v1/models
# Controlla i log dell'orchestrator
docker-compose logs -f cluster-manager| SoC | Modello | Quantizzazione | Token/s |
|---|---|---|---|
| Snapdragon 732G | Phi-3 Mini | Q4_K_M | 8–12 |
| Snapdragon 778G | Phi-3 Mini | Q4_K_M | 12–18 |
| Snapdragon 870 | Phi-3 Mini | Q4_K_M | 15–22 |
| Snapdragon 888 | Phi-3 Mini | Q4_K_M | 18–28 |
| Dimensity 1200 | Phi-3 Mini | Q4_K_M | 14–20 |
| Dimensity 9000 | Phi-3 Mini | Q4_K_M | 25–35 |
| Soluzione | Costo | Privacy | Token/s | Offline |
|---|---|---|---|---|
| Android Cluster (6 nodi) | ~300€ | ✅ Totale | ~100–180 | ✅ |
| Raspberry Pi 4 Cluster | ~400€ | ✅ | ~20–40 | ✅ |
| RTX 3060 12GB | ~280€ | ✅ | ~200–400 | ✅ |
| Claude API (Sonnet) | ~15$/M tok | ❌ Cloud | Illimitato | ❌ |
| Ollama su MacBook M2 | ~1000€ | ✅ | ~50–100 | ✅ |
MIT License — Vedi LICENSE per dettagli.
Pull request benvenute! Per cambiamenti importanti, apri prima una issue.
- llama.cpp — Motore di inferenza
- Termux — Terminal Android
- Continue — VS Code AI extension
- Hugging Face — Repository modelli