使用開放式機器學習模型製作 Web 應用生成器

釋出於 2023 年 7 月 3 日
在 GitHub 上更新

隨著越來越多的程式碼生成模型公開可用,現在可以以前所未有的方式實現文字到網頁甚至文字到應用程式的轉換。

本教程介紹了一種直接的 AI Web 內容生成方法,即一次性流式傳輸和渲染內容。

點選這裡體驗即時演示!Webapp Factory

main_demo.gif

在 Node 應用中使用 LLM

雖然我們通常認為 Python 適用於所有與 AI 和 ML 相關的事物,但 Web 開發社群嚴重依賴 JavaScript 和 Node。

以下是您可以在此平臺上使用大型語言模型的一些方法。

透過本地執行模型

在 Javascript 中執行 LLM 有多種方法,從使用 ONNX 到將程式碼轉換為 WASM 並呼叫用其他語言編寫的外部程序。

其中一些技術現在作為即用型 NPM 庫提供

然而,在此類環境中執行大型語言模型可能會消耗大量資源,特別是如果您無法使用硬體加速。

透過使用 API

如今,各種雲提供商都提供商業 API 以使用語言模型。這是 Hugging Face 當前的產品

免費的 推理 API 允許任何人使用社群中的中小型模型。

更高階且可用於生產的 推理端點 API 適用於需要更大模型或自定義推理程式碼的使用者。

這兩個 API 可以透過 NPM 上的 Hugging Face Inference API 庫 從 Node 中使用。

💡 效能最好的模型通常需要大量記憶體(32 GB、64 GB 或更多)和硬體加速才能獲得良好的延遲(請參閱基準測試)。但我們也看到模型尺寸縮小,同時在某些任務上保持相對良好的結果,記憶體需求低至 16 GB 甚至 8 GB。

架構

我們將使用 NodeJS 來建立我們的生成式 AI Web 伺服器。

該模型將是 WizardCoder-15B,執行在推理端點 API 上,但隨意嘗試其他模型和堆疊。

如果您對其他解決方案感興趣,以下是一些替代實現的參考

初始化專案

首先,我們需要設定一個新的 Node 專案(如果需要,您可以克隆此模板)。

git clone https://github.com/jbilcke-hf/template-node-express tutorial
cd tutorial
nvm use
npm install

然後,我們可以安裝 Hugging Face 推理客戶端

npm install @huggingface/inference

並在 `src/index.mts` 中進行設定

import { HfInference } from '@huggingface/inference'

// to keep your API token secure, in production you should use something like:
// const hfi = new HfInference(process.env.HF_API_TOKEN)
const hfi = new HfInference('** YOUR TOKEN **')

配置推理端點

💡 注意:如果您不想為本教程的端點例項付費,可以跳過此步驟並檢視此免費推理 API 示例。請注意,這僅適用於較小的模型,可能不如大型模型強大。

要部署新端點,您可以前往端點建立頁面

您需要從“模型儲存庫”下拉列表中選擇 WizardCoder,並確保選擇足夠大的 GPU 例項

new_endpoint.jpg

端點建立後,您可以從此頁面複製 URL

deployed_endpoints.jpg

配置客戶端以使用它

const hf = hfi.endpoint('** URL TO YOUR ENDPOINT **')

您現在可以告訴推理客戶端使用我們的私有端點並呼叫我們的模型

const { generated_text } = await hf.textGeneration({
  inputs: 'a simple "hello world" html page: <html><body>'
});

生成 HTML 流

現在是時候向訪問 URL(例如 /app)的 Web 客戶端返回一些 HTML 了。

我們將使用 Express.js 建立一個端點,以流式傳輸 Hugging Face 推理 API 的結果。

import express from 'express'

import { HfInference } from '@huggingface/inference'

const hfi = new HfInference('** YOUR TOKEN **')
const hf = hfi.endpoint('** URL TO YOUR ENDPOINT **')

const app = express()

