Categoria: coding

  • Costruire un generatore di verifiche per insegnanti con l’API di Claude

    Un docente che prepara una verifica strutturata — domande, soluzioni, punteggi, griglia di valutazione, magari in doppia fila per scoraggiare le copiature — ci mette comodamente un’ora. Il generatore di verifiche che ho costruito la produce in una trentina di secondi, partendo anche dalla dispensa del docente stesso.

    In questo articolo racconto com’è fatto dentro. Non è un tutorial passo-passo: è il resoconto delle decisioni tecniche, comprese quelle dettate dai vincoli — che come spesso accade sono state le più istruttive.

    I vincoli

    Il progetto doveva rispettare tre condizioni non negoziabili:

    1. Gratuito e senza registrazione per i docenti. Quindi l’endpoint è pubblico, e chiunque può chiamarlo.
    2. Hosting condiviso: niente Composer in deploy, niente processi persistenti, niente filesystem sopra la webroot, si carica via FTP.
    3. Niente dati conservati: i materiali dei docenti non finiscono in un database, né nei log.

    È un servizio basato su un LLM che sta in piedi su un hosting condiviso, con i costi sotto controllo e la privacy garantita dall’architettura, è un esercizio di progettazione.

    L’architettura è noiosa, di proposito

    Una singola pagina HTML (CSS e JavaScript inline, niente framework) e un endpoint PHP che fa da tramite verso la Messages API di Anthropic. Tutto qui. Niente build, niente dipendenze da aggiornare, niente database.

    Browser ──POST──▶ genera.php ──HTTPS──▶ api.anthropic.com/v1/messages
       ◀──JSON────────┘◀──────────────────────┘

    Il PHP fa cinque cose: valida l’input, applica i limiti anti-abuso, costruisce il prompt, chiama l’API con cURL, restituisce il JSON al browser. Il rendering della verifica e l’esportazione in Word avvengono lato client.

    La chiamata: PHP puro

    Su hosting condiviso senza Composer, la via più semplice è la chiamata HTTP diretta. La Messages API è un singolo endpoint POST:

    Il modello è Claude Sonnet: per un compito strutturato come questo il rapporto qualità/prezzo è il punto di equilibrio giusto — la qualità delle domande è da modello grande, il costo resta da centesimi (ci torno).

    La dispensa va dritta al modello

    La funzione più utile per i docenti è “basa le domande sulla mia dispensa”. Qui c’è la prima decisione che sorprende chi se lo immagina: il PDF non viene parsato. La Messages API accetta documenti PDF come blocco di contenuto, codificati in base64:

    Il modello legge il PDF direttamente — testo, struttura e anche le pagine scansionate, perché lo elabora anche visivamente. Zero librerie di parsing, zero casi limite da gestire, e domande che rispettano il materiale meglio di qualunque estrazione testuale che avrei potuto scrivere io.

    Per i DOCX invece un piccolo trucco che vale il prezzo del biglietto: un file Word è un archivio ZIP. Si apre con ZipArchive, si estrae word/document.xml, si spogliano i tag e si passa il testo nel prompt:

    Output strutturato: lo schema nel prompt

    La verifica deve arrivare al browser come dati, non come prosa: il frontend la impagina in vista studente e vista docente, calcola i punteggi, la esporta in Word. Quindi il prompt chiede esplicitamente un oggetto JSON secondo uno schema dichiarato — titolo, istruzioni, punteggio totale, criteri e un array di versioni (Fila A, Fila B, versione BES/DSA) ciascuna con le sue domande tipizzate: scelta multipla, vero/falso, aperta, esercizio.

    E siccome i modelli a volte incorniciano il JSON nei backtick nonostante le istruzioni, il parsing è difensivo:

    Nota per chi parte oggi: l’API ora supporta gli structured outputs nativi (output_config.format con un JSON Schema), che garantiscono output valido senza questa ginnastica. È il primo refactoring in lista.

    Endpoint pubblico e rate limit

    Una generazione costa pochi centesimi (qualche migliaio di token in ingresso, di più se c’è un PDF corposo, e 2-4 mila in uscita). Il costo unitario non è il problema. Il problema è che un endpoint pubblico che spende soldi a ogni chiamata è un invito a nozze per bot e burloni.

    La difesa è a due livelli, entrambi file-based (niente database):

    • Rate-limit per IP — un file JSON per IP in una cartella di log: 1 generazione ogni 30 secondi, massimo 10 al giorno.
    • Tetto globale giornaliero — un contatore unico condiviso da tutti gli strumenti del sito. Il rate-limit per IP non basta: chi ruota gli IP lo aggira. Il contatore globale no: raggiunto il tetto, il servizio risponde gentilmente “riprova domani” e il budget mensile ha un massimale matematico.

    Il secondo livello: la spesa giornaliera è tetto × costo massimo per generazione. Fine dell’ansia.

    La chiave API

    La chiave vive dentro la docroot, con tre difese indipendenti:

    1. sta in un file .php che non produce alcun output se richiesto via URL;
    2. la sua cartella ha un .htaccess con Require all denied;
    3. il file ha una guardia interna che risponde 404 all’accesso diretto.

    Affinché la chiave esca devono fallire tutte e tre insieme.

    La privacy è un’architettura

    I materiali dei docenti non restano da nessuna parte: nessun database, elaborazione al volo, e nei log finiscono solo data, durata e conteggio dei token:

    Niente materia, niente argomento, niente contenuti. I token servono per le statistiche interne e per tarare il tetto giornaliero. Tutto il resto evapora a fine richiesta.

    Ultimo miglio: il Word si fa nel browser

    L’export .docx è generato lato client con la libreria docx, caricata solo al click di chi esporta. Chi genera una verifica e la stampa dalla pagina non scarica un byte di JavaScript in più.

    Cosa mi porto a casa

    • I vincoli aiutano. “Deve girare su hosting condiviso” ha eliminato intere categorie di complessità: niente code, niente container, niente deploy pipeline. Il servizio è un file PHP.
    • Delegare il parsing al modello (PDF come documento allegato) è stata la singola decisione che ha semplificato di più il codice.
    • Il tetto di spesa globale è la prima cosa che rifarei identica in qualunque servizio pubblico basato su un LLM. Il rate-limit per IP da solo è una porta chiusa con la finestra aperta.

    Se vuoi vedere il risultato dal lato dei docenti, c’è il case study — con una sezione “dietro le quinte” che riassume queste scelte — e naturalmente lo strumento stesso, gratuito e senza registrazione.

    Ne avresti costruito uno diverso? Scrivimi — o passa da LinkedIn.