LLM e JavaScript:
approcci pratici
La rivoluzione dei modelli generativi: un caso d'uso con Transformers.js
Negli ultimi anni i modelli generativi basati su reti neurali profonde hanno rivoluzionato il panorama tecnologico. Inizialmente sviluppati per fini accademici, sono diventati parte integrante di applicazioni quotidiane, dalla generazione automatica di contenuti alla traduzione multilingue, fino al supporto decisionale avanzato.
La rapidità con cui queste tecnologie vengono adottate e integrate nei sistemi esistenti è sorprendente. Per accelerare ulteriormente questo processo e rendere accessibili concetti complessi come l'utilizzo, il training e l'implementazione dei modelli linguistici (LLM), è fondamentale adottare approcci pratici che si adattino al nostro modo di progettare e sviluppare soluzioni tecnologiche.
In questo articolo affronteremo l'integrazione di modelli linguistici in architetture client e server, utilizzando la libreria Transformers.js di Xenova. Sfrutteremo funzionalità avanzate di intelligenza artificiale sia direttamente all’interno di una web app standalone - senza necessità di server esterni - sia in un server backend tramite endpoint API, basato sul nostro progetto open source @volcanicminds/backend.
Perché JavaScript?
Sebbene Python sia il linguaggio dominante nel mondo dell'AI, JavaScript offre alcune caratteristiche uniche che lo rendono una scelta vincente in determinati contesti:
- esecuzione diretta nel browser: consente di eseguire modelli AI lato client, eliminando la necessità di server esterni e riducendo la latenza nelle risposte;
- flessibilità per il backend: grazie a tecnologie come Node.js, JavaScript permette di creare API efficienti per gestire modelli più complessi su server;
- integrazione fluida: si adatta facilmente a infrastrutture esistenti, semplificando l'inserimento di modelli generativi nelle web app;
- rapidità nello sviluppo: favorisce la creazione di prototipi grazie alla compatibilità con browser moderni e librerie come Transformers.js.
Questa versatilità rende JavaScript una scelta strategica per chi desidera integrare modelli generativi in applicazioni web moderne, offrendo sia la possibilità di eseguire l’AI direttamente nel browser, sia di sviluppare soluzioni scalabili su infrastrutture server.
La libreria Transformers.js
Transformers.js è un porting JavaScript della famosa libreria Transformers di HuggingFace. Questa versione consente di:
- eseguire inferenze direttamente nel browser senza server esterni;
- integrare modelli pre-addestrati in applicazioni esistenti;
- sfruttare una community attiva che fornisce modelli, strumenti e supporto per lo sviluppo.
La combinazione di queste caratteristiche rende Transformers.js uno strumento potente per creare applicazioni AI innovative in contesti client-side o server-side.
Caso pratico: web app standalone
La web app dimostrativa utilizza Transformers.js per eseguire inferenze direttamente nel browser. Una volta installata ed eseguita, l'applicazione è accessibile all'indirizzo http://localhost:5173. Il codice principale che dialoga con il modello si trova in src/transformers/worker.js:
import { pipeline, env } from '@xenova/transformers'
env.allowLocalModels = false
/**
* This class uses the Singleton pattern to ensure that only one instance of the pipeline is loaded.
*/
class SamplePipeline {
static task = 'text-generation'
static model = 'Xenova/gpt2'
static instance = null
static async getInstance(progress_callback = null) {
if (this.instance === null) {
this.instance = pipeline(this.task, this.model, { progress_callback })
}
return this.instance
}
}
// Listen for messages from the main thread
self.addEventListener('message', async (event) => {
const { model, task, text, ...rest } = event.data
if (SamplePipeline.model !== model || SamplePipeline.task !== task) {
// Invalidate model if different
SamplePipeline.model = model || SamplePipeline.model
SamplePipeline.task = task || SamplePipeline.task
if (SamplePipeline.instance !== null) {
;(await SamplePipeline.getInstance()).dispose()
SamplePipeline.instance = null
}
}
// Retrieve the code-completion pipeline. When called for the first time,
// this will load the pipeline and save it for future use.
const generator = await SamplePipeline.getInstance((x) => {
// We also add a progress callback to the pipeline so that we can
// track model loading.
self.postMessage(x)
})
// Actually perform the code-completion
let output = await generator(text, {
...rest,
// Allows for partial output
callback_function: (x) => {
self.postMessage({
status: 'update',
output: generator.tokenizer.decode(x[0].output_token_ids, { skip_special_tokens: true })
})
}
})
// Send the output back to the main thread
self.postMessage({
status: 'complete',
output: output
})
})Flusso di lavoro
- l'utente invia un messaggio attraverso l'interfaccia della web app;
- il messaggio viene passato a un web worker;
- il web worker elabora la richiesta utilizzando un modello LLM predefinito (in questo caso, Xenova/distilgpt2);
- il modello genera una risposta che viene visualizzata nell'interfaccia.
Preview di risposta LLM
Questo approccio è ideale per applicazioni che richiedono risposte rapide senza dipendere da server esterni. Tuttavia, l'utilizzo di un modello leggero come distilgpt2 implica alcune limitazioni in termini di qualità dell'output.
Caso pratico: backend API
Il progetto backend utilizza un'architettura basata su Fastify e TypeORM per fornire un endpoint API che sfrutta Transformers.js. Il codice relativo alla generazione del testo si trova in src/api/pipeline/controller/TextGenerationPipeline.ts:
export class TextGenerationPipeline {
static task = 'text-generation'
static model = 'Xenova/distilgpt2'
static instance = null
static async getInstance(progress_callback = null) {
if (this.instance === null) {
const { pipeline } = await Function('return import("@xenova/transformers")')()
this.instance = pipeline(this.task, this.model, { progress_callback })
}
return this.instance
}
static async execute({ text }) {
const executor: any = await this.getInstance()
let answer = await executor(text, {
temperature: 2,
max_new_tokens: 50,
repetition_penalty: 5,
no_repeat_ngram_size: 2,
num_beams: 2,
num_return_sequences: 1
})
answer = answer?.length > 0 ? answer[0] : answer
return answer?.generated_text || answer
}
}Flusso di lavoro
- una richiesta viene inviata all'endpoint API con un input di testo;
- l'API elabora l'input utilizzando un modello predefinito (anche in questo caso, Xenova/distilgpt2 per semplicità);
- la risposta generata viene restituita al client.
Risposta Postman
Questo approccio è ideale per applicazioni centralizzate, dove il carico di elaborazione può essere gestito su server più potenti. Ad esempio, modelli più complessi e performanti possono essere integrati per migliorare la qualità delle risposte.
Confronto tra web app e backend
I due approcci offrono vantaggi complementari:
- web app standalone: eliminando la necessità di un server, consente una maggiore autonomia del client. Tuttavia, le risorse limitate del browser possono rappresentare un ostacolo per modelli complessi;
- backend API: centralizza l'elaborazione, consentendo di utilizzare modelli più avanzati. È particolarmente adatto per scenari in cui le risorse hardware non sono un vincolo.
Una soluzione ibrida, in cui i task vengono distribuiti tra client e server, potrebbe rappresentare un equilibrio ottimale, ma richiede uno studio approfondito per gestire la comunicazione e il carico di lavoro.
Conclusione
I progetti dimostrativi illustrano come integrare funzionalità generative utilizzando Transformers.js sia in un contesto completamente client-side sia in un'architettura server-side. Anche se non pronti per la produzione, questi esempi rappresentano un punto di partenza per sviluppatori interessati a esplorare l'uso di modelli generativi in applicazioni reali.
Per ulteriori informazioni si possono consultare le seguenti risorse:
Con la continua evoluzione dei modelli generativi e delle librerie che li supportano, le possibilità di innovazione sono pressoché infinite. Che si tratti di una web app leggera o di un backend robusto, l'importante è iniziare a sperimentare e contribuire all'ecosistema in crescita dell'intelligenza artificiale.
Tag: Tecnologia, AI
Data di pubblicazione: 15 gennaio 2025
Ultima revisione: 16 gennaio 2025

