Användningen av chatbots och virtuella assistenter fortsätter att öka. Därför så söker många företag och utvecklare efter sätt att skapa sina egna AI-drivna chatbots. ChatGPT är en sådan chattbot, skapad av OpenAI, som kan delta i människoliknande samtal och besvara ett stort antal frågor.

Vad du ska bygga

I den här handledningen så kommer du att lära dig hur du bygger en ChatGPT-klonapplikation med hjälp av React och OpenAI API. Om du exempelvis vill prova på ett roligt och engagerande projekt under helgen så är detta ett utmärkt tillfälle att djupdyka i React och OpenAI.

Du kommer även att lära dig hur du distribuerar direkt från ditt GitHub-arkiv till Kinsta’s plattform för applikationshosting. Den tillhandahåller en kostnadsfri .kinsta.app-domän. Som ett resultat av denna så kan ditt projekt snabbt tas i drift. Och med Kinsta’s kostnadsfria provperiod och Hobbynivå så kan du dessutom komma igång enkelt utan kostnad.

Här är en live-demo av ChatGPT-klonapplikationen.

ChatGPT-klonapplikationen
ChatGPT-klonapplikationen

Om du vill titta närmare på det här projektet så kan du få tillgång till dess GitHub-arkiv.

Alternativt, med hjälp av den här startprojektmallen, så kan du välja Använd den här mallen > Skapa ett nytt arkiv – detta kopierar startkoden till ett nytt arkiv. Detta startprojekt inkluderar grundläggande element som stilar, Font Awesome CDN-länk, OpenAi-paket och grundläggande struktur för att hjälpa dig att komma igång.

Krav/förutsättningar

Den här handledningen är utformad för att du ska kunna följa med. Därför så rekommenderas det att du har följande för att få en enkel kodning:

Vad är OpenAI API?

OpenAI API är en molnbaserad plattform som ger utvecklare tillgång till OpenAI’s språkmodeller, exempelvis GPT-3, via ett API. Som ett resultat av detta så kan utvecklare lägga till funktioner för behandling av naturliga språk. Det kan exempelvis handla om textkomplettering, sentimentsanalys, sammanfattning och översättning i tillämpningarna utan att det krävs utveckling och träning på sina modeller.

För att använda OpenAI API så måste utvecklare skapa ett konto på OpenAI’s webbplats och få en API-nyckel. API-nyckeln används sedan för att autentisera API-begäranden och spåra användningen.

När API-nyckeln har erhållits så kan utvecklare använda API: et för att skicka text till språkmodellen och få svar.

Varför React?

React är ett populärt JavaScript-bibliotek för att bygga användargränssnitt. Enligt 2022 års utvecklarundersökning från Stack Overflow såär detta den näst vanligaste webbtekniken, med 42,62 % av marknadsandelen.

React gör det exempelvis möjligt för utvecklare att skapa deklarativa komponenter som representerar olika delar av användargränssnittet. Dessa komponenter definieras med hjälp av en syntax som kallas JSX. Detta är en kombination av JavaScript och HTML.

Tack vare dess stora ekosystem av komponentbibliotek och kit så kan utvecklare enkelt arbeta med och integrera API: er som OpenAI API. Som ett resultat så kan de bygga komplexa chattgränssnitt och det är det som gör det till ett utmärkt val för att bygga en ChatGPT-klonapplikation.

Så här ställer du in din React-utvecklingsmiljö

Det bästa sättet att installera React eller skapa ett React-projekt är att installera det med create-react-appen. En förutsättning är att Node.js är installerat på din maskin. För att bekräfta att du har Node installerat så kör du därför följande kommando i din terminal.

node -v

Om detta visar en version så finns den. För att kunna använda npx så måste du se till att din Node-version är minst v14.0.0.0 och att din NPM-version är minst v5.6. Annars så kan du behöva uppdatera den genom att köra npm update -g. När du har listat ut npm så kan du nu konfigurera ett React-projekt genom att köra kommandot nedan:

npx create-react-app chatgpt-clone

Observera: ”chatgpt-clone” är det applikationsnamn som vi skapar, men du kan ändra detta till ett valfritt namn.

Installationsprocessen kan sedan ta några minuter. När den har lyckats så kan du därefter navigera till katalogen och installera Node.js OpenAI-paketet. Det ger sedan en bekväm tillgång till OpenAI API: et från Node.js med hjälp av kommandot nedan:

