Il vibe coding..di cosa si tratta e le implicazioni nella didattica della programmazione

Il vibe coding è una tecnica di programmazione assistita dall’intelligenza artificiale, in cui, invece di scrivere manualmente il codice sorgente, lo sviluppatore fornisce brevi descrizioni in linguaggio naturale a un modello linguistico (ad esempio un chatbot basato su un LLM), che genera il codice al suo posto. In pratica, il programmatore passa dal ruolo di autore del codice a quello di “regista” o prompt engineer: guida l’AI descrivendo cosa deve fare il software, verifica i risultati e itera le richieste per correggere errori o migliorarne le funzionalità. Il termine vibe coding è stato introdotto recentemente nell’ambiente informatico per descrivere questa nuova modalità di programmazione “a vibrazioni”, ovvero totalmente guidata dall’intuito e dall’assistenza dell’AI.

Il vibe coding in pratica

Dal punto di vista pratico, fare vibe coding significa interagire in modo iterativo con un assistente AI all’interno di un ambiente di sviluppo (IDE) o di una chat, fornendo istruzioni in linguaggio naturale e lasciando che sia la macchina a generare il codice sorgente corrispondente. Il programmatore assume un ruolo simile a quello di un regista: descrive ad alto livello ciò che l’applicazione deve fare (ad esempio: “mi serve un sito web con un form di login e registrazione utenti”), e l’AI genera una struttura di progetto contenente il codice necessario. A quel punto, l’utente può richiedere perfezionamenti o modifiche, accettando eventualmente le soluzioni suggerite dall’AI.

In caso di errori o malfunzionamenti, anziché eseguire personalmente un debug dettagliato, il vibe coder copia e incolla il messaggio di errore nel prompt e chiede al modello di risolverlo. Lo sviluppo procede quindi per tentativi successivi, guidati dall’AI: se il codice generato non produce esattamente il risultato desiderato, si forniscono nuove istruzioni o correzioni in linguaggio naturale, finché l’output non è aderente alle aspettative dello sviluppatore (umano).

Questo approccio cambia fortemente l’idea tradizionale della programmazione come attività scientifica e manuale. Nel vibe coding, infatti, l’attenzione si sposta dalla scrittura del codice all’ideazione e alla validazione: la parte “meccanica” (scrivere sintassi, implementare algoritmi noti) viene delegata all’AI, mentre lo sviluppatore si concentra sul cosa fare e sul verificare che il come, generato automaticamente, sia accettabile.

Il vibe coding può essere paragonato a una forma di pair programming, in cui il “collega” con cui si programma in coppia è l’AI stessa. L’intelligenza artificiale partecipa attivamente alla scrittura del codice, proponendo soluzioni, e il ruolo umano assomiglia (o dovrebbe assomigliare) a quello di un senior developer che supervisiona e guida il processo.

Vantaggi e potenziale applicativo

Il concetto di vibe coding è stato accolto da alcuni con entusiamo perché promette di rendere più accessibile la programmazione e di accelerare lo sviluppo di nuove idee (è un pò come il padel per chi vorrebbe giocare a tennis senza aver preso mai lezioni… : – D).

Limiti

Non mancano però i punti deboli e i rischi associati al vibe coding. Qui di seguito sono elencate le principali criticità emerse nei dibattiti:

  • Perdita di comprensione del codice: affidarsi all’AI per generare intere porzioni di programma senza rivederle può lasciare gli sviluppatori con una conoscenza superficiale o nulla di come funzioni realmente il proprio software. Se una funzionalità è stata scritta quasi interamente dall’AI e integrata con minima supervisione, lo sviluppatore potrebbe faticare a correggerla o modificarla in futuro perché non ne comprende in pieno il codice – un problema serio in termini di manutenzione.
  • Qualità del codice e debugging: accettare acriticamente tutto il codice generato dall’AI può portare a introdurre bug, errori sottili o soluzioni non ottima senza accorgersene.
  • Illusione di semplicità : far scrivere buon software a un’AI “è un esercizio intellettuale non banale” che richiede grande impegno e conoscenze da parte dello sviluppatore umano. Non si tratta affatto di mettere il “pilota automatico”, al contrario, il programmatore deve mantenere alta concentrazione per guidare continuamente lo sviluppo dell’architettura del modello.

Il vibe coding nella didattica

L’introduzione del vibe coding nel panorama della didattica dell’informatica apre scenari inediti e al tempo stesso delicati. Si tratta di un cambiamento di prospettiva che non investe soltanto il modo in cui si scrive codice, ma anche il modo in cui si apprende, si progetta e si struttura il pensiero computazionale.

Il vibe coding, basato sull’interazione in linguaggio naturale con strumenti di intelligenza artificiale generativa, abbassa significativamente la soglia d’accesso alla programmazione. Studenti che normalmente si scoraggiano di fronte alla rigidità sintattica dei linguaggi tradizionali possono finalmente provare la soddisfazione di vedere le proprie idee trasformarsi in applicazioni funzionanti. In questo senso, la tecnologia non solo semplifica il processo, ma contribuisce a colmare il divario tra pensiero creativo e realizzazione tecnica.

Un ulteriore elemento di rilievo è il potenziale creativo. Con il vibe coding, gli studenti possono osare di più, sbagliare più velocemente e riprovare senza la frustrazione generata dagli errori sintattici più banali. Si crea così un ambiente favorevole alla sperimentazione continua. In ambito didattico, ciò significa poter progettare laboratori più liberi, nei quali gli allievi esplorano soluzioni originali, chiedono all’intelligenza artificiale di adattare il codice a nuove esigenze e imparano a dialogare con uno strumento che, più che eseguire comandi, è in grado di interpretarli.

Tuttavia, sarebbe ingenuo pensare che basti introdurre il vibe coding in aula per ottenere automaticamente risultati efficaci. Il rischio più evidente è che gli studenti si limitino a copiare e incollare codice generato dall’AI, senza comprenderne il funzionamento, scambiando l’efficienza per apprendimento. In questo scenario, il ruolo del docente diventa ancor più centrale: non più semplice trasmettitore di conoscenze, ma guida capace di orientare gli studenti tra immediatezza e profondità, tra funzionalità e consapevolezza. Occorre insegnare non solo come utilizzare l’intelligenza artificiale per programmare, ma soprattutto quando fidarsi, come verificare i risultati, perché una soluzione funziona e cosa può accadere in caso di errore.

Rimane quindi fondamentale che gli studenti acquisiscano solide basi nella programmazione, affinché possano ricoprire consapevolmente il ruolo di senior nel rapporto collaborativo con l’agente di AI che li assiste nella generazione del codice.

Il vibe coding, in definitiva, può rappresentare un’occasione straordinaria per ripensare la didattica dell’informatica. Può introdurre in aula un nuovo paradigma, in cui progettualità, linguaggio naturale e creatività diventano le nuove coordinate dell’apprendimento. Ma, come ogni strumento potente, richiede metodo, spirito critico e una solida cornice pedagogica. Non sostituisce la comprensione: ne rende semmai ancora più urgente lo sviluppo.

Riferimenti:

  • Andrej Karpathy, “There’s a new kind of coding I call vibe coding…”, X.com (post del 2 febbraio 2025)
  • Benj Edwards, “Will the future of software development run on vibes?”Ars Technica, 5 marzo 2025 web.archive.orgweb.archive.org.
  • Kevin Roose, “Not a Coder? With A.I., Just Having an Idea Can Be Enough”New York Times, 27 febbraio 2025 it.wikipedia.org.
  • Hasan Chowdhury, Jyoti Mann, “Silicon Valley’s next act: bringing ‘vibe coding’ to the world”Business Insider, 13 febbraio 2025 it.wikipedia.org.
  • Comunicazione “Con l’intelligenza artificiale diventeremo tutti programmatori?”Il Post, 24 marzo 2025 it.wikipedia.org.
  • Dany Kitishian, “Vibe Coding: Karpathy’s Viral Term, Ng’s Reality Check…”Klover.ai Blog, 11 giugno 2025 klover.aiklover.aiklover.aiklover.ai.
  • Simon Willison, “Not all AI-assisted programming is vibe coding (but vibe coding rocks)”, blog personale, 19 marzo 2025 simonwillison.netsimonwillison.netsimonwillison.net.
  • John Delaney, “Catching the Vibe of Vibe Coding”Communications of the ACM, 6 maggio 2025 cacm.acm.orgcacm.acm.org.
  • Ranjan Sapkota et al., “Vibe Coding vs. Agentic Coding: Fundamentals and Practical Implications of Agentic AI”, arXiv:2505.19443 [cs.SE], 26 May 2025 arxiv.org

L’Illusione del Pensiero: Quando l’Intelligenza Artificiale “Pensa” Davvero?

Nell’era dell’intelligenza artificiale, siamo sempre più stupiti dalle capacità dei modelli linguistici di ultima generazione, ovvero gli LRM. Prima approfondire questa affermazione, vediamo cosa sono gli LRM e che differenza con i più noti LLM (Per favore leggi tutto l’articolo, soprattutto le conclusioni).

Un LLM (Large Language Model) è un tipo di modello di intelligenza artificiale addestrato su una grandissima quantità di dati testuali, ma anche immagini, audio e video. Il suo scopo principale è comprendere, generare e manipolare il linguaggio umano.

Il termine LRM è più specifico e si riferisce a una sotto categoria o, più precisamente, a una nuova generazione di LLM che sono stati specificamente progettati o potenziati per affrontare compiti che richiedono un ragionamento esplicito e sequenziale. La caratteristica distintiva di un LRM è la sua capacità di generare “processi di ragionamento” passo-passo prima di arrivare a una risposta finale.

In pratica, ogni LRM è un tipo di LLM, ma non tutti gli LLM sono considerati LRM nel senso stretto di modelli che generano attivamente e strutturalmente processi di ragionamento complessi prima della risposta finale.

Questi “Large Reasoning Models” (LRM), non si limitano più a fornire risposte dirette; ora sono in grado di generare e mostrare “processi di cui è composto il pensiero” prima di arrivare a una soluzione. È un po’ come se il computer si mettesse a scarabocchiare appunti e a ragionare ad alta voce prima di dirci la risposta finale.

Ma c’è una domanda cruciale viene spontaneo porsi: questi modelli stanno davvero “pensando” o stanno solo simulando il pensiero illudendoci che stiano pensando come noi umani?

Un recente studio condotto da un team di ricercatori di Apple – Parshin Shojaee, Iman Mirzadeh, Keivan Alizadeh, Maxwell Horton, Samy Bengio e Mehrdad Farajtabar – intitolato “The Illusion of Thinking: Understanding the Strengths and Limitations of Reasoning Models via the Lens of Problem Complexity” affronta questo tema.


Oltre la Risposta Finale: Cosa C’è Dietro il “Pensiero” degli LRM?

Finora, la maggior parte delle valutazioni dei modelli AI si è concentrata sull’accuratezza della risposta finale in benchmark (modi per valutare le prestazioni) complessi, spesso di natura matematica o di programmazione. Tuttavia, questo approccio ha due limiti:

  • Contaminazione dei dati: I modelli potrebbero aver “visto” problemi simili durante il loro addestramento, rendendo difficile capire se stanno davvero ragionando o semplicemente richiamando informazioni memorizzate.
  • Mancanza di trasparenza: Non ci dicono nulla su come sono arrivati a quella risposta. Il processo di ragionamento interno rimane una “scatola nera”.

Per superare queste limitazioni, i ricercatori di Apple hanno adottato un approccio diverso dal solito. Invece di usare i benchmark, hanno usato dei giochi tipo puzzle. Immaginate una serie di rompicapo logici in cui la complessità può essere aumentata o diminuita pur mantenendo la stessa struttura logica di base. Questo ha permesso loro di osservare non solo se il modello risolveva il puzzle, ma anche come il modello provava a farlo, analizzando le “tracce di ragionamento” interne che generava.


Il Crollo dell’Accuratezza e un Limite Sorprendente

I risultati di questi esperimenti sono stati sorprendenti. Innanzitutto, è emerso che i “Large Reasoning Models di frontiera” (ovvero quelli più avanzati e performanti) mostrano un completo crollo dell’accuratezza quando la complessità del problema supera una certa soglia. Non si tratta di una graduale diminuzione delle prestazioni, ma di un vero e proprio “muro”. Oltre un certo punto, il modello smette semplicemente di essere efficace.

Ma la scoperta meno immaginabile riguarda lo sforzo di ragionamento dei modelli. Il numero di “token di pensiero di inferenza” generati ovvero quante “parole” o “passaggi” il modello genera per il suo processo di ragionamento, ci si aspetterebbe che aumentasse man mano che il problema diventa più difficile invece avviene esattamente il contrario.

I ricercatori hanno osservato che dopo aver raggiunto una specifica soglia del modello, lo sforzo di ragionamento diminuisce drasticamente. Questo accade nonostante i problemi siano diventati più difficili e i modelli siano ben al di sotto dei loro limiti di contesto e di generazione (cioè, hanno ancora spazio per generare più token).


Cosa Significa per il Futuro del “Pensiero” AI?

Questo “limite nella scalabilità” suggerisce una limitazione fondamentale e affascinante nel processo di “pensiero” degli LRM. Sembra che, oltre determinate soglie di complessità, questi modelli non solo falliscono nel risolvere i problemi, ma addirittura riducono il loro impegno computazionale nell’inferenza. È come se, di fronte a un problema troppo complesso, il modello decidesse inconsciamente di arrendersi, riducendo lo sforzo anziché aumentarlo. (Questo è un grande punto di contatto con gli esseri umani 🙂 ).

