I takt med at brugen af chatbots og virtuelle assistenter fortsætter med at vokse, søger mange virksomheder og udviklere efter måder at skabe deres egne AI-drevne chatbots på. ChatGPT er en sådan chatbot, der er skabt af OpenAI, og som er i stand til at indgå i menneskelignende samtaler og besvare en bred vifte af spørgsmål.

Hvad du skal bygge

I denne tutorial lærer du at bygge en ChatGPT-klonapplikation ved hjælp af React og OpenAI API’et. Hvis du vil prøve kræfter med et sjovt og engagerende projekt i løbet af weekenden, er dette en god mulighed for at dykke ned i React og OpenAI.

Du vil også lære, hvordan du kan distribuere direkte fra dit GitHub-repository til Kinas Applikation Hosting-platform, som giver et gratis .kinsta.app-domæne, så dit projekt hurtigt kan gå i luften. Og med Kinstas gratis prøveperiode og Hobby Tier kan du nemt komme i gang uden omkostninger.

Her er en live-demo af ChatGPT-klonapplikationen.

ChatGPT klon applikation
ChatGPT klon applikation

Hvis du gerne vil undersøge dette projekt nærmere, kan du få adgang til dets GitHub-repositorium.

Alternativt, ved at bruge denne start projektskabelon, kan du vælge Brug denne skabelon > Opret et nyt repository – dette vil kopiere startkoden til et nyt repository. Dette startprojekt kommer med grundlæggende elementer såsom stilarter, Font Awesome CDN-link, OpenAi-pakke og grundlæggende struktur for at hjælpe dig med at komme i gang.

Krav/forudsætninger

Denne tutorial er designet til at være en “follow-along”-oplevelse. Derfor anbefales det, at du har følgende for at kunne kode sammen med med lethed:

Hvad er OpenAI API?

OpenAI API er en cloud-baseret platform, der giver udviklere adgang til OpenAI’s sprogmodeller, såsom GPT-3, via et API. Det gør det muligt for udviklere at tilføje funktioner til behandling af naturligt sprog som tekstkomplettering, følelsesanalyse, opsummering og oversættelse til deres applikationer uden at udvikle og træne deres modeller.

For at bruge OpenAI API’et skal udviklere oprette en konto på OpenAI’s websted og få en API-nøgle. API-nøglen bruges til at autentificere API-forespørgsler og spore brugen.

Når API-nøglen er opnået, kan udviklere bruge API’et til at sende tekst til sprogmodellen og modtage svar.

Hvorfor React?

React er et populært JavaScript-bibliotek til opbygning af brugergrænseflader. Ifølge Stack Overflow-udviklerundersøgelsen fra 2022 er det den næstmest anvendte webteknologi med 42,62% af markedsandelen.

React giver udviklere mulighed for at oprette deklarative komponenter, der repræsenterer forskellige dele af brugergrænsefladen. Disse komponenter defineres ved hjælp af en syntaks kaldet JSX, som er en kombination af JavaScript og HTML.

Takket være det store økosystem af komponentbiblioteker og kits kan udviklere nemt arbejde med og integrere API’er som OpenAI API’et for at bygge komplekse chatgrænseflader, og det er det, der gør det til et fremragende valg til at bygge en ChatGPT-klonapplikation.

Sådan opstiller du dit React-udviklingsmiljø

Den bedste måde at installere React eller oprette et React-projekt på er at installere det med create-react-app. En forudsætning er at have Node.js installeret på din maskine. For at bekræfte, at du har Node installeret, skal du køre følgende kommando i din terminal.

node -v

Hvis den viser en version, så findes den. For at bruge npx skal du sikre dig, at din Node-version ikke er mindre end v14.0.0.0, og at din NPM-version ikke er mindre end v5.6. Ellers kan du blive nødt til at opdatere den ved at køre npm update -g. Når du har fundet ud af npm, kan du nu oprette et React-projekt ved at køre nedenstående kommando:

npx create-react-app chatgpt-clone

Bemærk: “chatgpt-clone” er det applikationnavn, vi opretter, men du kan ændre det til et navn efter eget valg.

Installationsprocessen kan tage et par minutter. Når det er lykkedes, kan du navigere til mappen og installere Node.js OpenAI-pakken, som giver bekvem adgang til OpenAI API’et fra Node.js ved hjælp af nedenstående kommando:

npm install openai

Du kan nu køre npm start for at se din applikation live på localhost:3000.

Når et React-projekt oprettes ved hjælp af kommandoen create-react-app, skaffer det automatisk en mappestruktur. Den vigtigste mappe, der vedrører dig, er mappen src, som er den mappe, hvor udviklingen finder sted. Denne mappe indeholder som standard mange filer, men du bør kun være bekymret for filerne App.js, index.js og index.css.

  1. App.js: App.js-filen er den vigtigste komponent i en React-applikation. Den repræsenterer typisk den komponent på øverste niveau, der renderer alle andre komponenter i applikationen.
  2. index.js: Denne fil er indgangspunktet for din React-applikation. Det er den første fil, der indlæses, når appen åbnes, og den er ansvarlig for at gengive App.js-komponenten i browseren.
  3. index.css: Denne fil er ansvarlig for at definere den overordnede styling og layout af din React-applikation.

Sådan bygges en ChatGPT-klon med React og OpenAI API

ChatGPT-klonapplikationen vil bestå af to komponenter for at gøre applikationen lettere at forstå og vedligeholde. Disse to komponenter er:

  1. Form Section: Denne komponent indeholder et tekstområdefelt og en knap, så brugerne kan interagere med chatbotten.
  2. Answer Section (svarsektion): Spørgsmålene og de tilsvarende svar gemmes i et array og vises i denne sektion. Du gennemløber arrayet kronologisk og viser det nyeste først.

Opsætning af ChatGPT-klonprogrammet

I denne tutorial starter vi med først at opbygge applikationsgrænsefladen, og derefter kan du implementere funktionalitet, så din applikation interagerer med OpenAI API’et. Start med at oprette de to komponenter, som du vil bruge i denne tutorial. For at sikre en ordentlig organisering opretter du en komponentmappe i src-mappen, hvor alle komponenterne vil blive gemt.

Komponent til formularsektionen

Dette er en simpel formular, der består af en textarea og en submit 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ådan forventes formularen at se ud, når du importerer den i din App.js-fil:

Formsektionskomponent til ChatGPT-klon
Formularafsnitskomponent

Komponent til svarsektionen

Dette afsnit er det sted, hvor alle spørgsmål og svar vil blive vist. Sådan vil dette afsnit se ud, når du også importerer det i din App.js-fil.

Svarsektionskomponent af ChatGPT-klonen
Komponent til svarsektionen

Du henter disse spørgsmål og svar fra et array og en loop for at gøre din kode lettere at læse og vedligeholde.

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

Hjemmesiden

Du har nu oprettet begge komponenter, men der vises ikke noget, når du kører din applikation, fordi du skal importere dem i din App.js-fil. I denne applikation vil du ikke implementere nogen form for routing, hvilket betyder, at App.js-filen vil fungere som applikationens hjemmekomponent/side.

Du kan tilføje noget indhold, som f.eks. titlen og beskrivelsen af din applikation, før du importerer komponenterne.

// 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 ovenfor importeres og tilføjes de to komponenter til applikationen. Når du kører din applikation, vil din applikation se sådan ud:

Komplet ChatGPT-klon applikation
Komplet ChatGPT-klon applikation

Tilføjelse af funktionalitet og integration af OpenAI API

Du har nu brugergrænsefladen for din applikation. Det næste skridt er at gøre applikationen funktionel, så den kan interagere med OpenAI API’et og få svar. Først skal du hente værdien fra din formular, når den er indsendt, fordi den skal bruges til at forespørge OpenAI API’et.

Hentning af data fra formularen

I React er den bedste måde at gemme og opdatere data på at bruge states. I funktionelle komponenter bruges useState() hook’en til at arbejde med states. Du kan oprette en tilstand, tildele værdien fra din formular til tilstanden og opdatere den, når dens værdi ændres. Lad os starte med at importere useState()-krogen til komponenten FormSection.jsx og derefter oprette en tilstand til at gemme og opdatere 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;

Derefter kan du tildele værdien af feltet textarea til tilstanden og oprette en onChange()-hændelse for at opdatere tilstanden, når inputværdien ændres:

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