Se din applikation live

npm install openai

Du kan nu köra npm start för att se din applikation live på localhost:3000.

När ett React-projekt skapas med kommandot create-react-app skapas automatiskt en mappstruktur. Den viktigaste mappen som berör dig är mappen src, där utvecklingen sker. Den här mappen inkluderar många filer som standard, men du bör endast bry dig om filerna App.js, index.js och index.css.

  1. App.js: Denna fil är huvudkomponenten i en React-applikation. Den representerar vanligtvis den komponent på högsta nivå som renderar alla andra komponenter i applikationen.
  2. index.js: Den här filen är startpunkten för din React-applikation. Det är den första filen som laddas när appen öppnas och ansvarar för att App.js-komponenten visas i webbläsaren.
  3. index.css: Den här filen är ansvarig för att definiera den övergripande stilen och layouten för din React-applikation.

Hur man bygger en ChatGPT-klon med React och OpenAI API

ChatGPT-klonapplikationen kommer att bestå av två komponenter för att göra applikationen lättare att förstå och underhålla. Dessa två komponenter är:

  1. Formulärsektionen: Den här komponenten innehåller ett textfält och en knapp för att användarna ska kunna interagera med chattroboten.
  2. Svarsdel: Frågorna och motsvarande svar lagras i en array och visas i denna sektion. Du kommer att gå igenom arrayen kronologiskt och visa det senaste först.

Inställning av ChatGPT-klonapplikationen

I den här handledningen så börjar vi med att först bygga applikationsgränssnittet. Som ett resultat så kan du sedan implementera funktionalitet så att din applikation interagerar med OpenAI API. Börja med att skapa de två komponenter som du kommer att använda i den här handledningen. För att få en ordentlig organisering så skapar du sedan en komponentmapp i src-mappen där alla komponenter kommer att lagras.

Komponent för formulärsektionen

Det här är ett enkelt formulär som består av en textarea och en sänd-button.


// 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;

Så här förväntas formuläret se ut när du importerar det till din App.js-fil:

Komponent för formulärsektionen
Komponent för formulärsektionen

Komponenten för svarsavsnittet

I det här avsnittet så kommer alla frågor och svar att visas. Så här kommer avsnittet att se ut när du även importerar det i din App.js-fil.

Komponent för svarssektionen
Komponent för svarssektionen

Du kommer att hämta frågorna och svaren från en matris och en slinga för att göra din kod lättare att läsa och underhålla.

// 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;

Hemsidan

Nu har du skapat båda komponenterna. Inget kommer dock att visas när du kör applikationen eftersom du måste importera dem i din App.js-fil. För den här applikationen så kommer du inte att implementera någon form av routing. Som ett resultat så kommer App.js-filen att fungera som applikationens hemkomponent/sida.

Du kan lägga till lite innehåll, som titeln och beskrivningen av din applikation, innan du importerar komponenterna.

// 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;

I koden ovan så importeras de två komponenterna och läggs till i applikationen. När du kör applikationen så kommer den att se ut så här:

Komplett ChatGPT-klonapplikation
Komplett ChatGPT-klonapplikation

Lägga till funktionalitet och integrera OpenAI API

Du har nu användargränssnittet för din applikation. Nästa steg är att göra applikationen funktionell så att den kan interagera med OpenAI API och få svar. Först så måste du hämta värdet från ditt formulär när det skickas eftersom det kommer att användas för att fråga OpenAI API.

Hämta data från formuläret

I React så är det bästa sättet att lagra och uppdatera data att använda tillstånd. I funktionella komponenter används kroken useState() för att arbeta med tillstånd. Du kan exempelvisd skapa ett tillstånd, tilldela värdet från ditt formulär till tillståndet och uppdatera detta när värdet ändras. Vi börjar med att importera useState()-kroken till komponenten FormSection.jsx och skapar sedan ett tillstånd för att lagra och uppdatera 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;

Därefter så kan du tilldela värdet av fältet textarea till tillståndet och skapa en händelse onChange() för att uppdatera tillståndet när inmatningsvärdet ändras:

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

Skapa en onClick-händelse

Slutligen så kan du skapa en onClick()-händelse för att ladda en funktion när du klickar på knappen Sänd. Metoden skapas i filen App.js och skickas som en prop till komponenten FormSection.jsx med värdena newQuestion och setNewQuestion som argument.

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