Questo studio fornisce nuove metriche per valutare le capacità di ragionamento dei modelli AI, ma apre anche nuove strade per la ricerca. Comprendere perché e come gli LRM raggiungono questi limiti è fondamentale per sviluppare modelli futuri che possano superare veramente l’”illusione del pensiero” e raggiungere un ragionamento più robusto e scalabile.

In sintesi, mentre i Large Reasoning Models ci affascinano con la loro capacità di simulare il pensiero, la ricerca di Apple sottolinea che il vero “pensiero” e soprattutto la sua scalabilità sono ancora un orizzonte da raggiungere.

È interessante notare come la risposta dell’algoritmo di fronte a problemi di elevata complessità lo renda, a mio parere, sorprendentemente “umano”. Questa tendenza a mostrare un “crollo dell’accuratezza” e, ancor più, una diminuzione dello sforzo di ragionamento oltre una certa soglia, ricorda molto il comportamento che la maggior parte di noi adotterebbe di fronte a un compito percepito come insormontabile o eccessivamente oneroso.

È fondamentale ricordare che l’Intelligenza Artificiale, nella sua essenza, si basa su algoritmi complessi che elaborano quantità massive di dati. È proprio questa elaborazione su vasta scala che le permette di generare risposte che, per la loro coerenza e pertinenza, spesso assomigliano in modo impressionante a quelle prodotte da un essere umano.

Questa somiglianza non dovrebbe però sorprenderci. Non potremmo forse considerare il cervello umano come una macchina estremamente più sofisticata e complessa rispetto a quelle che siamo stati capaci di costruire finora? Nonostante le sue mirabili capacità, forse non vi è nulla di intrinsecamente magico o divino nel suo funzionamento; ma potrebbe essere in ultima analisi, un sistema biologico e computazionale altamente evoluto. La ricerca sull’IA, esplorando i limiti dei modelli attuali, contribuisce a illuminare anche la natura dei nostri stessi processi cognitivi.

Massimo Fedeli

Riferimenti: “The Illusion of Thinking: Understanding the Strengths and Limitations of Reasoning Models via the Lens of Problem Complexity”, Parshin Shojaee∗† Iman Mirzadeh∗ Keivan Alizadeh Maxwell Horton Samy Bengio Mehrdad Farajtabar Apple, 2025.van Alizadeh Maxwell Horton Samy Bengio Mehrdad Farajtabar Apple, 2025

https://www.digitalmenti.eu

https://ml-site.cdn-apple.com/papers/the-illusion-of-thinking.pdf

Blockchain: Cos’è, Come Funziona e Perché Sta Cambiando il Mondo

La blockchain è una delle tecnologie più rivoluzionarie degli ultimi decenni. Nata con Bitcoin, oggi trova applicazione in settori che vanno dalla finanza alla logistica, dalla sanità alla gestione dell’identità digitale. Ma cosa la rende così speciale? Come funziona a livello tecnico? E quali sono le sue reali implicazioni per il futuro?

Le Origini: Da Bitcoin alla Blockchain Moderna

La blockchain è stata concepita nel 2008 con la pubblicazione del whitepaper “Bitcoin: A Peer-to-Peer Electronic Cash System” da parte dell’ancora misterioso Satoshi Nakamoto. Questo documento rappresentava una risposta alla crisi finanziaria globale, proponendo un sistema monetario alternativo basato sulla crittografia anziché su banche centrali.

Il 3 gennaio 2009 veniva minato il primo blocco della blockchain Bitcoin, contenente un messaggio emblematico: “The Times 03/Jan/2009 Chancellor on brink of second bailout for banks”. Questo riferimento alla crisi bancaria non era casuale, ma rappresentava il manifesto ideologico della nuova tecnologia.

Con il lancio di Ethereum nel 2015, la blockchain evolveva ulteriormente. Vitalik Buterin introduceva il concetto di smart contract, trasformando la tecnologia da semplice registro per transazioni a piattaforma computazionale decentralizzata. Oggi le applicazioni spaziano dalla finanza decentralizzata alla tracciabilità alimentare, dai sistemi di voto elettronico alla certificazione di opere d’arte digitali.

Architettura Tecnica della Blockchain

Blockchain

La blockchain è essenzialmente un registro distribuito che organizza i dati in blocchi collegati cronologicamente attraverso la crittografia. Ogni blocco contiene tre componenti fondamentali:

L’intestazione (header) include l’hash del blocco precedente, creando un collegamento crittografico immodificabile. Contiene inoltre un timestamp, un nonce (nel caso di Proof of Work) e la radice dell’albero di Merkle, una struttura dati che riassume tutte le transazioni nel blocco.

Il corpo del blocco contiene l’elenco effettivo delle transazioni o dei dati registrati. Queste informazioni, una volta validate e aggiunte alla catena, diventano praticamente immutabili grazie alle proprietà delle funzioni hash crittografiche.

La sicurezza del sistema si basa sulla decentralizzazione. Migliaia di nodi indipendenti mantengono copie identiche della blockchain, verificando costantemente la validità delle nuove transazioni. Questo modello rende estremamente difficile qualsiasi tentativo di manipolazione, poiché un eventuale attaccante dovrebbe modificare non solo un blocco, ma tutta la catena successiva su più della metà dei nodi contemporaneamente.

Meccanismi di Consenso: Il Cuore della Blockchain

Il funzionamento della blockchain dipende da algoritmi che garantiscono l’accordo tra i partecipanti senza bisogno di un’autorità centrale. I principali meccanismi di consenso includono:

Proof of Work e Proof of Stake: due modelli a confronto

Nel mondo della blockchain, il meccanismo con cui si validano le transazioni e si aggiungono nuovi blocchi alla catena è cruciale. Due dei sistemi più noti sono il Proof of Work (PoW) e il Proof of Stake (PoS), ciascuno con caratteristiche e implicazioni molto diverse, sia in termini tecnici che ambientali.

Il Proof of Work è il metodo originario introdotto da Bitcoin nel 2009. In questo modello, i partecipanti alla rete – chiamati “miner” – competono tra loro per risolvere un complesso problema matematico. Il compito consiste nel trovare un numero, detto nonce, che, una volta combinato con i dati del blocco e processato tramite una funzione hash, restituisca un risultato che soddisfi determinate condizioni (ad esempio, che inizi con un certo numero di zeri). Questo processo richiede miliardi di tentativi al secondo ed è completamente casuale. Il primo miner che riesce a trovare la soluzione valida ottiene il diritto di aggiungere il nuovo blocco alla blockchain e riceve in cambio una ricompensa in criptovaluta.

La forza del Proof of Work sta nella sua sicurezza. Per modificare una transazione già registrata, un attaccante dovrebbe rifare tutto il lavoro computazionale per quel blocco e per tutti i blocchi successivi, riuscendo al contempo a superare la potenza di calcolo dell’intera rete. Questo rende la manipolazione praticamente impossibile. Tuttavia, questa sicurezza ha un costo: un enorme dispendio di energia. L’elevato consumo elettrico dei data center dedicati al mining è stato oggetto di critiche crescenti, soprattutto in un’epoca in cui la sostenibilità ambientale è diventata centrale.

Proprio per affrontare questo limite, è stato sviluppato il modello alternativo del Proof of Stake. In questo sistema non è la potenza di calcolo a determinare chi aggiunge il prossimo blocco, ma l’interesse economico messo in gioco. I partecipanti, detti “validatori”, depositano una certa quantità di criptovaluta come garanzia, impegnandola temporaneamente (staking). La rete seleziona a turno un validatore, secondo criteri che possono includere l’importo messo in stake, la durata del deposito e una componente casuale per evitare che i più ricchi dominino il sistema. Una volta selezionato, il validatore propone il nuovo blocco, che viene poi verificato dagli altri partecipanti.

Il meccanismo è progettato per premiare i comportamenti corretti e penalizzare quelli disonesti. Se un validatore cerca di manipolare il sistema, può perdere parte o tutto il capitale messo in stake, attraverso un processo chiamato slashing. Questo crea un incentivo economico alla correttezza, senza necessità di consumare energia elettrica per calcoli complessi.

Il passaggio dal Proof of Work al Proof of Stake rappresenta una rivoluzione in termini di efficienza. Un esempio concreto è Ethereum, che nel 2022 ha completato la sua transizione al nuovo sistema, riducendo il proprio consumo energetico di oltre il 99%. Questo cambiamento ha aperto la strada a una blockchain più sostenibile e accessibile, pur mantenendo un buon livello di sicurezza e decentralizzazione.

In sintesi, il Proof of Work garantisce sicurezza attraverso il consumo di risorse fisiche, mentre il Proof of Stake punta sulla responsabilità economica dei partecipanti. Entrambi i modelli cercano di risolvere lo stesso problema – quello della fiducia in un sistema distribuito – ma lo fanno con filosofie e strumenti profondamente diversi.

Applicazioni Pratiche Oltre le Criptovalute

Nel settore finanziario, le blockchain pubbliche come Ethereum hanno dato vita alla finanza decentralizzata (DeFi), che replica servizi bancari tradizionali senza intermediari. Piattaforme come Uniswap o Aave permettono scambi e prestiti direttamente tra utenti, mentre i stablecoin cercano di combinare la stabilità delle valute fiat con l’efficienza delle criptovalute.

Nella logistica, progetti come VeChain utilizzano la blockchain per tracciare prodotti di lusso o alimentari. Walmart ha implementato un sistema basato su Hyperledger Fabric che riduce da giorni a secondi il tempo necessario per rintracciare l’origine di prodotti contaminati.

Nel pubblico, l’Estonia rappresenta un caso studio avanzato, avendo integrato la blockchain nel suo sistema giudiziario, sanitario e di registrazione aziendale. Anche Dubai ha annunciato piani per spostare tutti i documenti governativi su blockchain entro il 2025.

Sfide e Prospettive Future

Nonostante il potenziale, la tecnologia blockchain deve affrontare diverse sfide. Il problema della scalabilità rimane cruciale – mentre le reti tradizionali possono processare decine di migliaia di transazioni al secondo, Bitcoin gestisce circa 7 transazioni e Ethereum circa 30 nella sua configurazione base. Soluzioni come i layer 2 o lo sharding stanno cercando di colmare questo gap.

Le questioni normative rappresentano un altro ostacolo. Paesi come El Salvador hanno abbracciato Bitcoin come valuta legale, mentre altri come la Cina ne hanno vietato l’uso. La mancanza di standard internazionali crea incertezze per aziende e investitori.

Guardando al futuro, l’integrazione con altre tecnologie emergenti come l’AI e l’IoT potrebbe aprire nuovi scenari. Macchine autonome che effettuano micropagamenti, identità digitali autosovrane e sistemi di voto a prova di manipolazione sono solo alcune delle possibilità che potrebbero diventare realtà nei prossimi anni.

Conclusioni

La blockchain rappresenta una fondamentale innovazione nella gestione della fiducia e della verifica delle informazioni. Pur con le sue limitazioni attuali, la tecnologia continua ad evolversi, trovando applicazioni sempre più ampie. Che si tratti di rivoluzionare la finanza, garantire la provenienza dei beni di consumo o creare nuove forme di organizzazione sociale decentralizzata, la blockchain sembra destinata a giocare un ruolo significativo nel nostro futuro digitale.

La vera domanda non è se la blockchain avrà un impatto, ma quanto profondamente trasformerà i nostri sistemi economici e sociali nei prossimi decenni. Mentre il dibattito tra entusiasti e scettici continua, una cosa è certa: abbiamo appena iniziato a esplorare il potenziale di questa tecnologia.


  1. Nakamoto, S. (2008). Bitcoin: A Peer-to-Peer Electronic Cash System. White Paper.
    https://bitcoin.org/bitcoin.pdf
  2. Buterin, V. (2014). Ethereum White Paper: A Next-Generation Smart Contract and Decentralized Application Platform.
    https://ethereum.org/en/whitepaper/
  3. Tapscott, D., & Tapscott, A. (2016). Blockchain Revolution: How the Technology Behind Bitcoin Is Changing Money, Business, and the World. Penguin.
  4. Antonopoulos, A. M. (2017). Mastering Bitcoin: Programming the Open Blockchain (2nd ed.). O’Reilly Media.
  5. Wood, G. (2014). Ethereum: A Secure Decentralised Generalised Transaction Ledger (Yellow Paper).
    https://ethereum.github.io/yellowpaper/paper.pdf

Fedeli Massimo

Java e la Programmazione Multithread

In un’epoca in cui l’efficienza e la reattività sono fondamentali nello sviluppo software, la programmazione multithread in Java si presenta come uno strumento essenziale. Java offre un robusto supporto nativo per la gestione dei thread, consentendo a uno stesso programma di svolgere più operazioni in parallelo, riducendo il tempo di attesa e migliorando l’esperienza dell’utente.

In questo articolo approfondiamo cosa sono i thread, come funzionano, perché sono fondamentali in ambienti moderni e come Java ne facilita l’uso in modo strutturato, sicuro e performante.

Il concetto di thread e multitasking

Un thread rappresenta un flusso indipendente di esecuzione all’interno di un programma. In pratica, è come se un’applicazione avesse più flussi logici che operano simultaneamente. Questo permette, ad esempio, a un software di continuare a rispondere ai comandi dell’utente mentre in background si stanno elaborando dei dati o scaricando informazioni da Internet.

Il multitasking in Java si suddivide in due principali approcci: quello basato sui processi, in cui diversi programmi vengono eseguiti contemporaneamente, e quello basato sui thread, in cui un singolo programma può svolgere più attività allo stesso tempo. Quest’ultimo è ciò che Java gestisce in modo nativo ed efficiente.

