
Tutorial: Creare una chatbot di generazione di testo con OpenAI GPT API
In questo articolo esploreremo come utilizzare le API di OpenAI per creare una chat di generazione contenuti con Angular 15. OpenAI è un’organizzazione leader nello sviluppo d’intelligenza artificiale e ha reso disponibili diverse API per una vasta gamma di applicazioni, tra cui la generazione di testo. Combinando le API di OpenAI con il framework Angular 15, creeremo un chatbot in grado di generare risposte in tempo reale in base alle domande degli utenti. Il risultato finale sarà un’interfaccia utente intuitiva e potente che dimostra come l’IA può essere integrata con successo in molteplici progetti.
OpenAI API
Per prima cosa registriamoci come sviluppatori qui: https://openai.com/api/
Accediamo al nostro nuovo account e selezioniamo la voce API Keys. Ora clicchiamo su “Create new secret key” e salviamo la chiave generata da qualche parte, in modo da poterla utilizzare nelle nostre chiamate all’API future.
Ricordiamoci di dare un’occhiata alla documentazione ufficiale e in particolar modo a questa sezione: https://platform.openai.com/docs/api-reference/making-requests e https://platform.openai.com/docs/guides/chat/chat-completions-beta che ci mostra come effettuare le chiamate che ci interessano usando il modello ‘gpt-3.5-turbo‘.
Forse ti può interessare
Gpt-3.5-turbo è un modello di apprendimento sviluppato da OpenAI che può aiutare a generare testi in modo autonomo. È attualmente il modello di linguaggio più avanzato di OpenAI. Gpt-3.5-turbo stato addestrato su molti diversi tipi di testo, come libri, giornali e pagine web, quindi può scrivere su molte cose diverse con grande precisione e fluidità. È uno dei migliori strumenti del suo genere e può essere usato per creare assistenti virtuali o chatbot che scrivono risposte per le persone.
Setup Angular
Per iniziare a lavorare con Angular 15, è necessario installare prima il framework sul proprio computer. Ci sono due modi principali per farlo: utilizzare il gestore di pacchetti npm o scaricare e installare manualmente i file dal sito web ufficiale di Angular.
Per installare Angular 15 tramite npm, è necessario avere Node.js e npm già installati sul proprio computer. Aprire il prompt dei comandi e digitare il seguente comando:
npm install -g @angular/cli@15
Questo comando installerà l’ultima versione stabile di Angular 15 tramite npm.
Se si preferisce scaricare e installare manualmente Angular 15, è possibile farlo dal sito web ufficiale. Scaricare il pacchetto ZIP dal sito e decomprimerlo in una cartella sul proprio computer. Aprire il prompt dei comandi, navigare alla cartella dove è stato decomprimato il pacchetto e digitare il seguente comando:
npm install
Questo comando installerà tutti i pacchetti necessari per Angular 15.
È ora possibile creare il progetto per la chat di generazione contenuti. Apriamo il prompt dei comandi e digitiamo il seguente comando:
ng new chat-openai
Questo comando creerà un nuovo progetto Angular chiamato “chat-openai”. Navighiamo all’interno della cartella del progetto digitando il seguente comando:
cd chat-openai
Una volta all’interno della cartella del progetto, è possibile avviare il server di sviluppo locale digitando il seguente comando:
ng serve
Questo comando avvierà il server di sviluppo e sarà possibile visualizzare il progetto Angular predefinito aprendo un browser e navigando all’indirizzo http://localhost:4200/.
Ora che il progetto Angular è stato creato e il server di sviluppo è attivo, possiamo iniziare a creare le componenti e i modelli per la nostra applicazione.
Andiamo per prima cosa a definire il Model del messaggio.
MessageClass Model
Per prima cosa creiamo una cartella shared nel nostro nuovo progetto, sotto src/app. Dentro shared andremo ad aggiungere i modelli o le componenti in comune tra le varie componenti. Ricordiamoci di pensare che, nel tempo, la nostra pplicazione potrebbe subire dei cambiamenti o integrazioni, quindi mantenere il codice modulare il più possibile ci aiuterà nel caso di futuri update.
Dentro shared dal nostro terminale digitiamo:
ng generate class message-class
export class MessageClass { private type: string; private message: string; constructor(type: string, message: string ) { this.type = type; this.message = message; } getType(): string { return this.type; } getMessage() { return this.message; } }
Il messaggio sarà quindi composto da una stringa message, che rappresenta il messaggio vero e proprio, e da una stringa type che invece indicherà se il messaggio è inviato dall’utente o se è un messaggio di risposta dell’API.
Ora andremo a definire il service che si occuperà di effettuare la chiamata all’end-point dell’API di OpenAI.
Services
In Angular, un service è un’istanza di una classe che può essere utilizzata per condividere dati, funzionalità e logica di business tra diversi componenti all’interno di un’applicazione. Spesso i service vengono utilizzati per comunicare con un server per ottenere o salvare dati (ma non solo).
In src/app creiamo una nuova cartella e chiamiamola services, all’interno andremo a inserire tutti i services che ci occorreranno per la nostra applicazione.
Spostiamoci nella cartella dal terminale e andiamo a creare il primo service con Angular CLI (Command Line Interface):
ng g service chat
Apriamo il file chat.service.ts e modifichiamolo in questo modo:
@Injectable({ providedIn: 'root' }) export class ChatService { private _apiKey: string = 'YOUR_SECRET_KEY'; private headers = new HttpHeaders({ 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + this._apiKey }); constructor(private http: HttpClient) { } get apiKey(): string { return this._apiKey; } set apiKey(value: string) { this._apiKey = value; } generateContent(prompt: string): Observable<any> { const url = 'https://api.openai.com/v1/completions'; const body = {"model": "gpt-3.5-turbo", "messages": [{"role": "user", "content": prompt}], "temperature": 0.4, "max_tokens": 1024}; return this.http.post(url, body, { headers: this.headers }).pipe(catchError(this.errorHandler)); } errorHandler(error: HttpErrorResponse){ return throwError(() => new Error(error.message)); } }
Analizziamo il codice:
- La proprietà _apiKey rappresenta la chiave che abbiamo generato nell’interfaccia di OpenAI nei primi p0aragrafi
- Il metodo generateContent utilizza un oggetto di tipo HttpClient per inviare la richiesta, con il messaggio dell’utente (prompt), all’API
- Un metodo errorHandler per gestire gli errori
Nel body della chiamata andiamo a settare il model, la temperature e infine max_tokens. Tenete presente che è possibile andare a modificare questi e altri valori in base ai casi d’uso. Trovate la documentazione completa qui.
Forse ti può interessare
Il model è un parametro che specifica il tipo di modello testuale. La API di OpenAI è alimentata da una famiglia di modelli con diverse capacità e punti di forza. I modelli GPT-3.5 e GPT-3 hanno differenti livelli di potenza e sono adatti a compiti diversi.
Dalla documentazione ufficiale (link alla doc) essi sono:
- gpt-3.5-turbo – “Il modello GPT-3.5 più capace e ottimizzato per la chat al 10% del costo di text-davinci-003. Sarà aggiornato con la nostra ultima iterazione del modello.”
- text-davinci-003 – “Il modello GPT-3 più capace. Può svolgere qualsiasi compito che gli altri modelli possono fare, spesso con una qualità superiore, una produzione di output più lunga e una migliore capacità di seguire le istruzioni. Inoltre supporta l’inserimento di completamenti all’interno del testo.”
- text-curie-001 – “Molto capace, più veloce e meno costoso di Davinci.”
- text-babbage-001 – “In grado di svolgere compiti semplici, molto veloce e a basso costo.”
- text-ada-001 – “In grado di svolgere compiti molto semplici, di solito il modello più veloce della serie GPT-3 e il più economico.”
La temperature è un parametro che controlla la creatività e la diversità delle risposte generate dal modello. Deve essere compreso tra 0 e 1.
Infine max_tokens che rappresenta un parametro che controlla il numero massimo di token (parole o simboli) presenti nella risposta generata. Deve essere un numero intero compreso tra 1 e 2048.
Andiamo a generare il secondo service che fornirà i metodi per gestire un loader di caricamento
ng g service loader
apriamo il file loader.service.ts e inseriamo questo codice:
@Injectable({ providedIn: 'root' }) export class LoaderService { private loading: boolean = false; constructor() { } setLoading(loading: boolean) { this.loading = loading; } getLoading(): boolean { return this.loading; } }
La classe è molto semplice e presenta solamente i metodi per cambiare lo stato della proprietà loading.
Prima di creare le varie componenti abbiamo ancora un passaggio da effettuare.
Interceptor
Nella nostra applicazione vogliamo fare in modo che, durante l’attesa di una risposta dal server, l’utente visualizzi un loader. Nel capitolo precedente abbiamo definito il service LoaderService, ora non ci resta che aggiungere alla nostra applicazione un interceptor (che altro non è che una classe che implementa l’interfaccia HttpInterceptor) per intercettare le chiamate alle API di OpenAI e mostrare a schermo il widget di caricamento. Se vuoi approfondire dai un’occhiata a HttpInterceptor nella documentazione ufficiale di Angular.
Nella directory principale della nostra applicazione, aggiungiamo una nuova cartella interceptors e da terminale spostiamoci dentro di essa e digitiamo il comando:
ng generate interceptor loading-interceptor
Modifichiamo la classe in questo modo:
@Injectable() export class LoadingInterceptor implements HttpInterceptor { private totalRequests = 0; constructor(private loadingService: LoaderService) {} intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> { this.totalRequests++; this.loadingService.setLoading(true); return next.handle(request).pipe( finalize(() => { this.totalRequests--; if (this.totalRequests == 0) { this.loadingService.setLoading(false); } }) ); } }
Il metodo intercept viene chiamato ogni volta che viene effettuata una richiesta HTTP dall’applicazione. Inoltre questo metodo conta il numero totale di richieste HTTP che effetuiamo dall’applicazione, attraverso la variabile totalRequests. Il service LoaderService viene utilizzato per impostare lo stato di caricamento su true quando viene effettuata una richiesta.
La chiamata return next.handle(request) viene utilizzata per continuare la catena di gestione delle richieste HTTP (ricordiamo che teoricamente potremmo avere più chiamate).
L’operatore finalize infine, viene eseguito quando l’Observable restituito da next.handle(request) viene completato o restituisce un errore. In questo caso, viene utilizzato per diminuire il valore della variabile totalRequests e impostare lo stato di caricamento su false quando il numero totale di richieste HTTP diventa zero.
Ora che abbiamo aggiunto alla nostra applicazione il model, i services e l’interceptor, non ci resta che andare a costruire le componenti.
Componenti
Anche in questo caso creiamo una cartella dedicata in src/app e chiamiamola components. Nella cartella andremo a generare tutte le componenti che serviranno alla nostra applicazione.
Creiamo la prima componente usando Angular CLI (Command Line Interface), oppure, se proprio non vi piace Angular CLI, potete aggiungere i files e le classi manualmente. Spostiamoci nella cartella appena creata con il nostro terminale e poi:
ng g c chat-window
Apriamo chat-window.component.ts e modifichiamolo così:
@Component({ selector: 'app-chat-window', templateUrl: './chat-window.component.html', styleUrls: ['./chat-window.component.css'] }) export class ChatWindowComponent implements OnInit { @ViewChild('messageInput') messageInput: ElementRef; responses: MessageClass[] = []; error: string = ''; ngOnInit(): void { } constructor(private chatService: ChatService) { this.messageInput = new ElementRef(null); } sendMessage() { const messageText = this.messageInput.nativeElement.value; this.responses.push(new MessageClass('user', messageText)); this.chatService.generateContent(messageText).subscribe(response => { .subscribe(response => { this.responses.push(new MessageClass('ai', response.choices[0].message.content.toString())); this.error = ''; }, error => { console.log(error.message); this.error = error.message; } ); } }
La classe ha un campo dati @ViewChild messageInput che viene utilizzato per recuperare il valore dell’input dell’utente nell’interfaccia grafica che andremo a costruire.
L’array responses rappresenta il contenitore delle risposte in arrivo dall’API di OpenAI. È di tipo MessageClass, il modello che abbiamo definito nei capitoli precedenti e che rappresenta il messaggio che invia l’utente oppure che l’utente riceve dal server.
Nel costruttore andiamo anche a definire il ChatService. Il metodo SendMessage invece è l’incaricato d’inviare il messaggio in input dell’utente tramite il service.
Quando l’utente clicca sul button “Invia”, aggiungiamo all’array responses un nuovo MessageClass composto da una stringa “user”, che indica la fonte del messaggio, e dal messaggio inserito dall’utente.
Facciamo un subscribe al ChatService e, quando abbiamo la risposta dal server, inseriamo anche questa nel contenitore responses ma con il primo parametro settato su “ai”. In questo modo quando andiamo a visualizzare la pagina possiamo distinguere tra i messaggi inviati dall’utente (“user”) e quelli ricevuti dal server (“ai”).
Gestiamo un possibile errore durante la chiamata andando a popolare la proprietà error.
Nota: ricorda di sanificare tutti gli input utente!
È il momento di disegnare la UI.
Apriamo il file chat-window.component.html e inseriamo il codice seguente:
<div class="chat-window"> <div class="chat-output"> <div *ngIf="responses"> <div *ngFor="let response of responses"> <div *ngIf="response.getType() == 'ai'"> <div class="wp-gpt-baloon wp-gpt-ai-baloon"> <div class="wp-baloon-inner-container"> <div class="wp-gpt-main-text-col"> <div class="wp-gpt-text-icon wp-gpt-ai-icon"></div> <p> {{ response.getMessage() }} </p> </div> </div> </div> </div> <div *ngIf="response.getType() == 'user'"> <div class="wp-gpt-baloon wp-gpt-user-baloon"> <div class="wp-baloon-inner-container"> <div class="wp-gpt-main-text-col"> <div class="wp-gpt-text-icon wp-gpt-user-icon"></div> <p> {{ response.getMessage() }} </p> </div> </div> </div> </div> </div> </div> </div> <div class="wp-gpt-lower-section"> <div class="wp-gpt-errors-container"> <p class="wp-gpt-chat-errors">{{error}}</p> </div> <div class="wp-gpt-chat-input"> <div class="wp-gpt-chat-input-inner-container"> <input #messageInput type="text" placeholder="Invia un messaggio..."> <button (click)="sendMessage();">Invia</button> </div> </div> </div> </div>
Con la direttiva ngIf separiamo il caso di messaggio inviato dall’utente dal caso di messaggio ricevuto. In questo modo possiamo applicare classi o aggiungere/eliminare elementi in modo selettivo.
Abbiamo una sezione dedicata alla stampa dei possibili errori ( {{error}} ), mentre il metodo sendMessage viene legato al button tramite il click dell’utente.
Aggiungiamo un po’ di stile alla nostra componente perchè in effetti è un po’ bruttina. Apriamo chat-window.component.css e incolliamo queste regole:
.wp-gpt-baloon { padding: 15px; } .chat-output { max-height: 600px; overflow-y: auto; } .wp-gpt-baloon.wp-gpt-ai-baloon { background-color: rgb(247, 247, 248); border-top: 1px solid #e3e3e3; border-bottom: 1px solid #e3e3e3; } .wp-gpt-main-text-col { display: flex; align-items: center; gap: 12px; width: 100%; } .wp-gpt-main-text-col p { max-width: 80%; } .wp-gpt-text-icon { width: 24px; height: 24px; border-radius: 4px; } .wp-gpt-ai-icon { background: #03a894; } .wp-gpt-user-icon { background: #d27204; } .wp-baloon-inner-container { margin: 0 auto; display: flex; justify-content: space-between; align-items: center; gap: 28px; } .wp-gpt-chat-input-inner-container { display: flex; justify-content: center; align-items: center; width: 100%; } .wp-gpt-errors-container { display: flex; justify-content: center; } .wp-gpt-errors-container .wp-gpt-chat-errors { width: 100%; color: #a10000; } .wp-gpt-chat-input { display: flex; justify-content: center; } .wp-gpt-chat-input input { width: 100%; height: 30px; padding-left: 10px; border-radius: 4px 0 0 4px; color: #8A90A2; border: 1px solid #D5D5D5; } .wp-gpt-chat-input input:focus { outline: none; } .wp-gpt-chat-input button { border-radius: 0 4px 4px 0; } .wp-gpt-lower-section { padding-bottom: 24px; } @media (min-width: 1280px) { .wp-baloon-inner-container, .wp-gpt-chat-input-inner-container, .wp-gpt-chat-errors { max-width: 48rem; } }
Ok, ora andiamo a definire una semplice componente per la visualizzazione del loader. Sempre dentro la cartella components dal terminale digitiamo:
ng g c spinner
Apriamo il file spinner.component.html e incolliamo questo breve codice:
<div *ngIf="this.loader.getLoading()" class="wp-gpt-cssload-container"> <div class="wp-gpt-speeding-wheel"></div> </div>
Il codice presenta una direttiva ngIf che non fa altro che mostrare il div interno (che è un loader spinner, vedi il css seguente) solamente quando il LoaderService ha la proprietà loading settata a true.
Per quanto riguarda il file spinner.component.ts sarà quindi solamente necessario dichiarare il LoaderService nel costruttore di classe in questo modo:
constructor(public loader: LoaderService) { }
Inseriamo lo style per il nostro loader e abbiamo quasi finito. Nel file spinner.component.css aggiungiamo le regole che definiscono un semplice widget di caricamento:
.wp-gpt-cssload-container { position: fixed; width: 100%; left: 0; right: 0; top: 0; bottom: 0; background-color: rgba(255, 255, 255, 0.7); z-index: 9999; } .wp-gpt-speeding-wheel { content: ""; display: block; position: absolute; left: 48%; top: 40%; width: 63px; height: 63px; margin: 0 auto; border: 4px solid rgb(0, 0, 0); border-radius: 50%; border-left-color: transparent; border-right-color: transparent; animation: wp-gpt-spin 500ms infinite linear; } @keyframes wp-gpt-spin { 100% { transform: rotate(360deg); } } @-o-keyframes wp-gpt-spin { 100% { transform: rotate(360deg); } } @-ms-keyframes wp-gpt-spin { 100% { -ms-transform: rotate(360deg); transform: rotate(360deg); } } @-webkit-keyframes wp-gpt-spin { 100% { transform: rotate(360deg); } } @-moz-keyframes wp-gpt-spin { 100% { transform: rotate(360deg); } }
Apriamo app.component.html e includiamo le nostre componenti. Possiamo anche aggiungere un titolo se vogliamo:
<h1>GPT content generator</h1> <app-spinner></app-spinner> <app-chat-window></app-chat-window>
In app.component.css:
h1 { text-align: center; }
Nota importante: ricordati di aggiornare il file app.module.ts in quanto è incaricato di specificare i componenti, i servizi, i provider, le dipendenze necessarie e molto altro.
@NgModule({ declarations: [ AppComponent, ChatWindowComponent, SpinnerComponent, HeaderComponent, ], imports: [ BrowserModule, HttpClientModule, HttpClientJsonpModule, ], providers: [ { provide: HTTP_INTERCEPTORS, useClass: LoadingInterceptor, multi: true } ], bootstrap: [AppComponent] }) export class AppModule { }
Conclusioni
Ottimo! Abbiamo terminato l’app di generazione contenuti con Angular e OpenAI API.
Anche se può sembrare un po’ complesso, grazie alla flessibilità di Angular e alla potenza delle API OpenAI, puoi creare una soluzione personalizzata che si adatta alle tue esigenze specifiche.
Spero che l’articolo ti sia stato utile. Non dimenticare che se hai domande o dubbi puoi lasciare un commento.
Categorie
Categorie
- Reti (1)
- Web Developing (4)
Post recenti
Lascia un commento.
