Un’intelligenza artificiale che non chatta, ma agisce
I modelli linguistici (LLM) hanno fatto in modo di far utilizzare le IA a tutti, senza specifiche competenze. Quando sentiamo parlare di LLM, pensiamo subito a chatbot capaci di rispondere alle domande. Needle segue una filosofia diversa.
Needle è un piccolo modello di intelligenza artificiale (14MB) sviluppato da Cactus Compute che ha un unico obiettivo: trasformare una richiesta espressa in linguaggio naturale in una chiamata a una funzione Python. In pratica non serve per conversare, ma per controllare applicazioni, dispositivi e automazioni direttamente sul Raspberry Pi.
Ad esempio, invece di scrivere:
|
1 |
accendi_led() |
potremo semplicemente scrivere:
|
1 |
Accendi il LED |
e sarà Needle a decidere quale funzione eseguire.
La caratteristica più interessante è che può funzionare localmente sul Raspberry Pi 5, usando soltanto la CPU, senza connessioni cloud e senza hardware AI dedicato.
Prendendo spunto dalla news sul sito ufficiale, ho pensato a qualche implementazione di esempio e al suo completamento naturale: abbinare il riconoscimento vocale in lcoale!
i vostri progetti potrebbero ricevere comandi in linguaggio naturale, ad esempio “robot, vieni avanti!” senza bisogno di connessione alla rete.
Cosa realizzeremo
In questo tutorial creeremo un piccolo assistente locale capace di:
- salvare note in un file;
- leggere la temperatura del Raspberry Pi;
- accendere e spegnere un LED;
- comprendere comandi scritti in italiano.
Alla fine potremo scrivere:
|
1 |
Accendi il LED |
|
1 |
Salva una nota che dice di controllare il sensore domani |
A questo indirizzo troverai dei demo online di varie implementazioni (domotica, il controllo di un robot aspirapolvere, ma anche smartwatch, smartphone, occhiali AR e anche auto) in cui puoi testare il funzionamento di questo minuscolo LLM, scrivendo comandi in italiano naturale.

Prerequisiti
Serve:
- Raspberry Pi (5 consigliato)
- Raspberry Pi OS aggiornato
- Python 3
- connessione Internet solo per il primo download
Per la parte LED serviranno:
- 1 LED
- 1 resistenza da 220 o 330Ω
- cavetti jumper
Installazione di Needle
Apriamo il Terminale.
Creiamo un ambiente virtuale
|
1 |
python3 -m venv needle-env |
|
1 |
source needle-env/bin/activate |
Installiamo Needle
|
1 |
python -m pip install cactus-needle |
Primo esempio: salvare delle note
Creiamo un file chiamato note.py usando l’editor Nano:
|
1 |
nano note.py |
Inseriamo ora nell’editor il codice seguente.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
import needle from pathlib import Path # File che conterrà le note notes_path = Path("note.txt") @needle.tool def salva_nota(testo: str): """ Salva una nota nel file locale. """ with notes_path.open("a", encoding="utf-8") as file: file.write(testo + "\n") return { "testo": testo, "file": str(notes_path) } agent = needle.Needle( tools=[salva_nota] ) risposta = agent.run( "Salva una nota che dice di comprare un nuovo alimentatore" ) print(risposta) |
Come funziona il codice
Analizziamo le parti più importanti.
1. Il decoratore @needle.tool
|
1 |
@needle.tool |
“Questa funzione può essere richiamata dall’intelligenza artificiale.”
Senza questa riga Needle non saprebbe che la funzione esiste.
2. La docstring
|
1 2 3 |
""" Salva una nota nel file locale. """ |
Più la descrizione è chiara, più sarà facile per il modello scegliere l’azione corretta.
3. I type hint
|
1 |
def salva_nota(testo: str): |
L’indicazione str comunica a Needle che il parametro deve essere una stringa.
Needle usa nome della funzione, docstring e annotazioni di tipo per costruire automaticamente lo schema dello strumento.
Secondo esempio: leggere la temperatura
Aggiungiamo una nuova funzione.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
import subprocess @needle.tool def temperatura_cpu(): """ Restituisce la temperatura del Raspberry Pi. """ output = subprocess.check_output( ["vcgencmd", "measure_temp"], text=True ) temperatura = float( output.split("=")[1].split("'")[0] ) return { "temperatura": temperatura } |
|
1 2 3 4 5 6 |
agent = needle.Needle( tools=[ salva_nota, temperatura_cpu ] ) |
|
1 2 3 |
agent.run( "Qual è la temperatura del Raspberry?" ) |
Controllare un LED
Passiamo a un esempio che mostra il controllo dell’hardware.
Collegamenti
Collegare:
- GPIO17 → Resistenza → Anodo LED (positivo, piedino più lungo)
- Catodo LED (negativo, piedino più corto) → GND