Creazione di thread

Differenze tra thread e processi

Nel confronto tra thread e processi, le differenze sono sostanziali. I processi sono entità “pesanti“, ciascuno con il proprio spazio di memoria e risorse isolate, e comunicare tra di loro richiede tecniche complesse e costose in termini di performance. I thread, al contrario, sono più leggeri, condividono la stessa area di memoria e possono interagire direttamente, riducendo i costi e aumentando l’efficienza. Anche il cambio di contesto tra due thread risulta molto meno oneroso rispetto al passaggio tra due processi distinti.

Perché il multithreading è così importante

Un’applicazione che esegue un solo thread può trovarsi a dover attendere a lungo per operazioni lente, come la lettura da disco, la ricezione di dati via rete o l’input da parte dell’utente. In questi casi, la CPU rimane inutilizzata. Il multithreading consente invece di mantenere il processore attivo: mentre un thread è in attesa, un altro può essere eseguito. Questo principio migliora la reattività dell’applicazione e permette di sfruttare al massimo l’hardware disponibile, soprattutto nei sistemi multi-core, dove thread distinti possono essere effettivamente eseguiti in parallelo.

Il modello dei thread in Java

Java adotta un modello multithread completamente integrato nel suo ambiente di esecuzione. Questo significa che le librerie standard e il runtime sono progettati per supportare in modo nativo la concorrenza. A differenza di altri linguaggi che si basano su un ciclo principale che controlla gli eventi in attesa, Java permette a ciascun thread di eseguire in maniera indipendente. Quando un thread entra in stato di attesa, gli altri continuano normalmente senza essere bloccati.

Questo approccio è particolarmente utile in ambienti moderni, dove la presenza di CPU multi-core è la norma. Java offre strumenti come il framework Fork/Join, che consente di dividere attività complesse in sotto-task più piccoli eseguibili in parallelo, sfruttando automaticamente la potenza di calcolo disponibile.

Gli stati di un thread

Ogni thread può trovarsi in diversi stati durante il suo ciclo di vita. Può essere in esecuzione attiva, in attesa che la CPU sia disponibile, sospeso temporaneamente per una condizione esterna o definitivamente terminato. Java fornisce strumenti per gestire ciascuno di questi stati, consentendo uno stretto controllo sul comportamento del programma.

Le priorità giocano un ruolo importante nella gestione dei thread. Java assegna a ogni thread una priorità numerica che influenza l’ordine di esecuzione. Quando un thread con priorità più alta diventa pronto per l’esecuzione, può interrompere l’esecuzione di un thread con priorità inferiore, un meccanismo chiamato preemption. Nei casi in cui più thread abbiano la stessa priorità, il comportamento dipende dal sistema operativo: alcuni, come Windows, gestiscono automaticamente l’alternanza, mentre altri richiedono che i thread cedano volontariamente il controllo.

Stati di un thread

Sincronizzazione e comunicazione tra thread

Poiché i thread condividono la stessa memoria, è fondamentale evitare situazioni in cui più thread accedano simultaneamente agli stessi dati in modo conflittuale. Java risolve questo problema implementando un sistema di monitor interni. Ogni oggetto Java possiede un monitor implicito, attivato automaticamente quando si accede a un metodo sincronizzato. Questo garantisce che solo un thread per volta possa eseguire metodi sincronizzati su un determinato oggetto, impedendo condizioni di gara e accessi non sicuri.

Per la comunicazione, Java fornisce un sistema semplice ma potente basato sui metodi wait(), notify() e notifyAll(), che permettono a un thread di sospendersi in attesa di un segnale da un altro thread. Questo meccanismo facilita il coordinamento tra thread concorrenti senza dover ricorrere a tecniche complicate.

Il ruolo del main thread

Quando un’applicazione Java parte, il primo thread a essere eseguito è quello principale, chiamato main thread. Questo thread può avviare altri thread, detti figli, e attendere il loro completamento attraverso il metodo join(). È spesso l’ultimo thread a terminare, poiché si occupa delle fasi finali di chiusura dell’applicazione.

Pur essendo creato automaticamente, il main thread può essere gestito come qualunque altro, ottenendone un riferimento tramite il metodo currentThread() della classe Thread. Da quel momento, è possibile modificarne il nome, la priorità e persino sospenderlo temporaneamente.

Creazione dei thread: due approcci

Java permette di creare thread attraverso due strategie principali. La prima è implementare l’interfaccia Runnable, definendo il codice da eseguire nel metodo run() e passando l’istanza a un oggetto Thread. Il secondo approccio è estendere direttamente la classe Thread, sovrascrivendo il metodo run() per specificare il comportamento del nuovo thread.

Il primo metodo è generalmente preferito, poiché lascia aperta la possibilità di estendere un’altra classe. L’estensione di Thread è indicata solo quando si ha la necessità di personalizzare o arricchire ulteriormente il comportamento della classe stessa.

Controllo, ciclo di vita e terminazione

Una volta avviato, un thread esegue il proprio codice fino al termine del metodo run(). È possibile verificarne lo stato con il metodo isAlive() oppure sospendere l’esecuzione del thread chiamante fino al termine di un altro thread usando join(). Questa funzionalità è utile, ad esempio, quando si desidera che il main thread attenda la conclusione di tutti i thread figli prima di chiudere l’applicazione.

Join

Esempio

public class ThreadJoinExample {

public static void main(String[] args) {
    System.out.println("Main thread starts.");

    // Creazione del primo thread
    Thread thread1 = new Thread(new MyRunnable("Thread-1"));
    Thread thread2 = new Thread(new MyRunnable("Thread-2"));

    // Avvio dei thread
    thread1.start();
    thread2.start();

    try {
        // Il main thread aspetta il completamento di entrambi
        thread1.join();
        thread2.join();
    } catch (InterruptedException e) {
        System.out.println("Main thread interrupted.");
    }

    System.out.println("Main thread ends.");
}

// Classe che implementa Runnable
static class MyRunnable implements Runnable {
    private String name;

    public MyRunnable(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        System.out.println(name + " starts.");
        try {
            // Simula un’attività che dura 1 secondo
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            System.out.println(name + " interrupted.");
        }
        System.out.println(name + " ends.");
    }
}

}

Ordinamento di un array con merge sort e thread

