L’utilizzo dei chatbot e degli assistenti virtuali continua ad aumentare. Per questo molte aziende e sviluppatori stanno cercando di creare i propri chatbot dotati di intelligenza artificiale. ChatGPT è uno di questi chatbot, creato da OpenAI, in grado di avviare conversazioni simili a quelle umane e di rispondere a un’ampia gamma di domande.

Cosa Costruiremo

In questo tutorial impareremo a creare un clone di ChatGPT utilizzando React e le API di OpenAI. Se volete cimentarvi in un progetto divertente e stimolante per passare il weekend, questa potrebbe essere un’ottima occasione per scoprire React e OpenAI.

Imparerete anche a distribuire direttamente dal vostro repository GitHub alla piattaforma di Hosting di Applicazioni di Kinsta, che offre un dominio .kinsta.app gratuito per rendere il vostro progetto operativo rapidamente. Inoltre, grazie alla prova gratuita e al Piano Hobby di Kinsta, potrete iniziare a lavorarci su senza alcun costo.

Ecco una demo live dell’applicazione clone di ChatGPT.

Applicazione clone di ChatGPT
Applicazione clone di ChatGPT

Se volete esaminare più da vicino questo progetto, potete accedere al repository GitHub.

In alternativa, utilizzando questo template di progetto starter, è possibile selezionare Usa questo template > Crea un nuovo repository: in questo modo si copierà il codice starter in un nuovo repository. Questo progetto starter è dotato di elementi fondamentali come gli stili, il link al CDN di Font Awesome, il pacchetto OpenAi e la struttura di base per aiutarvi a cominciare.

Requisiti/Prequisiti

Questo tutorial è stato progettato per essere un’esperienza “follow-along”. Pertanto, è consigliabile che disponiate dei seguenti elementi per poter programmare senza intoppi:

Cos’è OpenAI API?

OpenAI API è una piattaforma basata sul cloud che consente agli sviluppatori di accedere ai modelli linguistici di OpenAI, come GPT-3, tramite un’API. Permette agli sviluppatori di aggiungere alle loro applicazioni alcune funzioni di elaborazione del linguaggio naturale come completamento del testo, sentiment analysis, sintesi e traduzione senza avere la necessità di sviluppare e allenare i propri modelli.

Per utilizzare l’API OpenAI, gli sviluppatori devono creare un account sul sito web OpenAI e ottenere una chiave API. La chiave API viene utilizzata per autenticare le richieste API e tenere traccia del suo utilizzo.

Una volta ottenuta la chiave API, gli sviluppatori possono utilizzare l’API per inviare il testo al modello linguistico e ricevere le risposte.

Perché React?

React è una popolare libreria JavaScript per la creazione di interfacce utente. Secondo il sondaggio Stack Overflow del 2022 sugli sviluppatori, è la seconda tecnologia web più utilizzata, con il 42,62% della quota di mercato.

React permette agli sviluppatori di creare componenti dichiarativi che rappresentano diverse parti dell’interfaccia utente. Questi componenti vengono definiti utilizzando una sintassi chiamata JSX, che è una combinazione di JavaScript e HTML.

Grazie al suo ampio ecosistema di librerie e kit di componenti, gli sviluppatori possono facilmente lavorare e integrare API, come l’API OpenAI, per costruire complesse interfacce di chat e questo lo rende una scelta eccellente per la realizzazione di un’applicazione clone di ChatGPT.

Come Impostare l’Ambiente di Sviluppo React

Il modo migliore per installare React o creare un progetto React è installarlo con create-react-app. Un prerequisito è avere Node.js installato sul proprio computer. Per verificare che Node sia installato, eseguite il seguente comando nel vostro terminale.

node -v

Se ne visualizzate una versione, vuol dire che ne avete già una installata. Per utilizzare npx, dovrete assicurarvi che la versione di Node non sia inferiore alla v14.0.0 e che la versione di NPM non sia inferiore alla v5.6; in caso contrario, potreste doverla aggiornare eseguendo npm update -g. Una volta capito come funziona npm, è possibile impostare un progetto React eseguendo il comando seguente:

npx create-react-app chatgpt-clone

Nota: “chatgpt-clone” è il nome dell’applicazione che stiamo creando, ma potete cambiarlo con un nome a vostra scelta.