Du har nu skapat ett tillstånd för att lagra och uppdatera formulärets värde, lagt till en metod som skickas som prop från App.js-filen och hanterat klickhändelsen. Så här kommer sedan den slutliga koden att se ut:

// 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;

Nästa steg blir att skapa en metod i App.js-filen för att hantera hela processen för att interagera med OpenAI API.

Interaktion med OpenAI API

För att interagera med OpenAI API och få API-nycklar i en React-applikation så måste du skapa ett OpenAI API-konto. Du kan registrera dig för ett konto på OpenAI’s webbplats med hjälp av ditt Google-konto eller din e-post. För att generera en API-nyckel så klickar du på Personligt i det övre högra hörnet på webbplatsen; några alternativ visas; klicka på Visa API-nycklar.

Få tillgång till OpenAI API-nycklar.
Få tillgång till OpenAI API-nycklar.

Klicka på knappen Skapa ny hemlig nyckel, kopiera nyckeln någonstans så att du kan använda den i den här applikationen för att interagera med OpenAI. Du kan nu fortsätta att initiera OpenAI genom att importera paketet openai (som du redan har installerat) tillsammans med konfigurationsmetoden. Skapa sedan en konfiguration med din genererade nyckel och använd den för att initiera 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;

I koden ovan så lagras OpenAI’s API-nyckel som en miljövariabel i .env-filen. Du kan skapa en .env-fil i din applikations rotmapp och lagra nyckeln i variabeln REACT_APP_OPENAI_API_KEY.

// .env
REACT_APP_OPENAI_API_KEY = sk-xxxxxxxxxx…

Nu kan du skapa metoden generateResponse i filen App.js och skicka in de två parametrar som förväntas från det formulär som du redan har skapat för att hantera begärandet och få svar från API: et.

// 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;

Skicka en begäran till OpenAI API

Det är nu möjligt att skicka en begäran till OpenAI API. OpenAI API kan utföra många operationer, exempelvis frågor och svar (Q&A), grammatikkorrigering, översättning och mycket mer. För var och en av dessa operationer så är alternativen olika. Motorvärdet för Q&A är exempelvis text-davinci-00, medan motorvärdet för SQL translate är code-davinci-002. Du får gärna kolla i OpenAI:s exempeldokumentation för de olika exemplen och deras alternativ.

I den här handledningen så arbetar vi endast med Q&A, så här ser alternativet ut:

{
  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: [""],
}

Observera: Jag har ändrat värdet för prompten.

Prompten är den fråga som skickas från formuläret. Som ett resultat så måste du få den från den formulärinmatning som du skickar in i generateResponse-metoden som en parameter. För att göra detta så definierar du alternativen och använder sedan spridningsoperatorn för att skapa ett komplett alternativ som innehåller prompten:

// 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;

Nu återstår bara att skicka en begäran via metoden createCompletion till OpenAI för att få ett svar.

// 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;

Testa din applikation

I koden ovan så kommer svarets text att visas i konsolen. Testa gärna din applikation genom att ställa vilken fråga som helst. Det sista steget skulle sedan vara att skapa ett tillstånd som kommer att hålla matrisen av frågor och svar och sedan skicka denna matris som en prop till komponenten AnswerSection. Så här kommer den slutliga koden för App.js att se ut:

// 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;

Redigera komponenten AnswerSection

Du kan nu redigera komponenten AnswerSection så att den tar emot värdet för prop från App.js och använda JavaScript-metoden Map() för att titta igenom matrisen 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;

När du kör applikationen och testar den genom att ställa frågor så visas svaret nedan. Men du kommer exempelvis att märka att kopieringsknappen inte fungerar. Du måste därför lägga till en onClick()-händelse till knappen så att den utlöser en metod för att hantera funktionaliteten. Nyttja metoden navigator.clipboard.writeText() för att hantera funktionaliteten. Så här kommer komponenten AnswerSection nu att se ut:

// 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;

När du kör applikationen så kommer din ChatGPT-klon att fungera perfekt. Du kan nu distribuera din applikation för att få tillgång till den online och dela den med vänner.

Så här distribuerar du din React-applikation till Kinsta

