如何用OpenAI、ChatGPT、Node.js和React搭建一個AI聊天機器人

如何用OpenAI、ChatGPT、Node.js和React搭建一個AI聊天機器人

人工智慧(AI)最近一直在掀起波瀾,ChatGPTChat completions徹底改變了網際網路。

你可以用它做很多事情:起草電子郵件或其他文章,回答關於一組檔案的問題,建立對話代理,給你的軟體一個自然語言介面,輔導各種科目,翻譯語言,等等。本教程使用Chat completions功能建立一個AI聊天應用程式的基本知識,使每個程式設計師都能輕鬆上手。它並不像看起來那樣艱難。在你跟隨本教程時,你會看到這一點。

您將學到以下內容:

  • 如何只用Node.js建立一個CLI聊天應用程式。
  • 如何只用React建立一個聊天應用。
  • 如何結合React和Node.js來建立更好的聊天AI軟體。

本教程將以 gpt-3.5-turbo 模型為基礎。

前提條件

本教程需要JavaScript、CSS、React和Node.js的基本知識。你還需要一個OpenAI平臺的賬戶,chatGPT就在這個平臺上。它是免費的,所以你可以在這裡建立一個。

如何用Node.js建立一個CLI聊天AI應用程式

本節將重點介紹建立一個只在終端使用Node.js執行的聊天應用程式。

首先,為該專案建立一個目錄:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
mkdir nodejs-chatgpt-tutorial
mkdir nodejs-chatgpt-tutorial
mkdir nodejs-chatgpt-tutorial

導航到該資料夾:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
cd nodejs-chatgpt-tutorial
cd nodejs-chatgpt-tutorial
cd nodejs-chatgpt-tutorial

初始化該專案:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
npm init -y
npm init -y
npm init -y

這將建立一個 package.json 檔案來跟蹤專案的細節

在該檔案中新增以下一行程式碼:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
"type": "module"
"type": "module"
"type": "module"

這將使你能夠使用ES6模組的匯入語句。用以下命令安裝OpenAI

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
npm i openai
npm i openai
npm i openai

建立一個檔案,所有的程式碼都在其中。命名為 index.js

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
touch index.js
touch index.js
touch index.js

OpenAI模組匯入 ConfigurationOpenAIApi ,從 readline 模組匯入readline

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import { Configuration, OpenAIApi } from "openai";
import readline from "readline";
import { Configuration, OpenAIApi } from "openai"; import readline from "readline";
import { Configuration, OpenAIApi } from "openai";
import readline from "readline";

像這樣建立OpenAI的配置:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
const configuration = new Configuration({
organization: "org-0nmrFWw6wSm6xIJXSbx4FpTw",
apiKey: "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",
});
const configuration = new Configuration({ organization: "org-0nmrFWw6wSm6xIJXSbx4FpTw", apiKey: "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", });
const configuration = new Configuration({
organization: "org-0nmrFWw6wSm6xIJXSbx4FpTw",
apiKey: "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",
});

這段程式碼建立了一個 Configuration 物件的新例項。在它裡面,你將輸入你的 organizationapiKey 的值。你可以在設定中找到你的組織的詳細資訊,在API金鑰中找到你的apiKey資訊。如果你沒有現有的API Key,你可以建立它。在配置後輸入以下程式碼,建立一個新的OpenAI API例項:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
const openai = new OpenAIApi(configuration);
const openai = new OpenAIApi(configuration);
const openai = new OpenAIApi(configuration);

你將在整個專案中使用它。

輸入下面的程式碼來測試 createChatCompletion 函式:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
openai
.createChatCompletion({
model: "gpt-3.5-turbo",
messages: [{ role: "user", content: "Hello" }],
})
.then((res) => {
console.log(res.data.choices[0].message.content);
})
.catch((e) => {
console.log(e);
});
openai .createChatCompletion({ model: "gpt-3.5-turbo", messages: [{ role: "user", content: "Hello" }], }) .then((res) => { console.log(res.data.choices[0].message.content); }) .catch((e) => { console.log(e); });
openai
.createChatCompletion({
model: "gpt-3.5-turbo",
messages: [{ role: "user", content: "Hello" }],
})
.then((res) => {
console.log(res.data.choices[0].message.content);
})
.catch((e) => {
console.log(e);
});