Endelig kan du oprette en onClick()-begivenhed for at indlæse en funktion, når der klikkes på indsendelsesknappen. Denne metode oprettes i filen App.js og sendes som en props til komponenten FormSection.jsx med værdierne newQuestion og setNewQuestion som argumenter.

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

Du har nu oprettet en tilstand til lagring og opdatering af formularens værdi, tilføjet en metode, der overføres som props fra App.js-filen, og håndteret klikbegivenheden. Sådan vil den endelige kode se ud:

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

Det næste skridt bliver at oprette en metode i App.js-filen for at håndtere hele processen med at interagere med OpenAI API.

Interaktion med OpenAI API

For at interagere med OpenAI API og få API-nøgler i en React-applikation skal du oprette en OpenAI API-konto. Du kan tilmelde dig en konto på OpenAI-webstedet ved hjælp af din Google-konto eller e-mail. For at generere en API-nøgle skal du klikke på Personligt øverst til højre på webstedet; der vises nogle muligheder; klik på Vis API-nøgler.

Få adgang til OpenAI API-nøgler
Få adgang til OpenAI API-nøgler.

Klik på knappen Opret ny hemmelig nøgle; kopier nøglen et sted, som du vil bruge den i denne applikation til at interagere med OpenAI. Du kan nu fortsætte med at initialisere OpenAI ved at importere pakken openai (du allerede har installeret) sammen med konfigurationsmetoden. Derefter opretter du en konfiguration med din genererede nøgle og bruger den til at initialisere 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 ovenfor er OpenAI API-nøglen gemt som en miljøvariabel i .env-filen. Du kan oprette en .env-fil i rodmappen for dit program og gemme nøglen i variablen REACT_APP_OPENAI_API_KEY.

// .env
REACT_APP_OPENAI_API_KEY = sk-xxxxxxxxxx…

Du kan nu fortsætte med at oprette metoden generateResponse i filen App.js og indsætte de to parametre, der forventes fra den formular, du allerede har oprettet for at håndtere anmodningen og få svar fra 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;

Du kan nu sende en anmodning til OpenAI API’et. OpenAI API’et kan udføre mange operationer, f.eks. spørgsmål og svar (Q&A), grammatikkorrektion, oversættelse og meget mere. For hver af disse operationer er mulighederne forskellige. For eksempel er motorværdien for Q&A text-davinci-00, mens den for SQL translate er code-davinci-002. Du er velkommen til at tjekke OpenAI-eksempeldokumentationen for de forskellige eksempler og deres indstillinger.

I denne vejledning arbejder vi kun med Q&A, sådan ser indstillingen ud:

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

Bemærk: Jeg har ændret prompt-værdien.

Prompten er det spørgsmål, der sendes fra formularen. Det betyder, at du skal modtage det fra det formularinput, som du sender ind i generateResponse-metoden som en parameter. For at gøre dette skal du definere indstillingerne og derefter bruge spredningsoperatoren til at oprette en komplet indstilling, der indeholder 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;

På dette tidspunkt er det, der er tilbage, at sende en anmodning via metoden createCompletion til OpenAI for at få et 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;

I koden ovenfor vises svarets tekst på din konsol. Du er velkommen til at teste din applikation ved at stille et hvilket som helst spørgsmål. Det sidste trin ville være at oprette en tilstand, der vil indeholde arrayet af spørgsmål og svar, og derefter sende dette array som en prop til AnswerSection-komponenten. Sådan vil den endelige kode i App.js se ud:

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

Du kan nu redigere komponenten AnswerSection, så den modtager props-værdien fra App.js, og bruge JavaScript-metoden Map() til at kigge gennem arrayet 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ører din applikation og tester den ved at stille spørgsmål, vises svaret nedenfor. Men du vil bemærke, at knappen “Kopier” ikke virker. Du skal tilføje en onClick() begivenhed til knappen, så den udløser en metode til at håndtere funktionaliteten. Du kan bruge metoden navigator.clipboard.writeText() til at håndtere funktionaliteten. Sådan vil komponenten AnswerSection nu se ud:

// 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ører din applikation, vil din ChatGPT-klonapplikation fungere perfekt. Du kan nu implementere din applikation for at få adgang til den online og dele den med venner.

Sådan distribuerer du din React-applikation til Kinsta

