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.
Sebbene Python sia il linguaggio dominante nel mondo dell'AI, JavaScript offre alcune caratteristiche uniche che lo rendono una scelta vincente in determinati contesti:
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.
Transformers.js è un porting JavaScript della famosa libreria Transformers di HuggingFace. Questa versione consente di:
La combinazione di queste caratteristiche rende Transformers.js uno strumento potente per creare applicazioni AI innovative in contesti client-side o server-side.
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
})
})
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.
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
}
}
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.
I due approcci offrono vantaggi complementari:
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.
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.
Data di pubblicazione: 15 gennaio 2025
Ultima revisione: 16 gennaio 2025