LLM-Manager è una suite Bash leggera e modulare, dotata di una doppia interfaccia JSON/interattiva, progettata per gestire motori di inferenza locali e remoti su Linux e WSL2.
Quando ho iniziato a sperimentare con i Large Language Model (LLM) per sviluppare un'applicazione RAG (Retrieval-Augmented Generation) On-Premise, mi sono scontrato con un ostacolo significativo: la frammentazione degli ambienti di esecuzione.
Gestire diversi motori di inferenza, come Ollama e Llama.cpp, significava dover ricordare opzioni della riga di comando, variabili d'ambiente e configurazioni differenti. Quando i miei prototipi frontend e backend sono stati pronti per i test, mi sono reso conto che stavo dedicando troppo tempo ad avviare, arrestare, caricare e scaricare manualmente i modelli.
Ho cercato soluzioni online. La maggior parte dei suggerimenti prevedeva script Python complessi, pesanti configurazioni Docker, workflow n8n oppure articolate dashboard web.
Non volevo tutta questa complessità. Cercavo qualcosa di leggero, che eseguisse i comandi esattamente come avrei fatto manualmente, ma senza dovermi occupare ogni volta dei dettagli operativi.
È per questo che ho sviluppato LLM-Manager: una suite di orchestrazione modulare scritta interamente in Bash puro.
Perché Bash?
La scelta di Bash non è stata dettata dalla nostalgia per i vecchi strumenti, ma da una decisione ingegneristica pragmatica:
- Nessun overhead: niente ambienti virtuali Python, niente
npm install, nessuna dipendenza da runtime aggiuntivi. È nativo ed estremamente veloce. - Accesso diretto al sistema operativo: permette di rilevare nativamente le metriche hardware (carico della CPU, RAM, disco, VRAM della GPU) e gestire i processi del sistema operativo.
- Compatibilità multipiattaforma tramite WSL2: utilizzando piccoli collegamenti con PowerShell solo quando necessario (ad esempio per avviare un server sul lato host Windows), gli stessi script Bash funzionano sia su Linux nativo sia in ambienti Windows/WSL2.
L'architettura
Il sistema è progettato secondo un'architettura rigorosamente modulare, di tipo plug-and-play. Al centro si trova un unico orchestratore come punto di ingresso (engine-run.sh), che verifica gli argomenti rispetto a liste di valori consentiti (whitelist) e indirizza le operazioni agli script specifici di ciascun motore.
.
├── engine.conf # Costanti di configurazione globali
├── engine-models.json # Registro dei modelli con metadati per motore
├── engine-templates.json # Template di prompt/modelli per famiglia
├── engine-run.sh # Orchestratore principale e punto di ingresso
├── engine-common.sh # Utilità condivise (rilevamento OS, formattazione JSON)
├── engine-status.sh # Aggregazione dello stato dei motori
├── engine-system.sh # Rilevamento delle metriche hardware
├── logs/ # Log centralizzati
├── llama/ # Script backend per Llama.cpp
└── ollama/ # Script backend per Ollama
Ogni directory dedicata a un motore implementa un'interfaccia coerente (start.sh, stop.sh, status.sh, load.sh, unload.sh, show.sh, remove.sh). Se un motore non supporta una determinata operazione, è sufficiente uno script segnaposto (stub) che termini con codice 0, mantenendo regolare il flusso di esecuzione.
Il doppio contratto di output (Uomo + Macchina)
Una delle caratteristiche fondamentali di LLM-Manager è il modo in cui gestisce l'output.
- Il testo per l'interazione con l'utente (log, guida, errori) viene indirizzato a
stderr. - I dati strutturati in formato JSON vengono indirizzati a
stdout.
Questa doppia modalità lo rende ideale per l'utilizzo interattivo locale, ma gli consente anche di funzionare come proxy locale. È possibile eseguirlo tramite SSH remoto e inoltrare direttamente il JSON, privo di messaggi aggiuntivi, a un altro script di monitoraggio, a un'interfaccia web personalizzata oppure a uno strumento di automazione.
Esempio 1: verifica dello stato globale
Eseguendo ./engine-run.sh status, il sistema rileva le metriche hardware e interroga le porte di rete attive, restituendo un insieme completo di informazioni:
{
"timestamp": "2026-05-29T06:38:30Z",
"status": "success",
"action": "status",
"engine": "all",
"data": {
"system": {
"os_type": "wsl",
"memory": { "total_mb": 5927, "available_mb": 4555 },
"gpu": { "detected": true, "name": "AMD Radeon(TM) Graphics", "vram_total_mb": 512 },
"cpu": { "cores": 4, "load_1m": 1.11 }
},
"engines": {
"ollama": { "state": "stopped", "port": 1234 },
"llama": { "state": "stopped", "port": 12345 }
}
}
}
Esempio 2: interfaccia di guida interattiva
Se un comando fallisce oppure viene richiamato senza parametri, il sistema restituisce un errore strutturato in JSON per l'elaborazione automatica, mentre l'operatore riceve un menu di utilizzo chiaro e leggibile:
Errore: LLM Manager
Uso: engine-run.sh <azione> [motore] [argomenti...]
Azioni:
config Configurazione globale
models [-h] Elenca i modelli disponibili (-h formato leggibile)
status <engine> Mostra lo stato globale o di un motore
start <engine> [model] [users] Avvia un motore
stop <engine> Arresta un motore
...
Generazione dinamica dei Modelfile
Gestire file .gguf direttamente con Ollama può risultare laborioso, poiché richiede la creazione di un Modelfile. LLM-Manager astrae completamente questa complessità nel backend, attraverso le strategie di caricamento dei modelli definite in engine-models.json.
Se un modello è configurato con un loader gguf, lo script load.sh genera dinamicamente il Modelfile necessario al momento dell'esecuzione, inserendo i template di prompt appropriati in base alla famiglia del modello e caricandolo automaticamente in Ollama.
Supporta inoltre la strategia native, che permette di scaricare direttamente i modelli dal registro ufficiale di Ollama, e la strategia auto, che prevede un meccanismo alternativo di caricamento qualora il file locale non sia disponibile.
Esplora il codice
Il progetto è completamente open source. Se vuoi scoprire come vengono gestiti i collegamenti tra WSL2 e PowerShell, come vengono generati dinamicamente i Modelfile, oppure se desideri utilizzare LLM-Manager per semplificare il tuo ambiente locale di sperimentazione con gli LLM, consulta il repository.
Attualmente sto lavorando al completamento dell'integrazione del motore vLLM e al perfezionamento dei controlli di integrità durante l'avvio (health-check), introducendo cicli automatici di verifica e nuovi tentativi in caso di problemi.
Fammi sapere cosa ne pensi o se hai sviluppato soluzioni altrettanto leggere per gestire i tuoi workflow di intelligenza artificiale!