public class MergeSortMultiThread {

// Metodo principale
public static void main(String[] args) {
    int[] array = {38, 27, 43, 3, 9, 82, 10};

    System.out.println("Array iniziale:");
    printArray(array);

    // Crea e avvia il thread principale per l'ordinamento
    MergeSortTask task = new MergeSortTask(array, 0, array.length - 1);
    Thread mainThread = new Thread(task);
    mainThread.start();

    try {
        mainThread.join();  // Attende la fine dell'ordinamento
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

    System.out.println("\nArray ordinato:");
    printArray(array);
}

// Metodo di supporto per stampare l'array
private static void printArray(int[] array) {
    for (int val : array) {
        System.out.print(val + " ");
    }
    System.out.println();
}

// Classe che implementa il compito da eseguire in un thread
static class MergeSortTask implements Runnable {
    private int[] array;
    private int left, right;

    public MergeSortTask(int[] array, int left, int right) {
        this.array = array;
        this.left = left;
        this.right = right;
    }

    @Override
    public void run() {
        if (left < right) {
            int mid = (left + right) / 2;

            // Crea i thread per le due metà
            Thread leftThread = new Thread(new MergeSortTask(array, left, mid));
            Thread rightThread = new Thread(new MergeSortTask(array, mid + 1, right));

            leftThread.start();
            rightThread.start();

            try {
                leftThread.join();
                rightThread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            merge(array, left, mid, right);
        }
    }

    // Metodo per unire due sotto-array ordinati
    private void merge(int[] array, int left, int mid, int right) {
        int n1 = mid - left + 1;
        int n2 = right - mid;

        int[] L = new int[n1];
        int[] R = new int[n2];

        for (int i = 0; i < n1; ++i)
            L[i] = array[left + i];
        for (int j = 0; j < n2; ++j)
            R[j] = array[mid + 1 + j];

        int i = 0, j = 0;
        int k = left;

        while (i < n1 && j < n2) {
            if (L[i] <= R[j]) {
                array[k++] = L[i++];
            } else {
                array[k++] = R[j++];
            }
        }

        while (i < n1) {
            array[k++] = L[i++];
        }

        while (j < n2) {
            array[k++] = R[j++];
        }
    }
}

}

L’ordinamento dell’array viene effettuato distribuendo il carico computazionale tra due thread, a tutto vantaggio della velocità di esecuzione.

Conclusione

Il multithreading in Java è una risorsa potentissima per lo sviluppatore moderno. Permette di creare applicazioni fluide, reattive e capaci di sfruttare appieno i sistemi multicore che oggi dominano il mercato. Tuttavia, una gestione non corretta dei thread può portare a problemi complessi come deadlock, race condition o starvation.

Conoscere a fondo il modello di concorrenza di Java, utilizzando con criterio Runnable, Thread, synchronized e i metodi di coordinamento tra thread, consente di scrivere software robusto, scalabile e al passo con le esigenze dell’informatica contemporanea.

Riferimenti: Java the Complete Reference Book, 2019, Mr Kotyana Publishing

La Direttiva NIS 2: cosa cambia per la sicurezza informatica in Europa

Negli ultimi anni, gli attacchi informatici sono cresciuti in frequenza, complessità e impatto. Ransomware, phishing, attacchi DDoS (Distributed Denial of Service) e compromissione della supply chain sono solo alcuni dei pericoli che colpiscono aziende, pubbliche amministrazioni e infrastrutture critiche.

Il ransomware, è un tipo di malware che cifra i dati presenti nei computer e nei server, rendendoli inaccessibili. Gli aggressori chiedono poi un riscatto (solitamente in criptovaluta) per fornire la chiave di decrittazione. Un caso noto è l’attacco al sistema sanitario irlandese nel 2021, che ha costretto ospedali e ambulatori a sospendere attività per diversi giorni.

Il phishing consiste nell’invio di email fraudolente che imitano comunicazioni ufficiali (come quelle di banche, servizi postali o enti pubblici) per ingannare l’utente e spingerlo a fornire credenziali d’accesso, numeri di carte di credito o altre informazioni sensibili. Ad esempio, un dipendente può ricevere un’email che sembra provenire dall’ufficio IT interno con la richiesta urgente di aggiornare la password aziendale: cliccando sul link, finisce invece su un sito controllato dagli hacker.

Gli attacchi DDoS mirano a sovraccaricare un sito web o un servizio online inviando un numero massiccio di richieste contemporaneamente, spesso da migliaia di dispositivi compromessi (botnet). Un esempio pratico è il blocco di piattaforme bancarie online, che impedisce ai clienti di accedere ai propri conti per ore o giorni.

La compromissione della catena di fornitura (supply chain attack) avviene quando gli attaccanti prendono di mira un fornitore o un partner tecnologico meno protetto, per poi propagare l’attacco all’organizzazione principale. Il caso SolarWinds del 2020 è emblematico: il software di gestione IT dell’azienda è stato modificato con un malware, che ha infettato migliaia di clienti, incluse agenzie governative e multinazionali.

Negli ultimi tempi, un ruolo sempre più rilevante nel potenziamento delle minacce informatiche è svolto dall’intelligenza artificiale (IA). Gli attaccanti sfruttano l’IA per automatizzare e perfezionare tecniche di attacco, rendendole più rapide ed efficaci. Ad esempio, l’IA può essere utilizzata per generare email di phishing altamente personalizzate, imitare lo stile di scrittura di una persona specifica o analizzare in tempo reale grandi volumi di dati per identificare le vulnerabilità di un sistema. Nei ransomware avanzati, l’IA può anche aiutare a scegliere i bersagli più redditizi o a nascondere meglio le attività malevole nei sistemi compromessi. Inoltre, alcuni attacchi DDoS sono oggi orchestrati da botnet intelligenti in grado di adattare dinamicamente il proprio comportamento per evitare il rilevamento.

Per fronteggiare queste minacce crescenti, l’Unione Europea ha introdotto la Direttiva NIS 2 (Network and Information Security), ovvero la Direttiva (UE) 2022/2555. Approvata il 14 dicembre 2022 ed entrata in vigore il 17 gennaio 2023, mira a garantire un livello elevato e omogeneo di sicurezza informatica in tutti gli Stati membri. I Paesi dell’UE dovranno recepirla nei rispettivi ordinamenti nazionali entro il 17 ottobre 2024.

Perché NIS 2?

La NIS 2 nasce per aggiornare e potenziare la precedente Direttiva NIS del 2016, che ha rappresentato il primo tentativo dell’UE di affrontare in modo coordinato la sicurezza delle reti e dei sistemi informativi. Tuttavia, la NIS 1 si è rivelata inadeguata sotto diversi aspetti:

  • Applicazione disomogenea: ogni Stato membro aveva margini di discrezionalità nell’applicazione, con conseguente frammentazione normativa e differenze significative nei livelli di sicurezza.
  • Ambito troppo ristretto: la NIS 1 copriva solo pochi settori (energia, trasporti, finanza e sanità) e lasciava fuori molte realtà digitali oggi fondamentali.
  • Requisiti poco specifici: le misure di sicurezza richieste erano generiche, lasciando le organizzazioni senza indicazioni chiare su cosa implementare.
  • Monitoraggio inefficace: mancava un sistema di controllo e sanzioni efficace per garantire la conformità.

Eventi recenti hanno reso ancora più evidente la necessità di un cambiamento: la pandemia ha costretto milioni di persone al lavoro da remoto, aumentando la superficie di attacco; l’invasione dell’Ucraina ha mostrato come il cyberspazio possa essere un campo di battaglia strategico; infine, il crescente numero di attacchi a catena di fornitura ha dimostrato quanto siano interconnesse le vulnerabilità digitali.

NIS 2 vuole quindi rafforzare la resilienza dei sistemi informatici europei, garantire risposte rapide ed efficaci agli incidenti e creare una cultura condivisa della cybersicurezza tra pubblico e privato.

Principali novità della Direttiva NIS 2

  1. Ampliamento dei settori coinvolti NIS 2 si applica a un numero più ampio di settori rispetto alla versione precedente. Oltre a energia, trasporti, banche e sanità, ora include:
    • Pubblica amministrazione centrale e locale
    • Gestione delle acque reflue e dei rifiuti
    • Spazio e tecnologie satellitari
    • Servizi postali e di corriere
    • Produzione e distribuzione di prodotti critici (es. microchip, farmaci)
  2. Obblighi di sicurezza informatica Le organizzazioni interessate devono adottare misure concrete e documentate di prevenzione e risposta agli incidenti, tra cui:
    • Analisi dei rischi e valutazione delle vulnerabilità
    • Gestione e risposta agli incidenti informatici
    • Continuità operativa e piani di ripristino
    • Sicurezza nella catena di approvvigionamento
    • Crittografia e controllo degli accessi
    • Formazione del personale
    Esempio: un’azienda che fornisce energia elettrica dovrà dimostrare di avere un piano per reagire rapidamente a un attacco ransomware, garantendo comunque la fornitura minima di servizio.
  3. Notifica degli incidenti In caso di attacco informatico significativo, l’organizzazione deve:
    • Inviare una prima segnalazione entro 24 ore dall’accaduto
    • Inviare una relazione tecnica dettagliata entro 72 ore
    • Fornire una relazione finale entro un mese
    Esempio: un ospedale colpito da malware che blocca i sistemi diagnostici dovrà informare subito le autorità competenti (in Italia, l’Agenzia per la Cybersicurezza Nazionale – ACN).
  4. Ruolo attivo della dirigenza I dirigenti aziendali non possono più delegare la sicurezza informatica solo ai tecnici: devono approvare le misure di sicurezza, ricevere formazione specifica e rispondere in caso di negligenza.
  5. Sanzioni più severe In caso di mancato rispetto delle norme:
    • Fino a 10 milioni di euro o al 2% del fatturato globale annuo per i soggetti essenziali
    • Fino a 7 milioni di euro o all’1,4% del fatturato globale annuo per gli altri soggetti

Applicazione in Italia

Il recepimento della direttiva NIS 2 in Italia è avvenuto con il Decreto Legislativo n. 138 del 4 settembre 2024. Ogni soggetto rientrante nei criteri della normativa deve registrarsi presso l’Agenzia per la Cybersicurezza Nazionale (ACN) e seguire le indicazioni operative stabilite.

L’Agenzia per la Cybersicurezza Nazionale (ACN), istituita nel 2021, è l’ente pubblico italiano responsabile della protezione e del rafforzamento della sicurezza cibernetica del Paese. Essa opera sotto la supervisione della Presidenza del Consiglio dei Ministri e ha il compito di coordinare le attività di prevenzione, monitoraggio, risposta e recupero in caso di incidenti informatici gravi.

Tra i suoi principali compiti figurano:

  • Elaborare la strategia nazionale di cybersicurezza e aggiornarla periodicamente;
  • Coordinare la risposta a incidenti informatici di rilevanza nazionale;
  • Offrire supporto tecnico-operativo alle amministrazioni pubbliche e agli operatori privati dei settori critici;
  • Gestire le comunicazioni obbligatorie previste dalla normativa NIS 2 (ad esempio le notifiche di incidenti);
  • Promuovere la cultura della cybersicurezza attraverso campagne di sensibilizzazione, formazione e cooperazione con il mondo accademico e industriale.

L’ACN svolge inoltre funzioni di vigilanza e può imporre sanzioni in caso di inadempienze, collaborando con autorità europee e internazionali per garantire una risposta coordinata alle minacce cibernetiche globali.

Obblighi per le imprese secondo la Direttiva NIS 2

La Direttiva NIS 2 impone una serie di obblighi alle imprese operanti in settori critici, con l’obiettivo di rafforzare la sicurezza delle reti e dei sistemi informativi nell’Unione Europea. In Italia, il recepimento della direttiva è avvenuto tramite il Decreto Legislativo 138/2024, entrato in vigore il 16 ottobre 2024.​ Focus Namirial IT+1Leofortis Law Studio+1

Ambito di applicazione

Gli obblighi previsti dalla NIS 2 si applicano a:​

  • Settori ad alta criticità: energia, trasporti, settore bancario, infrastrutture dei mercati finanziari, settore sanitario, acqua potabile, acque reflue, infrastrutture digitali, gestione dei servizi TIC e spazio.​Focus Namirial IT+1Diritto.it+1
  • Altri settori critici: servizi postali e di corriere, gestione dei rifiuti, fabbricazione, produzione e distribuzione di sostanze chimiche, produzione, trasformazione e distribuzione di alimenti, fabbricazione, fornitori di servizi digitali e ricerca.​ Focus Namirial IT

Inoltre, la direttiva si applica alle aziende con almeno 50 dipendenti o un fatturato annuo superiore ai 10 milioni di euro. Tuttavia, anche le piccole imprese possono essere coinvolte se forniscono servizi essenziali o operano in settori critici. ​Intesa SpA+1Leofortis Law Studio+1

Principali obblighi per le imprese

  1. Registrazione presso l’Agenzia per la Cybersicurezza Nazionale (ACN): Entro il 28 febbraio 2025, le imprese devono registrarsi sulla piattaforma digitale dell’ACN, fornendo informazioni dettagliate sulla propria organizzazione e sui servizi offerti. ​Diritto.it+1Leofortis Law Studio+1
  2. Adozione di misure di gestione del rischio: Le aziende devono implementare misure tecniche, operative e organizzative adeguate per gestire i rischi alla sicurezza dei sistemi informativi e garantire la continuità operativa. ​Agenda Digitale
  3. Notifica degli incidenti: In caso di incidenti significativi che impattano sulla fornitura dei servizi, le imprese sono tenute a notificare l’evento al Computer Security Incident Response Team (CSIRT) e alle autorità competenti entro 72 ore, fornendo aggiornamenti regolari fino alla risoluzione dell’incidente. ​Cyber Security 360
  4. Designazione di un responsabile per la cybersecurity: Le organizzazioni devono nominare un referente interno responsabile della sicurezza informatica e garantire la formazione continua del personale su tematiche di cybersecurity. ​Diritto.it
  5. Gestione della supply chain: È obbligatorio valutare e gestire i rischi legati ai fornitori e ai partner commerciali, assicurando che anch’essi rispettino standard di sicurezza adeguati. ​
  6. Responsabilità degli organi direttivi: I dirigenti aziendali sono tenuti a supervisionare l’implementazione delle misure di sicurezza e possono essere ritenuti direttamente responsabili in caso di inadempienze. ​Intesa SpA

Sanzioni per il mancato rispetto

Il mancato rispetto degli obblighi previsti dalla NIS 2 può comportare sanzioni fino a 10 milioni di euro o al 2% del fatturato annuo globale dell’azienda, oltre a possibili sanzioni interdittive nei confronti dei dirigenti, come l’incapacità a svolgere funzioni dirigenziali. ​Leofortis Law Studio+1

Benefici concreti per i cittadini

Sebbene la NIS 2 sia rivolta principalmente a enti pubblici, aziende e operatori di servizi essenziali, i suoi effetti positivi ricadono anche direttamente sui cittadini. Ecco alcuni dei benefici concreti:

  • Maggiore continuità dei servizi pubblici e privati: grazie all’obbligo per ospedali, enti pubblici e utility di rafforzare le proprie difese informatiche, si riducono i rischi di interruzione di servizi fondamentali come energia elettrica, acqua, trasporti, sanità e giustizia. Esempio: un attacco ransomware a un ospedale potrebbe bloccare gli appuntamenti medici o ritardare cure urgenti. Con NIS 2, questi enti devono essere pronti a reagire velocemente e garantire la continuità operativa.
  • Protezione dei dati personali: molte delle strutture coinvolte trattano dati sensibili dei cittadini. Migliorando le misure di sicurezza, si riduce il rischio di furti d’identità e violazioni della privacy.
  • Maggiore fiducia nei servizi digitali: con infrastrutture più sicure, i cittadini saranno più propensi a usare servizi online, come fascicoli sanitari elettronici, home banking, piattaforme della pubblica amministrazione, con la consapevolezza che i loro dati sono meglio protetti.
  • Prevenzione di truffe informatiche: la diffusione della cultura della cybersicurezza, promossa anche tramite campagne informative dell’ACN, aiuta i cittadini a riconoscere e difendersi da tentativi di phishing, truffe online e altre frodi digitali.
  • Risposte più rapide agli incidenti: in caso di attacco, le organizzazioni devono notificare l’incidente tempestivamente alle autorità e prendere contromisure. Questo si traduce in meno disagi per gli utenti finali.

In conclusione la Direttiva NIS 2 è un passo fondamentale verso un’Europa più sicura digitalmente. Tutte le organizzazioni coinvolte devono prepararsi ad affrontare nuove responsabilità, adottare misure di sicurezza adeguate e contribuire attivamente alla costruzione di un ecosistema digitale resiliente.

Riferimenti bibliografici:

Node.js

Node.js è uno strumento molto utilizzato nel mondo della programmazione web moderna. Ma cos’è esattamente? Possiamo immaginarlo come un ambiente che ci permette di eseguire codice JavaScript al di fuori del browser, ad esempio su un server. Questo significa che possiamo usare JavaScript non solo per creare pagine web interattive, ma anche per costruire vere e proprie applicazioni che rispondono alle richieste degli utenti, salvano dati, gestiscono file, e molto altro.

Node.js è veloce ed efficiente, perché utilizza un sistema chiamato “event-driven” e “non bloccante”, che permette di eseguire molte operazioni contemporaneamente senza rallentare il programma. Node.js è un ambiente per eseguire programmi su un webserver. Ecco un piccolo esempio per creare un semplice server web con Node.js.

Questo server risponde con un messaggio quando apriamo una pagina nel browser:

// Importa il modulo 'http'
const http = require('http');

// Crea il server
const server = http.createServer((req, res) => {
  res.writeHead(200, { 'Content-Type': 'text/plain' });
  res.end('Ciao dal tuo primo server Node.js!');
});

// Avvia il server sulla porta 3000
server.listen(3000, () => {
  console.log('Server in ascolto su http://localhost:3000');
});

Una volta salvato questo codice in un file (es. server.js), basterà eseguirlo con il comando node server.js da terminale. Aprendo il browser all’indirizzo http://localhost:3000, vedrai il messaggio “Ciao dal tuo primo server Node.js!”.

Interazione di una pagina html con node.js

La maggior parte delle applicazioni web sono progettare in base al modello client-server dove il ruolo del client viene svolto dalle pagine html visualizzate dall’utente tramite il proprio browser mentre il ruolo del server viene svolto dai programmi in esecuzione su di un webserver (backend). Node.js assolve al compito di server nell’ambito di questo paradigma di programmazione. Vediamo di seguito un esempio:

Lato client abbiamo un file chiamato index.html con il seguente contenuto:

<!DOCTYPE html>
<html>
<head>
  <title>Pagina HTML con Node.js</title>
</head>
<body>
  <h1>Benvenuto!</h1>
  <form action="/saluto" method="POST">
    <label for="nome">Inserisci il tuo nome:</label>
    <input type="text" id="nome" name="nome">
    <button type="submit">Invia</button>
  </form>
</body>
</html>

Lato server invece è in esecuzione un programma Node.js che riceve i dati dal form e risponde con un messaggio personalizzato:

const http = require('http');
const fs = require('fs');
const path = require('path');
const querystring = require('querystring');

const server = http.createServer((req, res) => {
  if (req.method === 'GET' && req.url === '/') {
    const filePath = path.join(__dirname, 'index.html');
    fs.readFile(filePath, (err, data) => {
      if (err) {
        res.writeHead(500, { 'Content-Type': 'text/plain' });
        res.end('Errore nel caricamento della pagina');
      } else {
        res.writeHead(200, { 'Content-Type': 'text/html' });
        res.end(data);
      }
    });
  } else if (req.method === 'POST' && req.url === '/saluto') {
    let body = '';
    req.on('data', chunk => {
      body += chunk.toString();
    });
    req.on('end', () => {
      const parsedData = querystring.parse(body);
      const nome = parsedData.nome || 'Utente';
      res.writeHead(200, { 'Content-Type': 'text/html' });
      res.end(`<h1>Ciao, ${nome}!</h1>`);
    });
  } else {
    res.writeHead(404, { 'Content-Type': 'text/plain' });
    res.end('Pagina non trovata');
  }
});

server.listen(3000, () => {
  console.log('Server in ascolto su http://localhost:3000');
});

In questo esempio, quando l’utente inserisce il proprio nome e invia il form, il server riceve i dati e risponde con un messaggio personalizzato, mostrando come può avvenire una semplice interazione tra client (pagina HTML) e server (Node.js).

Installazione passo passo di Node.js

Per iniziare a usare Node.js sul tuo computer, segui questi semplici passaggi:

1. Vai sul sito ufficiale

Apri il browser e vai all’indirizzo: https://nodejs.org

Vedrai due pulsanti principali:

  • LTS (Long Term Support): versione stabile consigliata per la maggior parte degli utenti
  • Current: versione più recente con funzionalità sperimentali

Ti consiglio di scaricare la versione LTS.

2. Scarica e installa

Clicca su LTS e scarica l’installer per il tuo sistema operativo:

  • Windows: .msi
  • macOS: .pkg
  • Linux: segui le istruzioni riportate sul sito

Esegui il file scaricato e segui i passaggi dell’installazione guidata, lasciando tutte le opzioni predefinite.

3. Verifica l’installazione

Una volta completata l’installazione, apri il prompt dei comandi (su Windows), Terminale (su macOS o Linux) e digita:

node -v

Questo comando ti mostrerà la versione di Node.js installata, ad esempio:

v18.17.1

Digita anche:

npm -v

Per verificare che anche npm, il gestore dei pacchetti di Node.js, sia installato correttamente.

Kit di sopravvivenza

Nel resto dell’articolo troverai una cheatsheet pratica (e salvavita) per sopravvivere al mondo di Node.js: dall’inizializzazione del progetto fino a quando il tuo server ascolta in silenzio e non ti dà più errori assurdi. È la guida che avremmo voluto avere quando abbiamo digitato per la prima volta npm init e abbiamo scoperto che “package.json” non è una marca di cereali.

Che tu sia alle prime armi o un veterano con più console.log() alle spalle che ore di sonno, qui troverai comandi utili, trucchi rapidi e qualche dritta per non perdere la pazienza (e la porta 3000).

Inizializzazione progetto

npm init            # Questo comando avvia una procedura guidata nel terminale per creare un nuovo progetto di tipo Node.js. 

La procedura chiederà delle informazioni all’utente tra cui nome, versione, descrizione, autore, ecc.

Alla fine dell’intervista viene creato un file denominato package.json che contiene la descrizione del progetto.

npm init -y         # Attraverso l’opzione -y il comando diventa più discreto, crea subito un nuovo progetto Node.js con tutte le impostazioni predefinite (senza farti domande).

Una volta configurato il progetto, si devono scrivere i programmi nel linguaggio Javascript. Questo programmi avranno come estensione .js.

Esecuzione file JavaScript

node nomefile.js   

Una volta scritti i programmi che compongono il nostro progetto è possibile eseguirli usando il comando node seguito dal nome del programma.

Il programma potrebbe contenere le istruzioni per avviare un webserver, in questo caso dopo averlo eseguito, il webserver si metterà in ascolto di eventuali richieste http.

Avvio di un server (es. Express)

Se abbiamo scritto un programma che contiene all’interno istruzioni che creano un webserver allora usando l’istruzione che segue (naturalmente il programma potrebbe avere un nome di verso da index.js) allora il webserver verrà messo in esecuzione.

node server.js       # Avvia il server descritto nel file index.js. È il punto di partenza del tuo backend.

Esempio.

fedeli@fedeli-HP-ProBook-440-G8-Notebook-PC ~/D/N/ES_31_ScritturaDatiStudenti> node server.js

Questo significa che potremo effettuare richieste al webserver usando l’indirizzo https://nomeserver:3000/nomeprogramma.js

Il valore della porta può essere scelto nel programma nell’ambito delle porte a disposizione dei programmatori.

Arrestare il server

Per fermare il server in esecuzione nel terminale:

  • Premi Ctrl + C sulla tastiera (funziona su Windows, macOS e Linux)
  • Questo comando termina il processo in esecuzione nel terminale (cioè il server)

Se stai usando nodemon, anche in quel caso puoi arrestarlo con Ctrl + C.


Come risolvere l’errore EADDRINUSE: address already in use

Quando lanci un’applicazione Node.js (o un server in generale) e ottieni un errore simile a:

Error: listen EADDRINUSE: address already in use :::3000

significa che la porta sulla quale il tuo server sta cercando di mettersi in ascolto (in questo caso la 3000) è già occupata da un altro processo. Questo impedisce l’avvio del tuo server perché due processi non possono utilizzare contemporaneamente la stessa porta sulla stessa interfaccia di rete.


Passaggio 1 – Identificare il processo che occupa la porta

Per scoprire quale processo sta usando la porta (es. 3000), puoi utilizzare il comando:

lsof -i :3000

(list of open files)

Questo comando restituirà un output simile a:

COMMAND   PID   USER   FD   TYPE DEVICE SIZE/OFF NODE NAME

node     12345  user   22u  IPv6  12345      0t0  TCP *:3000 (LISTEN)

📌 PID (Process ID) è il numero identificativo del processo che sta usando la porta. In questo esempio, è 12345.


Passaggio 2 – Terminare il processo

Una volta individuato il PID, puoi terminare il processo in due modi:

Metodo brutale (terminazione immediata):

kill -9 12345

🗺 Usa -9 solo se necessario, perché forza la terminazione senza possibilità di pulizia da parte del processo.

Metodo più “gentile” (preferibile se possibile):

kill 12345

Aspetta qualche secondo e verifica se la porta è stata liberata.


Soluzione alternativa: cambiare la porta nel codice

Se non vuoi chiudere il processo esistente (magari perché è un altro servizio che ti serve), puoi modificare il tuo codice per usare una porta diversa.

Ad esempio, se usi Express o un server Node.js classico, nel tuo file principale potresti avere qualcosa del genere:

const PORT = 3001; // cambia la porta da 3000 a 3001

app.listen(PORT, () => {

  console.log(`Server listening on port ${PORT}`);

});


Metodo rapido con kill-port

Se vuoi liberare la porta con un solo comando senza dover cercare manualmente il PID, puoi usare il pacchetto kill-port.

Esegui direttamente:

npx kill-port 3000

Oppure, installalo globalmente:

npm install -g kill-port

Dopo l’installazione, potrai usarlo ogni volta così:

kill-port 3000

Questo comando si occuperà automaticamente di individuare e terminare il processo che sta usando la porta indicata.


Monitoraggio automatico file (con nodemon)

npm install -g nodemon   

Installa globalmente “nodemon“, un programma che controlla i tuoi file: se li cambi, riavvia automaticamente il server.

nodemon index.js         # Avvia il server e lo tiene sotto controllo: ogni modifica al codice lo riavvia.

Gestione pacchetti

npm install nome-pacchetto       # Aggiunge una libreria al tuo progetto. La trovi nella libreria  node_modules e viene registrata nel file package.json

npm install nome@versione        # Installa una versione precisa della libreria, utile se un progetto richiede quella specifica

npm uninstall nome-pacchetto     # Rimuove una libreria dal tuo progetto

Installazione pacchetti di sviluppo

npm install –save-dev nome      # Installa una libreria che serve solo in fase di sviluppo (es. nodemon), non verrà inclusa nel prodotto finale

Visualizzare pacchetti installati

npm list                 # Mostra tutte le librerie installate nel tuo progetto con le loro versioni

npm list -g              # Mostra tutte le librerie installate globalmente nel tuo sistema (disponibili ovunque)

Altri comandi utili per la manutenzione del progetto Node.js

Aggiornare le librerie del progetto

npm update

Questo comando controlla tutte le dipendenze elencate nel file package.json e aggiorna ciascuna di esse all’ultima versione compatibile con i vincoli specificati (es. ^1.2.3 permette di aggiornare fino a 1.x.x ma non 2.x.x). Le versioni aggiornate vengono registrate nel file package-lock.json.

Se desideri aggiornare le dipendenze all’ultima versione disponibile in assoluto, anche oltre i vincoli dichiarati, dovrai usare strumenti come npm-check-updates.

Esecuzione test (se configurati)

npm test                 # Avvia i test automatizzati del progetto, se sono stati definiti nel file package.json (utile per controllare che tutto funzioni)

Script personalizzati

Nel file package.json puoi aggiungere comandi personalizzati (script) per facilitare lo sviluppo.

“scripts”: {

  “start”: “node index.js”,         // Avvia il server normalmente

  “dev”: “nodemon index.js”         // Avvia il server in modalità sviluppo (con nodemon)

}

Poi puoi eseguirli con:

npm run dev              # Esegue lo script “dev” (con nodemon)

npm start                # Esegue lo script “start” (standard per far partire l’app)

Debug

node inspect nomefile.js         # Avvia il programma in modalità “debug” da terminale: puoi vedere passo per passo cosa succede

node –inspect nomefile.js       # Avvia il debug collegabile agli strumenti di sviluppo di Chrome (DevTools)

Variabili d’ambiente

Le variabili d’ambiente ti permettono di cambiare alcune impostazioni senza toccare il codice, ad esempio la porta del server.

PORT=3000 node index.js          # Avvia il server e imposta la variabile PORT solo per questa volta

Oppure puoi usare un file .env (consigliato per progetti più grandi):

npm install dotenv               # Installa la libreria “dotenv” per leggere il file .env

Contenuto del file .env:

PORT=3000

Nel tuo codice:

require(‘dotenv’).config();      // Carica le variabili definite nel file .env

console.log(process.env.PORT);   // Stampa il valore della variabile PORT

I pacchetti più utilizzati in Node.js – La tua cassetta degli attrezzi

Vostruire un’applicazione Node.js senza pacchetti esterni è un po’ come cercare di montare un mobile IKEA senza cacciavite: tecnicamente possibile… ma perché infliggersi tanta sofferenza?

Per fortuna, l’ecosistema di Node.js è ricco di strumenti già pronti che ti permettono di fare (quasi) tutto con poche righe di codice. Dalla creazione di server alla gestione di richieste HTTP, dalla sicurezza alla connessione con database, c’è un pacchetto per ogni esigenza. Basta saper scegliere i giusti compagni di viaggio.

Ecco quindi una selezione dei pacchetti più usati e amati da chi lavora con Node.js: utili, affidabili e spesso indispensabili. Se stai iniziando un nuovo progetto o vuoi semplicemente capire “quella libreria che tutti usano a cosa serve davvero”, questa lista fa per te.

1. express

Cosa fa: Ti aiuta a creare facilmente un server web. Esempio: puoi dire “quando l’utente va su /home, mandagli questa pagina”. Utilità: è la base di tantissime app web in Node.js.

2. cors

Cosa fa: Permette al frontend (es. React su localhost:3000) di comunicare con il backend (Node.js su localhost:5000). Utilità: senza cors, i browser bloccherebbero queste richieste per motivi di sicurezza.

3. dotenv

Cosa fa: Ti permette di tenere le informazioni sensibili (es. password del database o porta del server) in un file .env nascosto. Utilità: migliora la sicurezza e la pulizia del codice.

4. nodemon

Cosa fa: Riavvia automaticamente il server ogni volta che modifichi un file. Utilità: velocizza lo sviluppo, evitando di riavviare tutto a mano.

5. axios

Cosa fa: Ti permette di fare richieste HTTP (come GET, POST, PUT…) da JavaScript. Utilità: è usato per parlare con API o inviare dati al backend.

6. mongoose

Cosa fa: Ti semplifica la vita quando lavori con MongoDB, un database non relazionale. Utilità: ti fa usare oggetti JavaScript per leggere/salvare/modificare i dati nel database.

7. bcrypt

Cosa fa: Cripta le password degli utenti prima di salvarle nel database. Utilità: migliora la sicurezza in caso di furto del database.

8. jsonwebtoken (jwt)

Cosa fa: Crea dei token che rappresentano l’identità di un utente (es. dopo il login). Utilità: il backend può verificare il token per capire se l’utente è autenticato.

9. body-parser

Cosa fa: Aiuta il server a capire i dati che arrivano nel corpo delle richieste (es. moduli HTML). Nota: oggi è incluso direttamente in Express.

10. morgan

Cosa fa: Mostra nel terminale un log di tutte le richieste che arrivano al server. Utilità: è utile per il debugging e per sapere cosa succede.

11. express-validator

Fedeli Massimo.

Intelligenza artificiale: cosa sono e come funzionano i Large Language Model(LLM)

Negli ultimi tempi si sente parlare sempre più spesso di LLM (Large Language Models), al punto che, forse, persino nei circoli di burraco e nei più improbabili bar di Caracas è diventato un argomento di discussione. 😊 Questo avviene perché strumenti di intelligenza artificiale come ChatGPT, Claude.ai, DeepSeek e molti altri stanno trovando un’ampia applicazione in ambito lavorativo, scolastico e quotidiano. Ormai, molti si affidano ad essi per correggere lo stile di una email o di una relazione, per scrivere una porzione di codice o semplicemente per avere delle risposte. Ma cosa sono esattamente gli LLM, come funzionano e perché è così importante comprenderne la logica complessa? e, soprattutto, possiamo fidarci completamente delle risposte che ci forniscono? Scopriamolo insieme.

Come Funzionano i LLM?

Gli LLM si basano su un’architettura chiamata Transformer, introdotta nel 2017 con un paper di Google denominato “Attention Is All You Need”. Questa architettura utilizza una tecnica chiamata self-attention, che permette al modello di analizzare il contesto di ogni parola in una frase, considerando non solo le parole vicine ma anche quelle più lontane nel testo. Questa architettura ha rivoluzionato il modo in cui i modelli di deep learning elaborano il linguaggio naturale, sostituendo le precedenti tecniche basate su reti ricorrenti (RNN, LSTM) con un approccio più efficiente e scalabile. Un modello Transformer è composto principalmente da due elementi fondamentali:

  • Meccanismo di Self-Attention (auto-attenzione)
  • Feed-Forward Neural Networks (reti neurali completamente connesse)

Entrambi lavorano insieme per elaborare il testo e generare output di alta qualità. Il cuore dell’architettura Transformer è il meccanismo di self-attention, che consente al modello di analizzare il contesto di ogni parola nella frase in relazione a tutte le altre parole, indipendentemente dalla loro posizione. Perché è importante? Le reti ricorrenti (RNN) e le LSTM analizzavano il testo sequenzialmente, rendendo difficile catturare dipendenze a lungo raggio. Il self-attention, invece, permette di considerare l’intera sequenza in parallelo e attribuire un “peso” a ogni parola in base alla sua importanza rispetto alle altre.

Come Funziona il Self-Attention?

Immaginiamo di avere una frase: “Il gatto salta sul divano”. Il modello deve capire il significato di ciascuna parola, considerando il contesto. Durante l’elaborazione per la parola “salta”, il modello non guarda solo le parole vicine (“Il”, “gatto”), ma anche parole più lontane come “divano”, che è il complemento del verbo. Ogni parola viene rappresentata come un vettore numerico e confrontata con tutte le altre parole nella frase per calcolare un punteggio di attenzione (un valore che indica la rilevanza di una parola rispetto a un’altra). Esempio numerico semplificato: se il modello deve determinare la parola più probabile dopo “Il gatto salta sul”, valuterà le parole più frequentemente associate a questa struttura durante l’addestramento. Potrebbe quindi dare punteggi di attenzione più alti a parole come “divano”, “letto”, “tavolo”, perché in contesti simili ha spesso incontrato queste parole. Questo permette di mantenere coerenza nel testo generato e comprendere meglio il significato anche in frasi complesse.

Il Passaggio ai pesi della rete neurale

Dopo che il meccanismo di self-attention ha calcolato le relazioni tra le parole all’interno della frase, il Transformer passa l’output a una rete neurale completamente connessa (Fully Connected Feed-Forward Neural Network). Questi strati servono a elaborare ulteriormente le informazioni e a raffinare la rappresentazione dei token, migliorando la capacità del modello di comprendere e generare testo coerente.

Perché Serve una Rete Feed-Forward Dopo il Self-Attention?

Il self-attention è il cuore dell’architettura Transformer ed è estremamente efficace nel catturare le relazioni tra parole in una frase, indipendentemente dalla loro posizione. Tuttavia, il suo output è una rappresentazione grezza delle parole, basata sui pesi assegnati a ciascun token in base alla sua importanza nel contesto.

Questo output iniziale necessita di ulteriori trasformazioni per diventare utilizzabile nella generazione di testo. Per fare ciò, il Transformer applica una rete neurale feed-forward dopo il meccanismo di self-attention. Questo passaggio è cruciale per due motivi principali:

Introduzione della Non-Linearità

l self-attention è essenzialmente una trasformazione lineare tra vettori, ovvero calcola una combinazione pesata dei token senza introdurre variazioni complesse nei dati. Questo limita la capacità del modello di catturare relazioni intricate nel linguaggio naturale.

Per migliorare questa capacità, il Transformer utilizza una rete neurale feed-forward che introduce non-linearità tramite una funzione di attivazione come ReLU (Rectified Linear Unit).

Perché la Non-Linearità è Importante?

  • I modelli lineari hanno una capacità limitata nel rappresentare relazioni complesse.
  • La ReLU o altre funzioni di attivazione permettono al modello di apprendere strutture più sofisticate nel linguaggio.
  • Grazie alla non-linearità, il modello può catturare dipendenze linguistiche più profonde che un meccanismo puramente lineare non riuscirebbe a distinguere.

Esempio pratico:
Se il self-attention capisce che “Il gatto salta sul…” si collega a “divano”, la rete feed-forward raffina questa connessione, modellando concetti più astratti come “animali domestici” e “mobilio”, migliorando la comprensione semantica complessiva.

Elaborazione locale dei token

Mentre il self-attention lavora a livello globale, analizzando la relazione tra tutti i token della frase contemporaneamente, la rete feed-forward elabora ogni token individualmente, raffinando la sua rappresentazione nel contesto specifico.

Perché l’elaborazione locale è importante?

  • Il self-attention collega le parole tra loro, ma non migliora la loro rappresentazione individuale.
  • Il feed-forward layer raffina la rappresentazione di ogni parola, rendendola più informativa.
  • Questo passaggio è fondamentale per gestire strutture linguistiche complesse, come metafore, espressioni idiomatiche e concetti sfumati.

Esempio pratico:
Se la frase è:

“La banca del fiume è affollata”

Il self-attention potrebbe assegnare pesi sia a “banca” (istituto finanziario) sia a “banca” (riva del fiume). La rete feed-forward aiuta a raffinare questa ambiguità, assegnando alla parola “banca” il significato corretto in base al contesto (in questo caso, “riva del fiume”).

Struttura del feed-forward layer

Ogni Transformer ha uno strato feed-forward identico per ogni token della sequenza elaborata. Questo strato è composto da due passaggi principali:

  1. Primo Strato Lineare (Proiezione in uno spazio di dimensione maggiore): l’output del self-attention è moltiplicato per una matrice di pesi e viene applicata una funzione di attivazione. Questo passaggio aiuta il modello a catturare relazioni più astratte tra le parole.
  2. Secondo Strato Lineare (Proiezione nella dimensione originale)
    • Il risultato viene trasformato nuovamente in una rappresentazione compatta, riportandolo alla dimensione originale della sequenza di input.
    • Serve a rendere l’output del Transformer più compatibile con gli strati successivi.

Reti neurali feed-forward

Le reti neurali feed-forward (FNN, Feed-Forward Neural Networks) sono un tipo di rete neurale artificiale in cui le informazioni fluiscono in una sola direzione: dall’input all’output, senza cicli o connessioni retroattive.

Una rete neurale feed-forward è composta da diversi strati (layers):

  1. Strato di Input (Input Layer): Riceve i dati in ingresso.
  2. Strati Nascosti (Hidden Layers): Elaborano i dati applicando trasformazioni non lineari.
  3. Strato di Output (Output Layer): Fornisce il risultato finale.

Esempio di Struttura

Immaginiamo una rete per classificare le immagini di numeri scritti a mano (MNIST). La struttura potrebbe essere:

  • Input: 28×28 pixel → 784 neuroni (ogni pixel è un neurone)
  • Hidden Layer 1: 128 neuroni con funzione di attivazione ReLU
  • Hidden Layer 2: 64 neuroni con funzione di attivazione ReLU
  • Output: 10 neuroni (uno per ciascun numero da 0 a 9) con attivazione Softmax

Il processo di elaborazione avviene in tre fasi principali:

Propagazione in avanti (Forward Propagation)

  1. Ogni neurone riceve input xi​ e li moltiplica per pesi wi​, aggiungendo un bias b.
  2. Il valore risultante passa attraverso una funzione di attivazione per introdurre non-linearità.
  3. L’output viene passato allo strato successivo fino ad arrivare all’output finale.

Formula per il calcolo di un neurone in uno strato nascosto:

Dove f(z) è una funzione di attivazione.

Esempio Pratico: Se stiamo classificando un numero scritto a mano:

  • Input: immagine con pixel (valori normalizzati tra 0 e 1)
  • I pesi della rete trasformano questi input in caratteristiche più complesse
  • L’output finale avrà una probabilità associata a ciascuna classe (0-9)

Funzione di attivazione

Per rendere la rete in grado di apprendere pattern complessi, usiamo funzioni di attivazione:

Calcolo della perdita

Per capire quanto la rete sta performando bene, calcoliamo una funzione di perdita:

  • Errore quadratico medio (MSE, Mean Squared Error): per regressione.
  • Entropia incrociata (Cross-Entropy Loss): per classificazione.

Esempio: Se vogliamo classificare un numero scritto a mano come “3” ma il modello prevede “5”, la funzione di perdita misura quanto la previsione sia lontana dalla realtà.

Retropropagazione dell’errore

Per migliorare l’accuratezza, aggiorniamo i pesi usando il gradiente della funzione di perdita rispetto ai pesi stessi.

  1. Calcolo del Gradiente: Deriviamo la funzione di perdita rispetto a ciascun peso.
  2. Discesa del Gradiente (Gradient Descent): Aggiorniamo i pesi usando:

Dove η è il tasso di apprendimento e L è la funzione di perdita.

Come fa il feed-forward layer a generare il testo successivo?

Una volta elaborata l’intera sequenza di testo con self-attention e feed-forward layers, il Transformer è pronto per predire il token successivo. Questo avviene attraverso i seguenti passi:

  1. Softmax Output Layer: l’output finale del Transformer è una rappresentazione vettoriale per ogni token. Questi vettori vengono passati attraverso un livello Softmax, che assegna probabilità a tutte le possibili parole successive.
  2. Selezione della Parola Successiva: la parola con la probabilità più alta viene selezionata come output (o attraverso altre tecniche di sampling, come temperature scaling o nucleus sampling).

Esempio pratico: l’utente fornisce un input testuale

Il processo inizia quando l’utente digita un messaggio, ad esempio:

“Oggi piove e porto con me…”

Tokenizzazione dell’Input

Prima che il modello possa lavorare sul testo, questo viene suddiviso in unità più piccole chiamate token. Nel nostro caso, il modello potrebbe scomporre la frase in token come:
[“Oggi”, “piove”, “e”, “porto”, “con”, “me”, “…”]

Assegnazione di rappresentazioni numeriche

Poiché un modello di deep learning lavora con numeri e non con testo, ogni token viene convertito in una rappresentazione vettoriale numerica. Ad esempio:
“Oggi” → [0.56, 0.12, -0.34, …]
“piove” → [0.89, -0.22, 0.77, …]

Elaborazione con il meccanismo di Self-Attention

Il modello Transformer analizza il contesto della frase usando il self-attention, che determina quali parole sono più rilevanti rispetto alle altre.

Generazione della parola successiva con Softmax

Dopo aver compreso il contesto, il modello deve prevedere la parola più probabile. Lo fa assegnando una probabilità a ogni possibile completamento usando un livello Softmax.

Formazione della risposta finale

Se l’utente ha digitato solo una parte della frase, il chatbot potrebbe rispondere con il completamento più probabile:

“Oggi piove e porto con me un ombrello.”

Se invece la frase fosse parte di una domanda, il chatbot potrebbe generare una risposta più articolata.

Post-Elaborazione e output finale

Prima di mostrare la risposta all’utente, il modello può applicare correzioni grammaticali, eliminare ripetizioni e adattare il tono del messaggio a seconda del contesto. Se il chatbot ha un filtro etico o un meccanismo di sicurezza, potrebbe bloccare risposte inappropriate.

Il processo di interazione tra umano e chatbot può essere riassunto così:
1. L’utente scrive un messaggio
2. Il testo viene tokenizzato e convertito in numeri
3. Il modello analizza il contesto con self-attention
4. Calcola la parola più probabile con il softmax
5. Genera una risposta coerente
6. Applica filtri e post-elaborazione
7. Restituisce la risposta all’utente

In che modo gli LLM riescono a scrivere saggi e articoli?

Gli LLM (Large Language Models) riescono a produrre testi coerenti e ben strutturati perché il loro addestramento si basa sull’analisi di una quantità enorme di dati testuali. Durante questa fase, il modello apprende a riconoscere schemi complessi nella lingua, identificando non solo la grammatica e il lessico, ma anche la logica e la struttura dei testi in diversi contesti. Capacità di contestualizzazione: il modello riesce a contestualizzare le informazioni fornite dall’utente, adattando la risposta in base alla domanda. Se gli viene chiesto di scrivere in modo formale o informale, o di riassumere un testo, modificherà il suo output di conseguenza. Analisi statistica del linguaggio: il modello non “comprende” il testo come farebbe un essere umano, ma utilizza probabilità statistiche per prevedere la parola successiva in una frase. Ad esempio, se vede la sequenza “Il clima sta cambiando perché…”, basandosi sui dati con cui è stato addestrato, il modello calcolerà quali parole sono più probabili in quel contesto. Apprendimento di strutture e stili diversi: avendo analizzato milioni di documenti, libri, articoli accademici e testi di vario genere, l’LLM può riconoscere schemi di scrittura e riprodurli. Se gli viene chiesto di scrivere un saggio, seguirà la classica struttura con introduzione, sviluppo e conclusione. Se gli viene chiesto un articolo giornalistico, utilizzerà uno stile informativo e conciso.

Come riescono a generare codice per programmatori?

Gli LLM possono anche aiutare gli sviluppatori a scrivere codice. Durante l’addestramento, il modello ha analizzato milioni di righe di codice in diversi linguaggi di programmazione, imparando a riconoscere sintassi, funzioni e strutture logiche.

🔹 Esempio: Se chiedi di scrivere una funzione in Python che calcoli la somma di due numeri, il modello potrebbe generare:

def somma(a, b):

    return a + b

Questo perché ha imparato a riconoscere la struttura tipica di una funzione Python e a replicarla.

Come riescono a rispondere a domande complesse?

Gli LLM sono in grado di rispondere a domande complesse perché hanno accesso a una vasta gamma di informazioni apprese durante l’addestramento. Quando ricevono una domanda, analizzano il contesto e cercano di generare una risposta coerente e pertinente.

🔹 Esempio: Se chiedi “Qual è la capitale della Francia?”, il modello risponderà correttamente “Parigi” perché questa informazione è presente nei dati su cui è stato addestrato. Tuttavia, se la domanda è più complessa o richiede conoscenze aggiornate, il modello potrebbe commettere errori o inventare risposte.

Tuttavia, è importante ricordare che questi modelli non “capiscono” veramente il linguaggio come farebbe un essere umano: lavorano su probabilità e pattern, il che può portare a errori o risposte inventate. Per questo, è sempre consigliabile verificare le informazioni fornite da un LLM, soprattutto quando si tratta di dati sensibili o complessi.

Questa struttura di addestramento a due fasi permette ai modelli come GPT di essere flessibili e adattabili a diversi scenari, mantenendo al contempo una solida comprensione della lingua naturale.

Limiti degli LLM

Nonostante la loro potenza, gli LLM hanno ancora alcune limitazioni:

  • Possono “allucinare”, ovvero inventare informazioni inesatte.
  • Sono computazionalmente costosi e richiedono molta energia.
  • Se non aggiornati frequentemente, possono fornire risposte obsolete.

Cosa sono le allucinazioni

Le allucinazioni sono uno dei limiti più noti dei Large Language Models (LLM). Si tratta di situazioni in cui il modello genera informazioni inesatte, inventate o completamente false, pur presentandole in modo convincente e plausibile. Questo fenomeno è dovuto a diversi fattori intrinseci al funzionamento degli LLM.

  1. Apprendimento basato su probabilità, non su comprensione
    Gli LLM non “capiscono” il significato delle parole o dei concetti come farebbe un essere umano. Funzionano prevedendo la parola o la frase più probabile in base ai dati su cui sono stati addestrati. Se il modello non ha informazioni precise su un argomento, può “inventare” una risposta che sembra coerente, ma che in realtà è errata. Ad esempio: Se chiedi “Chi ha inventato la macchina del tempo?”, il modello potrebbe rispondere con un nome fittizio o un dettaglio inventato, perché non esiste una risposta corretta a questa domanda.
  2. Dati di addestramento imperfetti o incompleti
    Gli LLM imparano da enormi quantità di testo, ma questi dati possono contenere errori, imprecisioni o informazioni obsolete. Se il modello non ha accesso a fonti affidabili su un determinato argomento, potrebbe generare risposte basate su informazioni errate.Ad Esempio: se il modello è stato addestrato su testi che contengono errori storici, potrebbe ripetere quegli errori nelle sue risposte.
  3. Mancanza di aggiornamenti in tempo reale
    Molti LLM non hanno accesso a informazioni aggiornate in tempo reale. Se un evento accade dopo il periodo di addestramento, il modello non lo conoscerà e potrebbe fornire risposte obsolete o inventate. Ad esempio: se chiedi “Chi ha vinto le ultime elezioni in un paese X?” e il modello non è stato aggiornato, potrebbe fornire un risultato errato o inventato.
  4. Tendenza a completare il testo in modo coerente
    Gli LLM sono progettati per generare testo che sembri coerente e fluido. A volte, questa priorità sulla coerenza può portare il modello a “riempire i vuoti” con informazioni inventate, pur di mantenere un discorso logico.🔹 Esempio: Se chiedi “Qual è la capitale della Luna?”, il modello potrebbe inventare un nome plausibile, anche se la domanda è priva di senso.
  5. Errori nei calcoli matematici: gli LLM basano le loro risposte su modelli statistici addestrati su enormi quantità di testo. In altre parole, non eseguono calcoli nel senso tradizionale, ma prevedono la sequenza di parole (o numeri) più probabile basandosi sui dati visti durante l’addestramento.
    🔹 Esempio di problema matematico:
    Se chiedi a un LLM:
    “Quanto fa 27 × 14?”
    il modello cercherà di completare la frase basandosi su esempi simili che ha visto in passato. Se ha incontrato spesso la frase “27 × 14 = 378”, risponderà correttamente. Ma se ha visto vari esempi con risposte errate o non ha appreso quel calcolo in modo diretto, potrebbe generare un numero sbagliato

Qual’è dunque l’approccio da usare per sfruttare al meglio l’Intelligenza Artificiale e riconoscerne gli Errori

Le IA generative (come GPT-4) non ragionano come un essere umano, ma generano testo basandosi su modelli probabilistici addestrati su grandi quantità di dati. Questo significa che possono:

  • Generare risposte coerenti ma errate.
  • Confondere fatti se non hanno dati aggiornati.
  • Inventare informazioni (allucinazioni) quando manca un riferimento solido.

Per utilizzare in modo proficuo gli strumenti di AI è importante conoscere i principi base dell’architettura Transformer e dell’apprendimento automatico. Non è necessario essere esperti di machine learning, ma comprendere come un LLM elabora il linguaggio aiuta a prevedere quando può sbagliare.

Verificare e confrontare le risposte dell’IA

L’IA non ha una conoscenza “perfetta”, quindi è sempre utile verificare ciò che produce. Ecco alcune strategie:

  1. Cross-check con fonti affidabili
    Se l’IA fornisce un dato tecnico, un numero o una citazione, controllalo con fonti ufficiali (siti governativi, articoli scientifici, documentazione ufficiale).
  2. Chiedere spiegazioni e prove all’IA stessa
    Se un’IA fa un’affermazione sorprendente, chiedile:
    “Puoi citare la fonte?”
    “Puoi spiegare il ragionamento passo dopo passo?” Se non può fornire fonti verificabili, il dato è probabilmente impreciso.

Usare l’IA come assistente, non come autorità assoluta

L’IA è un potente strumento di supporto, ma non sostituisce il pensiero critico umano. Per sfruttarla al meglio:

Affidarsi all’IA per: creare idee e brainstorming, generare bozze e riassunti, suggerire strutture di codice o migliorare testi

Non affidarsi all’IA per: decisioni critiche senza verifica (legali, finanziarie, mediche), informazioni sensibili o personali, questioni che richiedono giudizio umano complesso.

In conclusione abbiamo visto che gli LLM non comprendono realmente il linguaggio come farebbe un essere umano, ma generano testo sulla base di modelli probabilistici appresi da enormi quantità di dati. Questa caratteristica li rende straordinariamente abili nel produrre risposte coerenti e contestualmente appropriate, ma li espone anche al rischio di errori e allucinazioni, specialmente in ambiti complessi come la matematica, la logica e la conoscenza fattuale aggiornata.

Per questo motivo, non possiamo affidarci ciecamente alle loro risposte, ma dobbiamo sviluppare un approccio critico nel loro utilizzo. Verificare le informazioni, confrontare fonti affidabili, chiedere spiegazioni dettagliate e utilizzarli come strumenti di supporto piuttosto che come autorità assolute sono pratiche essenziali per evitare errori e ottenere il massimo da questi modelli.

L’intelligenza artificiale è una risorsa straordinaria, ma il pensiero critico umano resta insostituibile. Imparare a collaborare con l’IA anziché subirne passivamente le risposte sarà la chiave per un utilizzo efficace e consapevole di queste tecnologie nel futuro.

Riferimenti Bibliografici

  1. Vaswani, A., Shazeer, N., Parmar, N., Uszkoreit, J., Jones, L., Gomez, A. N., … & Polosukhin, I. (2017). Attention is all you need. Advances in Neural Information Processing Systems, 30.
  2. Brown, T., Mann, B., Ryder, N., Subbiah, M., Kaplan, J., Dhariwal, P., … & Amodei, D. (2020). Language models are few-shot learners. NeurIPS.

OpenAI. (2023). GPT-4 Technical Report. OpenAI Research.


Firewall: la prima linea di difesa nella sicurezza delle reti

Viviamo in un’era digitale in cui la connettività gioca un ruolo centrale nella vita quotidiana, sia a livello personale che aziendale. Le reti informatiche, che siano domestiche o aziendali, sono costantemente esposte a una serie di minacce, tra cui malware, attacchi hacker, phishing, ransomware e intrusioni non autorizzate.

Proteggere i dati e le infrastrutture digitali è diventato un obiettivo primario per governi, imprese e privati. Gli attacchi informatici non solo possono compromettere la privacy degli utenti, ma possono anche causare danni economici ingenti e compromettere il funzionamento di interi sistemi. Per questo motivo, l’adozione di misure di sicurezza adeguate è essenziale per garantire la protezione delle informazioni e la continuità operativa.

Quali sono i principali dispositivi di protezione

Per difendere le reti da minacce e attacchi, vengono utilizzati diversi dispositivi e soluzioni di sicurezza. Tra i più comuni troviamo:

  • Firewall: un sistema di filtraggio che monitora e controlla il traffico di rete in base a regole predefinite, impedendo connessioni non autorizzate.
  • Antivirus e antimalware: software progettati per rilevare, bloccare ed eliminare minacce come virus, spyware, trojan e ransomware.
  • Sistemi di rilevamento e prevenzione delle intrusioni (IDS/IPS): strumenti avanzati che analizzano il traffico di rete alla ricerca di comportamenti anomali o potenzialmente dannosi.
  • VPN (Virtual Private Network): una tecnologia che cifra la comunicazione tra dispositivi e server remoti, proteggendo la privacy e impedendo intercettazioni di dati.
  • Proxy e gateway di sicurezza: soluzioni che fungono da intermediari tra utenti e internet, filtrando il traffico e migliorando la sicurezza.
  • Autenticazione a due fattori (2FA): una misura di sicurezza che richiede un secondo metodo di verifica oltre alla password per accedere a un sistema.

Questi strumenti, utilizzati in combinazione tra loro, possono garantire un elevato livello di protezione per le reti e i dispositivi connessi.

Il firewall e le sue peculiarità

Firewall di rete

Il firewall è uno dei dispositivi più importanti per la sicurezza informatica. Si tratta di un sistema che agisce come un “muro virtuale” tra una rete sicura (ad esempio, la rete aziendale o domestica) e una rete non sicura (internet). Il suo compito principale è quello di filtrare il traffico di rete, consentendo solo il passaggio dei dati autorizzati e bloccando le connessioni potenzialmente pericolose.

Le principali funzionalità di un firewall includono:

  • Monitoraggio del traffico di rete: analizza ogni pacchetto di dati in transito tra reti diverse.
  • Filtraggio basato su regole predefinite: permette di stabilire quali connessioni devono essere consentite o bloccate.
  • Protezione contro accessi non autorizzati: impedisce che utenti non autorizzati possano accedere a risorse di rete.
  • Ispezione approfondita dei pacchetti (DPI – Deep Packet Inspection): esamina il contenuto dei pacchetti per individuare minacce nascoste o attacchi avanzati.
  • Registrazione e analisi degli eventi: conserva log dettagliati di tutte le connessioni, utile per individuare tentativi di attacco o anomalie.

I tipi di firewall

Tipi di firewall

Esistono diverse tipologie di firewall, ciascuna con caratteristiche specifiche. Le principali categorie sono:

Packet Filtering Firewall

Questa è la forma più semplice di firewall. Funziona analizzando i pacchetti di dati in base a parametri come indirizzo IP sorgente e destinazione, numero di porta e protocollo di comunicazione. Se un pacchetto non soddisfa i criteri stabiliti nelle regole del firewall, viene bloccato. Questo tipo di firewall è veloce ed efficiente, ma offre una protezione limitata poiché non tiene traccia dello stato delle connessioni.

Stateful Inspection Firewall

A differenza del packet filtering, questo tipo di firewall tiene traccia dello stato delle connessioni attive. Ogni pacchetto viene analizzato nel contesto della sessione di comunicazione di cui fa parte. In questo modo, il firewall può riconoscere pacchetti legittimi appartenenti a connessioni già autorizzate, bloccando invece quelli sospetti o inattesi. Questo approccio offre una sicurezza superiore rispetto al semplice filtraggio dei pacchetti.

Personal Firewall

Questo tipo di firewall è installato direttamente su dispositivi personali, come computer e smartphone. Il suo scopo principale è proteggere il singolo utente da minacce provenienti dalla rete, impedendo connessioni non autorizzate verso il dispositivo. Spesso integra funzionalità di rilevamento delle intrusioni e monitoraggio delle applicazioni che accedono a internet.

Le ACL standard ed Extended

Esempio di ACL

Le Access Control List (ACL) sono un insieme di regole utilizzate dai firewall e dai router per filtrare il traffico di rete. Esistono due principali categorie di ACL:

  • ACL Standard: operano basandosi esclusivamente sull’indirizzo IP sorgente del pacchetto. Sono semplici da configurare, ma offrono un controllo limitato.
  • ACL Extended: permettono un filtraggio più dettagliato, basandosi su più parametri, come indirizzo IP sorgente e destinazione, tipo di protocollo (TCP, UDP, ICMP) e numero di porta. Offrono un controllo più granulare rispetto alle ACL standard.

Le ACL vengono utilizzate nei firewall per limitare l’accesso a determinate risorse di rete e prevenire traffico indesiderato, contribuendo alla sicurezza complessiva del sistema.

Considerazioni finali

Il firewall è uno strumento essenziale per garantire la sicurezza delle reti, ma non può essere considerato l’unica misura di protezione. Per una sicurezza efficace, è importante adottare un approccio multilivello, combinando firewall con altre tecnologie di sicurezza come antivirus, IDS/IPS, VPN e autenticazione a due fattori.

Inoltre, la configurazione del firewall deve essere costantemente aggiornata e adattata alle nuove minacce emergenti. Le aziende e gli utenti devono anche adottare una buona prassi nella gestione della sicurezza informatica, evitando di esporre informazioni sensibili e proteggendo adeguatamente i propri dispositivi.

Fonti

  • Le immagini utilizzate in questo documento provengono da fonti royalty-free:
  • Firewall di rete: immagine royalty-free da Pixabay (Fonte)
  • Tipi di firewall: immagine royalty-free da Unsplash (Fonte)
  • Esempio di ACL: immagine royalty-free da Pexels (Fonte)
  1. W. Stallings, Network Security Essentials: Applications and Standards, Pearson, 2020.
  2. Cisco Systems, Firewall Technologies, disponibile su www.cisco.com.
  3. NIST (National Institute of Standards and Technology), Guidelines on Firewalls and Firewall Policy, 2021.

GDPR cos’è e perché è importante?

Il Regolamento Generale sulla Protezione dei Dati (GDPR, acronimo di General Data Protection Regulation) è una normativa europea entrata in vigore il 25 maggio 2018 con l’obiettivo di proteggere i dati personali e la privacy dei cittadini dell’Unione Europea (UE).

In che modo raggiunge questo obiettivo?

L’idea alla base del GDPR è garantire che le aziende, gli enti pubblici e qualsiasi altra organizzazione che gestisce dati personali rispettino determinati principi di responsabilità e trasparenza. Inoltre, il regolamento assicura ai cittadini un maggiore controllo sulle proprie informazioni.

Ma cosa significa, in pratica, avere un maggiore controllo sui propri dati?

Immagina di essere iscritto a un social network o di aver fatto acquisti online. Il GDPR ti dà il diritto di sapere quali dati personali un’azienda ha raccolto su di te (ad esempio, nome, email, cronologia degli acquisti o interazioni). Puoi anche correggere queste informazioni se sono errate o non aggiornate e, in alcuni casi, puoi persino chiedere che vengano cancellate (il famoso “diritto all’oblio”) se non ci sono più motivi validi per conservarle.

Ad esempio, se in passato hai fornito il tuo indirizzo email per ricevere offerte commerciali, ma ora non vuoi più riceverle, puoi chiedere che il tuo indirizzo venga rimosso dalle liste di invio. Oppure, se vuoi trasferire i tuoi dati da una piattaforma di streaming musicale a un’altra, puoi esercitare il diritto alla portabilità: otterrai un file con le informazioni essenziali (come playlist e preferenze), così da non perdere tutto ciò che avevi personalizzato.

Tutte queste possibilità sono esempi concreti di come il GDPR ti dia un maggiore controllo sui tuoi dati personali.


Origini e obiettivi

Prima dell’entrata in vigore del GDPR, la tutela dei dati personali era regolata da direttive e leggi nazionali in tutta Europa (come la Direttiva 95/46/CE). Il problema? Ogni Paese aveva livelli di protezione e obblighi diversi, creando confusione e differenze significative tra gli Stati membri. Il GDPR ha unificato e modernizzato il quadro normativo europeo, con l’obiettivo di:

  • Armonizzare la legislazione tra i Paesi dell’UE in tema di protezione dei dati.
  • Aumentare la trasparenza su come le aziende trattano i dati personali.
  • Restituire ai cittadini il controllo su come vengono raccolti, archiviati e utilizzati i loro dati.
  • Responsabilizzare le aziende, obbligandole a dimostrare di rispettare i principi di sicurezza e riservatezza.

Ambito di applicazione

Uno degli aspetti più innovativi del GDPR è la sua portata extraterritoriale. Il regolamento non si applica solo alle aziende e agli enti situati nell’Unione Europea, ma anche a qualsiasi organizzazione che, indipendentemente dalla sua sede, offre beni o servizi a cittadini europei o ne monitora il comportamento (per esempio, tramite il tracciamento online).

In altre parole, se un’azienda statunitense o asiatica raccoglie dati di un utente europeo, deve comunque rispettare il GDPR.


Principi fondamentali del GDPR

Il GDPR si basa su alcuni principi chiave che stabiliscono come i dati personali devono essere trattati.

1. Liceità, correttezza e trasparenza
I dati devono essere trattati in modo legale, corretto e trasparente.
Esempio: Un sito e-commerce chiede il consenso esplicito per inviare newsletter e specifica chiaramente quali dati raccoglie (nome, email) e per quale scopo (comunicazioni promozionali). L’utente sa esattamente cosa accadrà ai suoi dati.

2. Limitazione delle finalità
I dati devono essere raccolti e utilizzati solo per scopi specifici, espliciti e legittimi.
Esempio: Se un’azienda raccoglie il tuo indirizzo email per inviarti una ricevuta d’acquisto, non può poi usarlo per inviarti offerte commerciali senza il tuo consenso.

3. Minimizzazione dei dati
Si possono raccogliere solo i dati effettivamente necessari.
Esempio: Se ti iscrivi a un servizio di streaming, è sufficiente l’email e il metodo di pagamento. Chiedere anche il numero di telefono o l’indirizzo di casa, se non necessari, sarebbe eccessivo.

4. Esattezza
I dati devono essere aggiornati e corretti.
Esempio: Se cambi indirizzo email, l’azienda deve consentirti di aggiornarlo facilmente.

5. Limitazione della conservazione
I dati non devono essere conservati più a lungo del necessario.
Esempio: Dopo la fine di un contratto, un’azienda può conservare i dati solo per il periodo richiesto dalla legge. Poi deve eliminarli o anonimizzarli.

6. Integrità e riservatezza
I dati devono essere protetti da accessi non autorizzati o usi illeciti.
Esempio: Un’azienda che gestisce pagamenti online utilizza la crittografia per proteggere i dati delle transazioni.

7. Responsabilità (accountability)
Chi tratta i dati deve dimostrare di rispettare il GDPR.
Esempio: Un’azienda documenta tutte le procedure, forma il personale e tiene aggiornato un registro dei trattamenti.


Diritti degli interessati (cioè, i tuoi diritti)

Il GDPR garantisce ai cittadini una serie di diritti per proteggere la propria privacy:

  • Diritto di accesso → Puoi sapere quali dati un’azienda ha su di te.
  • Diritto di rettifica → Puoi correggere dati errati o incompleti.
  • Diritto alla cancellazione (oblio) → Puoi chiedere la rimozione dei tuoi dati.
  • Diritto alla limitazione del trattamento → Puoi chiedere di bloccare temporaneamente il trattamento dei tuoi dati.
  • Diritto alla portabilità dei dati → Puoi trasferire i tuoi dati a un altro servizio.
  • Diritto di opposizione → Puoi opporti all’uso dei tuoi dati per determinate finalità (ad esempio, il marketing diretto).
  • Diritto di non essere sottoposto a decisioni automatizzate → Nessuna decisione che ti riguarda può essere presa esclusivamente da un algoritmo senza intervento umano.

Obblighi per le aziende

Le aziende devono implementare misure adeguate per garantire la sicurezza dei dati personali, tra cui:

  • Nomina del DPO (Data Protection Officer) per il monitoraggio dei dati su larga scala.
  • Registro dei trattamenti, per tenere traccia delle operazioni sui dati.
  • Valutazioni d’impatto sulla protezione dei dati (DPIA) per trattamenti ad alto rischio.
  • Notifica delle violazioni (data breach) entro 72 ore in caso di perdita o accesso non autorizzato ai dati.

Sanzioni per chi non rispetta il GDPR

Il GDPR prevede multe fino al 4% del fatturato globale di un’azienda o 20 milioni di euro (viene applicata la cifra più alta). Le Autorità di controllo nazionali (in Italia il Garante per la Protezione dei Dati Personali) sono responsabili di vigilare sul rispetto del GDPR e sanzionare eventuali violazioni.


Il GDPR nel mondo: come si stanno adeguando altri Paesi?

Il GDPR ha influenzato la legislazione sulla privacy in tutto il mondo:

  • USA (California – CCPA/CPRA) → Simile al GDPR, ma applicato solo a grandi aziende.
  • Brasile (LGPD) → Normativa ispirata al GDPR, ma con sanzioni più basse.
  • Cina (PIPL) → Normativa con regole severe, ma più orientata al controllo governativo.

Anche Regno Unito, Canada e India hanno adottato normative simili, dimostrando l’impatto globale del GDPR sulla tutela della privacy.


In sintesi, il GDPR ha cambiato il modo in cui i dati personali vengono gestiti, dando ai cittadini maggiore controllo e imponendo alle aziende regole più rigorose sulla trasparenza e sulla sicurezza.

Riferimenti

  1. Testo ufficiale del GDPR
  2. Commissione Europea – Sezione Data Protection
  3. Garante per la Protezione dei Dati Personali (Italia)
    • https://www.garanteprivacy.it
      Sito ufficiale dell’Autorità italiana di controllo, con provvedimenti, linee guida, modulistica e approfondimenti specifici per l’Italia.
  4. EDPB (European Data Protection Board)
  5. Approfondimenti su data breach e misure di sicurezza (ENISA)

Intelligenza artificiale e valutazione formativa: un aiuto concreto per i docenti

La maggior parte dei docenti come me è ben consapevole che le metodologie didattiche attive rappresentano un approccio efficace all’insegnamento, in cui gli studenti diventano protagonisti del proprio apprendimento attraverso attività esperienziali, cooperative e riflessive (Bonwell & Eison, 1991).

Uno degli elementi chiave per il successo di queste strategie è la valutazione formativa, un processo continuo che permette di monitorare i progressi degli studenti e di adattare l’insegnamento in base ai loro bisogni. Questo tipo di valutazione ha un carattere diagnostico e regolativo, promuovendo un apprendimento più consapevole e partecipativo (Black & Wiliam, 1998). Studi dimostrano che l’uso della valutazione formativa migliora non solo la motivazione, ma anche i risultati degli studenti, fornendo loro un feedback costruttivo e strumenti per l’autoregolazione (Sadler, 1989).

Il rovescio della medaglia

Tuttavia, per quanto efficace, l’adozione di queste metodologie comporta un impegno notevole per i docenti. La preparazione di ogni singola lezione richiede tempo, e chi deve gestire un carico didattico di 18 ore settimanali – specialmente nei primi anni di insegnamento – può facilmente trovarsi a lavorare ben oltre le 40 ore settimanali.

Se si vuole insegnare con professionalità e qualità, questo sforzo è inevitabile. Tuttavia, oggi esiste un supporto inaspettato: l’intelligenza artificiale. In questo articolo non tratterò i limiti e le criticità di questa tecnologia – tema che approfondirò in un’altra occasione – ma voglio concentrarmi su come possa effettivamente alleggerire il lavoro degli insegnanti nella preparazione delle lezioni e, in particolare, delle verifiche formative.

Google Moduli e IA: creare una verifica formativa in pochi passi

Uno degli strumenti più semplici ed efficaci per realizzare una verifica formativa è Google Moduli, che, se combinato con un chatbot basato su IA, può ridurre drasticamente il tempo necessario per la creazione di test personalizzati.

Ecco il procedimento:

  1. Selezionare l’argomento della verifica – Questo passaggio è ovviamente scontato, ma resta il punto di partenza imprescindibile.
  2. Raccogliere il materiale didattico – Possono essere utilizzati file PDF, documenti Word, testi, immagini… l’intelligenza artificiale è in grado di elaborare diversi formati.
  3. Interagire con il chatbot – Io utilizzo prevalentemente ChatGPT, che al momento offre risposte di alta qualità, ma esistono molte alternative valide, come Claude.ai, DeepSeek, Gemini e altre.
  4. Inserire il prompt e caricare i documenti – Dopo aver fornito il materiale, è sufficiente chiedere al chatbot di generare domande basate sui contenuti analizzati. L’IA proporrà quesiti pertinenti, ma il giudizio finale resta nelle mani del docente.

Provate a inserire il prompt (domanda) che vedete nell’immagine dopo aver caricato il materiale su cui avete basato la vostra ultima lezione.

Il chatbot genererà una serie di domande che, secondo il suo algoritmo, risultano pertinenti rispetto al contenuto fornito. Tuttavia, il nostro giudizio resta fondamentale: consiglio quindi di analizzare con attenzione le domande proposte, correggerle, eliminarle o aggiungerne di nuove.

Personalizzare la verifica con il supporto dell’IA

L’intelligenza artificiale può essere un valido alleato, ma non deve sostituire la riflessione critica del docente. Per ottenere un test davvero efficace, consiglio di:

  • Analizzare attentamente le domande generate, valutandone coerenza e livello di difficoltà.
  • Modificare, eliminare o aggiungere quesiti, adattandoli alle esigenze della classe.
  • Chiedere al chatbot di riformulare o semplificare alcune domande, per migliorare la chiarezza e la comprensione.
  • Strutturare il test in modo equilibrato, alternando domande chiuse e aperte per favorire sia il consolidamento delle conoscenze sia la riflessione critica.

L’uso dell’IA non solo permette di risparmiare tempo, ma consente anche di sperimentare nuove modalità di valutazione, più interattive e personalizzate.

Dopo aver definito l’elenco di domande che a vostro  avviso è corretto arriva il momento di creare il google moduli. Chiedete a ChatGpt di scrivere il codice per generarlo.

Appena il chatbot avrà terminato copiate tutto il codice e aprite un nuovo Google Moduli.

Scegliete “Quiz in bianco”

Fate un click sull’immagine raffigurante i tre pallini a destra del pulsante “Pubblica” e poi clicca su “App Script”.

Incollate il codice generato dal chatbot, salvate il progetto poi cliccate su “Esegui”. Se viene richiesta l’autorizzazione a creare il progetto, accettate.

Al termine dell’esecuzione cercate nel vostro drive, tra gli i file recenti dovreste trovare il Modulo pronto. Non vi resta che selezionare per ogni domanda la risposta giusta e il test è pronto.

Questo procedimento vi sembra lungo? provate a farlo a mano….Buon lavoro.

F.M.

Riferimenti:

  • Bonwell, C. C., & Eison, J. A. (1991). Active Learning: Creating Excitement in the Classroom. ERIC.
  • Black, P., & Wiliam, D. (1998). Assessment and classroom learning. Assessment in Education, 5(1), 7-74.

Sadler, D. R. (1989). Formative assessment and the design of instructional systems. Instructional Science, 18(2), 119-144.