65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 OpenAI ChatGPT-4o 创建 Web 开发机器人

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.93/5 (8投票s)

2024 年 8 月 16 日

CPOL

2分钟阅读

viewsIcon

19816

downloadIcon

460

使用 ChatGPT-4o API 创建一个可视化的自动网页生成器机器人

引言

本文旨在展示如何创建一个开发机器人,使用系统提示和 ChatGPT-4o 模型,以可视化的方式生成网页。

背景

Claude3.5 sonnet 的推出展示了一种使用所谓的“artifacts”(内联解释生成的代码)来创建应用程序和网页的新方法。通过使用 JavaScript 捕获生成的代码并将其放入 blob 中,我们可以使用 OpenAI 的 chatGPT3.5 和 ChatGPT-4o 模型以简单直接的方式重现此功能。

使用代码

机器人的 HTML 界面应该简单,主要由一个提示输入区域、一个 API 密钥输入字段和一个空 div 组成,所有消息(用户和机器人)都将附加到该 div 中。

<main>
<h1>OpenAI Web Dev Bot</h1>
      <div class="userinput">
         <input type="password" id="apikey" placeholder="Your API key"><br>
        <textarea rows=6 type="text" id="prompt" placeholder="Your prompt" autocomplete="off" autofocus></textarea><br>
        <button class="btncopy" onclick="sendMessage()">Send</button>
      </div>
      <section id="content">
        <div id="chathistory">
        </div>
      </section>
</main>

主要需要实现三个函数

  • sendMessage()
  • showMessage()
  • getChatGPTResponse()

sendMessage() 函数旨在收集所有相关输入,即提示和 API 密钥,并将它们发送到后续函数

// function to send the message
async function sendMessage() {
    const apikey = document.getElementById("apikey").value;
    console.log(apikey); // for debug purpose

    if (apikey === "") {
        alert("No OpenAI API Key found.");
    } else {
        console.log(apikey);
    }
    const inputElement = document.getElementById("prompt");
    const userInput = inputElement.value.trim();
    if (userInput !== "") {
        showMessage("Guest", userInput, "");
        chatMemory = await getChatGPTResponse(userInput, chatMemory);

        inputElement.value = "";
    }
}

该函数使用一个内存数组来保存创建的先前步骤,以及系统提示。

这个内存数组可以很容易地处理

// Creation of the memory array with system prompt
let chatMemory = [];
chatMemory = createMemory([{
    role: "system",
    content: "You are a web developer bot. You don't talk, you don't explain your answers. Your only output is made of code to satisfy the received request. You will not explain the code, you will not introduce it, you will not greet or thank the user for the request, you will only produce a well structured code line by line without interruptions and without dividing it in sections."
}]);

// Definition of structure for context memory
function createMemory(messages) {
    const memory = [];
    for (const msg of messages) {
        memory.push({
            role: msg.role,
            content: msg.content
        });
    }
    return memory;
}

showMessage() 函数稍微复杂一些,因为它处理发送到模型的请求以及从模型接收到的响应。在这个函数中,我们实现了显示答案中的各种装饰,以及主要部分,由一个动态 iFrame 组成,其内容通过 blob 创建。

为了清晰和教学目的,在此实现中,我们还添加了基于 OpenAI 最新费率的 token 和成本管理。