Il processo di installazione potrebbe richiedere alcuni minuti. Una volta terminato, potete navigare nella directory e installare il pacchetto Node.js OpenAI, che fornisce un comodo accesso all’API OpenAI da Node.js, utilizzando il comando seguente:

npm install openai

Ora potete eseguire npm start per vedere la vostra applicazione in diretta su localhost:3000.

Quando un progetto React viene creato con il comando create-react-app, viene creata automaticamente una struttura di cartelle. La cartella principale che vi interessa è la cartella src, dove avviene lo sviluppo. Questa cartella contiene molti file per impostazione predefinita, ma a voi dovrebbero interessare solo i file App.js, index.js e index.css.

  1. App.js: il file App.js è il componente principale di un’applicazione React. In genere rappresenta il componente di primo livello che esegue il rendering di tutti gli altri componenti dell’applicazione.
  2. index.js: questo file è il punto di ingresso dell’applicazione React. È il primo file caricato all’apertura dell’applicazione ed è responsabile del rendering del componente App.js nel browser.
  3. index.css: Questo file è responsabile della definizione dello stile e del layout generale dell’applicazione React.

Come Creare un Clone di ChatGPT con React e OpenAI API

L’applicazione clone di ChatGPT sarà composta da due componenti per rendere l’applicazione più facile da capire e da mantenere. Questi due componenti sono:

  1. Form Section: Questo componente include un’area di testo e un pulsante per consentire agli utenti di interagire con il chatbot.
  2. Answer Section: Le domande e le risposte corrispondenti saranno memorizzate in un array e visualizzate in questa sezione. Si scorrerà l’array in ordine cronologico, che mostrerà la più recente per prima.

Impostazione dell’Applicazione Clone di ChatGPT

In questa esercitazione, inizieremo a costruire l’interfaccia dell’applicazione per poi implementare le funzionalità in modo che l’applicazione interagisca con l’API OpenAI. Iniziamo creando i due componenti che utilizzeremo in questo tutorial. Per una corretta organizzazione, creemo una cartella components nella cartella src in cui saranno memorizzati tutti i componenti.

Il Componente Form Section

Si tratta di un semplice modulo che consiste in una textarea e in un button invio.


// components/FormSection.jsx

const FormSection = () => {

    return (
        <div className="form-section">
            <textarea
                rows="5"
                className="form-control"
                placeholder="Ask me anything..."
            ></textarea>
            <button className="btn">
                Generate Response 🤖
            </button>
        </div>
    )
}

export default FormSection;

Questo è l’aspetto che dovrebbe avere il modulo quando lo importerete nel vostro file App.js:

Componente Form Section per il clone di ChatGPT
Componente sezione modulo

Il Componente Answer Section

In questa sezione verranno visualizzate tutte le domande e le risposte. Ecco come apparirà questa sezione quando la importerete anche nel vostro file App.js.

Componente Answer Section del clone di ChatGPT
Componente Answer Section

Le domande e le risposte verranno recuperate da un array e da un ciclo per rendere il codice più facile da leggere e da mantenere.

// components/AnswerSection.jsx

const AnswerSection = () => {
    return (
        <>
            <hr className="hr-line" />
            <div className="answer-container">
                <div className="answer-section">
                    <p className="question">Who is the founder of OpenAi?</p>
                    <p className="answer">OpenAI was founded in December 2015 by Elon Musk, Sam Altman, Greg Brockman, Ilya Sutskever, Wojciech Zaremba, and John Schulman.</p>
                    <div className="copy-icon">
                        <i className="fa-solid fa-copy"></i>
                    </div>
                </div>
            </div>
        </>
    )
}

export default AnswerSection;

La Pagina Iniziale

Ora avete creato entrambi i componenti, ma quando eseguirete l’applicazione non apparirà nulla perché dovrete prima importarli nel vostro file App.js. Per questa applicazione, non implementerete alcuna forma di routing, quindi il file App.js servirà come componente/pagina iniziale dell’applicazione.

Potete aggiungere alcuni contenuti, come il titolo e la descrizione dell’applicazione, prima di importare i componenti.

// App.js

import FormSection from './components/FormSection';
import AnswerSection from './components/AnswerSection';