Det räcker dock inte att bygga den här applikationen och lämna den på dina lokala datorer. Du bör dela den på nätet så att andra kan få tillgång till den. Låt oss se hur du gör detta med hjälp av exempelvis GitHub och Kinsta.

Skicka din kod till GitHub

För att skicka din kod till GitHub så kan du använda Git-kommandon. Detta är ett pålitligt och effektivt sätt att hantera kodändringar, samarbeta i projekt och upprätthålla versionshistorik.

Det första steget för att pusha dina koder är att skapa ett nytt arkiv genom att logga in på ditt GitHub-konto, klicka sedan på +-knappen i det övre högra hörnet av skärmen och välja Nytt arkiv i rullgardinsmenyn.

Skapa ett nytt arkiv på GitHub
Skapa ett nytt arkiv på GitHub

Ge ditt arkiv ett namn, lägg till en beskrivning (valfritt) och välj om du vill att det ska vara offentligt eller privat. Klicka på Skapa arkiv för att skapa det.

När ditt arkiv har skapats så ska du sedan se till att du får tag på webbadressen till arkivet från huvudsidan för ditt arkiv, som du behöver för att skicka din kod till GitHub.

Få tillgång till webbadressen för ditt arkiv
Få tillgång till webbadressen för ditt arkiv

Öppna terminalen eller kommandotolken och navigera till katalogen som innehåller ditt projekt. Kör följande kommandon ett efter ett för att skicka din kod till ditt GitHub-arkiv:

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

git init initialiserar ett lokalt Git-arkiv, git add . lägger till alla filer i den aktuella katalogen och dess underkataloger till det nya Git-arkivet. git commit -m "my first commit" överför sedan ändringarna till arkivet med ett kort meddelande. git remote add origin [repository URL] anger webbadressen för arkivet som fjärrarkivet och git push -u origin master skjuter koden till fjärrarkivet (ursprunget) i master-gren.

Distribuera din ChatGPT Clone React-applikation till Kinsta

För att distribuera ditt arkiv till Kinsta så följer du de här stegen:

  1. Logga in på eller skapa ditt Kinsta-konto på instrumentpanelen MyKinsta.
  2. Klicka på Applikationer i det vänstra sidofältet och klicka sedan på Lägg till tjänst.
  3. Välj Applikation i rullgardinsmenyn för att distribuera en React-applikation till Kinsta.
  4. Välj det arkiv som du vill distribuera från den modala dialogrutan som visas. Om du har flera grenar så kan du välja den som du vill distribuera och tilldela applikationen ett namn. Välj en datacenterplats bland de tillgängliga 25. Som ett resultat så kommer Kinsta automatiskt att upptäcka ett startkommando.
  5. Slutligen så är det inte säkert att skicka ut API-nycklar till offentliga hostar som GitHub, den lades till som en miljövariabel lokalt. När du är host så kan du även lägga till den som en miljövariabel med samma variabelnamn och nyckeln som värde.
Distribuera ChatGPT-klon till Kinsta.
Distribuera ChatGPT-klon till Kinsta.

Din applikation börjar distribueras och inom några minuter så kommer en länk att tillhandahållas för att komma åt den distribuerade versionen av din applikation. I det här fallet så är detta https://chatgpt-clone-g9q10.kinsta.app/

OBS: Du kan aktivera automatisk distribuering, så att Kinsta distribuerar din applikation på nytt när du ändrar din kodbas och skickar den till GitHub.

Sammanfattning

OpenAI API kan användas för att bygga ett stort antal potentiella tillämpningar. Det kan exempelvis handla om allt ifrån kundsupport och personliga hjälpredor till språköversättning och innehållsskapande.

I den här handledningen så har du lärt dig hur man bygger en ChatGPT-klonapplikation med React och OpenAI. Du kan sedan integrera den här applikationen/funktionen i andra applikationer för att ge användarna människoliknande samtalsupplevelser.

Det finns mer om vad som kan göras med OpenAI API och hur man kan förbättra den här klonapplikationen. Du kan exempelvis implementera lokal lagring så att tidigare frågor och svar inte försvinner även när du uppdaterar din webbläsare.

Med Kinsta’s kostnadsfria provperiod och Hobbynivå så kan du enkelt komma igång utan kostnad med vårt applikationshosting. Så varför inte ge det ett försök och se vad du kan skapa?

Dela med dig av ditt projekt och dina erfarenheter i kommentaren nedan.

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.