這段程式碼呼叫 createChatCompletion 函式,觸發一個端點( https://api.openai.com/v1/chat/completions )。該函式接受一個引數物件(使用中的chatGPT model 和使用者與AI之間的 messages 陣列。我們將在下一節中研究如何使用 messages 陣列來儲存聊天曆史並改進應用程式)。每個訊息都是一個物件,包含 role(即誰傳送了該訊息。如果是來自人工智慧,該值可以是助理,如果是來自人類的訊息,該值可以是使用者)和 content(傳送的資訊)。最後,程式碼列印了來自人工智慧的響應( res.data.choice[0].message.content )。用這個命令在終端執行該檔案:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
node index
node index
node index

這將在幾秒鐘後返回人工智慧的響應。這就是建立聊天機器人的全部內容!但通過請求使用者輸入資訊而不是將資訊內容硬編碼到程式碼中,使應用程式更具互動性將是很有幫助的。readline模組將在這方面幫助我們。要使其具有互動性,請刪除你最後輸入的程式碼,並新增以下內容:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
const userInterface = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
const userInterface = readline.createInterface({ input: process.stdin, output: process.stdout, });
const userInterface = readline.createInterface({
input: process.stdin,
output: process.stdout,
});

這段程式碼在終端建立了一個使用者介面,允許使用者輸入他們的問題。

接下來,用下面的程式碼提示使用者輸入一個資訊:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
userInterface.prompt();
userInterface.prompt();
userInterface.prompt();

最後,輸入以下程式碼:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
userInterface.on("line", async (input) => {
await openai
.createChatCompletion({
model: "gpt-3.5-turbo",
messages: [{ role: "user", content: input }],
})
.then((res) => {
console.log(res.data.choices[0].message.content);
userInterface.prompt();
})
.catch((e) => {
console.log(e);
});
});
userInterface.on("line", async (input) => { await openai .createChatCompletion({ model: "gpt-3.5-turbo", messages: [{ role: "user", content: input }], }) .then((res) => { console.log(res.data.choices[0].message.content); userInterface.prompt(); }) .catch((e) => { console.log(e); }); });
userInterface.on("line", async (input) => {
await openai
.createChatCompletion({
model: "gpt-3.5-turbo",
messages: [{ role: "user", content: input }],
})
.then((res) => {
console.log(res.data.choices[0].message.content);
userInterface.prompt();
})
.catch((e) => {
console.log(e);
});
});

在上面的程式碼中

  • 當使用者輸入東西並點選 Enter 時,上面的程式碼會觸發一個回撥函式。
  • 它將使用者輸入的任何內容作為 input
  •  input 的內容現在被用作 content
  • 在顯示人工智慧的響應後,在 then 塊中提示使用者輸入另一條資訊。

檢視GitHub上的所有程式碼。執行該檔案並與人工智慧進行對話。它將看起來像下面的圖片:

與AI的CLI聊天

與AI的CLI聊天

很好! 這是一個互動式CLI聊天。這對少數人(如工程師)很有用,但它有很好的安全性,因為它是在伺服器端。但其他可能不瞭解如何使用CLI應用程式的人呢?他們將需要一些更容易使用的、具有更好的使用者介面(UI)和使用者體驗(UX)的東西。下一節將重點介紹使用React構建這種應用程式。

如何使用React建立一個聊天應用程式

本節旨在幫助前端開發者快速掌握ChatGPT API,以建立一個聊天應用程式,並構建一個更好的使用者介面,給使用者帶來更好的體驗。你可以把在這裡獲得的知識應用於其他前端框架或庫。

首先要做的是設定一個基本的React模板。我將使用Vite來實現這一目的。你可以用Vite來搭建任何現代JavaScript前端專案的腳手架。使用下面的命令:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
npm create vite@latest
npm create vite@latest
npm create vite@latest

該命令將提示你為你的專案建立一個名稱和資料夾,並選擇一個框架或庫(本教程使用React)。之後,你將導航到該資料夾,並執行以下命令:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
npm install
npm run dev
npm install npm run dev
npm install
npm run dev

這些命令將安裝必要的依賴性,並在 5173 埠啟動本地伺服器,接下來,用以下命令安裝OpenAI

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
npm i openai
npm i openai
npm i openai

這個模組提供了我們建立聊天應用程式所需的所有許可權。現在我們準備開始寫程式碼了!導航到 src/App.jsx 檔案,刪除其所有內容。然後新增以下匯入語句:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import { useState } from "react";
import { Configuration, OpenAIApi } from "openai";
import { useState } from "react"; import { Configuration, OpenAIApi } from "openai";
import { useState } from "react";
import { Configuration, OpenAIApi } from "openai";

上面的程式碼匯入了用於設定配置值的 Configuration 和用於讓我們訪問Chat completions的 OpenAIApi 。之後,像這樣建立配置:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
const configuration = new Configuration({
organization: "org-0nmrFWw6wSm6xIJXSbx4FpTw",
apiKey: "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",
});
const configuration = new Configuration({ organization: "org-0nmrFWw6wSm6xIJXSbx4FpTw", apiKey: "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", });
const configuration = new Configuration({
organization: "org-0nmrFWw6wSm6xIJXSbx4FpTw",
apiKey: "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",
});

這段程式碼建立了一個 Configuration 物件的新例項。在它裡面,你輸入你的 organization 和 apiKey 的值。你可以在設定中找到你的組織的詳細資訊,你的apiKey資訊在API金鑰中。如果你沒有現有的API金鑰,你可以建立它。在配置後輸入以下程式碼,建立一個新的OpenAI API例項:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
const openai = new OpenAIApi(configuration);
const openai = new OpenAIApi(configuration);
const openai = new OpenAIApi(configuration);

我們將在整個專案中使用它。建立並匯出一個預設函式:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
function App() {
return (
<main>
<h1>Chat AI Tutorial</h1>
<main/>
);
}
export default App;
function App() { return ( <main> <h1>Chat AI Tutorial</h1> <main/> ); } export default App;
function App() {
return (
<main>
<h1>Chat AI Tutorial</h1>
<main/>
);
}
export default App;

這個函式將容納其餘的程式碼。在 return 語句之前設定以下狀態:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
const [message, setMessage] = useState("");
const [chats, setChats] = useState([]);
const [isTyping, setIsTyping] = useState(false);
const [message, setMessage] = useState(""); const [chats, setChats] = useState([]); const [isTyping, setIsTyping] = useState(false);
  const [message, setMessage] = useState("");
const [chats, setChats] = useState([]);
const [isTyping, setIsTyping] = useState(false);
  • message 將儲存從應用程式傳送至人工智慧的資訊。
  • chats 陣列將記錄雙方(使用者和人工智慧)傳送的所有資訊。
  • isTyping 變數將通知使用者,機器人是否正在打字。

在h1標籤下輸入以下幾行程式碼

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<div className={isTyping ? "" : "hide"}>
<p>
<i>{isTyping ? "Typing" : ""}</i>
</p>
</div>
<div className={isTyping ? "" : "hide"}> <p> <i>{isTyping ? "Typing" : ""}</i> </p> </div>
      <div className={isTyping ? "" : "hide"}>
<p>
<i>{isTyping ? "Typing" : ""}</i>
</p>
</div>

上面的程式碼將顯示 Typing ,只要使用者在等待AI的響應。建立一個表單,使用者可以在其中輸入資訊,將下面的程式碼新增到 main 元素中:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<form action="" onSubmit={(e) => chat(e, message)}>
<input
type="text"
name="message"
value={message}
placeholder="Type a message here and hit Enter..."
onChange={(e) => setMessage(e.target.value)}
/>
</form>
<form action="" onSubmit={(e) => chat(e, message)}> <input type="text" name="message" value={message} placeholder="Type a message here and hit Enter..." onChange={(e) => setMessage(e.target.value)} /> </form>
      <form action="" onSubmit={(e) => chat(e, message)}>
<input
type="text"
name="message"
value={message}
placeholder="Type a message here and hit Enter..."
onChange={(e) => setMessage(e.target.value)}
/>
</form>

這段程式碼建立了一個有一個輸入的表單。每當點選 Enter 鍵提交表單時,就會觸發 chat 函式。聊天函式將接受兩(2)個引數(emessage),像這樣:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
const chat = async (e, message) => {
}
const chat = async (e, message) => { }
const chat = async (e, message) => {
}

在該函式中輸入以下幾行:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
e.preventDefault();
if (!message) return;
setIsTyping(true);
e.preventDefault(); if (!message) return; setIsTyping(true);
    e.preventDefault();
if (!message) return;
setIsTyping(true);

上面的程式碼防止 form 重新載入網頁,檢查提交前是否輸入了資訊,並將 isTyping 設定為 true ,以表明應用程式已經開始處理所提供的輸入。ChatGPT有一個資訊的格式。它採取以下模式:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
{role: user | assistant, content: message to be sent
{role: user | assistant, content: message to be sent
{role: user | assistant, content: message to be sent

每條資訊(content)都必須顯示誰傳送的。當聊天是來自人工智慧時,角色是 assistant,但如果是來自人類,則是 user 。因此,在傳送訊息之前,一定要正確地格式化它,並像這樣把它新增到陣列(chats)中:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
let msgs = chats;
msgs.push({ role: "user", content: message });
setChats(msgs);
setMessage("");
let msgs = chats; msgs.push({ role: "user", content: message }); setChats(msgs); setMessage("");
    let msgs = chats;
msgs.push({ role: "user", content: message });
setChats(msgs);
setMessage("");

上面的最後一行清除了輸入,以便使用者輸入另一個音符。現在我們將通過使用下面的程式碼觸發 createChatCompletion 函式來呼叫 createChatCompletion 端點:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
await openai
.createChatCompletion({
model: "gpt-3.5-turbo",
messages: [
{
role: "system",
content:
"You are a EbereGPT. You can help with graphic design tasks",
},
...chats,
],
})
await openai .createChatCompletion({ model: "gpt-3.5-turbo", messages: [ { role: "system", content: "You are a EbereGPT. You can help with graphic design tasks", }, ...chats, ], })
  await openai
.createChatCompletion({
model: "gpt-3.5-turbo",
messages: [
{
role: "system",
content:
"You are a EbereGPT. You can help with graphic design tasks",
},
...chats,
],
})

createChatCompletion 函式至少需要兩(2)個引數(model 和 messages):

  • 模型指定了正在使用的chatGPT的版本。
  • 訊息是迄今為止使用者和人工智慧之間的所有訊息的列表,以及一個系統訊息,讓人工智慧瞭解它能提供什麼樣的幫助。
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
{
role: "system",
content:
"You are a EbereGPT. You can help with graphic design tasks",
}
{ role: "system", content: "You are a EbereGPT. You can help with graphic design tasks", }
          {
role: "system",
content:
"You are a EbereGPT. You can help with graphic design tasks",
}

你可以把內容改成任何適合你的東西。messages 不一定要在陣列中包含一個以上的物件。它可以只是一條訊息。但是當它是一個陣列時,它提供了一個訊息歷史,人工智慧可以依靠它在未來給出更好的回覆,而且它使使用者打字更少,因為可能沒有必要一直過度描述。 createChatCompletion 函式返回一個承諾。所以使用 then...catch... 塊來獲取響應。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
.then((res) => {
msgs.push(res.data.choices[0].message);
setChats(msgs);
setIsTyping(false);
})
.catch((error) => {
console.log(error);
});
.then((res) => { msgs.push(res.data.choices[0].message); setChats(msgs); setIsTyping(false); }) .catch((error) => { console.log(error); });
      .then((res) => {
msgs.push(res.data.choices[0].message);
setChats(msgs);
setIsTyping(false);
})
.catch((error) => {
console.log(error);
});

這段程式碼將從人工智慧返回的訊息新增到聊天陣列中,並將 isTyping 設定為 false ,表示人工智慧已經完成了回覆。你現在應該在每次傳送訊息時收到反饋(Typing):

聊天應用程式在人工智慧即將作出反應時給予反饋

聊天應用程式在人工智慧即將作出反應時給予反饋

現在是顯示聊天曆史給使用者看的時候了。在 h1 標籤下面輸入以下程式碼:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<section>
{chats && chats.length
? chats.map((chat, index) => (
<p key={index} className={chat.role === "user" ? "user_msg" : ""}>
<span>
<b>{chat.role.toUpperCase()}</b>
</span>
<span>:</span>
<span>{chat.content}</span>
</p>
))
: ""}
</section>
<section> {chats && chats.length ? chats.map((chat, index) => ( <p key={index} className={chat.role === "user" ? "user_msg" : ""}> <span> <b>{chat.role.toUpperCase()}</b> </span> <span>:</span> <span>{chat.content}</span> </p> )) : ""} </section>
      <section>
{chats && chats.length
? chats.map((chat, index) => (
<p key={index} className={chat.role === "user" ? "user_msg" : ""}>
<span>
<b>{chat.role.toUpperCase()}</b>
</span>
<span>:</span>
<span>{chat.content}</span>
</p>
))
: ""}
</section>

上面的程式碼迴圈瀏覽 chats ,並將它們一個接一個地顯示給使用者。它把 role 的大寫字母和訊息的 content 並排輸出。以下是輸出結果的樣子:

聊天機器人在沒有CSS的情況下按預期工作

聊天機器人在沒有CSS的情況下按預期工作

這看起來很酷!但新增一些造型會讓它看起來像WhatsAppMessenger一樣吸引人。用以下內容替換 src/index.css 檔案的內容:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
:root {
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
line-height: 1.5;
font-weight: 400;
color-scheme: light dark;
color: rgba(255, 255, 255, 0.87);
background-color: #242424;
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
-webkit-text-size-adjust: 100%;
}
h1 {
font-size: 3.2em;
line-height: 1.1;
text-align: center;
position: sticky;
top: 0;
background-color: #242424;
}
main{
max-width: 500px;
margin: auto;
}
p{
background-color: darkslategray;
max-width: 70%;
padding: 15px;
border-radius: 50px;
}
p span{
margin: 5px;
}
p span:first-child{
margin-right: 0;
}
.user_msg{
text-align: right;
margin-left: 30%;
display: flex;
flex-direction: row-reverse;
}
.hide {
visibility: hidden;
display: none;
}
form{
text-align: center;
position: sticky;
bottom: 0;
}
input{
width: 100%;
height: 40px;
border: none;
padding: 10px;
font-size: 1.2rem;
}
input:focus{
outline: none;
}
:root { font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; line-height: 1.5; font-weight: 400; color-scheme: light dark; color: rgba(255, 255, 255, 0.87); background-color: #242424; font-synthesis: none; text-rendering: optimizeLegibility; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; -webkit-text-size-adjust: 100%; } h1 { font-size: 3.2em; line-height: 1.1; text-align: center; position: sticky; top: 0; background-color: #242424; } main{ max-width: 500px; margin: auto; } p{ background-color: darkslategray; max-width: 70%; padding: 15px; border-radius: 50px; } p span{ margin: 5px; } p span:first-child{ margin-right: 0; } .user_msg{ text-align: right; margin-left: 30%; display: flex; flex-direction: row-reverse; } .hide { visibility: hidden; display: none; } form{ text-align: center; position: sticky; bottom: 0; } input{ width: 100%; height: 40px; border: none; padding: 10px; font-size: 1.2rem; } input:focus{ outline: none; }
:root {
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
line-height: 1.5;
font-weight: 400;
color-scheme: light dark;
color: rgba(255, 255, 255, 0.87);
background-color: #242424;
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
-webkit-text-size-adjust: 100%;
}
h1 {
font-size: 3.2em;
line-height: 1.1;
text-align: center;
position: sticky;
top: 0;
background-color: #242424;
}
main{
max-width: 500px;
margin: auto;
}
p{
background-color: darkslategray;
max-width: 70%;
padding: 15px;
border-radius: 50px;
}
p span{
margin: 5px;
}
p span:first-child{
margin-right: 0;
}
.user_msg{
text-align: right;
margin-left: 30%;
display: flex;
flex-direction: row-reverse;
}
.hide {
visibility: hidden;
display: none;
}
form{
text-align: center;
position: sticky;
bottom: 0;
}
input{
width: 100%;
height: 40px;
border: none;
padding: 10px;
font-size: 1.2rem;
}
input:focus{
outline: none;
}

並刪除 src/App.css 檔案中的所有樣式。

你可以在GitHub上找到完整的程式碼。現在應用程式應該有一個新的外觀:

聊天機器人如期使用CSS工作

聊天機器人如期使用CSS工作

用React和ChatGPT建立一個聊天機器人的工作就這樣結束了。它並不像聽起來那麼困難。但像這樣的前端應用最好是用於演示,而不是生產。這樣建立應用程式的問題是,前端將API金鑰暴露給網路攻擊。

要解決這個問題,明智的做法可能是將API Key和Organisation Id儲存在雲端某個安全的地方並引用它,或者為你的應用程式建立一個具有更好安全性的後端。下面的部分將致力於解決這個問題。

如何結合React和Node.js來製作一個全棧式的聊天AI軟體

本節現在將加入前幾節的力量,建立一個更安全的應用程式,同時表現出更好的使用者介面和使用者體驗。

我們將改進Node部分,使用伺服器來暴露一個端點供前端使用,並簡化前端與後臺的互動,而不是直接聯絡OpenAI

如何設定專案

這一部分將建立專案所需的資料夾和檔案。建立專案目錄:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
mkdir react-node-chatgpt-tutorial
mkdir react-node-chatgpt-tutorial
mkdir react-node-chatgpt-tutorial

導航到該資料夾:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
cd react-node-chatgpt-tutorial
cd react-node-chatgpt-tutorial
cd react-node-chatgpt-tutorial

使用Vite安裝React,並將資料夾命名為 frontend 。使用這個命令:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
npm create vite@latest
npm create vite@latest
npm create vite@latest

之後,你將瀏覽到該資料夾並執行以下命令:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
npm install
npm run dev
npm install npm run dev
npm install
npm run dev

這些命令將安裝必要的依賴,並在 5173 埠啟動本地伺服器。建立後臺資料夾:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
mkdir backend
mkdir backend
mkdir backend

現在導航到後端資料夾,用這個命令初始化專案:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
npm init -y
npm init -y
npm init -y

這將建立一個 package.json 檔案來跟蹤專案的細節。在該檔案中新增以下一行程式碼:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
"type": "module"
"type": "module"
"type": "module"

這將使ES6模組匯入語句的使用成為可能。用下面的命令安裝OpenAI和其他依賴項:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
npm i openai body-parser cors express
npm i openai body-parser cors express
npm i openai body-parser cors express

建立一個檔案,所有的程式碼都在其中。命名為 index.js

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
touch index.js
touch index.js
touch index.js

這就完成了專案的設定。現在有兩個資料夾(frontend 和 backend)。

如何搭建伺服器

這一部分將著重於建立一個本地伺服器,以監聽 8000 埠。

首先要做的是像這樣匯入必要的模組:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import { Configuration, OpenAIApi } from "openai";
import express from "express";
import bodyParser from "body-parser";
import cors from "cors";
import { Configuration, OpenAIApi } from "openai"; import express from "express"; import bodyParser from "body-parser"; import cors from "cors";
import { Configuration, OpenAIApi } from "openai";
import express from "express";
import bodyParser from "body-parser";
import cors from "cors";

接下來,設定 express 、監聽 port、用於接收輸入的 body-parser 以及允許前端和後端自由通訊的 cors 。使用下面的程式碼:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
const app = express();
const port = 8000;
app.use(bodyParser.json());
app.use(cors());
const app = express(); const port = 8000; app.use(bodyParser.json()); app.use(cors());
const app = express();
const port = 8000;
app.use(bodyParser.json());
app.use(cors());

最後,輸入以下程式碼:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
app.listen(port, () => {
console.log(`listening on port ${port}`);
});
app.listen(port, () => { console.log(`listening on port ${port}`); });
app.listen(port, () => {
console.log(`listening on port ${port}`);
});

這就完成了伺服器的設定。當你執行 index.js 時,你應該得到以下輸出:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
listening on port 8000
listening on port 8000
listening on port 8000

如何建立端點

在這一部分,我們將建立一個端點,該端點將使用請求體接收來自前端的訊息,並向呼叫者返回一個響應。開始時,我們要像前幾節那樣建立配置引數:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
const configuration = new Configuration({
organization: "org-0nmrFWw6wSm6xIJXSbx4FpTw",
apiKey: "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",
});
const openai = new OpenAIApi(configuration);
const configuration = new Configuration({ organization: "org-0nmrFWw6wSm6xIJXSbx4FpTw", apiKey: "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", }); const openai = new OpenAIApi(configuration);
const configuration = new Configuration({
organization: "org-0nmrFWw6wSm6xIJXSbx4FpTw",
apiKey: "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",
});
const openai = new OpenAIApi(configuration);

接下來,使用下面的程式碼建立一個非同步POST路由:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
app.post("/", async (request, response) => {
});
app.post("/", async (request, response) => { });
app.post("/", async (request, response) => {
});

這個端點將使用 http://localhost:8000/,在回撥函式中,輸入以下程式碼,從請求體( request.body )接收 chats 的輸入:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
const { chats } = request.body;
const { chats } = request.body;
const { chats } = request.body;

現在像我們在React部分做的那樣,呼叫 createChatCompletion 端點:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
const result = await openai.createChatCompletion({
model: "gpt-3.5-turbo",
messages: [
{
role: "system",
content: "You are a EbereGPT. You can help with graphic design tasks",
},
...chats,
],
});
const result = await openai.createChatCompletion({ model: "gpt-3.5-turbo", messages: [ { role: "system", content: "You are a EbereGPT. You can help with graphic design tasks", }, ...chats, ], });
  const result = await openai.createChatCompletion({
model: "gpt-3.5-turbo",
messages: [
{
role: "system",
content: "You are a EbereGPT. You can help with graphic design tasks",
},
...chats,
],
});

這裡的區別是,我們沒有使用 then...catch... 塊,而是將其分配給一個變數( result ),並使用 response.json() 返回響應,如以下程式碼:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
response.json({
output: result.data.choices[0].message,
});
response.json({ output: result.data.choices[0].message, });
  response.json({
output: result.data.choices[0].message,
});

GitHub上找到這部分的程式碼。以下是在Postman上測試時的輸出:

來自Postman的輸出

來自Postman的輸出

程式碼的後端部分就這樣結束了。下一部分將使用剛剛建立的端點( http://localhost:8000/ )連線前端和後端。

如何從前端連線到後端

這一部分把我們帶到前臺,在那裡我們將建立一個表單。該表單將通過API端點向後端傳送訊息,並通過相同的媒介接收響應。導航到 frontend/src/App.jsx 檔案並輸入以下程式碼:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import { useState } from "react";
function App() {
const [message, setMessage] = useState("");
const [chats, setChats] = useState([]);
const [isTyping, setIsTyping] = useState(false);
const chat = async (e, message) => {
e.preventDefault();
if (!message) return;
setIsTyping(true);
let msgs = chats;
msgs.push({ role: "user", content: message });
setChats(msgs);
setMessage("");
alert(message);
};
return (
<main>
<h1>FullStack Chat AI Tutorial</h1>
<section>
{chats && chats.length
? chats.map((chat, index) => (
<p key={index} className={chat.role === "user" ? "user_msg" : ""}>
<span>
<b>{chat.role.toUpperCase()}</b>
</span>
<span>:</span>
<span>{chat.content}</span>
</p>
))
: ""}
</section>
<div className={isTyping ? "" : "hide"}>
<p>
<i>{isTyping ? "Typing" : ""}</i>
</p>
</div>
<form action="" onSubmit={(e) => chat(e, message)}>
<input
type="text"
name="message"
value={message}
placeholder="Type a message here and hit Enter..."
onChange={(e) => setMessage(e.target.value)}
/>
</form>
</main>
);
}
export default App;
import { useState } from "react"; function App() { const [message, setMessage] = useState(""); const [chats, setChats] = useState([]); const [isTyping, setIsTyping] = useState(false); const chat = async (e, message) => { e.preventDefault(); if (!message) return; setIsTyping(true); let msgs = chats; msgs.push({ role: "user", content: message }); setChats(msgs); setMessage(""); alert(message); }; return ( <main> <h1>FullStack Chat AI Tutorial</h1> <section> {chats && chats.length ? chats.map((chat, index) => ( <p key={index} className={chat.role === "user" ? "user_msg" : ""}> <span> <b>{chat.role.toUpperCase()}</b> </span> <span>:</span> <span>{chat.content}</span> </p> )) : ""} </section> <div className={isTyping ? "" : "hide"}> <p> <i>{isTyping ? "Typing" : ""}</i> </p> </div> <form action="" onSubmit={(e) => chat(e, message)}> <input type="text" name="message" value={message} placeholder="Type a message here and hit Enter..." onChange={(e) => setMessage(e.target.value)} /> </form> </main> ); } export default App;
import { useState } from "react";
function App() {
const [message, setMessage] = useState("");
const [chats, setChats] = useState([]);
const [isTyping, setIsTyping] = useState(false);
const chat = async (e, message) => {
e.preventDefault();
if (!message) return;
setIsTyping(true);
let msgs = chats;
msgs.push({ role: "user", content: message });
setChats(msgs);
setMessage("");
alert(message);
};
return (
<main>
<h1>FullStack Chat AI Tutorial</h1>
<section>
{chats && chats.length
? chats.map((chat, index) => (
<p key={index} className={chat.role === "user" ? "user_msg" : ""}>
<span>
<b>{chat.role.toUpperCase()}</b>
</span>
<span>:</span>
<span>{chat.content}</span>
</p>
))
: ""}
</section>
<div className={isTyping ? "" : "hide"}>
<p>
<i>{isTyping ? "Typing" : ""}</i>
</p>
</div>
<form action="" onSubmit={(e) => chat(e, message)}>
<input
type="text"
name="message"
value={message}
placeholder="Type a message here and hit Enter..."
onChange={(e) => setMessage(e.target.value)}
/>
</form>
</main>
);
}
export default App;

這段程式碼與上一節的程式碼相似。但我們刪除了OpenAI的配置,因為我們在本節中不再需要它們。

在這一點上,每當表單被提交時,就會彈出一個警報。這一點一會兒就會改變。在聊天函式中,去掉 alert 資訊,然後輸入以下內容:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
fetch("http://localhost:8000/", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
chats,
}),
})
.then((response) => response.json())
.then((data) => {
msgs.push(data.output);
setChats(msgs);
setIsTyping(false);
})
.catch((error) => {
console.log(error);
});
fetch("http://localhost:8000/", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ chats, }), }) .then((response) => response.json()) .then((data) => { msgs.push(data.output); setChats(msgs); setIsTyping(false); }) .catch((error) => { console.log(error); });
fetch("http://localhost:8000/", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
chats,
}),
})
.then((response) => response.json())
.then((data) => {
msgs.push(data.output);
setChats(msgs);
setIsTyping(false);
})
.catch((error) => {
console.log(error);
});

