標題:使用 NextJS 和 Wing 建立您自己的 ChatGPT 圖形客戶端 🤯

描述:使用 Winglang 和 NextJS 建立的 ChatGPT 客戶端應用程式

canonical_url:https://www.winglang.io/blog/2024/05/16/chatgpt-client-with-nextjs-and-wing

發表:真實


長話短說

在本文結束時,您將使用 Wing 和 Next.js 建置並部署 ChatGPT 用戶端。

該應用程式可以在本地執行(在本地雲端模擬器中)或將其部署到您自己的雲端提供者。

舞蹈


介紹

建置 ChatGPT 用戶端並將其部署到您自己的雲端基礎架構是確保對資料進行控制的好方法。

將 LLM 部署到您自己的雲端基礎架構可為您的專案提供隱私和安全性。

有時,在使用 OpenAI 的 ChatGPT 等專有 LLM 平台時,您可能會擔心資料在遠端伺服器上儲存或處理,這可能是由於輸入平台的資料的敏感度或其他隱私原因。

在這種情況下,將 LLM 自託管到您的雲端基礎架構或在您的電腦上本地執行可以讓您更好地控制資料的隱私和安全性。

Wing是一種面向雲端的程式語言,可讓您建置和部署基於雲端的應用程式,而無需擔心底層基礎架構。

它允許您使用相同的語言定義和管理雲端基礎架構和應用程式程式碼,從而簡化了您在雲端上建置的方式。

Wing 與雲端無關——用它建置的應用程式可以編譯並部署到各種雲端平台。

