{"id":17,"date":"2026-07-11T16:07:40","date_gmt":"2026-07-11T16:07:40","guid":{"rendered":"https:\/\/www.carbonimario.com\/articoli\/?p=17"},"modified":"2026-08-16T15:13:08","modified_gmt":"2026-08-16T15:13:08","slug":"generatore-verifiche-insegnanti-api-claude","status":"publish","type":"post","link":"https:\/\/www.carbonimario.com\/articoli\/generatore-verifiche-insegnanti-api-claude\/","title":{"rendered":"Costruire un generatore di verifiche per insegnanti con l&#8217;API di Claude"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Un docente che prepara una verifica strutturata \u2014 domande, soluzioni, punteggi, griglia di valutazione, magari in doppia fila per scoraggiare le copiature \u2014 ci mette comodamente un&#8217;ora. Il <a href=\"\/verifiche\/\">generatore di verifiche<\/a> che ho costruito la produce in una trentina di secondi, partendo anche dalla dispensa del docente stesso.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In questo articolo racconto com&#8217;\u00e8 fatto dentro. Non \u00e8 un tutorial passo-passo: \u00e8 il resoconto delle decisioni tecniche, comprese quelle dettate dai vincoli \u2014 che come spesso accade sono state le pi\u00f9 istruttive.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">I vincoli<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Il progetto doveva rispettare tre condizioni non negoziabili:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Gratuito e senza registrazione<\/strong> per i docenti. Quindi l&#8217;endpoint \u00e8 pubblico, e chiunque pu\u00f2 chiamarlo.<\/li>\n\n\n\n<li><strong>Hosting condiviso<\/strong>: niente Composer in deploy, niente processi persistenti, niente filesystem sopra la webroot, si carica via FTP.<\/li>\n\n\n\n<li><strong>Niente dati conservati<\/strong>: i materiali dei docenti non finiscono in un database, n\u00e9 nei log.<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">\u00c8 un servizio basato su un LLM che sta in piedi su un hosting condiviso, con i costi sotto controllo e la privacy garantita dall&#8217;architettura, \u00e8 un esercizio di progettazione.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">L&#8217;architettura \u00e8 noiosa, di proposito<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Una singola pagina HTML (CSS e JavaScript inline, niente framework) e un endpoint PHP che fa da tramite verso la <a href=\"https:\/\/platform.claude.com\/docs\">Messages API<\/a> di Anthropic. Tutto qui. Niente build, niente dipendenze da aggiornare, niente database.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Browser \u2500\u2500POST\u2500\u2500\u25b6 genera.php \u2500\u2500HTTPS\u2500\u2500\u25b6 api.anthropic.com\/v1\/messages\n   \u25c0\u2500\u2500JSON\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\u25c0\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Il PHP fa cinque cose: valida l&#8217;input, applica i limiti anti-abuso, costruisce il prompt, chiama l&#8217;API con cURL, restituisce il JSON al browser. Il rendering della verifica e l&#8217;esportazione in Word avvengono <strong>lato client<\/strong>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">La chiamata: PHP puro<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Su hosting condiviso senza Composer, la via pi\u00f9 semplice \u00e8 la chiamata HTTP diretta. La Messages API \u00e8 un singolo endpoint POST:<\/p>\n\n\n<div\n    class=\"align wp-block-bch-code-highlight\"    id=\"bhcCodeHighlight-1\"\n    data-attributes='{&quot;cId&quot;:&quot;acaf7732-e&quot;,&quot;language&quot;:&quot;php&quot;,&quot;headerPart&quot;:true,&quot;codeBg&quot;:{&quot;color&quot;:&quot;#F3F3F3&quot;},&quot;align&quot;:&quot;&quot;,&quot;lineNumbers&quot;:true,&quot;copyBtnOption&quot;:&quot;Text&quot;,&quot;theme&quot;:&quot;default&quot;,&quot;headingTitleTypo&quot;:{&quot;fontWeight&quot;:400,&quot;fontSize&quot;:{&quot;desktop&quot;:18,&quot;tablet&quot;:13,&quot;mobile&quot;:11},&quot;lineHeight&quot;:1.5,&quot;fontFamily&quot;:&quot;Aleo&quot;,&quot;textTransform&quot;:&quot;uppercase&quot;},&quot;codeTypo&quot;:{&quot;desktop&quot;:18,&quot;tablet&quot;:15,&quot;mobile&quot;:14},&quot;clipBoard&quot;:true,&quot;wordWrap&quot;:true,&quot;width&quot;:{&quot;desktop&quot;:&quot;100%&quot;,&quot;tablet&quot;:&quot;100%&quot;,&quot;mobile&quot;:&quot;100%&quot;},&quot;height&quot;:{&quot;desktop&quot;:&quot;0px&quot;,&quot;tablet&quot;:&quot;0px&quot;,&quot;mobile&quot;:&quot;0px&quot;},&quot;padding&quot;:{&quot;top&quot;:&quot;0px&quot;,&quot;right&quot;:&quot;0px&quot;,&quot;bottom&quot;:&quot;0px&quot;,&quot;left&quot;:&quot;0px&quot;},&quot;background&quot;:{&quot;color&quot;:&quot;#d3cfcf42&quot;},&quot;headerBg&quot;:{&quot;color&quot;:&quot;#2f446e&quot;},&quot;headerTextColor&quot;:{&quot;color&quot;:&quot;#ffff&quot;},&quot;headerThemeOpt&quot;:&quot;one&quot;,&quot;layout&quot;:{&quot;align&quot;:&quot;left&quot;},&quot;border&quot;:{&quot;color&quot;:&quot;#000&quot;,&quot;style&quot;:&quot;solid&quot;,&quot;width&quot;:&quot;0px&quot;},&quot;shadow&quot;:[{&quot;hOffset&quot;:&quot;0px&quot;,&quot;vOffset&quot;:&quot;0px&quot;,&quot;blur&quot;:&quot;0px&quot;,&quot;spread&quot;:&quot;0px&quot;,&quot;color&quot;:&quot;rgba(0, 0, 0, 0.16)&quot;,&quot;isInset&quot;:false}],&quot;alignment&quot;:&quot;center&quot;,&quot;copyTypo&quot;:{&quot;fontWeight&quot;:400,&quot;fontSize&quot;:{&quot;desktop&quot;:13,&quot;tablet&quot;:13,&quot;mobile&quot;:11},&quot;lineHeight&quot;:1.5,&quot;fontFamily&quot;:&quot;Aleo&quot;},&quot;clipBoardColors&quot;:{&quot;color&quot;:&quot;#fff&quot;,&quot;bg&quot;:&quot;#00000024&quot;},&quot;copyIconSize&quot;:{&quot;desktop&quot;:24,&quot;tablet&quot;:20,&quot;mobile&quot;:18},&quot;copyBtnPadding&quot;:{&quot;desktop&quot;:{&quot;top&quot;:&quot;2px&quot;,&quot;left&quot;:&quot;5px&quot;,&quot;bottom&quot;:&quot;2px&quot;,&quot;right&quot;:&quot;5px&quot;},&quot;tablet&quot;:{&quot;top&quot;:&quot;2px&quot;,&quot;left&quot;:&quot;5px&quot;,&quot;bottom&quot;:&quot;2px&quot;,&quot;right&quot;:&quot;5px&quot;},&quot;mobile&quot;:{&quot;top&quot;:&quot;2px&quot;,&quot;left&quot;:&quot;5px&quot;,&quot;bottom&quot;:&quot;2px&quot;,&quot;right&quot;:&quot;5px&quot;}},&quot;headerPadding&quot;:{&quot;desktop&quot;:{&quot;top&quot;:&quot;14px&quot;,&quot;left&quot;:&quot;14px&quot;,&quot;bottom&quot;:&quot;14px&quot;,&quot;right&quot;:&quot;14px&quot;},&quot;tablet&quot;:{&quot;top&quot;:&quot;12px&quot;,&quot;left&quot;:&quot;12px&quot;,&quot;bottom&quot;:&quot;12px&quot;,&quot;right&quot;:&quot;12px&quot;},&quot;mobile&quot;:{&quot;top&quot;:&quot;10px&quot;,&quot;left&quot;:&quot;10px&quot;,&quot;bottom&quot;:&quot;10px&quot;,&quot;right&quot;:&quot;10px&quot;}},&quot;copyBtnPosition&quot;:{&quot;desktop&quot;:{&quot;top&quot;:&quot;5px&quot;,&quot;left&quot;:&quot;&quot;,&quot;bottom&quot;:&quot;&quot;,&quot;right&quot;:&quot;15px&quot;},&quot;tablet&quot;:{&quot;top&quot;:&quot;5px&quot;,&quot;left&quot;:&quot;&quot;,&quot;bottom&quot;:&quot;&quot;,&quot;right&quot;:&quot;15px&quot;},&quot;mobile&quot;:{&quot;top&quot;:&quot;5px&quot;,&quot;left&quot;:&quot;&quot;,&quot;bottom&quot;:&quot;&quot;,&quot;right&quot;:&quot;15px&quot;}},&quot;outputOption&quot;:false,&quot;outputText&quot;:&quot;Hello Word !&quot;,&quot;outputColors&quot;:{&quot;color&quot;:&quot;#fff&quot;,&quot;bg&quot;:&quot;#2F446E&quot;},&quot;outputTypo&quot;:{&quot;fontWeight&quot;:400,&quot;fontSize&quot;:{&quot;desktop&quot;:21,&quot;tablet&quot;:17,&quot;mobile&quot;:13},&quot;lineHeight&quot;:1.5,&quot;fontFamily&quot;:&quot;Aleo&quot;},&quot;outputPadding&quot;:{&quot;desktop&quot;:{&quot;top&quot;:&quot;8px&quot;,&quot;left&quot;:&quot;8px&quot;,&quot;bottom&quot;:&quot;8px&quot;,&quot;right&quot;:&quot;8px&quot;},&quot;tablet&quot;:{&quot;top&quot;:&quot;7px&quot;,&quot;left&quot;:&quot;7px&quot;,&quot;bottom&quot;:&quot;7px&quot;,&quot;right&quot;:&quot;7px&quot;},&quot;mobile&quot;:{&quot;top&quot;:&quot;5px&quot;,&quot;left&quot;:&quot;5px&quot;,&quot;bottom&quot;:&quot;5px&quot;,&quot;right&quot;:&quot;5px&quot;}},&quot;outputBorder&quot;:{&quot;color&quot;:&quot;#000&quot;,&quot;style&quot;:&quot;solid&quot;,&quot;width&quot;:&quot;0px&quot;,&quot;radius&quot;:&quot;7px&quot;}}'\n>\n    <pre class=\"bchCodeData\" style=\"display:none;\">&quot;$payload = [\\n  &#039;model&#039;      =&gt; &#039;claude-sonnet-5&#039;,\\n  &#039;max_tokens&#039; =&gt; 6000,\\n  &#039;system&#039;     =&gt; $sistema,   \\\/\\\/ il \\&quot;ruolo\\&quot; fisso del modello\\n  &#039;messages&#039;   =&gt; [[&#039;role&#039; =&gt; &#039;user&#039;, &#039;content&#039; =&gt; $content]],\\n];\\n\\n$ch = curl_init(&#039;https:\\\/\\\/api.anthropic.com\\\/v1\\\/messages&#039;);\\ncurl_setopt_array($ch, [\\n  CURLOPT_RETURNTRANSFER =&gt; true,\\n  CURLOPT_POST           =&gt; true,\\n  CURLOPT_HTTPHEADER     =&gt; [\\n    &#039;content-type: application\\\/json&#039;,\\n    &#039;anthropic-version: 2023-06-01&#039;,\\n    &#039;x-api-key: &#039; . ANTHROPIC_API_KEY,\\n  ],\\n  CURLOPT_POSTFIELDS =&gt; json_encode($payload, JSON_UNESCAPED_UNICODE),\\n]);&quot;<\/pre>\n<\/div>\n\n\n\n<p class=\"wp-block-paragraph\">Il modello \u00e8 <strong>Claude Sonnet<\/strong>: per un compito strutturato come questo il rapporto qualit\u00e0\/prezzo \u00e8 il punto di equilibrio giusto \u2014 la qualit\u00e0 delle domande \u00e8 da modello grande, il costo resta da centesimi (ci torno).<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">La dispensa va dritta al modello<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">La funzione pi\u00f9 utile per i docenti \u00e8 &#8220;basa le domande sulla mia dispensa&#8221;. Qui c&#8217;\u00e8 la prima decisione che sorprende chi se lo immagina: <strong>il PDF non viene parsato<\/strong>. La Messages API accetta documenti PDF come blocco di contenuto, codificati in base64:<\/p>\n\n\n<div\n    class=\"align wp-block-bch-code-highlight\"    id=\"bhcCodeHighlight-2\"\n    data-attributes='{&quot;cId&quot;:&quot;4cc692ac-7&quot;,&quot;language&quot;:&quot;php&quot;,&quot;headerPart&quot;:true,&quot;codeBg&quot;:{&quot;color&quot;:&quot;#F3F3F3&quot;},&quot;align&quot;:&quot;&quot;,&quot;lineNumbers&quot;:true,&quot;copyBtnOption&quot;:&quot;Text&quot;,&quot;theme&quot;:&quot;default&quot;,&quot;headingTitleTypo&quot;:{&quot;fontWeight&quot;:400,&quot;fontSize&quot;:{&quot;desktop&quot;:18,&quot;tablet&quot;:13,&quot;mobile&quot;:11},&quot;lineHeight&quot;:1.5,&quot;fontFamily&quot;:&quot;Aleo&quot;,&quot;textTransform&quot;:&quot;uppercase&quot;},&quot;codeTypo&quot;:{&quot;desktop&quot;:18,&quot;tablet&quot;:15,&quot;mobile&quot;:14},&quot;clipBoard&quot;:true,&quot;wordWrap&quot;:true,&quot;width&quot;:{&quot;desktop&quot;:&quot;100%&quot;,&quot;tablet&quot;:&quot;100%&quot;,&quot;mobile&quot;:&quot;100%&quot;},&quot;height&quot;:{&quot;desktop&quot;:&quot;0px&quot;,&quot;tablet&quot;:&quot;0px&quot;,&quot;mobile&quot;:&quot;0px&quot;},&quot;padding&quot;:{&quot;top&quot;:&quot;0px&quot;,&quot;right&quot;:&quot;0px&quot;,&quot;bottom&quot;:&quot;0px&quot;,&quot;left&quot;:&quot;0px&quot;},&quot;background&quot;:{&quot;color&quot;:&quot;#d3cfcf42&quot;},&quot;headerBg&quot;:{&quot;color&quot;:&quot;#2f446e&quot;},&quot;headerTextColor&quot;:{&quot;color&quot;:&quot;#ffff&quot;},&quot;headerThemeOpt&quot;:&quot;one&quot;,&quot;layout&quot;:{&quot;align&quot;:&quot;left&quot;},&quot;border&quot;:{&quot;color&quot;:&quot;#000&quot;,&quot;style&quot;:&quot;solid&quot;,&quot;width&quot;:&quot;0px&quot;},&quot;shadow&quot;:[{&quot;hOffset&quot;:&quot;0px&quot;,&quot;vOffset&quot;:&quot;0px&quot;,&quot;blur&quot;:&quot;0px&quot;,&quot;spread&quot;:&quot;0px&quot;,&quot;color&quot;:&quot;rgba(0, 0, 0, 0.16)&quot;,&quot;isInset&quot;:false}],&quot;alignment&quot;:&quot;center&quot;,&quot;copyTypo&quot;:{&quot;fontWeight&quot;:400,&quot;fontSize&quot;:{&quot;desktop&quot;:13,&quot;tablet&quot;:13,&quot;mobile&quot;:11},&quot;lineHeight&quot;:1.5,&quot;fontFamily&quot;:&quot;Aleo&quot;},&quot;clipBoardColors&quot;:{&quot;color&quot;:&quot;#fff&quot;,&quot;bg&quot;:&quot;#00000024&quot;},&quot;copyIconSize&quot;:{&quot;desktop&quot;:24,&quot;tablet&quot;:20,&quot;mobile&quot;:18},&quot;copyBtnPadding&quot;:{&quot;desktop&quot;:{&quot;top&quot;:&quot;2px&quot;,&quot;left&quot;:&quot;5px&quot;,&quot;bottom&quot;:&quot;2px&quot;,&quot;right&quot;:&quot;5px&quot;},&quot;tablet&quot;:{&quot;top&quot;:&quot;2px&quot;,&quot;left&quot;:&quot;5px&quot;,&quot;bottom&quot;:&quot;2px&quot;,&quot;right&quot;:&quot;5px&quot;},&quot;mobile&quot;:{&quot;top&quot;:&quot;2px&quot;,&quot;left&quot;:&quot;5px&quot;,&quot;bottom&quot;:&quot;2px&quot;,&quot;right&quot;:&quot;5px&quot;}},&quot;headerPadding&quot;:{&quot;desktop&quot;:{&quot;top&quot;:&quot;14px&quot;,&quot;left&quot;:&quot;14px&quot;,&quot;bottom&quot;:&quot;14px&quot;,&quot;right&quot;:&quot;14px&quot;},&quot;tablet&quot;:{&quot;top&quot;:&quot;12px&quot;,&quot;left&quot;:&quot;12px&quot;,&quot;bottom&quot;:&quot;12px&quot;,&quot;right&quot;:&quot;12px&quot;},&quot;mobile&quot;:{&quot;top&quot;:&quot;10px&quot;,&quot;left&quot;:&quot;10px&quot;,&quot;bottom&quot;:&quot;10px&quot;,&quot;right&quot;:&quot;10px&quot;}},&quot;copyBtnPosition&quot;:{&quot;desktop&quot;:{&quot;top&quot;:&quot;5px&quot;,&quot;left&quot;:&quot;&quot;,&quot;bottom&quot;:&quot;&quot;,&quot;right&quot;:&quot;15px&quot;},&quot;tablet&quot;:{&quot;top&quot;:&quot;5px&quot;,&quot;left&quot;:&quot;&quot;,&quot;bottom&quot;:&quot;&quot;,&quot;right&quot;:&quot;15px&quot;},&quot;mobile&quot;:{&quot;top&quot;:&quot;5px&quot;,&quot;left&quot;:&quot;&quot;,&quot;bottom&quot;:&quot;&quot;,&quot;right&quot;:&quot;15px&quot;}},&quot;outputOption&quot;:false,&quot;outputText&quot;:&quot;Hello Word !&quot;,&quot;outputColors&quot;:{&quot;color&quot;:&quot;#fff&quot;,&quot;bg&quot;:&quot;#2F446E&quot;},&quot;outputTypo&quot;:{&quot;fontWeight&quot;:400,&quot;fontSize&quot;:{&quot;desktop&quot;:21,&quot;tablet&quot;:17,&quot;mobile&quot;:13},&quot;lineHeight&quot;:1.5,&quot;fontFamily&quot;:&quot;Aleo&quot;},&quot;outputPadding&quot;:{&quot;desktop&quot;:{&quot;top&quot;:&quot;8px&quot;,&quot;left&quot;:&quot;8px&quot;,&quot;bottom&quot;:&quot;8px&quot;,&quot;right&quot;:&quot;8px&quot;},&quot;tablet&quot;:{&quot;top&quot;:&quot;7px&quot;,&quot;left&quot;:&quot;7px&quot;,&quot;bottom&quot;:&quot;7px&quot;,&quot;right&quot;:&quot;7px&quot;},&quot;mobile&quot;:{&quot;top&quot;:&quot;5px&quot;,&quot;left&quot;:&quot;5px&quot;,&quot;bottom&quot;:&quot;5px&quot;,&quot;right&quot;:&quot;5px&quot;}},&quot;outputBorder&quot;:{&quot;color&quot;:&quot;#000&quot;,&quot;style&quot;:&quot;solid&quot;,&quot;width&quot;:&quot;0px&quot;,&quot;radius&quot;:&quot;7px&quot;}}'\n>\n    <pre class=\"bchCodeData\" style=\"display:none;\">&quot;$content = [];\\nif ($pdf_base64 !== null) {\\n  $content[] = [\\n    &#039;type&#039;   =&gt; &#039;document&#039;,\\n    &#039;source&#039; =&gt; [\\n      &#039;type&#039;       =&gt; &#039;base64&#039;,\\n      &#039;media_type&#039; =&gt; &#039;application\\\/pdf&#039;,\\n      &#039;data&#039;       =&gt; $pdf_base64,\\n    ],\\n  ];\\n}\\n$content[] = [&#039;type&#039; =&gt; &#039;text&#039;, &#039;text&#039; =&gt; $compito];&quot;<\/pre>\n<\/div>\n\n\n\n<p class=\"wp-block-paragraph\">Il modello legge il PDF direttamente \u2014 testo, struttura e anche le pagine scansionate, perch\u00e9 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.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Per i DOCX invece un piccolo trucco che vale il prezzo del biglietto: un file Word <strong>\u00e8<\/strong> un archivio ZIP. Si apre con <code>ZipArchive<\/code>, si estrae <code>word\/document.xml<\/code>, si spogliano i tag e si passa il testo nel prompt:<\/p>\n\n\n<div\n    class=\"align wp-block-bch-code-highlight\"    id=\"bhcCodeHighlight-3\"\n    data-attributes='{&quot;cId&quot;:&quot;78c69937-d&quot;,&quot;language&quot;:&quot;php&quot;,&quot;headerPart&quot;:true,&quot;align&quot;:&quot;&quot;,&quot;lineNumbers&quot;:true,&quot;copyBtnOption&quot;:&quot;Text&quot;,&quot;theme&quot;:&quot;default&quot;,&quot;headingTitleTypo&quot;:{&quot;fontWeight&quot;:400,&quot;fontSize&quot;:{&quot;desktop&quot;:18,&quot;tablet&quot;:13,&quot;mobile&quot;:11},&quot;lineHeight&quot;:1.5,&quot;fontFamily&quot;:&quot;Aleo&quot;,&quot;textTransform&quot;:&quot;uppercase&quot;},&quot;codeTypo&quot;:{&quot;desktop&quot;:18,&quot;tablet&quot;:15,&quot;mobile&quot;:14},&quot;clipBoard&quot;:true,&quot;wordWrap&quot;:true,&quot;width&quot;:{&quot;desktop&quot;:&quot;100%&quot;,&quot;tablet&quot;:&quot;100%&quot;,&quot;mobile&quot;:&quot;100%&quot;},&quot;height&quot;:{&quot;desktop&quot;:&quot;0px&quot;,&quot;tablet&quot;:&quot;0px&quot;,&quot;mobile&quot;:&quot;0px&quot;},&quot;padding&quot;:{&quot;top&quot;:&quot;0px&quot;,&quot;right&quot;:&quot;0px&quot;,&quot;bottom&quot;:&quot;0px&quot;,&quot;left&quot;:&quot;0px&quot;},&quot;background&quot;:{&quot;color&quot;:&quot;#d3cfcf42&quot;},&quot;headerBg&quot;:{&quot;color&quot;:&quot;#2f446e&quot;},&quot;headerTextColor&quot;:{&quot;color&quot;:&quot;#ffff&quot;},&quot;headerThemeOpt&quot;:&quot;one&quot;,&quot;layout&quot;:{&quot;align&quot;:&quot;left&quot;},&quot;border&quot;:{&quot;color&quot;:&quot;#000&quot;,&quot;style&quot;:&quot;solid&quot;,&quot;width&quot;:&quot;0px&quot;},&quot;shadow&quot;:[{&quot;hOffset&quot;:&quot;0px&quot;,&quot;vOffset&quot;:&quot;0px&quot;,&quot;blur&quot;:&quot;0px&quot;,&quot;spread&quot;:&quot;0px&quot;,&quot;color&quot;:&quot;rgba(0, 0, 0, 0.16)&quot;,&quot;isInset&quot;:false}],&quot;alignment&quot;:&quot;center&quot;,&quot;copyTypo&quot;:{&quot;fontWeight&quot;:400,&quot;fontSize&quot;:{&quot;desktop&quot;:13,&quot;tablet&quot;:13,&quot;mobile&quot;:11},&quot;lineHeight&quot;:1.5,&quot;fontFamily&quot;:&quot;Aleo&quot;},&quot;clipBoardColors&quot;:{&quot;color&quot;:&quot;#fff&quot;,&quot;bg&quot;:&quot;#00000024&quot;},&quot;copyIconSize&quot;:{&quot;desktop&quot;:24,&quot;tablet&quot;:20,&quot;mobile&quot;:18},&quot;copyBtnPadding&quot;:{&quot;desktop&quot;:{&quot;top&quot;:&quot;2px&quot;,&quot;left&quot;:&quot;5px&quot;,&quot;bottom&quot;:&quot;2px&quot;,&quot;right&quot;:&quot;5px&quot;},&quot;tablet&quot;:{&quot;top&quot;:&quot;2px&quot;,&quot;left&quot;:&quot;5px&quot;,&quot;bottom&quot;:&quot;2px&quot;,&quot;right&quot;:&quot;5px&quot;},&quot;mobile&quot;:{&quot;top&quot;:&quot;2px&quot;,&quot;left&quot;:&quot;5px&quot;,&quot;bottom&quot;:&quot;2px&quot;,&quot;right&quot;:&quot;5px&quot;}},&quot;headerPadding&quot;:{&quot;desktop&quot;:{&quot;top&quot;:&quot;14px&quot;,&quot;left&quot;:&quot;14px&quot;,&quot;bottom&quot;:&quot;14px&quot;,&quot;right&quot;:&quot;14px&quot;},&quot;tablet&quot;:{&quot;top&quot;:&quot;12px&quot;,&quot;left&quot;:&quot;12px&quot;,&quot;bottom&quot;:&quot;12px&quot;,&quot;right&quot;:&quot;12px&quot;},&quot;mobile&quot;:{&quot;top&quot;:&quot;10px&quot;,&quot;left&quot;:&quot;10px&quot;,&quot;bottom&quot;:&quot;10px&quot;,&quot;right&quot;:&quot;10px&quot;}},&quot;copyBtnPosition&quot;:{&quot;desktop&quot;:{&quot;top&quot;:&quot;5px&quot;,&quot;left&quot;:&quot;&quot;,&quot;bottom&quot;:&quot;&quot;,&quot;right&quot;:&quot;15px&quot;},&quot;tablet&quot;:{&quot;top&quot;:&quot;5px&quot;,&quot;left&quot;:&quot;&quot;,&quot;bottom&quot;:&quot;&quot;,&quot;right&quot;:&quot;15px&quot;},&quot;mobile&quot;:{&quot;top&quot;:&quot;5px&quot;,&quot;left&quot;:&quot;&quot;,&quot;bottom&quot;:&quot;&quot;,&quot;right&quot;:&quot;15px&quot;}},&quot;codeBg&quot;:{&quot;color&quot;:&quot;&quot;},&quot;outputOption&quot;:false,&quot;outputText&quot;:&quot;Hello Word !&quot;,&quot;outputColors&quot;:{&quot;color&quot;:&quot;#fff&quot;,&quot;bg&quot;:&quot;#2F446E&quot;},&quot;outputTypo&quot;:{&quot;fontWeight&quot;:400,&quot;fontSize&quot;:{&quot;desktop&quot;:21,&quot;tablet&quot;:17,&quot;mobile&quot;:13},&quot;lineHeight&quot;:1.5,&quot;fontFamily&quot;:&quot;Aleo&quot;},&quot;outputPadding&quot;:{&quot;desktop&quot;:{&quot;top&quot;:&quot;8px&quot;,&quot;left&quot;:&quot;8px&quot;,&quot;bottom&quot;:&quot;8px&quot;,&quot;right&quot;:&quot;8px&quot;},&quot;tablet&quot;:{&quot;top&quot;:&quot;7px&quot;,&quot;left&quot;:&quot;7px&quot;,&quot;bottom&quot;:&quot;7px&quot;,&quot;right&quot;:&quot;7px&quot;},&quot;mobile&quot;:{&quot;top&quot;:&quot;5px&quot;,&quot;left&quot;:&quot;5px&quot;,&quot;bottom&quot;:&quot;5px&quot;,&quot;right&quot;:&quot;5px&quot;}},&quot;outputBorder&quot;:{&quot;color&quot;:&quot;#000&quot;,&quot;style&quot;:&quot;solid&quot;,&quot;width&quot;:&quot;0px&quot;,&quot;radius&quot;:&quot;7px&quot;}}'\n>\n    <pre class=\"bchCodeData\" style=\"display:none;\">&quot;$zip = new ZipArchive();\\n$zip-&gt;open($file[&#039;tmp_name&#039;]);\\n$xml = $zip-&gt;getFromName(&#039;word\\\/document.xml&#039;);\\n\\\/\\\/ strip dei tag XML \\u2192 testo pulito nel prompt&quot;<\/pre>\n<\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Output strutturato: lo schema nel prompt<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">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 \u2014 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.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">E siccome i modelli a volte incorniciano il JSON nei backtick nonostante le istruzioni, il parsing \u00e8 difensivo:<\/p>\n\n\n<div\n    class=\"align wp-block-bch-code-highlight\"    id=\"bhcCodeHighlight-4\"\n    data-attributes='{&quot;cId&quot;:&quot;d1d6c6f4-0&quot;,&quot;language&quot;:&quot;php&quot;,&quot;headerPart&quot;:true,&quot;align&quot;:&quot;&quot;,&quot;lineNumbers&quot;:true,&quot;copyBtnOption&quot;:&quot;Text&quot;,&quot;theme&quot;:&quot;default&quot;,&quot;headingTitleTypo&quot;:{&quot;fontWeight&quot;:400,&quot;fontSize&quot;:{&quot;desktop&quot;:18,&quot;tablet&quot;:13,&quot;mobile&quot;:11},&quot;lineHeight&quot;:1.5,&quot;fontFamily&quot;:&quot;Aleo&quot;,&quot;textTransform&quot;:&quot;uppercase&quot;},&quot;codeTypo&quot;:{&quot;desktop&quot;:18,&quot;tablet&quot;:15,&quot;mobile&quot;:14},&quot;clipBoard&quot;:true,&quot;wordWrap&quot;:true,&quot;width&quot;:{&quot;desktop&quot;:&quot;100%&quot;,&quot;tablet&quot;:&quot;100%&quot;,&quot;mobile&quot;:&quot;100%&quot;},&quot;height&quot;:{&quot;desktop&quot;:&quot;0px&quot;,&quot;tablet&quot;:&quot;0px&quot;,&quot;mobile&quot;:&quot;0px&quot;},&quot;padding&quot;:{&quot;top&quot;:&quot;0px&quot;,&quot;right&quot;:&quot;0px&quot;,&quot;bottom&quot;:&quot;0px&quot;,&quot;left&quot;:&quot;0px&quot;},&quot;background&quot;:{&quot;color&quot;:&quot;#d3cfcf42&quot;},&quot;headerBg&quot;:{&quot;color&quot;:&quot;#2f446e&quot;},&quot;headerTextColor&quot;:{&quot;color&quot;:&quot;#ffff&quot;},&quot;headerThemeOpt&quot;:&quot;one&quot;,&quot;layout&quot;:{&quot;align&quot;:&quot;left&quot;},&quot;border&quot;:{&quot;color&quot;:&quot;#000&quot;,&quot;style&quot;:&quot;solid&quot;,&quot;width&quot;:&quot;0px&quot;},&quot;shadow&quot;:[{&quot;hOffset&quot;:&quot;0px&quot;,&quot;vOffset&quot;:&quot;0px&quot;,&quot;blur&quot;:&quot;0px&quot;,&quot;spread&quot;:&quot;0px&quot;,&quot;color&quot;:&quot;rgba(0, 0, 0, 0.16)&quot;,&quot;isInset&quot;:false}],&quot;alignment&quot;:&quot;center&quot;,&quot;copyTypo&quot;:{&quot;fontWeight&quot;:400,&quot;fontSize&quot;:{&quot;desktop&quot;:13,&quot;tablet&quot;:13,&quot;mobile&quot;:11},&quot;lineHeight&quot;:1.5,&quot;fontFamily&quot;:&quot;Aleo&quot;},&quot;clipBoardColors&quot;:{&quot;color&quot;:&quot;#fff&quot;,&quot;bg&quot;:&quot;#00000024&quot;},&quot;copyIconSize&quot;:{&quot;desktop&quot;:24,&quot;tablet&quot;:20,&quot;mobile&quot;:18},&quot;copyBtnPadding&quot;:{&quot;desktop&quot;:{&quot;top&quot;:&quot;2px&quot;,&quot;left&quot;:&quot;5px&quot;,&quot;bottom&quot;:&quot;2px&quot;,&quot;right&quot;:&quot;5px&quot;},&quot;tablet&quot;:{&quot;top&quot;:&quot;2px&quot;,&quot;left&quot;:&quot;5px&quot;,&quot;bottom&quot;:&quot;2px&quot;,&quot;right&quot;:&quot;5px&quot;},&quot;mobile&quot;:{&quot;top&quot;:&quot;2px&quot;,&quot;left&quot;:&quot;5px&quot;,&quot;bottom&quot;:&quot;2px&quot;,&quot;right&quot;:&quot;5px&quot;}},&quot;headerPadding&quot;:{&quot;desktop&quot;:{&quot;top&quot;:&quot;14px&quot;,&quot;left&quot;:&quot;14px&quot;,&quot;bottom&quot;:&quot;14px&quot;,&quot;right&quot;:&quot;14px&quot;},&quot;tablet&quot;:{&quot;top&quot;:&quot;12px&quot;,&quot;left&quot;:&quot;12px&quot;,&quot;bottom&quot;:&quot;12px&quot;,&quot;right&quot;:&quot;12px&quot;},&quot;mobile&quot;:{&quot;top&quot;:&quot;10px&quot;,&quot;left&quot;:&quot;10px&quot;,&quot;bottom&quot;:&quot;10px&quot;,&quot;right&quot;:&quot;10px&quot;}},&quot;copyBtnPosition&quot;:{&quot;desktop&quot;:{&quot;top&quot;:&quot;5px&quot;,&quot;left&quot;:&quot;&quot;,&quot;bottom&quot;:&quot;&quot;,&quot;right&quot;:&quot;15px&quot;},&quot;tablet&quot;:{&quot;top&quot;:&quot;5px&quot;,&quot;left&quot;:&quot;&quot;,&quot;bottom&quot;:&quot;&quot;,&quot;right&quot;:&quot;15px&quot;},&quot;mobile&quot;:{&quot;top&quot;:&quot;5px&quot;,&quot;left&quot;:&quot;&quot;,&quot;bottom&quot;:&quot;&quot;,&quot;right&quot;:&quot;15px&quot;}},&quot;codeBg&quot;:{&quot;color&quot;:&quot;&quot;},&quot;outputOption&quot;:false,&quot;outputText&quot;:&quot;Hello Word !&quot;,&quot;outputColors&quot;:{&quot;color&quot;:&quot;#fff&quot;,&quot;bg&quot;:&quot;#2F446E&quot;},&quot;outputTypo&quot;:{&quot;fontWeight&quot;:400,&quot;fontSize&quot;:{&quot;desktop&quot;:21,&quot;tablet&quot;:17,&quot;mobile&quot;:13},&quot;lineHeight&quot;:1.5,&quot;fontFamily&quot;:&quot;Aleo&quot;},&quot;outputPadding&quot;:{&quot;desktop&quot;:{&quot;top&quot;:&quot;8px&quot;,&quot;left&quot;:&quot;8px&quot;,&quot;bottom&quot;:&quot;8px&quot;,&quot;right&quot;:&quot;8px&quot;},&quot;tablet&quot;:{&quot;top&quot;:&quot;7px&quot;,&quot;left&quot;:&quot;7px&quot;,&quot;bottom&quot;:&quot;7px&quot;,&quot;right&quot;:&quot;7px&quot;},&quot;mobile&quot;:{&quot;top&quot;:&quot;5px&quot;,&quot;left&quot;:&quot;5px&quot;,&quot;bottom&quot;:&quot;5px&quot;,&quot;right&quot;:&quot;5px&quot;}},&quot;outputBorder&quot;:{&quot;color&quot;:&quot;#000&quot;,&quot;style&quot;:&quot;solid&quot;,&quot;width&quot;:&quot;0px&quot;,&quot;radius&quot;:&quot;7px&quot;}}'\n>\n    <pre class=\"bchCodeData\" style=\"display:none;\">&quot;$testo = preg_replace(&#039;\\\/^```(?:json)?\\\\s*\\\/&#039;, &#039;&#039;, $testo);\\n$testo = preg_replace(&#039;\\\/\\\\s*```$\\\/&#039;, &#039;&#039;, $testo);\\n$dati  = json_decode($testo, true);&quot;<\/pre>\n<\/div>\n\n\n\n<p class=\"wp-block-paragraph\">Nota per chi parte oggi: l&#8217;API ora supporta gli <strong>structured outputs<\/strong> nativi (<code>output_config.format<\/code> con un JSON Schema), che garantiscono output valido senza questa ginnastica. \u00c8 il primo refactoring in lista.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Endpoint pubblico e rate limit<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Una generazione costa pochi centesimi (qualche migliaio di token in ingresso, di pi\u00f9 se c&#8217;\u00e8 un PDF corposo, e 2-4 mila in uscita). Il costo unitario non \u00e8 il problema. Il problema \u00e8 che <strong>un endpoint pubblico che spende soldi a ogni chiamata \u00e8 un invito a nozze<\/strong> per bot e burloni.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">La difesa \u00e8 a due livelli, entrambi file-based (niente database):<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Rate-limit per IP<\/strong> \u2014 un file JSON per IP in una cartella di log: 1 generazione ogni 30 secondi, massimo 10 al giorno.<\/li>\n\n\n\n<li><strong>Tetto globale giornaliero<\/strong> \u2014 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 &#8220;riprova domani&#8221; e il budget mensile ha un massimale matematico.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Il secondo livello: la spesa giornaliera \u00e8 <code>tetto \u00d7 costo massimo per generazione<\/code>. Fine dell&#8217;ansia.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">La chiave API<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">La chiave vive dentro la docroot, con tre difese indipendenti:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>sta in un <strong>file <code>.php<\/code><\/strong> che non produce alcun output se richiesto via URL;<\/li>\n\n\n\n<li>la sua cartella ha un <strong><code>.htaccess<\/code> con <code>Require all denied<\/code><\/strong>;<\/li>\n\n\n\n<li>il file ha una <strong>guardia interna<\/strong> che risponde 404 all&#8217;accesso diretto.<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">Affinch\u00e9 la chiave esca devono fallire tutte e tre insieme.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">La privacy \u00e8 un&#8217;architettura<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">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:<\/p>\n\n\n<div\n    class=\"align wp-block-bch-code-highlight\"    id=\"bhcCodeHighlight-5\"\n    data-attributes='{&quot;cId&quot;:&quot;dfd6410b-0&quot;,&quot;language&quot;:&quot;php&quot;,&quot;headerPart&quot;:true,&quot;align&quot;:&quot;&quot;,&quot;lineNumbers&quot;:true,&quot;copyBtnOption&quot;:&quot;Text&quot;,&quot;theme&quot;:&quot;default&quot;,&quot;headingTitleTypo&quot;:{&quot;fontWeight&quot;:400,&quot;fontSize&quot;:{&quot;desktop&quot;:18,&quot;tablet&quot;:13,&quot;mobile&quot;:11},&quot;lineHeight&quot;:1.5,&quot;fontFamily&quot;:&quot;Aleo&quot;,&quot;textTransform&quot;:&quot;uppercase&quot;},&quot;codeTypo&quot;:{&quot;desktop&quot;:18,&quot;tablet&quot;:15,&quot;mobile&quot;:14},&quot;clipBoard&quot;:true,&quot;wordWrap&quot;:true,&quot;width&quot;:{&quot;desktop&quot;:&quot;100%&quot;,&quot;tablet&quot;:&quot;100%&quot;,&quot;mobile&quot;:&quot;100%&quot;},&quot;height&quot;:{&quot;desktop&quot;:&quot;0px&quot;,&quot;tablet&quot;:&quot;0px&quot;,&quot;mobile&quot;:&quot;0px&quot;},&quot;padding&quot;:{&quot;top&quot;:&quot;0px&quot;,&quot;right&quot;:&quot;0px&quot;,&quot;bottom&quot;:&quot;0px&quot;,&quot;left&quot;:&quot;0px&quot;},&quot;background&quot;:{&quot;color&quot;:&quot;#d3cfcf42&quot;},&quot;headerBg&quot;:{&quot;color&quot;:&quot;#2f446e&quot;},&quot;headerTextColor&quot;:{&quot;color&quot;:&quot;#ffff&quot;},&quot;headerThemeOpt&quot;:&quot;one&quot;,&quot;layout&quot;:{&quot;align&quot;:&quot;left&quot;},&quot;border&quot;:{&quot;color&quot;:&quot;#000&quot;,&quot;style&quot;:&quot;solid&quot;,&quot;width&quot;:&quot;0px&quot;},&quot;shadow&quot;:[{&quot;hOffset&quot;:&quot;0px&quot;,&quot;vOffset&quot;:&quot;0px&quot;,&quot;blur&quot;:&quot;0px&quot;,&quot;spread&quot;:&quot;0px&quot;,&quot;color&quot;:&quot;rgba(0, 0, 0, 0.16)&quot;,&quot;isInset&quot;:false}],&quot;alignment&quot;:&quot;center&quot;,&quot;copyTypo&quot;:{&quot;fontWeight&quot;:400,&quot;fontSize&quot;:{&quot;desktop&quot;:13,&quot;tablet&quot;:13,&quot;mobile&quot;:11},&quot;lineHeight&quot;:1.5,&quot;fontFamily&quot;:&quot;Aleo&quot;},&quot;clipBoardColors&quot;:{&quot;color&quot;:&quot;#fff&quot;,&quot;bg&quot;:&quot;#00000024&quot;},&quot;copyIconSize&quot;:{&quot;desktop&quot;:24,&quot;tablet&quot;:20,&quot;mobile&quot;:18},&quot;copyBtnPadding&quot;:{&quot;desktop&quot;:{&quot;top&quot;:&quot;2px&quot;,&quot;left&quot;:&quot;5px&quot;,&quot;bottom&quot;:&quot;2px&quot;,&quot;right&quot;:&quot;5px&quot;},&quot;tablet&quot;:{&quot;top&quot;:&quot;2px&quot;,&quot;left&quot;:&quot;5px&quot;,&quot;bottom&quot;:&quot;2px&quot;,&quot;right&quot;:&quot;5px&quot;},&quot;mobile&quot;:{&quot;top&quot;:&quot;2px&quot;,&quot;left&quot;:&quot;5px&quot;,&quot;bottom&quot;:&quot;2px&quot;,&quot;right&quot;:&quot;5px&quot;}},&quot;headerPadding&quot;:{&quot;desktop&quot;:{&quot;top&quot;:&quot;14px&quot;,&quot;left&quot;:&quot;14px&quot;,&quot;bottom&quot;:&quot;14px&quot;,&quot;right&quot;:&quot;14px&quot;},&quot;tablet&quot;:{&quot;top&quot;:&quot;12px&quot;,&quot;left&quot;:&quot;12px&quot;,&quot;bottom&quot;:&quot;12px&quot;,&quot;right&quot;:&quot;12px&quot;},&quot;mobile&quot;:{&quot;top&quot;:&quot;10px&quot;,&quot;left&quot;:&quot;10px&quot;,&quot;bottom&quot;:&quot;10px&quot;,&quot;right&quot;:&quot;10px&quot;}},&quot;copyBtnPosition&quot;:{&quot;desktop&quot;:{&quot;top&quot;:&quot;5px&quot;,&quot;left&quot;:&quot;&quot;,&quot;bottom&quot;:&quot;&quot;,&quot;right&quot;:&quot;15px&quot;},&quot;tablet&quot;:{&quot;top&quot;:&quot;5px&quot;,&quot;left&quot;:&quot;&quot;,&quot;bottom&quot;:&quot;&quot;,&quot;right&quot;:&quot;15px&quot;},&quot;mobile&quot;:{&quot;top&quot;:&quot;5px&quot;,&quot;left&quot;:&quot;&quot;,&quot;bottom&quot;:&quot;&quot;,&quot;right&quot;:&quot;15px&quot;}},&quot;codeBg&quot;:{&quot;color&quot;:&quot;&quot;},&quot;outputOption&quot;:false,&quot;outputText&quot;:&quot;Hello Word !&quot;,&quot;outputColors&quot;:{&quot;color&quot;:&quot;#fff&quot;,&quot;bg&quot;:&quot;#2F446E&quot;},&quot;outputTypo&quot;:{&quot;fontWeight&quot;:400,&quot;fontSize&quot;:{&quot;desktop&quot;:21,&quot;tablet&quot;:17,&quot;mobile&quot;:13},&quot;lineHeight&quot;:1.5,&quot;fontFamily&quot;:&quot;Aleo&quot;},&quot;outputPadding&quot;:{&quot;desktop&quot;:{&quot;top&quot;:&quot;8px&quot;,&quot;left&quot;:&quot;8px&quot;,&quot;bottom&quot;:&quot;8px&quot;,&quot;right&quot;:&quot;8px&quot;},&quot;tablet&quot;:{&quot;top&quot;:&quot;7px&quot;,&quot;left&quot;:&quot;7px&quot;,&quot;bottom&quot;:&quot;7px&quot;,&quot;right&quot;:&quot;7px&quot;},&quot;mobile&quot;:{&quot;top&quot;:&quot;5px&quot;,&quot;left&quot;:&quot;5px&quot;,&quot;bottom&quot;:&quot;5px&quot;,&quot;right&quot;:&quot;5px&quot;}},&quot;outputBorder&quot;:{&quot;color&quot;:&quot;#000&quot;,&quot;style&quot;:&quot;solid&quot;,&quot;width&quot;:&quot;0px&quot;,&quot;radius&quot;:&quot;7px&quot;}}'\n>\n    <pre class=\"bchCodeData\" style=\"display:none;\">&quot;$riga = sprintf(\\&quot;%s | durata: %.2fs | token: %d (in %d \\\/ out %d)\\\\n\\&quot;,\\n  date(&#039;Y-m-d H:i:s&#039;), $durata, $tin + $tout, $tin, $tout);&quot;<\/pre>\n<\/div>\n\n\n\n<p class=\"wp-block-paragraph\">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.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Ultimo miglio: il Word si fa nel browser<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">L&#8217;export <code>.docx<\/code> \u00e8 generato lato client con la libreria <a href=\"https:\/\/docx.js.org\/\">docx<\/a>, caricata <strong>solo al click<\/strong> di chi esporta. Chi genera una verifica e la stampa dalla pagina non scarica un byte di JavaScript in pi\u00f9.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Cosa mi porto a casa<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>I vincoli aiutano.<\/strong> &#8220;Deve girare su hosting condiviso&#8221; ha eliminato intere categorie di complessit\u00e0: niente code, niente container, niente deploy pipeline. Il servizio \u00e8 un file PHP.<\/li>\n\n\n\n<li><strong>Delegare il parsing al modello<\/strong> (PDF come documento allegato) \u00e8 stata la singola decisione che ha semplificato di pi\u00f9 il codice.<\/li>\n\n\n\n<li><strong>Il tetto di spesa globale<\/strong> \u00e8 la prima cosa che rifarei identica in qualunque servizio pubblico basato su un LLM. Il rate-limit per IP da solo \u00e8 una porta chiusa con la finestra aperta.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Se vuoi vedere il risultato dal lato dei docenti, c&#8217;\u00e8 il <a href=\"\/case-study-verifiche\/\">case study<\/a> \u2014 con una sezione &#8220;dietro le quinte&#8221; che riassume queste scelte \u2014 e naturalmente <a href=\"https:\/\/www.carbonimario.com\/verifiche\/\">lo strumento stesso<\/a>, gratuito e senza registrazione.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Ne avresti costruito uno diverso? <a href=\"mailto:mario@carbonimario.com\">Scrivimi<\/a> \u2014 o passa da <a href=\"https:\/\/www.linkedin.com\/in\/mariocarbonicssdesigner\/\">LinkedIn<\/a>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Un docente che prepara una verifica strutturata \u2014 domande, soluzioni, punteggi, griglia di valutazione, magari in doppia fila per scoraggiare le copiature \u2014 ci mette comodamente un&#8217;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&#8217;\u00e8 fatto dentro. [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[7],"tags":[],"class_list":["post-17","post","type-post","status-publish","format-standard","hentry","category-coding"],"_links":{"self":[{"href":"https:\/\/www.carbonimario.com\/articoli\/wp-json\/wp\/v2\/posts\/17","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.carbonimario.com\/articoli\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.carbonimario.com\/articoli\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.carbonimario.com\/articoli\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.carbonimario.com\/articoli\/wp-json\/wp\/v2\/comments?post=17"}],"version-history":[{"count":22,"href":"https:\/\/www.carbonimario.com\/articoli\/wp-json\/wp\/v2\/posts\/17\/revisions"}],"predecessor-version":[{"id":66,"href":"https:\/\/www.carbonimario.com\/articoli\/wp-json\/wp\/v2\/posts\/17\/revisions\/66"}],"wp:attachment":[{"href":"https:\/\/www.carbonimario.com\/articoli\/wp-json\/wp\/v2\/media?parent=17"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.carbonimario.com\/articoli\/wp-json\/wp\/v2\/categories?post=17"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.carbonimario.com\/articoli\/wp-json\/wp\/v2\/tags?post=17"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}