const App = () => {
    return (
        <div>
            <div className="header-section">
                <h1>ChatGPT CLONE 🤖</h1>
                <p>
                    I am an automated question and answer system, designed to assist you
                    in finding relevant information. You are welcome to ask me any queries
                    you may have, and I will do my utmost to offer you a reliable
                    response. Kindly keep in mind that I am a machine and operate solely
                    based on programmed algorithms.
                </p>
            </div>

            <FormSection />
            <AnswerSection />
        </div>
    );
};

export default App;

Nel codice qui sopra, i due componenti vengono importati e aggiunti all’applicazione. Quando eseguirete l’applicazione, avrà questo aspetto:

Applicazione clone di ChatGPT completa
Applicazione clone di ChatGPT completa

Aggiunta di Funzionalità e Integrazione dell’API OpenAI

Ora avete l’interfaccia utente della vostra applicazione. Il passo successivo è rendere l’applicazione funzionale in modo che possa interagire con l’API OpenAI e ottenere risposte. Per prima cosa, dovete ottenere il valore dal vostro modulo quando viene inviato perché verrà utilizzato per interrogare l’API OpenAI.

Ottenere i Dati dal Modulo

In React, il modo migliore per memorizzare e aggiornare i dati è utilizzare gli stati. Nei componenti funzionali, l’hook useState() è utilizzato per lavorare con gli stati. È possibile creare uno stato, assegnare il valore del modulo allo stato e aggiornarlo ogni volta che il suo valore cambia. Iniziamo importando l’hook useState() nel componente FormSection.jsx e poi creiamo uno stato per memorizzare e aggiornare newQuestions.

// components/FormSection.jsx

import { useState } from 'react';

const FormSection = ({ generateResponse }) => {
    const [newQuestion, setNewQuestion] = useState('');

    return (
        // Form to submit a new question
    )
}

export default FormSection;

Successivamente, potrete assegnare il valore del campo textarea allo stato e creare un evento onChange() per aggiornare lo stato ogni volta che il valore dell’input cambia:

<textarea
    rows="5"
    className="form-control"
    placeholder="Ask me anything..."
    value={newQuestion}
    onChange={(e) => setNewQuestion(e.target.value)}
></textarea>

Infine, potete creare un evento onClick() per caricare una funzione ogni volta che viene cliccato il pulsante di invio. Questo metodo verrà creato nel file App.js e passato come props nel componente FormSection.jsx con i valori newQuestion e setNewQuestion come argomenti.

<button className="btn" onClick={() => generateResponse(newQuestion, setNewQuestion)}>
    Generate Response 🤖
</button>

Ora avete creato uno stato per memorizzare e aggiornare il valore del modulo, avete aggiunto un metodo che viene passato come props dal file App.js e avete gestito l’evento click. Ecco come apparirà il codice finale:

// components/FormSection.jsx

import { useState } from 'react';

const FormSection = ({ generateResponse }) => {
    const [newQuestion, setNewQuestion] = useState('');

    return (
        <div className="form-section">
            <textarea
                rows="5"
                className="form-control"
                placeholder="Ask me anything..."
                value={newQuestion}
                onChange={(e) => setNewQuestion(e.target.value)}
            ></textarea>
            <button className="btn" onClick={() => generateResponse(newQuestion, setNewQuestion)}>
                Generate Response 🤖
            </button>
        </div>
    )
}

export default FormSection;

Il prossimo passo sarà quello di creare un metodo nel file App.js per gestire l’intero processo di interazione con l’API OpenAI.

Interagire con OpenAI API

Per interagire con OpenAI API e ottenere le chiavi API in un’applicazione React, dovrete creare un account OpenAI API. Potete registrarvi sul sito web di OpenAI utilizzando il vostro account google o la vostra email. Per generare una chiave API, cliccate su Personal nell’angolo in alto a destra del sito; appariranno alcune opzioni; cliccate su View API keys.

Accedere alle chiavi API di OpenAI
Accedere alle chiavi API di OpenAI.

Cliccate sul pulsante Create new secret key e copiate la chiave da qualche parte per utilizzarla in questa applicazione per interagire con OpenAI. Ora potete procedere all’inizializzazione di OpenAI importando il pacchetto openai (che avrete già installato) insieme al metodo di configurazione. Quindi create una configurazione con la chiave generata e usatela per inizializzare OpenAI.

// src/App.js