// Show the message in the chat container
function showMessage(sender, message, tokens, downloadLink) {
    const chatContainer = document.getElementById("chathistory");
    const typingIndicator = document.getElementById("typing-indicator");

    if (typingIndicator && sender === "Chatbot") {
        chatContainer.removeChild(typingIndicator);
    }

    // Create a new message element
    const messageElement = document.createElement("div");

    // manage Guest and Chatbot messages differently
    if (sender === "Guest") {
        messageElement.innerHTML = `+[${hour}:${minute}] - ${sender}: ${message}`;
        messageElement.classList.add("user-message");
    } else {
        // Show chatbot message and token messages
        const timestampElement = document.createElement("p");
        timestampElement.innerHTML = `-[${hour}:${minute}] - ${sender}: `;
        timestampElement.classList.add("chatgpt-message");

        // Add a timestamp
        messageElement.appendChild(timestampElement);

        // Create an iFrame to trap the code generated in the response
        const iframe = document.createElement("iframe");
        iframe.style.width = "100%";
        iframe.style.height = "600px";
        iframe.style.border = "1px solid black";

        messageElement.appendChild(iframe);

        // Crea te a Blob to trap the generated code
        const blob = new Blob([`
        <!DOCTYPE html>
        <html lang="en">
        <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Generated Code</title>
        </head>
        <body>
        ${message}
        </body>
        </html>
            type: 'text/html'
        });

        // Create a fictitious URL for the Blob
        const url = URL.createObjectURL(blob);

        // Set the iFrame to the Blob URL
        iframe.src = url;

        // Show the chatbot answer and token message
        const separator = document.createElement("p");
        separator.innerHTML = `${tokens}`;
        //messageElement.innerHTML += `-[${hour}:${minute}] - ${sender}: `;
        messageElement.classList.add("chatgpt-message");
        messageElement.appendChild(separator);

        // Add a link to download the generated code
        const downloadElem = document.createElement("div");
        downloadElem.innerHTML = downloadLink;
        messageElement.appendChild(downloadElem);
    }

    // Append the message to the chat container
    chatContainer.appendChild(messageElement);
    chatContainer.scrollTop = chatContainer.scrollHeight;
}

getChatGPTResponse() 函数扮演着核心角色:它查询 OpenAI 模型,收集响应中的 JSON 对象,并将其字符串化以提取文本内容。添加了一系列正则表达式来清理响应。我们还计算请求和响应的 token 和成本,并创建下载生成代码的链接。

// Function to interrogate OpenAI model and get response 
async function getChatGPTResponse(userInput, chatMemory = []) {
    const apikey = document.getElementById("apikey").value;
    if (apikey === "") {
        alert("No OpenAI API Key found.");
    } else {

    }
    const chatContainer = document.getElementById("chathistory");
    const typingIndicator = document.createElement("p");
    typingIndicator.id = "typing-indicator";
    typingIndicator.innerHTML =
        '<img src="preloader.gif" class="preloader" alt="Loading...">'; // place a gif in your folder
    chatContainer.appendChild(typingIndicator);

    try {
        const response = await fetch("https://api.openai.com/v1/chat/completions", {
            method: "POST",
            headers: {
                "Content-Type": "application/json",

                Authorization: "Bearer " + apikey
            },
            body: JSON.stringify({
                // Chose your model here by uncommenting the relevant line. 4o is default
                //model: "gpt-3.5-turbo-0125",
                model: "gpt-4o-mini",
                messages: [...chatMemory, {
                    role: "user",
                    content: userInput
                }]
            })
        });
        if (!response.ok) {
            throw new Error("Error while requesting to the API");
        }
        const data = await response.json();
        if (
            !data.choices ||
            !data.choices.length ||
            !data.choices[0].message ||
            !data.choices[0].message.content
        ) {
            throw new Error("Invalid API response");
        }

        const chatGPTResponse = data.choices[0].message.content.trim();

        var cleanResponse = chatGPTResponse.replace(
            /(||||||.net|||)(.*?)/gs,
            "$2"
        );
        console.log(chatGPTResponse); // for debug purpose only
        // Sanitize the response
        cleanResponse = cleanResponse.replace( //g, "");
            cleanResponse = cleanResponse.replace(/\*\*(.*?)\*\*/g, "$1");

            // manages tokens from the response
            const tokenCount = document.createElement("p");

            // Check if "completion_tokens" is present in the structure of objects returned by your API
            if (data.usage.completion_tokens) {
                const requestTokens = data.usage.prompt_tokens;
                const responseTokens = data.usage.completion_tokens;
                const totalTokens = data.usage.total_tokens;
                const pricepertokenprompt = 0.15 / 1000000; // uses gpt-4o-mini price of 0.15/Mt USD
                const pricepertokenresponse = 0.60 / 1000000; // uses gpt-4o-mini price of 0.15/Mt USD
                const priceperrequest = pricepertokenprompt * requestTokens;
                const priceperresponse = pricepertokenresponse * responseTokens;
                const totalExpense = priceperrequest + priceperresponse;
                tokenCount.innerHTML = `<hr>Your request used ${requestTokens} tokens and costed ${priceperrequest.toFixed(6)} USD<br>This response used ${responseTokens} tokens and costed ${priceperresponse.toFixed(6)} USD<br>Total Tokens: ${totalTokens}. This interaction costed you: ${totalExpense.toFixed(6)} USD.`;
            } else {
                tokenCount.innerHTML = "Unable to track the number of used tokens.";
            }

            // Create a blob and a link to download the code
            const blob = new Blob([cleanResponse], {
                type: 'text/html'
            });
            const url = URL.createObjectURL(blob);
            const downloadLink = `<a href="${url}" download="generated_code.html">Click here to download the generated HTML code</a>`;

            // Show the message with the final download link now created
            showMessage("Chatbot", cleanResponse, tokenCount.innerHTML, downloadLink);

            // adds response to memory context
            chatMemory.push({
                role: "user",
                content: userInput
            }); chatMemory.push({
                role: "assistant",
                content: cleanResponse
            });

            // updates context memory
            return chatMemory;
        }
        catch (error) {
            console.error(error);
            // manage errors with a warning
            alert(
                "An error occurred during the request. Check your OpenAI account or retry later."
            );
        }
    }

关注点

此代码的主要兴趣点在于

  • 编写结构化且务实的系统提示,以克服 OpenAI 模型自然倾向于冗长和描述性的倾向
  • 实现动态 iFrame 结构并将其指向代码 blob,以便将生成的 HTML 和 CSS 与容器网页隔离,避免对容器网页进行滥用样式重置
  • 此版本使用本地输入字段来捕获 API 密钥。在更安全的版本中,API 密钥可以由一个面板设置存储在 localstorage
<input type="text" id="openaikeyInput" placeholder="Enter OpenAI key"><br>
<p id="modalfeedback"></p>
<button id="saveKeys">Save Key</button>
                            
<script>
function checkLocalStorageKeys() {
    if(localStorage.getItem('openaikey')) {
        var openaiKey = localStorage.getItem('openaikey');
        document.getElementById('openaikeyInput').value = openaiKey;
    } else {
        document.getElementById('modalfeedback').innerText = "Configura le API key";
    }
}

checkLocalStorageKeys();
</script>

然后,API 密钥可以通过修改 const 定义,在 sendMessage()getChatGPTResponse() 函数中从 localstorage 中检索。

const apikey = localStorage.getItem("openaikey");
document.getElementById("apikey").value = apikey;

历史

版本 1.0

© . All rights reserved.