La libreria GPIO Zero dovrebbe essere pre-installata, ma nel caso, ecco come si installa:
|
1 |
pip install gpiozero |
Funzioni per accendere e spegnere il LED
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
from gpiozero import LED led = LED(17) @needle.tool def accendi_led(): """ Accende il LED. """ led.on() return { "stato": "acceso" } @needle.tool def spegni_led(): """ Spegne il LED. """ led.off() return { "stato": "spento" } |
Aggiorniamo l’agente:
|
1 2 3 4 5 6 7 8 |
agent = needle.Needle( tools=[ salva_nota, temperatura_cpu, accendi_led, spegni_led ] ) |
Proviamo in italiano
Creiamo un semplice ciclo interattivo.
|
1 2 3 4 5 6 7 8 9 10 |
while True: comando = input("\nComando: ") if comando.lower() == "esci": break risultato = agent.run(comando) print(risultato) |
Esempi:
|
1 |
Comando: accendi il led |
|
1 |
Comando: spegni il led |
|
1 |
Comando: salva una nota che dice di controllare la batteria |
|
1 |
Comando: qual è la temperatura del Raspberry? |
Perché Needle è interessante?
La maggior parte delle automazioni tradizionali richiede:
- pulsanti;
- menu;
- comandi predefiniti;
- API complesse.
Con Needle possiamo creare interfacce molto più naturali.
Invece di un comando Python:
|
1 |
accendi_led() |
|
1 |
Per favore accendi il LED rosso |
|
1 |
Puoi attivare la luce? |
Idee per progetti futuri
Una volta compreso il meccanismo, è possibile controllare:
- relè domotici;
- sensori ambientali;
- telecamere Raspberry Pi;
- robot;
- display OLED;
- sistemi di irrigazione;
- stazioni meteo;
- dispositivi IoT.
Sul sito ufficiale sono reperibili esempi con LED, acquisizione di fotografie e lettura di parametri di sistema tramite funzioni Python dedicate.
Conclusioni?
Needle rappresenta un approccio molto interessante all’intelligenza artificiale “embedded”: invece di generare testo come un chatbot, interpreta una richiesta e decide quale funzione Python eseguire. Grazie alle sue dimensioni ridotte e alla possibilità di funzionare localmente, è particolarmente adatto ai progetti Raspberry Pi e all’automazione domestica.
Per chi inizia, il concetto fondamentale da ricordare è uno solo:
Non si insegna a Needle come fare qualcosa, gli si mostra quali funzioni sono disponibili. Sarà lui a scegliere quella corretta quando riceve un comando in linguaggio naturale.
Questo apre la strada a progetti che possono essere controllati semplicemente scrivendo o parlando in italiano, rendendo l’interazione con Raspberry Pi più naturale e accessibile.
OPS! Ho scritto “parlando”.. in effetti questa è, a mio parere la svolta per cambiare completamente il modo in cui ci si interfaccia ai nostri progetti.
Dare “orecchie” a Needle
Per un progetto in cui Needle diventa il “cervello” che decide quale funzione Python eseguire, la soluzione più pratica oggi è accoppiarlo con il riconoscimento vocale in locale offerto da Wisper.ccp: la riscrittura in puro C/C++ del modello di riconoscimento vocale Whisper creato da OpenAI.
Whisper.cpp + Needle
Per tre motivi:
- Funziona completamente in locale
- Supporta molto bene l’italiano
- Genera direttamente il testo da passare a Needle
L’architettura diventerebbe:
Microfono│▼Whisper.cpp(Speech-to-Text)│▼Testo in italiano│▼Needle(Function Calling)│▼Funzione Python│▼LED, relè, fotocamera, sensori…
In pratica la frase:
“Accendi il LED rosso”
|
1 |
agent.run("Accendi il LED rosso") |
|
1 |
accendi_led() |
Cominciamo con questo schema di funzionamento: premo INVIO, registra 5 secondi, riconosci il testo, invialo a Needle, esegui l’azione.
Esempio pratico: installiamo!
Installiamo:
|
1 |
pip install sounddevice scipy cactus-needle |
1. Installa i prerequisiti
git e cmake:|
1 2 3 |
<span class="undefined" data-copy-service-computed-style="font-family: monospace; font-size: 14px; font-weight: 400; margin: 0px; text-decoration: none; border-bottom: 0px rgb(0, 29, 53);">sudo apt update sudo apt install -y build-essential cmake git </span> |
2. Clona il repository
|
1 2 3 |
<span class="undefined" data-copy-service-computed-style="font-family: monospace; font-size: 14px; font-weight: 400; margin: 0px; text-decoration: none; border-bottom: 0px rgb(0, 29, 53);">git clone </span><span class="undefined" data-copy-service-computed-style="font-family: monospace; font-size: 14px; font-weight: 400; margin: 0px; text-decoration: none; border-bottom: 0px rgb(0, 29, 53);">https://github.com/ggml-org/whisper.cpp.git</span><span class="undefined" data-copy-service-computed-style="font-family: monospace; font-size: 14px; font-weight: 400; margin: 0px; text-decoration: none; border-bottom: 0px rgb(0, 29, 53);"> cd whisper.cpp </span> |
3. Compila il progetto
|
1 2 3 |
<span class="undefined" data-copy-service-computed-style="font-family: monospace; font-size: 14px; font-weight: 400; margin: 0px; text-decoration: none; border-bottom: 0px rgb(0, 29, 53);">cmake -B build cmake --build build -j --config Release </span> |
-j userà tutti i core disponibili del processore del tuo Raspberry Pi per velocizzare la compilazione).4. Scarica un modello
tiny o base che sono più leggeri e adatti alle risorse limitate del Raspberry Pi):|
1 2 |
<span class="undefined" data-copy-service-computed-style="font-family: monospace; font-size: 14px; font-weight: 400; margin: 0px; text-decoration: none; border-bottom: 0px rgb(0, 29, 53);">bash ./models/download-ggml-model.sh tiny </span> |
5. Prova il funzionamento
|
1 |
./build/bin/whisper-cli -m models/ggml-tiny.bin -l it -f tuo_file_audio.wav |
Come vedi qui su, aggiungi il flag -l it per evitare che l’algoritmo perda tempo a rilevare automaticamente la lingua (ed evitare che scambi l’italiano per un’altra lingua simile). Essenso modelli multilingua, l’accuratezza per la sola lingua italiana è limitata, se noti che il modello tiny fa troppi errori, sul Raspberry Pi il miglior compromesso tra velocità e precisione per l’italiano è solitamente il modello base
Registrare l’audio
|
1 2 3 4 5 6 7 8 9 10 11 12 |
import sounddevice as sd from scipy.io.wavfile import write durata = 5 frequenza = 16000 print("Parla...") audio = sd.rec(int(durata * frequenza), samplerate=frequenza, channels=1) sd.wait() write("comando.wav", frequenza, audio) print("Registrazione completata") |
Trascrivere con Whisper
Supponendo di avere Whisper.cpp installato:
|
1 2 3 4 5 6 7 8 9 10 |
import subprocess risultato = subprocess.run( ["./main", "-m", "models/ggml-base.bin", "-f", "comando.wav", "-l", "it"], capture_output=True, text=True, ) testo = risultato.stdout print(testo) |
Inviare il testo a Needle
|
1 2 3 4 5 |
comando = testo risposta = agent.run(comando) print(risposta) |
Tutto qui.
Letteralmente Needle non saprà nemmeno che l’input arriva dal microfono.
Per lui sarà semplicemente:
|
1 |
agent.run("Accendi il led rosso") |
Evoluzione successiva: assistente vocale vero e proprio
Una volta completata la versione base, puoi fare un enorme salto di qualità aggiungendo una “wake word”.
Ad esempio: “Ehi Raspberry” oppure “Ciao Needle”
Microfono│▼Wake Word(parola di attivazione)│▼Whisper.cpp│▼Needle│▼Funzioni Python
In questo caso il sistema resta sempre in ascolto ma registra il comando solo dopo aver sentito la parola chiave.
RaspberryItaly Community Italiana