Det er ikke nok at bygge denne applikation og lade den ligge på dine lokale computere. Du vil gerne dele den online, så andre kan få adgang til den. Lad os se, hvordan du gør dette ved hjælp af GitHub og Kinsta.

Skub din kode til GitHub

For at skubbe din kode til GitHub kan du bruge Git-kommandoer, som er en pålidelig og effektiv måde at administrere kodeændringer, samarbejde om projekter og vedligeholde versionshistorik.

Det første skridt til at skubbe dine koder vil være at oprette et nyt repository ved at logge ind på din GitHub-konto, klikke på knappen + i øverste højre hjørne af skærmen og vælge Nyt repository i rullemenuen.

Opret et nyt repository på GitHub
Opret et nyt repository på GitHub

Giv dit repository et navn, tilføj en beskrivelse (valgfrit), og vælg, om det skal være offentligt eller privat. Klik på Create repository (Opret repository) for at oprette det.

Når dit repository er oprettet, skal du sikre dig, at du får repository-URL’en fra hovedsiden for dit repository, som du skal bruge til at skubbe din kode til GitHub.

Få adgang til dit repositoriums URL
Få adgang til dit repositoriums URL

Åbn din terminal eller kommando prompt, og naviger til den mappe, der indeholder dit projekt. Kør følgende kommandoer en efter en for at skubbe din kode til dit GitHub-repositorium:

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

git init initialiserer et lokalt Git-repositorium, git add . tilføjer alle filer i den aktuelle mappe og dens undermapper til det nye Git-repositorium. git commit -m "my first commit" committerer ændringerne til repositoriet med en kort meddelelse. git remote add origin [repository URL] indstiller repositoriets URL som fjernrepositoriet, og git push -u origin master skubber koden til fjernrepositoriet (origin) i master-branchen.

Implementer din ChatGPT-klonede React-applikation til Kinsta

Følg disse trin for at distribuere dit repository til Kinsta:

  1. Log ind på eller opret din Kinsta-konto på instrumentbrættet MyKinsta-dashboard.
  2. Klik på Applikationer i venstre sidepanel, og klik derefter på Tilføj tjeneste.
  3. Vælg Applikation i rullemenuen for at distribuere en React-applikation til Kinsta.
  4. Vælg det repository, du ønsker at implementere, i den modal, der vises. Hvis du har flere filialer, kan du vælge den filial, du vil implementere, og tildele et navn til applikationen. Vælg en datacenterplacering blandt de tilgængelige 25, og Kinsta vil automatisk registrere en startkommando.
  5. Endelig er det ikke sikkert at skubbe API-nøgler ud til offentlige værter som GitHub, det blev tilføjet som en miljøvariabel lokalt. Når du hoster, kan du også tilføje den som en miljøvariabel ved at bruge det samme variabelnavn og nøglen som værdi.
Implementering af klon til Kinsta
Implementering af klon til Kinsta

Din applikation vil begynde at blive implementeret, og inden for få minutter vil der blive givet et link til at få adgang til den implementerede version af din applikation. I dette tilfælde er dette https://chatgpt-clone-g9q10.kinsta.app/

Note: Du kan aktivere automatisk deployering, så Kinsta vil implementere din applikation, når du ændrer din kodebase og skubber den til GitHub.

Opsummering

OpenAI API’et kan bruges til at opbygge en lang række potentielle applikationer, lige fra kundesupport og personlige assistenter til sprogoversættelse og indholdsskabelse.

I denne tutorial har du lært at bygge en ChatGPT-klonapplikation med React og OpenAI. Du kan integrere denne applikation/funktion i andre applikationer for at give menneskelignende samtaleoplevelser til brugerne.

Der er mere til, hvad der kan gøres med OpenAI API, og hvordan man kan forbedre denne klonapplikation. Du kan f.eks. implementere lokal lagring, så tidligere spørgsmål og svar ikke forsvinder, selv når du opdaterer din browser.

Med Kinsta’s gratis prøveperiode og Hobby Tier kan du nemt komme i gang uden omkostninger på vores Applikation Hosting. Så hvorfor ikke give det en chance og se, hvad du kan skabe?

Del dit projekt og dine erfaringer i kommentaren nedenfor.

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.