import { Configuration, OpenAIApi } from 'openai';

import FormSection from './components/FormSection';
import AnswerSection from './components/AnswerSection';

const App = () => {
    const configuration = new Configuration({
        apiKey: process.env.REACT_APP_OPENAI_API_KEY,
    });

    const openai = new OpenAIApi(configuration);

    return (
        // Render FormSection and AnswerSection
    );
};

export default App;

Nel codice qui sopra, la chiave API di OpenAI è memorizzata come variabile d’ambiente nel file .env. Potete creare un file .env nella cartella principale della vostra applicazione e memorizzare la chiave nella variabile REACT_APP_OPENAI_API_KEY.

// .env
REACT_APP_OPENAI_API_KEY = sk-xxxxxxxxxx…

Ora potete creare il metodo generateResponse nel file App.js e passare i due parametri previsti dal modulo che avete già creato per gestire la richiesta e ottenere la risposta dall’API.

// src/App.js

import FormSection from './components/FormSection';
import AnswerSection from './components/AnswerSection';

const App = () => {
    const generateResponse = (newQuestion, setNewQuestion) => {
        // Set up OpenAI API and handle response
    };

    return (
        // Render FormSection and AnswerSection
    );
};

export default App;

Adesso potete inviare una richiesta all’API OpenAI. L’API OpenAI può eseguire molte operazioni, come domande e risposte (Q&A), correzione grammaticale, traduzione e molto altro. Per ognuna di queste operazioni, le opzioni sono diverse. Ad esempio, il valore del motore per Q&A è text-davinci-00, mentre per SQL translate è code-davinci-002. Consultate la documentazione di OpenAI per vedere vari esempi e le relative opzioni.

In questo tutorial lavoreremo solo con il Q&A: ecco come si presenta l’opzione:

{
  model: "text-davinci-003",
  prompt: "Who is Obama?",
  temperature: 0,
  max_tokens: 100,
  top_p: 1,
  frequency_penalty: 0.0,
  presence_penalty: 0.0,
  stop: [""],
}

Nota: ho cambiato il valore del prompt.

Il prompt è la domanda che viene inviata dal modulo. Ciò significa che dovrete riceverlo dall’input del modulo che passerete al metodo generateResponse come parametro. Per farlo, dovrete definire le opzioni e poi utilizzare l’operatore spread per creare un’opzione completa che includa il prompt:

// src/App.js

import { Configuration, OpenAIApi } from 'openai';
import FormSection from './components/FormSection';
import AnswerSection from './components/AnswerSection';

const App = () => {
    const configuration = new Configuration({
        apiKey: process.env.REACT_APP_OPENAI_API_KEY,
    });

    const openai = new OpenAIApi(configuration);

    const generateResponse = async (newQuestion, setNewQuestion) => {
        let options = {
            model: 'text-davinci-003',
            temperature: 0,
            max_tokens: 100,
            top_p: 1,
            frequency_penalty: 0.0,
            presence_penalty: 0.0,
            stop: ['/'],
        };

        let completeOptions = {
            ...options,
            prompt: newQuestion,
        };

    };

    return (
         // Render FormSection and AnswerSection
    );
};

export default App;

A questo punto, non resta che inviare una richiesta tramite il metodo createCompletion a OpenAI per ottenere una risposta.

// src/App.js

import { Configuration, OpenAIApi } from 'openai';
import FormSection from './components/FormSection';
import AnswerSection from './components/AnswerSection';

import { useState } from 'react';

const App = () => {
    const configuration = new Configuration({
        apiKey: process.env.REACT_APP_OPENAI_API_KEY,
    });

    const openai = new OpenAIApi(configuration);

    const [storedValues, setStoredValues] = useState([]);

    const generateResponse = async (newQuestion, setNewQuestion) => {
        let options = {
            model: 'text-davinci-003',
            temperature: 0,
            max_tokens: 100,
            top_p: 1,
            frequency_penalty: 0.0,
            presence_penalty: 0.0,
            stop: ['/'],
        };

        let completeOptions = {
            ...options,
            prompt: newQuestion,
        };

        const response = await openai.createCompletion(completeOptions);

        console.log(response.data.choices[0].text);
    };

    return (
        // Render FormSection and AnswerSection
    );
};

export default App;