由於我們目前沒有任何 UI,因此介面將是一個簡單的 URL 引數,用於提示

app.get('/', async (req, res) => {

  // send the beginning of the page to the browser (the rest will be generated by the AI)
  res.write('<html><head></head><body>')

  const inputs = `# Task
Generate ${req.query.prompt}
# Out
<html><head></head><body>`

  for await (const output of hf.textGenerationStream({
    inputs,
    parameters: {
      max_new_tokens: 1000,
      return_full_text: false,
    }
  })) {
    // stream the result to the browser
    res.write(output.token.text)

    // also print to the console for debugging
    process.stdout.write(output.token.text)
  }

  req.end()
})

app.listen(3000, () => { console.log('server started') })

啟動您的 Web 伺服器

npm run start

並開啟 https://:3000?prompt=some%20prompt。稍等片刻後,您應該會看到一些原始 HTML 內容。

調整提示

每個語言模型對提示的反應都不同。對於 WizardCoder 來說,簡單的指令通常效果最好

const inputs = `# Task
Generate ${req.query.prompt}
# Orders
Write application logic inside a JS <script></script> tag.
Use a central layout to wrap everything in a <div class="flex flex-col items-center">
# Out
<html><head></head><body>`

使用 Tailwind

Tailwind 是一個流行的 CSS 框架,用於樣式化內容,WizardCoder 開箱即用,表現出色。

這使得程式碼生成能夠即時建立樣式,而無需在頁面開頭或結尾生成樣式表(這會使頁面感覺卡頓)。

為了改進結果,我們還可以透過展示方式(<body class="p-4 md:p-8">)來引導模型。

const inputs = `# Task
Generate ${req.query.prompt}
# Orders
You must use TailwindCSS utility classes (Tailwind is already injected in the page).
Write application logic inside a JS <script></script> tag.
Use a central layout to wrap everything in a <div class="flex flex-col items-center'>
# Out
<html><head></head><body class="p-4 md:p-8">`

防止幻覺

與大型通用模型相比,在專用於程式碼生成的輕量模型上,可靠地防止幻覺和故障(例如重複整個指令,或寫入“lorem ipsum”佔位符文字)可能很困難,但我們可以嘗試緩解它。

您可以嘗試使用命令語氣並重復指令。一個有效的方法是透過提供部分英文輸出內容來引導模型

const inputs = `# Task
Generate ${req.query.prompt}
# Orders
Never repeat these instructions, instead write the final code!
You must use TailwindCSS utility classes (Tailwind is already injected in the page)!
Write application logic inside a JS <script></script> tag!
This is not a demo app, so you MUST use English, no Latin! Write in English! 
Use a central layout to wrap everything in a <div class="flex flex-col items-center">
# Out
<html><head><title>App</title></head><body class="p-4 md:p-8">`

新增圖片支援

我們現在有一個可以生成 HTML、CSS 和 JS 程式碼的系統,但它在生成影像時容易產生錯誤的 URL 幻覺。

幸運的是,在影像生成模型方面,我們有很多選擇!

→ 最快的入門方法是使用我們的免費推理 API 呼叫 Stable Diffusion 模型,並使用 Hub 上可用的公共模型之一。

app.get('/image', async (req, res) => {
  const blob = await hf.textToImage({
    inputs: `${req.query.caption}`,
    model: 'stabilityai/stable-diffusion-2-1'
  })
  const buffer = Buffer.from(await blob.arrayBuffer())
  res.setHeader('Content-Type', blob.type)
  res.setHeader('Content-Length', buffer.length)
  res.end(buffer)
})

在提示中新增以下行足以指示 WizardCoder 使用我們的新 /image 端點!(您可能需要為其他模型進行調整)

To generate images from captions call the /image API: <img src="/image?caption=photo of something in some place" />

您也可以更具體,例如

Only generate a few images and use descriptive photo captions with at least 10 words!

preview_image.jpg

新增一些 UI