{% cta https://git.new/wing-repo %} 看 ⭐ Wing {% endcta %}

給我們一顆星星


讓我們開始吧!

要繼續操作,您需要:

  • 對 Next.js 有一定了解

  • 在您的機器上安裝 Wing 。如果您不知道如何操作,請不要擔心。我們將在這個專案中一起討論它。

  • 取得您的 OpenAI API 金鑰。

建立您的專案

首先,您需要在電腦上安裝 Wing。執行以下命令:

npm install -g winglang

透過檢查版本確認安裝:

wing -V

建立您的 Next.js 和 Wing 應用程式。

mkdir assistant
cd assistant
npx create-next-app@latest frontend
mkdir backend && cd backend
wing new empty

我們已在 Assistant 目錄中成功建立了 Wing 和 Next.js 專案。我們的 ChatGPT 用戶端的名稱是 Assistant。聽起來很酷,對吧?

前端和後端目錄分別包含我們的 Next 和 Wing 應用程式。 wing new empty建立三個檔案: package.jsonpackage-lock.jsonmain.w 。後者是應用程式的入口點。

在 Wing 模擬器中本地執行您的應用程式

Wing 模擬器可讓您在本機電腦內執行程式碼、編寫單元測試和偵錯程式碼,而無需部署到實際的雲端供應商,從而幫助您更快地進行迭代。

使用以下命令在本機上執行您的 Wing 應用程式:

wing it

您的 Wing 應用程式將在localhost:3000上執行。

安慰

設定您的後端

  • 讓我們安裝 Wing 的 OpenAI 和 React 函式庫。 OpenAI 庫提供了與 LLM 互動的標準介面。 React 程式庫可讓您將 Wing 後端連接到 Next 應用程式。
npm i @winglibs/openai @winglibs/react
  • 將這些套件匯入到main.w檔案中。我們還導入需要的所有其他庫。
bring openai
bring react
bring cloud
bring ex
bring http

bring是 Wing 中的導入語句。這樣想,Wing 使用bring來實現與 JavaScript 中import相同的功能。

cloud是 Wing 的雲端庫。它公開了雲端 API、儲存桶、計數器、網域、端點、函數和更多雲端資源的標準介面。 ex是用於與表格和雲端 Redis 資料庫介面的標準庫, http用於呼叫不同的 HTTP 方法 - 從遠端資源發送和檢索資訊。

取得您的 OpenAI API 金鑰

我們將在我們的應用程式中使用gpt-4-turbo但您可以使用任何 OpenAI 模型。

OpenAI 金鑰

  • 設定名稱專案權限,然後按一下建立金鑰。

OpenAI Key2

初始化 OpenAI

建立一個Class來初始化您的 OpenAI API。我們希望它可以重複使用。

我們將向Assistant類別加入personality ,以便在向 AI 助手傳遞提示時可以指定 AI 助手的個性。

let apiKeySecret = new cloud.Secret(name: "OAIAPIKey") as "OpenAI Secret";

class Assistant {
    personality: str;
    openai: openai.OpenAI;

    new(personality: str) {
        this.openai = new openai.OpenAI(apiKeySecret: apiKeySecret);
        this.personality = personality;
    }

    pub inflight ask(question: str): str {
        let prompt = `you are an assistant with the following personality: ${this.personality}. ${question}`;
        let response = this.openai.createCompletion(prompt, model: "gpt-4-turbo");
        return response.trim();
    }
}

Wing 分別使用preflightinflight概念來統一基礎設施定義和應用程式邏輯。

預檢程式碼(通常是基礎設施定義)在編譯時執行一次,而執行程式碼將在執行時執行以實現應用程式的行為。

雲端儲存桶、佇列和 API 端點是預檢的一些範例。定義預檢時不需要新增預檢關鍵字,Wing 預設知道這一點。但對於飛行塊,您需要在其中加入“飛行”一詞。

上面的程式碼中有一個飛行中的區塊。 Inflight 區塊是您編寫非同步執行時間程式碼的地方,這些程式碼可以透過其 inflight API 直接與資源互動。

測試和儲存雲端秘密

讓我們來看看如何保護我們的 API 金鑰,因為我們肯定要考慮安全性

讓我們在後端的根目錄中建立一個.env檔案並傳入我們的 API 金鑰:

OAIAPIKey = Your_OpenAI_API_key

我們可以在本地引用 .env 檔案來測試 OpenAI API 金鑰,然後由於我們計劃部署到 AWS,因此我們將逐步設定AWS Secrets Manager

AWS 主控台

首先,我們前往 AWS 並登入控制台。如果您沒有帳戶,可以免費建立一個。

AWS平台

導覽至 Secrets Manager,讓我們儲存 API 金鑰值。

AWS 秘密管理器

圖片描述

我們已將 API 金鑰儲存在名為OAIAPIKey的雲端機密中。複製您的金鑰,我們將跳到終端並連接到現在儲存在 AWS 平台中的金鑰。

wing secrets

現在將您的 API 金鑰貼上為終端中的值。您的密鑰現已正確存儲,我們可以開始與我們的應用程式互動。


將人工智慧的回應儲存在雲端。

將人工智慧的回應儲存在雲端可以讓您控制資料。它駐留在您自己的基礎設施上,與 ChatGPT 等專有平台不同,您的資料位於您無法控制的第三方伺服器上。您也可以在需要時檢索這些回應。

讓我們建立另一個類,使用 Assistant 類來傳遞 AI 的個性和提示。我們還將每個模型的回應作為txt檔案儲存在雲端儲存桶中。

let counter = new cloud.Counter();

class RespondToQuestions {
    id: cloud.Counter;
    gpt: Assistant;
    store: cloud.Bucket;

    new(store: cloud.Bucket) {
        this.gpt = new Assistant("Respondent");
        this.id = new cloud.Counter() as "NextID";
        this.store = store;
    }

    pub inflight sendPrompt(question: str): str {
        let reply = this.gpt.ask("{question}");
        let n = this.id.inc();
        this.store.put("message-{n}.original.txt", reply);
        return reply;
    }
}

我們為我們的助理設定了「受訪者」的個性。我們希望它能夠回答問題。您也可以讓前端使用者在發送提示時指定此個性。

每次產生回應時,計數器都會遞增,並且計數器的值會傳遞到用於在雲端中儲存模型回應的n變數中。然而,我們真正想要的是建立一個資料庫來儲存來自前端的使用者提示和模型的回應。

讓我們定義我們的資料庫。

定義我們的資料庫

Wing 內建了ex.Table - 一個用於儲存和查詢資料的 NoSQL 資料庫。

let db = new ex.Table({
    name: "assistant",
    primaryKey: "id",
    columns: {
        question: ex.ColumnType.STRING,
        answer: ex.ColumnType.STRING
    }
});

我們在資料庫定義中新增了兩列 - 第一列用於儲存使用者提示,第二列用於儲存模型的回應。

建立 API 路由和邏輯

我們希望能夠在後端發送和接收資料。讓我們建立 POST 和 GET 路由。

let api = new cloud.Api({ cors: true });

api.post("/assistant", inflight((request) => {
    // POST request logic goes here
}));

api.get("/assistant", inflight(() => {
    // GET request logic goes here
}));

let myAssistant = new RespondToQuestions(store) as "Helpful Assistant";

api.post("/assistant", inflight((request) => {
    let prompt = request.body;
    let response = myAssistant.sendPrompt(JSON.stringify(prompt)); 
    let id = counter.inc(); 

    // Insert prompt and response in the database
    db.insert(id, { question: prompt, answer: response });

    return cloud.ApiResponse({
        status: 200
    });
}));

在 POST 路由中,我們希望將從前端收到的使用者提示傳遞到模型中並獲得回應。提示和回應都將儲存在資料庫中。 cloud.ApiResponse可讓您傳送對使用者要求的回應。

新增前端發出 GET 請求時檢索資料庫專案的邏輯。


新增前端發出 GET 請求時檢索資料庫專案的邏輯。

api.get("/assistant", inflight(() => {
    let questionsAndAnswers = db.list();

    return cloud.ApiResponse({
        body: JSON.stringify(questionsAndAnswers),
        status: 200
    });
}));

我們的後端已經準備好了。我們在本地雲端模擬器中測試一下。

wing it

讓我們轉到localhost:3000並向我們的助理詢問一個問題。

助理回應

我們的問題和助理的回答都已儲存到資料庫中。看一看。

表資料

向前端公開您的 API URL

我們需要將後端的 API URL 公開給 Next 前端。這就是之前安裝的 React 函式庫派上用場的地方。

let website = new react.App({
    projectPath: "../frontend",
    localPort: 4000
});

website.addEnvironment("API_URL", api.url);

將以下內容加入 Next 應用程式的layout.js中。

import { Inter } from "next/font/google";
import "./globals.css";

const inter = Inter({ subsets: ["latin"] });

export const metadata = {
    title: "Create Next App",
    description: "Generated by create next app",
};

export default function RootLayout({ children }) {
    return (
        <html lang="en">
            <head>
            <script src="./wing.js" defer></script>
           </head>
            <body className={inter.className}>{children}</body>
        </html>
    );
}

我們現在可以在 Next 應用程式中存取API_URL

實作前端邏輯

讓我們實作前端邏輯來呼叫後端。

import { useEffect, useState, useCallback } from 'react';
import axios from 'axios';

function App() {

    const [isThinking, setIsThinking] = useState(false);
    const [input, setInput] = useState("");
    const [allInteractions, setAllInteractions] = useState([]);

    const retrieveAllInteractions = useCallback(async (api_url) => {
            await axios ({
              method: "GET",
              url: `${api_url}/assistant`,
            }).then(res => {
              setAllInteractions(res.data)
            })
  }, [])

    const handleSubmit = useCallback(async (e)=> {
        e.preventDefault()

        setIsThinking(!isThinking)

        if(input.trim() === ""){
          alert("Chat cannot be empty")
          setIsThinking(true)

        }

          await axios({
            method: "POST",
            url: `${window.wingEnv.API_URL}/assistant`,
            headers: {
              "Content-Type": "application/json"
            },
            data: input
          })
          setInput("");
          setIsThinking(false);
          await retrieveAllInteractions(window.wingEnv.API_URL);     

  })

    useEffect(() => {
        if (typeof window !== "undefined") {
            retrieveAllInteractions(window.wingEnv.API_URL);
        }
    }, []);

    // Here you would return your component's JSX
    return (
        // JSX content goes here
    );
}

export default App;

retrieveAllInteractions函數取得後端資料庫中的所有問題和答案。 handSubmit函數將使用者的提示傳送到後端。

讓我們加入 JSX 實作。

import { useEffect, useState } from 'react';
import axios from 'axios';
import './App.css';

function App() {
    // ...

    return (
        <div className="container">
            <div className="header">
                <h1>My Assistant</h1>
                <p>Ask anything...</p>
            </div>

            <div className="chat-area">
                <div className="chat-area-content">
                    {allInteractions.map((chat) => (
                        <div key={chat.id} className="user-bot-chat">
                            <p className='user-question'>{chat.question}</p>
                            <p className='response'>{chat.answer}</p>
                        </div>
                    ))}
                    <p className={isThinking ? "thinking" : "notThinking"}>Generating response...</p>
                </div>

                <div className="type-area">
                    <input 
                        type="text" 
                        placeholder="Ask me any question" 
                        value={input} 
                        onChange={(e) => setInput(e.target.value)} 
                    />
                    <button onClick={handleSubmit}>Send</button>
                </div>
            </div>
        </div>
    );
}

export default App;

在本地執行您的專案

導航到您的後端目錄並使用以下命令在本地執行您的 Wing 應用程式

cd ~assistant/backend
wing it

也執行您的 Next.js 前端:

cd ~assistant/frontend
npm run dev

讓我們看一下我們的應用程式。

聊天應用程式

讓我們透過 Next 應用程式向 AI 助理詢問幾個開發人員問題。

聊天應用程式2

將您的應用程式部署到 AWS

我們已經了解了我們的應用程式如何在本地執行。 Wing 也允許您部署到包括 AWS 在內的任何雲端提供者。要部署到 AWS,您需要使用您的憑證來設定TerraformAWS CLI

  • 使用tf-aws編譯到 Terraform/AWS 。此指令指示編譯器使用 Terraform 作為配置引擎,將所有資源綁定到預設的 AWS 資源集。
cd ~/assistant/backend
wing compile --platform tf-aws main.w

  • 執行 Terraform 初始化並應用
cd ./target/main.tfaws
terraform init
terraform apply

注意: terraform apply需要一些時間才能完成。

您可以在此處找到本教程的完整程式碼。

總結一下

正如我之前提到的,我們都應該關心我們的應用程式的安全性,建立您自己的 ChatGPT 用戶端並將其部署到您的雲端基礎設施可以為您的應用程式提供一些非常好的保障

我們在本教程中演示了Wing如何提供一種簡單的方法來建置可擴展的雲端應用程式,而無需擔心底層基礎設施。

如果您有興趣建立更酷的東西,Wing 擁有一個活躍的開發人員社區,他們可以合作建立雲端願景。我們很高興在那裡見到你。

只需前往我們的Discord打個招呼即可!


原文出處:https://dev.to/winglang/building-your-own-chatgpt-graphical-client-with-nextjs-and-wing-29jj


共有 0 則留言