Nel codice qui sopra, il testo della risposta verrà visualizzato nella console. Sentitevi liberi di testare la vostra applicazione ponendo qualsiasi domanda. Il passo finale consiste nel creare uno stato che contenga l’array di domande e risposte e poi inviare questo array come prop nel componente AnswerSection. Ecco come apparirà il codice finale di App.js:

// src/App.js
import { Configuration, OpenAIApi } from 'openai';

import FormSection from './components/FormSection';
import AnswerSection from './components/AnswerSection';

import { useState } from 'react';

const App = () => {
    const configuration = new Configuration({
        apiKey: process.env.REACT_APP_OPENAI_API_KEY,
    });

    const openai = new OpenAIApi(configuration);

    const [storedValues, setStoredValues] = useState([]);

    const generateResponse = async (newQuestion, setNewQuestion) => {
        let options = {
            model: 'text-davinci-003',
            temperature: 0,
            max_tokens: 100,
            top_p: 1,
            frequency_penalty: 0.0,
            presence_penalty: 0.0,
            stop: ['/'],
        };

        let completeOptions = {
            ...options,
            prompt: newQuestion,
        };

        const response = await openai.createCompletion(completeOptions);

        if (response.data.choices) {
            setStoredValues([
                {
                    question: newQuestion,
                    answer: response.data.choices[0].text,
                },
                ...storedValues,
            ]);
            setNewQuestion('');
        }
    };

    return (
        <div>
            <div className="header-section">
                <h1>ChatGPT CLONE 🤖</h1>
                    <p>
                        I am an automated question and answer system, designed to assist you
                        in finding relevant information. You are welcome to ask me any
                        queries you may have, and I will do my utmost to offer you a
                        reliable response. Kindly keep in mind that I am a machine and
                        operate solely based on programmed algorithms.
                    </p>
            </div>

            <FormSection generateResponse={generateResponse} />

            <AnswerSection storedValues={storedValues} />
        </div>
    );
};

export default App;

A questo punto, potete modificare il componente AnswerSection in modo che riceva il valore di prop da App.js e utilizzare il metodo JavaScript Map() per esaminare l’array storedValues:

// components/AnswerSection.jsx

const AnswerSection = ({ storedValues }) => {
    return (
        <>
            <hr className="hr-line" />
            <div className="answer-container">
                {storedValues.map((value, index) => {
                    return (
                        <div className="answer-section" key={index}>
                            <p className="question">{value.question}</p>
                            <p className="answer">{value.answer}</p>
                            <div className="copy-icon">
                                <i className="fa-solid fa-copy"></i>
                            </div>
                        </div>
                    );
                })}
            </div>
        </>
    )
}

export default AnswerSection;

Quando eseguirete l’applicazione e la testerete ponendo delle domande, le risposte verranno visualizzate qui sotto. Ma noterete che il pulsante di copia non funziona. Dovrete aggiungere un evento onClick() al pulsante, in modo da attivare un metodo per gestire la funzionalità. Potete utilizzare il metodo navigator.clipboard.writeText() per gestire la funzionalità. Ecco come apparirà ora il componente AnswerSection:

// components/AnswerSection.jsx

const AnswerSection = ({ storedValues }) => {
    const copyText = (text) => {
        navigator.clipboard.writeText(text);
    };

    return (
        <>
            <hr className="hr-line" />
            <div className="answer-container">
                {storedValues.map((value, index) => {
                    return (
                        <div className="answer-section" key={index}>
                            <p className="question">{value.question}</p>
                            <p className="answer">{value.answer}</p>
                            <div
                                className="copy-icon"
                                onClick={() => copyText(value.answer)}
                            >
                                <i className="fa-solid fa-copy"></i>
                            </div>
                        </div>
                    );
                })}
            </div>
        </>
    )
}

export default AnswerSection;

Quando eseguirete l’applicazione, il clone di ChatGPT funzionerà perfettamente. È arrivato finalmente il momento di distribuire l’applicazione per accedervi online e condividerla con gli amici.

Come Distribuire l’Applicazione React su Kinsta

Non è sufficiente creare questa applicazione e lasciarla sui vostri computer locali. Dovrete condividerla online, in modo che altri possano accedervi. Vediamo come fare utilizzando GitHub e Kinsta.

Inviare il Codice a GitHub