Alpine.js 是一個極簡主義框架,允許我們建立互動式 UI,而無需任何設定、構建管道、JSX 處理等。

一切都在頁面內完成,這使其成為建立快速演示 UI 的絕佳選擇。

這是一個靜態 HTML 頁面,您可以將其放在 /public/index.html

<html>
  <head>
    <title>Tutorial</title>
    <script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
    <script src="https://cdn.tailwindcss.com"></script>
  </head>
  <body>
    <div class="flex flex-col space-y-3 p-8" x-data="{ draft: '', prompt: '' }">
      <textarea
          name="draft"
          x-model="draft"
          rows="3"
          placeholder="Type something.."
          class="font-mono"
         ></textarea> 
      <button
        class="bg-green-300 rounded p-3"
        @click="prompt = draft">Generate</button>
      <iframe :src="`/app?prompt=${prompt}`"></iframe>
    </div>
  </body>
</html>

為了使其正常工作,您需要進行一些更改

...

// going to localhost:3000 will load the file from /public/index.html
app.use(express.static('public'))

// we changed this from '/' to '/app'
app.get('/app', async (req, res) => {
   ...

最佳化輸出

到目前為止,我們一直在生成完整的 Tailwind 實用程式類序列,這對於賦予語言模型設計自由度非常有用。

但這種方法也非常冗長,消耗了我們大部分的 token 配額。

為了使輸出更緊湊,我們可以使用 Daisy UI,這是一個 Tailwind 外掛,它將 Tailwind 實用程式類組織成一個設計系統。其思想是為元件使用簡寫類名,其餘的則使用實用程式類。

一些語言模型可能沒有 Daisy UI 的內部知識,因為它是一個小眾庫,在這種情況下,我們可以將 API 文件新增到提示中

# DaisyUI docs
## To create a nice layout, wrap each article in:
<article class="prose"></article>
## Use appropriate CSS classes
<button class="btn ..">
<table class="table ..">
<footer class="footer ..">

進一步探索

最終的演示空間包含一個更完整的使用者介面示例

以下是一些進一步擴充套件此概念的想法

  • 測試其他語言模型,例如 StarCoder
  • 為中間語言(React、Svelte、Vue 等)生成檔案和程式碼
  • 將程式碼生成整合到現有框架中(例如 NextJS)
  • 從失敗或部分程式碼生成中恢復(例如,自動修復 JS 中的問題)
  • 將其連線到聊天機器人外掛(例如,在聊天討論中嵌入微型 webapp iframe)

main_demo.jpg

社群

此評論已隱藏(標記為圖形內容)

import React, { useState, useEffect } from 'eact';
import axios from 'axios';

const ChatSupport = () => {
const [messages, setMessages] = useState([]);
const [newMessage, setNewMessage] = useState('');

useEffect(() => {
axios.get('/api/messages')
.then(response => {
setMessages(response.data);
})
.catch(error => {
console.error(error);
});
}, []);

const handleSendMessage = () => {
axios.post('/api/messages', { message: newMessage })
.then(response => {
setMessages([...messages, response.data]);
setNewMessage('');
})
.catch(error => {
console.error(error);
});
};

return (


聊天支援



    {messages.map((message, index) => (
  • {message.text}

  • ))}

<input
type="text"
value={newMessage}
onChange={e => setNewMessage(e.target.value)}
placeholder="輸入訊息..."
/>


);
};

export default ChatSupport;
後端 (Node.js)

const express = require('express');
const app = express();
const axios = require('axios');

app.use(express.json());

const messages = [];

app.get('/api/messages', (req, res) => {
res.json(messages);
});

app.post('/api/messages', (req, res) => {
const message = req.body.message;
messages.push({ text: message });
res.json({ text: message });
});

app.listen(3000, () => {
console.log('伺服器監聽埠 3000');
});

註冊登入發表評論

© . This site is unofficial and not affiliated with Hugging Face, Inc.