Data di pubblicazione: 30 aprile 2026
Grazie all'AI integrata, il tuo sito web o la tua applicazione web può eseguire attività basate sull'AI senza dover eseguire il deployment, gestire o ospitare autonomamente i modelli. Potresti avere difficoltà a passare da una demo a una funzionalità pronta per la produzione. Questo documento illustra le considerazioni tecniche e relative all'esperienza utente per aiutarti a evitare gli errori più comuni.
Preparare il modello in anticipo
Si applica a: tutte le API, ad esempio Summarizer, Translator e Writer.
Esegui: inizializza la sessione non appena riconosci l'intento dell'utente. Poiché per inizializzare una sessione è necessaria l'attivazione dell'utente, puoi utilizzare qualsiasi interazione, ad esempio un clic in qualsiasi punto della pagina che offre una funzionalità basata sull'AI. In questo modo, il modello e il runtime vengono preparati mentre l'utente interagisce con l'interfaccia utente. Quando è pertinente, avvia l'attività AI successiva più probabile non appena inizi il rendering del risultato.
Non: attendere che l'utente faccia clic su "Genera" per inizializzare la sessione. Ciò comporta un ritardo di avvio a freddo di diversi secondi, perché il modello deve prima essere caricato in memoria e preparare la pipeline di esecuzione.
Impostare i prompt iniziali durante la creazione
Si applica a: API Prompt.
Esegui: fornisci istruzioni di sistema durante l'inizializzazione della sessione per migliorare la velocità del primo prompt.
Non: iniziare con una sessione vuota e inviare istruzioni di sistema come parte di
la prima chiamata prompt(). Ciò aumenta la latenza perché costringe il modello a elaborare queste istruzioni all'ultimo momento.
// ✅ DO: Create the session as early as possible (tip on warming up the model early) and use initialPrompts for system instructions in the create call
const session = await LanguageModel.create({
initialPrompts: [
{ role: 'system', content: 'You are a helpful assistant specialized in code reviews.' }
]
});
// A few moments later, when the user triggers the AI feature
const review = await session.prompt(`Review the following code:\n\n${code}`);
// ❌ DON'T: Send instructions using prompt() after creation
// const slowerSession = await LanguageModel.create();
// await slowerSession.prompt(`You are a helpful assistant specialized in code reviews.\n\nReview the following code:\n\n${code}`); // Higher latency
Clonare le sessioni per le attività ripetitive
Si applica a: API Prompt.
Per l'API Prompt, ogni sessione tiene traccia del contesto della conversazione, tenendo conto di tutte le interazioni precedenti. Poiché un clone eredita tutto dalla sessione principale, inclusi i prompt iniziali e tutta la cronologia delle interazioni fino al punto di clonazione, struttura l'utilizzo in modo da ereditare solo ciò di cui hai bisogno.
Esegui:
- Crea una sessione di base: per gestire in modo efficiente le attività non correlate, crea una sessione di base che contenga solo le istruzioni di sistema e nessun contesto conversazionale precedente.
- Clona la baseline: utilizza
clone()nella sessione di base per le nuove attività per evitare il sovraccarico di dover analizzare nuovamente le istruzioni di sistema complesse. In questo modo, puoi creare conversazioni parallele o reimpostare un'attività sulla sua baseline.
Non:
- Non riutilizzare la stessa sessione per attività non correlate ed evita di clonare qualsiasi sessione che contenga già una cronologia delle interazioni non necessaria. Entrambi i pattern possono causare l'interferenza del contesto precedente non correlato con l'attività corrente.
- Non chiamare ripetutamente
create()con istruzioni di sistema identiche e complesse. Utilizza invece il pattern di clonazione per ottimizzare le prestazioni.
// ✅ DO: Create a baseline session and clone it for each new task
const baseSession = await LanguageModel.create({
initialPrompts: [{
role: 'system',
content: 'You are a technical editor...',
}],
});
// Clone the base session once for the first task
const task1 = await baseSession.clone();
const response1 = await task1.prompt("Review this first draft...");
// ... Repeat the cloning pattern for subsequent independent tasks
// Each task starts fresh from the baseline system instructions
// ❌ DON'T:
// Bad performance pattern: repeated create() calls for identical tasks.
// This forces the model to re-parse instructions every time, increasing latency.
// const sessionA = await LanguageModel.create({ initialPrompts: [...] });
// await sessionA.prompt("Task 1...");
// const sessionB = await LanguageModel.create({ initialPrompts: [...] });
// await sessionB.prompt("Task 2...");
// Bad quality pattern: reusing the same session for unrelated tasks.
// const session = await LanguageModel.create();
// await session.prompt("Analyze this financial report...");
// Unrelated task in the same session:
// await session.prompt("Now write a children's story...");
Eliminare le sessioni inutilizzate
Si applica a: tutte le API.
Esegui: chiama esplicitamente destroy() nelle
sessioni che non ti servono più per liberare memoria quando una funzionalità
non è più in uso. Se utilizzi un pattern di clonazione, mantieni la sessione di base ed elimina i cloni che non ti servono più.
Non: mantenere attive più sessioni di grandi dimensioni. Ogni sessione consuma memoria, il che crea un utilizzo non necessario delle risorse e potrebbe diventare un problema. Le sessioni verranno pulite naturalmente dal Garbage Collector, ma chiamare destroy() libera la memoria più rapidamente.
// ✅ DO: Use the clone and destroy it immediately after
const clone = await baseSession.clone();
const response = await clone.prompt("Quick task...");
// Free memory right away: destry the clone, keep the baseSession
clone.destroy();
Eseguire il rendering delle risposte di streaming in modo sicuro ed efficiente
Si applica a: tutte le API con supporto per lo streaming (Prompt, Summarizer, Writer, Rewriter e Translator).
Esegui: tratta tutti gli output LLM come contenuti non attendibili. Sanitizza l'output combinato completo, non solo i blocchi, perché il codice dannoso potrebbe essere suddiviso tra gli aggiornamenti. Prima del rendering, utilizza l'API Sanitizer dove supportata. Per evitare una riduzione delle prestazioni, utilizza un parser Markdown di streaming come streaming-markdown.
Non: impostare direttamente innerHTML su ogni aggiornamento del blocco. Questa operazione è lenta, soprattutto con formattazioni complesse come l'evidenziazione della sintassi, ed è vulnerabile all'iniezione.
import * as smd from "streaming-markdown";
// Set up virtual buffer and Sanitizer API
const sanitizer = new Sanitizer({
allowElements: ['figure', 'figcaption', 'p', 'br', 'strong', 'em', 'img', 'a'],
allowAttributes: {
'loading': ['img'], 'decoding': ['img'], 'src': ['img'], 'href': ['a']
}
});
// Create an off-screen fragment so the parser doesn't cause flicker
// or trigger XSS in the live DOM during the building process.
const buffer = new DocumentFragment();
const parser = smd.parser_new(buffer);
// Use sanitizer as a gatekeeper / cleaner function so we can combine it with the streaming Markdown parser
function syncSanitized(target, sourceFragment) {
// .sanitize() returns a fresh, clean DocumentFragment
const cleanFragment = sanitizer.sanitize(sourceFragment);
// replaceChildren is the modern high-performance way to swap DOM content
target.replaceChildren(cleanFragment);
}
// Streaming Logic
// `chunks` keeps track of the raw string (useful for logs/debug)
chunks += chunk;
// Let the parser build the DOM incrementally in the buffer.
// This is high-performance because the buffer is not live
smd.parser_write(parser, chunk);
// Use the Sanitizer API to port the content safely to the container.
syncSanitized(container, buffer);
Ottimizzare l'input per la velocità
Si applica a: tutte le API.
Esegui: passa al modello solo ciò che è strettamente necessario. Rimuovi tutto ciò che non è pertinente all'attività in questione. Per i set di dati di grandi dimensioni, fornisci una breve panoramica e una piccola selezione di elementi pertinenti.
Non: inviare testo non elaborato, metadati non necessari, tag HTML o elenchi di grandi dimensioni non filtrati alle API. La latenza aumenta in modo significativo con le dimensioni dell'input, il che può far sembrare che la funzionalità AI non funzioni su molti dispositivi.
// ✅ DO: Send only relevant text
const cleanText = document.querySelector('#article').innerText;
const summary = await Summarizer.summarize(cleanText);
// ❌ DON'T: Send the entire DOM structure
// const dirtyText = document.querySelector('#article').innerHTML;
Utilizzare l'output strutturato per risultati prevedibili
Si applica a: API Prompt.
Esegui: quando devi fare in modo che il modello restituisca i dati in un formato specifico, utilizza l'output
strutturato
fornendo un campo responseConstraint per fornire uno schema JSON. In questo modo, l'output è prevedibile e non è necessario eseguire un'elaborazione post-elaborazione complessa o un'analisi manuale.
Non: fare affidamento solo sulle istruzioni in linguaggio naturale (ad esempio "output solo JSON") da solo. I modelli potrebbero includere riempitivi conversazionali che interrompono il parser.
// ✅ DO: Use a JSON Schema for predictable results
const schema = {
type: "object",
properties: {
isTopicCats: { type: "boolean" }
}
};
const result = await session.prompt(`Is this post about cats?\n\n${post}`, {
responseConstraint: schema,
});
console.log(JSON.parse(result).isTopicCats);
Disaccoppiare la generazione dai vincoli di lunghezza
Si applica a: API Prompt, poiché è l'unica API che supporta schemi di output strutturati.
Esegui: lascia che il modello generi la risposta in modo naturale, quindi utilizza la logica lato client per troncare il testo in modo che si adatti all'interfaccia utente.
Non: applicare limiti di caratteri rigorosi come maxLength: 125 utilizzando
schemi di output strutturati. Quando la risposta di un modello è più lunga del limite impostato, il modello potrebbe passare a token ad alta densità come lingue straniere o emoji per comprimere il significato, generando un output senza senso.
/* DO: Handle overflow using CSS */
.result {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis; /* Displays '…' */
}
// ❌ DON'T: Force length in the prompt
const result = await session.prompt("Write a bio in exactly 50 characters.");
Gestire la pazienza dell'utente
Si applica a: tutte le API.
Esegui: utilizza animazioni e tecniche di UI per gestire la pazienza dell'utente. L'approccio ottimale dipende dal caso d'uso e dalla lunghezza prevista dell'output dell'API. Ecco alcune idee:
- Streaming per contenuti lunghi: per riepiloghi o chat, lo streaming crea un effetto macchina da scrivere per token per impostazione predefinita. Questo può sembrare naturale e fornire un feedback immediato.
- Non streaming per attività brevi (o attività asincrone lunghe): per output brevi, ad esempio testo alternativo, il non streaming può creare un'interfaccia utente più raffinata. Fornisce anche il tempo per preparare in modo speculativo l'attività AI successiva durante il rendering di quella corrente. Questo approccio funziona anche per attività asincrone o in background più lunghe. Se l'utente non è bloccato sull'output per continuare il suo percorso, non è urgente produrre l'output man mano che si verifica. Indica che il processo è in corso nell'interfaccia utente.
- Transizioni visive per gli aggiornamenti: quando traduci o riscrivi il testo, utilizza animazioni, ad esempio la trasformazione delle parole.
Non: aggiornare l'interfaccia utente senza indicazioni visive.
Allinearsi al modello mentale dell'utente di tempo e lavoro
Si applica a: tutte le API.
Esegui: valuta la possibilità di introdurre un ritardo artificiale di uno o due secondi se una risposta è quasi istantanea. Paradossalmente, gli utenti potrebbero trovare i risultati più attendibili quando percepiscono un processo di generazione che si allinea alla difficoltà percepita dell'attività. Utilizza le animazioni per indicare che si è verificato un processo di AI.
Non: sorprendere gli utenti con sostituzioni istantanee dell'interfaccia utente.
Consentire agli utenti di navigare rapidamente e annullare le modifiche AI
Si applica a: tutte le API.
Esegui: dota l'interfaccia utente di uno stepper o di una cronologia di navigazione che consenta agli utenti di esplorare con sicurezza risultati diversi e di annullare rapidamente le modifiche AI. In questo modo, le diverse versioni rimangono facilmente disponibili.
Non: sovrascrivere la bozza precedente dell'utente o un risultato AI che potrebbe aver apprezzato senza un modo per tornare indietro, ripristinare o confrontare le versioni.
Consentire il controllo e le sostituzioni da parte dell'utente
Si applica a: tutte le API.
Esegui: lascia sempre all'utente l'ultima parola. Fornisci un modo per sostituire manualmente i suggerimenti. Le API potrebbero produrre risultati errati.
Non: forzare un risultato generato dall'AI come unica opzione.
Memorizzare nella cache i risultati per le attività ripetute
Si applica a: tutte le API.
Esegui: implementa una cache dei risultati locale (ad esempio utilizzando sessionStorage o IndexedDB) per input o query ripetuti. Normalizza l'input rimuovendo gli spazi vuoti e convertendo in minuscolo per aumentare i successi della cache. Per input pesanti, ad esempio immagini, genera un hash da utilizzare come chiave cache. Imposta una durata (TTL) conservativa per la cache (o pubblica i risultati memorizzati nella cache durante l'aggiornamento in background). Consenti all'utente di attivare un'inferenza nuova se il risultato non è soddisfacente.
Non: eseguire di nuovo la stessa inferenza per una query di ricerca ripetuta o un input di dati identico , ad esempio quando un utente naviga avanti e indietro tra i risultati di ricerca. Sebbene l'inferenza sul dispositivo sia senza costi in termini di costi cloud, è costosa in termini di tempo dell'utente e durata della batteria.
// ✅ DO: Check a local cache before running inference
async function getAiResponse(userInput, forceRefresh = false) {
// Normalize the query to increase cache hits
const query = userInput.trim().toLowerCase();
const cacheKey = `ai_results_${query}`;
const TTL_MS = 3600000; // 1 hour conservative TTL
if (!forceRefresh) {
const itemStr = localStorage.getItem(cacheKey);
if (itemStr) {
const item = JSON.parse(itemStr);
const now = Date.now();
// Check if the item has expired
if (now < item.expiry) {
// Lightweight safety check before rendering
if (isValid(item.value)) return item.value;
} else {
// Delete the stale entry if the TTL has passed
localStorage.removeItem(cacheKey);
}
}
}
// Fallback: Run inference if no valid cache exists
const session = await LanguageModel.create();
const response = await session.prompt(userInput);
// Store the result for future use (with an expiration)
const cacheData = {
value: response,
expiry: Date.now() + TTL_MS
};
localStorage.setItem(cacheKey, JSON.stringify(cacheData));
return response;
}