上面的程式碼呼叫了我們建立的端點,並傳入 chats 陣列供其處理。然後它返回一個響應,該響應被新增到 chats 中並顯示在使用者介面上:

樣式設計前的全棧聊天UI

樣式設計前的全棧聊天UI

如果你在 frontend/src/index.css 檔案中新增以下樣式,UI會看起來更好:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
:root {
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
line-height: 1.5;
font-weight: 400;
color-scheme: light dark;
color: rgba(255, 255, 255, 0.87);
background-color: #242424;
font-synthesis: 無;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
-webkit-text-size-adjust: 100%;
}
html, body{
scroll-behavior: smooth;
}
h1 {
font-size: 3.2em;
line-height: 1.1;
text-align: center;
position: sticky;
top: 0;
background-color: #242424;
}
main{
max-width: 800px;
margin: auto;
}
p{
background-color: darkslategray;
max-width: 70%;
padding: 15px;
border-radius: 50px;
}
p span{
margin: 5px;
}
p span:first-child{
margin-right: 0;
}
.user_msg{
text-align: right;
margin-left: 30%;
display: flex;
flex-direction: row-reverse;
}
.hide {
visibility: hidden;
display: none;
}
form{
text-align: center;
position: sticky;
bottom: 0;
}
input{
width: 100%;
height: 40px;
border: none;
padding: 10px;
font-size: 1.2rem;
background-color: rgb(28, 23, 23);
}
input:focus{
outline: none;
}
:root { font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; line-height: 1.5; font-weight: 400; color-scheme: light dark; color: rgba(255, 255, 255, 0.87); background-color: #242424; font-synthesis: 無; text-rendering: optimizeLegibility; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; -webkit-text-size-adjust: 100%; } html, body{ scroll-behavior: smooth; } h1 { font-size: 3.2em; line-height: 1.1; text-align: center; position: sticky; top: 0; background-color: #242424; } main{ max-width: 800px; margin: auto; } p{ background-color: darkslategray; max-width: 70%; padding: 15px; border-radius: 50px; } p span{ margin: 5px; } p span:first-child{ margin-right: 0; } .user_msg{ text-align: right; margin-left: 30%; display: flex; flex-direction: row-reverse; } .hide { visibility: hidden; display: none; } form{ text-align: center; position: sticky; bottom: 0; } input{ width: 100%; height: 40px; border: none; padding: 10px; font-size: 1.2rem; background-color: rgb(28, 23, 23); } input:focus{ outline: none; }
:root {
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
line-height: 1.5;
font-weight: 400;
color-scheme: light dark;
color: rgba(255, 255, 255, 0.87);
background-color: #242424;
font-synthesis: 無;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
-webkit-text-size-adjust: 100%;
}
html, body{
scroll-behavior: smooth;
}
h1 {
font-size: 3.2em;
line-height: 1.1;
text-align: center;
position: sticky;
top: 0;
background-color: #242424;
}
main{
max-width: 800px;
margin: auto;
}
p{
background-color: darkslategray;
max-width: 70%;
padding: 15px;
border-radius: 50px;
}
p span{
margin: 5px;
}
p span:first-child{
margin-right: 0;
}
.user_msg{
text-align: right;
margin-left: 30%;
display: flex;
flex-direction: row-reverse;
}
.hide {
visibility: hidden;
display: none;
}
form{
text-align: center;
position: sticky;
bottom: 0;
}
input{
width: 100%;
height: 40px;
border: none;
padding: 10px;
font-size: 1.2rem;
background-color: rgb(28, 23, 23);
}
input:focus{
outline: none;
}

並刪除 frontend/src/App.css 檔案中的所有樣式。

這一部分的程式碼在GitHub上。現在,這裡是最終的輸出:

全棧式聊天機器人如期使用CSS工作

全棧式聊天機器人如期使用CSS工作

恭喜你完成了這個專案!全棧聊天機器人的工作更多,但它幫助我們分離了關注點,建立了一個更安全和有吸引力的應用程式,併為使用者提供了更好的體驗。所以,這些努力是值得的。你可以在GitHub上找到這一部分的程式碼。

小結

本教程希望向你展示,任何具有基本程式設計知識的人都可以構建人工智慧驅動的軟體。你學會了如何使用React和Nodejs構建一個聊天機器人,我們還討論了每種技術的利弊。最後,我們建立了一個既實用、安全又有視覺吸引力的解決方案。讀完本教程後,你現在可以探索AI的功能,如影象處理和音訊互動。花點時間瀏覽一下文件,看看你可以如何擴充套件我們在這裡涉及的內容。

評論留言