Il problema: Assist è rigido, gli LLM sono lontani
Chi usa Home Assistant con la voce conosce bene il dilemma.
Da una parte c’è l’agente conversazionale di default di Assist. È locale, veloce, affidabile e gratuito, ma funziona per template di frasi: dietro c’è un motore di pattern matching (hassil) con un dizionario di frasi per ogni lingua. “Accendi la luce della cucina” funziona. “In cucina c’è troppo buio” no. “Spegni il bagno e accendi il salotto” dipende da quanto sei fortunato con la formulazione. Ogni frase deve assomigliare a un template previsto.
Dall’altra parte ci sono gli agenti basati su LLM: Gemini, OpenAI, Anthropic, oppure Ollama in locale. Capiscono il linguaggio naturale, gestiscono formulazioni libere, condizioni, più azioni nella stessa frase. Hanno però un costo, in uno di due modi:
Cloud: ogni frase detta in casa esce dalla rete domestica verso un servizio esterno. È un problema di privacy, di dipendenza da internet e di costi per chiamata.
Locale: un modello generalista da qualche miliardo di parametri vuole una GPU o almeno una macchina seria. Un Raspberry Pi 5, il server di casa tipico, non basta.
La mia domanda era semplice: esiste una via di mezzo? Un modello abbastanza piccolo da stare su un Pi 5, insieme a Home Assistant, ma abbastanza capace da trasformare una frase libera in un’azione sulla casa?
Il ruolo di Needle: cosa fa e cosa non fa
Prima di scendere nei dettagli dell’implementazione, è fondamentale inquadrare con precisione il ruolo di Needle per evitare di riporre aspettative errate in un modello da pochi megabyte.
Needle è un modello estremamente specializzato. È forte in due compiti principali: scegliere e strutturare. Data una frase e un elenco di funzioni, sceglie quella corretta e ne riempie i parametri; data una frase e uno schema, ne estrae i dati in forma strutturata. Si tratta di ambiti ben definiti con un insieme chiuso di risposte possibili.
Needle non è progettato per l’interpretazione di frasi complesse: ragionamenti multi-passo, condizioni logiche articolate da verificare, riferimenti impliciti o gestione del contesto su conversazioni lunghe. Non è un limite imprevisto, ma la diretta conseguenza della sua dimensione (circa 35 MB). Per questo motivo è stato posizionato all’interno di Assist a supporto e non in sostituzione dell’LLM generalista:
CaratteristicaNeedle (locale, ~35 MB)LLM Generalista (Claude, Gemini, Ollama...) Punto di forza Strutturazione rapida, tool calling direttoRagionamento, pianificazione, testo libero Casi d’uso ideali Frasi brevi, dirette, ripetitive (”accendi la cucina”, “spegni tutto”)Richieste complesse, automazioni articolate, contesto lungo Latenza / Risorse ~0,15 s, impatto minimo sul server (Raspberry Pi 5)Richiede GPU dedicata o chiamate cloud esterne Obiettivo in casa Rendere Assist più flessibile dei template rigidi, 100% localeGestire compiti complessi, creativi o ad alto ragionamento
L’obiettivo è intercettare il traffico vocale frequente e diretto della casa; tutto ciò che richiede ragionamento profondo resta delegato ai modelli più grandi.
Needle: un modello che non chiacchiera, chiama funzioni
Needle, sviluppato da Cactus Compute, è progettato specificamente per questo scopo. Non è un chatbot, ma un modello di tool calling. Riceve una frase e un elenco di funzioni disponibili (nome, descrizione, parametri in formato JSON Schema) e restituisce quali funzioni chiamare e con quali argomenti.
Le specifiche chiave dell’architettura:
Dimensioni contenute: Needle 3 spazia da 8 a 29 MB, con 29–121 milioni di parametri distribuiti su 2–20 layer. Il file dei pesi utilizzato (
needle3.cact) pesa circa 35 MB.Quantizzazione spinta: Architettura Laddered Simple Attention Networks con quantizzazione a 2 bit (CQ2).
Engine d’inferenza nativo (< 1 MB): Compatibile con macOS, Linux (incluso ARM64), Windows, Android, iOS e WebAssembly. Non richiede PyTorch o CUDA.
Efficienza embedded: Prestazioni dichiarate tra 400 e 4.000 token/s in decodifica su Raspberry Pi 5.
Output strutturato: Ogni risposta restituisce
function_calls, un camporeasoninge un indice di confidenza calibrato. In caso di frasi fuori contesto, il modello restituisce una lista di chiamate vuota invece di allucinare.Licenza Apache-2.0: Codice e pesi completamente open source.
Su ambiente di sviluppo macOS, l’elaborazione di una richiesta richiede circa 0,15 secondi, con un consumo di memoria di picco di circa 120 MB.
Un aspetto determinante per la progettazione dell’integrazione: Needle non genera testo libero. Non produce risposte come “Ho spento la luce del bagno”, ma restituisce direttamente la chiamata HassTurnOff(name="luce del bagno"). La risposta parlata verso l’utente deve quindi essere sintetizzata dall’integrazione.
L’idea: “come Gemini, ma locale”
L’architettura dell’integrazione ricalca il modello di funzionamento con cui Home Assistant gestisce i provider LLM ufficiali (Google Generative AI, OpenAI, Ollama): il modello non interagisce direttamente con Home Assistant, ma riceve il testo e la lista degli strumenti disponibili e restituisce le chiamate da eseguire.
L’integrazione esegue le azioni tramite l’API LLM “Assist”: un insieme di strumenti generati automaticamente e vincolati alle sole entità esposte.
+-----------------------------------------------------------------------+
| Setup Cloud / LLM |
| |
| Assist ──► Integrazione Generative AI ──► API Esterna / Cloud |
+-----------------------------------------------------------------------+
+-----------------------------------------------------------------------+
| Setup Needle Locale |
| |
| Assist ──► Integrazione Needle ──────► Add-on Needle Locale |
+-----------------------------------------------------------------------+
L’Add-on gestisce l’esecuzione locale del modello, mentre la Custom Integration coordina il passaggio dei dati con Home Assistant.
Perché un add-on
Un add-on è un container Docker gestito dal Supervisor di Home Assistant. La scelta di isolare l’engine di Needle nell’Add-on offre diversi vantaggi architetturali:
Isolamento del runtime: L’engine precompilato di Needle per Linux richiede l’ambiente
manylinux2014(glibc), mentre l’immagine Home Assistant Core si basa su Alpine Linux (musl). L’Add-on utilizza un’immagine Debian (python:3.13-slim), evitando conflitti di libreria.Indipendenza delle dipendenze: L’ambiente Python di Home Assistant Core non viene modificato; l’integrazione comunica con l’Add-on esclusivamente via HTTP.
Gestione del ciclo di vita: I pesi del modello risiedono nella directory persistente
/data. Gli aggiornamenti dell’Add-on non interferiscono con l’istanza principale di HA.Principio del minimo privilegio: L’Add-on non possiede token d’accesso o credenziali per las API di Home Assistant. Restituisce unicamente proposte di chiamate che HA deve successivamente convalidare ed eseguire.
Disattivazione della telemetria: Configurata di default tramite la variabile d’ambiente
NEEDLE_TELEMETRY=0.
L’interfaccia dell’Add-on espone un’API minimale:
GET /health → 200 {"stato": "ok"}
POST /v1/infer → {"tools": [...], "turni": ["frase", "risultati dei tool", ...]}
← {"function_calls": [...], "confidence": 0.62, "reasoning": "..."}
Perché una custom integration
Per integrarsi come agente conversazionale in Assist è necessario implementare un componente dedicato (custom_components/needle/) basato sull’entità ConversationEntity.
L’integrazione gestisce la logica specifica di Home Assistant:
Interfaccia diretta con l’API LLM “Assist” di Home Assistant.
Inoltro delle chiamate al registro delle conversazioni (
chat_log.async_add_assistant_content).Generazione della risposta vocale/testuale in lingua italiana.
Gestione dei casi di fallimento o timeout dell’Add-on.
┌──────────────────────── Home Assistant Core ────────────────────────┐
│ │
│ Assist ──► custom_components/needle (ConversationEntity) │
│ │ 1. Chiede ad HA i tool dell'API "Assist" │
│ │ 2. Li converte in JSON schema │
│ │ 3. Chiama l'add-on ────────────┐ │
│ │ 5. Passa le tool call al chat log │ │
│ │ 6. HA le esegue (solo entità esposte) │ │
│ │ 7. Costruisce la risposta in italiano │ │
│ ▼ │ │
│ intent di HA ──► light.turn_off, climate.set_temp... │ │
└────────────────────────────────────────────────────────────────┼────┘
│ HTTP (rete interna)
┌────────────────────────── Add-on Needle ───────────────────────┼────┐
│ POST /v1/infer ──► Needle 3 Engine Nativo (~35 MB) ──► 4. Tool call│
│ Nessun accesso a HA · nessuna porta sull'host · telemetria spenta │
└─────────────────────────────────────────────────────────────────────┘
Come funziona una richiesta, passo per passo
Flusso di esecuzione lineare
Utente Integrazione Needle Add-on Needle Home Assistant Core
│ │ │ │
│ "Spegni la luce │ │ │
│ del bagno" │ │ │
├───────────────────────►│ │ │
│ │ POST /v1/infer │ │
│ │ (tool di Assist) │ │
│ ├─────────────────────────►│ │
│ │ │ │
│ │ HassTurnOff(name=...) │ │
│ │◄─────────────────────────┤ │
│ │ │
│ │ Esegui tool call (chat_log) │
│ ├──────────────────────────────────────────────────►│
│ │ │
│ │ Successo: [Luce bagno] │
│ │◄──────────────────────────────────────────────────┤
│ "Ho spento │ │
│ Luce bagno." │ │
│◄───────────────────────┤ │
In caso di comandi multipli (”Spegni il bagno e accendi la cucina”), Needle restituisce entrambe le chiamate all’interno del medesimo payload JSON, consentendo a Home Assistant di eseguirle in sequenza.
Gestione delle frasi condizionali e controllo deterministico
Le richieste condizionali (es. “Se la luce del bagno è spenta accendi quella della camera”) richiederebbero idealmente due passaggi sequenziali: una lettura di stato seguita da un’azione.
I test sul modello con pesi base hanno evidenziato che Needle tende ad estrarre ed eseguire direttamente l’azione finale ignorando la verifica dello stato intermedio. Per garantire la sicurezza dell’installazione domotica, l’integrazione implementa un blocco di controllo: se la frase contiene strutture condizionali e il modello propone un’azione senza una prioritaria lettura di stato, l’esecuzione viene bloccata.
Per superare questo limite, la logica di valutazione della condizione viene demandata direttamente al codice dell’integrazione:
[Frase con condizione]
│
▼
[Needle: Estrazione Strutturata] ──► ( Dati estratti: Condizione + Azione )
│
▼
[Integrazione Needle] ──► Lettura stato attuale ──► [Home Assistant Core]
│ │
│◄──────────────── Stato attuale ────────────────────┘
▼
{ Condizione verificata? }
├── Sì ──► [Esecuzione Azione]
└── No ──► [Annullamento Azione]
Sicurezza ed esecuzione senza stato
Il ciclo di conversazione è gestito senza mantenere stato interno nell’Add-on (stateless):
Ad ogni iterazione, l’integrazione invia l’intera cronologia della trascrizione.
L’Add-on esegue un
reset()del modello ed elabora la sequenza aggiornata.
Regole di sicurezza applicate dall’integrazione:
Chiusura ciclo su azione: L’esecuzione di un’azione conclude immediatamente la sequenza per prevenire ripetizioni accidentali di comandi.
Sanificazione argomenti: Gli argomenti non coerenti con lo schema o identificati come allucinazioni vengono automaticamente rimossi.
Validazione entità: L’esecuzione è limitata esclusivamente alle entità esplicitamente esposte all’agente Assist.
Risposta parlata
Poiché Needle non genera testo libero, le risposte rivolte all’utente sono modellate tramite template deterministici in lingua italiana:
Azione eseguita: “Ho spento Luce bagno.”
Lettura di stato: “Temperatura bagno: 21 °C.”
Fuori dominio: “Non ho capito, posso aiutarti solo con i dispositivi di casa.”
Analisi dei benchmark e ottimizzazione dell’interfaccia
I test sono stati condotti su un set di benchmark composto da 31 frasi in lingua italiana (comandi diretti, domande di stato, frasi condizionali e input fuori dominio).
La misurazione iniziale basata sui pesi standard e 18 strumenti esposti da HA ha riportato un tasso di successo di 5/31.
L’analisi dei log ha mostrato che, sebbene il ragionamento interno (reasoning) fosse corretto, il modello selezionava sistematicamente chiamate errate (es. media_player__HassMediaNext). La causa è stata individuata nei prefissi di namespace standard di Home Assistant (intent__, light __, media_player__), che creavano interferenza nell’attenzione del modello da 35 MB.
Rimuovendo i prefissi durante l’invio dello schema a Needle (e ripristinandoli prima dell’esecuzione in HA), i risultati sono variati significativamente:
ConfigurazioneTool correttiTool e argomenti correttiLatenza media Con prefissi namespace 6 / 316 / 31~250 ms Senza prefissi namespace 20 / 3117 / 31~160 ms
La semplice ottimizzazione dell’interfaccia d’ingresso ha incrementato la precisione dal 19% al 64% a parità di modello e pesi.
Roadmap di sviluppo
Valutazione delle condizioni logiche tramite logica deterministica integrata.
Addestramento di un adattatore LoRA dedicato sugli intent italiani di Home Assistant.
Generazione e validazione guidata di automazioni domotiche.
Esposizione dell’integrazione come tool MCP (Model Context Protocol) per agenti esterni.
Il codice sorgente, l’Add-on e la Custom Integration sono disponibili su GitHub nel repository savez/ha-addon-needle.
Conclusione
Needle Local AI non sostituisce un LLM generalista di grandi dimensioni: i comandi diretti e le azioni multiple mostrano una buona affidabilità, mentre le letture di stato e la gestione delle condizioni richiedono un supporto deterministico lato codice.
L’adozione di un modello ultra-leggero da 35 MB integrato in locale su Raspberry Pi 5 si dimostra tuttavia efficace per gestire il traffico di comandi frequenti e strutturati della casa con latenze ridotte e in totale riservatezza. L’esperienza di sviluppo evidenzia come, lavorando con modelli di piccole dimensioni, la strutturazione e la pulizia dell’interfaccia dei dati siano fattori cruciali quanto la capacità computazionale del modello stesso.
Top comments (0)