Per inviare il vostro codice a GitHub, potete utilizzare i comandi di Git, un metodo affidabile ed efficiente per gestire le modifiche al codice, collaborare ai progetti e mantenere la cronologia delle versioni.

Il primo passo da fare per inviare il codice è creare un nuovo repository accedendo al vostro account GitHub, cliccando sul pulsante + nell’angolo in alto a destra dello schermo e selezionando Nuovo repository dal menu a tendina.

Creare un nuovo repository su GitHub
Creare un nuovo repository su GitHub

Date un nome al vostro repository, aggiungete una descrizione (facoltativa) e scegliete se volete che sia pubblico o privato. Cliccate su Crea repository per crearlo.

Una volta creato, assicuratevi di ottenere l’URL del repository dalla pagina principale del repository, che vi servirà per inviare il codice a GitHub.

Accedere all'URL del proprio repository
Accedere all’URL del proprio repository

Aprite il vostro terminale o il prompt dei comandi e naviga nella directory che contiene il tuo progetto. Esegui i seguenti comandi uno alla volta per inviare il codice al tuo repository GitHub:

git init
git add .
git commit -m "my first commit"
git remote add origin [repository URL]
git push -u origin master

git init git add . aggiunge tutti i file della directory corrente e delle sue sottodirectory al nuovo repository Git. git commit -m "my first commit" apporta le modifiche al repository con un breve messaggio. git remote add origin [repository URL] imposta l’URL del repository come repository remoto e git push -u origin master invia il codice al repository remoto (origin) nel branch master.

Distribuire l’Applicazione ChatGPT Clone React su Kinsta

Per distribuire il vostro repository su Kinsta, seguite i seguenti passaggi:

  1. Effettuare l’accesso o creare un account Kinsta nel cruscotto MyKinsta.
  2. Cliccare su Applicazioni nella barra laterale di sinistra e poi su Aggiungi servizio.
  3. Selezionare Applicazione dal menu a tendina per distribuire un’applicazione React su Kinsta.
  4. Selezionare il repository che desiderate distribuire dalla finestra di dialogo che appare. Se avete più branch, scegliete quella che volete distribuire e assegnate un nome all’applicazione. Selezionate un data centre tra quelli disponibili tra i nostri 25 e Kinsta rileverà automaticamente un comando di avvio.
  5. Infine, non è sicuro inviare le chiavi API a host pubblici come GitHub, quindi la abbiamo aggiunta come variabile d’ambiente a livello locale. Durante l’hosting, è possibile aggiungerla come variabile d’ambiente utilizzando lo stesso nome della variabile e la chiave come valore.
Distribuzione del clone di ChatGPT su Kinsta
Distribuzione del clone di ChatGPT su Kinsta.

L’applicazione inizierà ad essere distribuita ed entro pochi minuti verrà fornito un link per accedere alla sua versione distribuita. In questo caso, si tratta di https://chatgpt-clone-g9q10.kinsta.app/

Note: Potete attivare la distribuzione automatica, in modo che Kinsta distribuisca nuovamente l’applicazione ogni volta che modificate la base di codice e la inviate a GitHub.

Riepilogo

L’API OpenAI può essere utilizzata per creare un’ampia gamma di potenziali applicazioni, dall’assistenza clienti agli assistenti personali, dalla traduzione linguistica alla creazione di contenuti.

In questo tutorial abbiamo imparato a costruire un’applicazione clone di ChatGPT con React e OpenAI. Potete integrare questa applicazione/funzione in altre applicazioni per fornire agli utenti esperienze di conversazione simili a quelle umane.

C’è molto altro che si può fare sia con l’API OpenAI che per migliorare questa applicazione clone. Ad esempio, potete implementare la memorizzazione locale in modo che le domande e le risposte precedenti non scompaiano anche dopo l’aggiornamento del browser.

Grazie alla prova gratuita di Kinsta e al Piano Hobby, potete iniziare facilmente e senza alcun costo a utilizzare il nostro Hosting di Applicazioni. Allora perché non provare e vedere cosa riuscite a creare?

Condividete i vostri progetti e le vostre esperienze nei commenti qui sotto.

Joel Olawanle Kinsta

Joel is a Frontend developer working at Kinsta as a Technical Editor. He is a passionate teacher with love for open source and has written over 200 technical articles majorly around JavaScript and it's frameworks.