從入門到精通:如何在React中構(gòu)建人工智能驅(qū)動(dòng)的梗圖生成器 原創(chuàng)
本文將介紹如何使用OpenAI、React、Fabric.js和DALL-E 3構(gòu)建人工智能梗圖生成器,并創(chuàng)建配文,設(shè)計(jì)梗圖畫布,以及優(yōu)化成本和性能。
為什么要構(gòu)建人工智能梗圖生成器?
梗圖(Meme)堪稱互聯(lián)網(wǎng)時(shí)代的“全民語言”。無論是想調(diào)侃朋友,還是想表達(dá)編程讓人崩潰的無奈,梗圖總能精準(zhǔn)地表達(dá)其意境。然而,人工制作一張梗圖需要花費(fèi)很長(zhǎng)時(shí)間。首先需要找到合適的圖片,然后構(gòu)思出幽默并且貼合情境的配文,還要巧妙地將圖片與文字融為一體,并且看起來不能像孩子的隨意涂鴉。
不過好在已經(jīng)有了OpenAI和DeepSeek等工具,不僅可以自動(dòng)化創(chuàng)作幽默內(nèi)容,還能自動(dòng)化生成當(dāng)前流行的格式,讓用戶幾秒鐘內(nèi)就能創(chuàng)作出梗圖。以下是生成梗圖的方法:
- 為了從梗圖中生成引人入勝的配文,采用了一種特定于情境的方法。
- 構(gòu)建了一個(gè)超級(jí)簡(jiǎn)單直觀的拖放式設(shè)計(jì)界面。
- 找到降低API費(fèi)用的新方法,能夠有效地控制預(yù)算。
- 允許用戶保存他們最喜歡的梗圖,并添加了文本轉(zhuǎn)圖片的功能。
用戶喜歡使用的工具
在深入研究代碼的細(xì)節(jié)之前,首先探討技術(shù)堆棧。在不知道需要使用什么工具的情況下就開始制造是不切實(shí)際的行為。
- React + TypeScript。React為用戶帶來了流暢、響應(yīng)迅速的用戶界面,而TypeScript 可以幫助制作者捕捉到許多以前可能會(huì)出現(xiàn)的錯(cuò)誤。
- OpenAI/DeepSeek API。只要有預(yù)算,其他問題就不足為慮,因?yàn)镈ivision-04能夠使用GPT-4 Turbo隨心所欲地生成犀利而有趣的配文。當(dāng)預(yù)算有限時(shí),DeepSeek就能發(fā)揮重要的作用。
- Fabric.js。使用Fabric.js庫(kù),可以輕松地拖動(dòng)包含文本的圖像,而不是感覺難以掌控。
- Vercel。在部署項(xiàng)目時(shí),即使處在業(yè)務(wù)高峰時(shí)段,Vercel的邊緣緩存功能也能很好地緩解壓力。
- Redis。Redis的入門門檻較低,實(shí)現(xiàn)過程簡(jiǎn)便易行,同時(shí)還能有效防范API濫用現(xiàn)象,避免觸發(fā)速率限制 。
步驟1:設(shè)置自己的人工智能大腦
顯然,人工智能從互聯(lián)網(wǎng)上復(fù)制的短語并不適用于梗圖。梗圖需要將態(tài)度、措辭和一定程度的克制相結(jié)合。這就引出了一個(gè)更根本的問題——究竟該如何引導(dǎo)人工智能學(xué)會(huì)講笑話。其答案或許在于調(diào)整人工智能本身的提示。
以下是用于創(chuàng)建字幕的代碼片段:
1 // src/services/aiService.ts
2 type MemePrompt = {
3 template: string; // e.g., "Distracted Soul"
4 context: string; // e.g., "When your code works on the first try"
5 };
6
7 const generateMemeCaption = async ({ template, context }: MemePrompt) => {
8 const prompt = `
9 Generate a sarcastic meme caption for the "${template}" template about "${context}".
10 Rules:
11 - Use Gen-Z slang (e.g., "rizz", "sigma")
12 - Max 12 words
13 - Add emojis related to the context
14 `;
15
16 const response = await openai.chat.completions.create({
17 model: "gpt-4-turbo",
18 messages: [{ role: "user", content: prompt }],
19 temperature: 0.9, // Higher = riskier jokes
20 max_tokens: 50,
21 });
22
23 return stripEmojis(response.choices[0].message.content); // No NSFW stuff allowed
24 };
專業(yè)提示:如果為了營(yíng)造幽默效果,可將相關(guān)參數(shù)值設(shè)定在0.7至0.9的范圍內(nèi),但出于安全考慮,需要確保始終通過OpenAI的調(diào)節(jié)端點(diǎn)來調(diào)節(jié)反應(yīng)。
步驟2:構(gòu)建梗圖畫布
如果使用過 HTML5 Canvas API,就會(huì)明白處理它們并非易事。幸運(yùn)的是,F(xiàn)abric.js可以實(shí)現(xiàn)這一功能。它直接在React中提供了類似photoshop的控件,并額外附帶了拖放功能。
以下是簡(jiǎn)化版的Canvas組件:
1 // src/components/MemeCanvas.tsx
2 import { FabricJSCanvas, useFabricJSEditor } from "fabricjs-react";
3
4 export default function MemeCanvas() {
5 const { editor, onReady } = useFabricJSEditor();
6 const [textColor, setTextColor] = useState("#FFFFFF");
7
8 const addTextLayer = (text: string) => {
9 editor?.addText(text, {
10 fill: textColor,
11 fontFamily: "Impact",
12 fontSize: 40,
13 stroke: "#000000",
14 strokeWidth: 2,
15 shadow: "rgba(0,0,0,0.5) 2px 2px 2px",
16 });
17 };
18
19 return (
20 <>
21 <button onClick={() => addTextLayer("Why React, why?!")}>Add Default Text</button>
22 <input type="color" onChange={(e) => setTextColor(e.target.value)} />
23 <FabricJSCanvas className="canvas" onReady={onReady} />
24 </>
25 );
26 }
該功能有以下一些優(yōu)勢(shì):
- 釋放文本圖層,以便在文檔的任何位置拖動(dòng)。
- 使用高級(jí)顏色選擇器添加描邊和陰影效果。
- 雙擊可以編輯文本,以簡(jiǎn)化編輯過程。
步驟3:速率限制
試想一下這樣的場(chǎng)景:在應(yīng)用程序發(fā)布之后,很多人也萌生了制作梗圖的想法,這聽起來是不是很有趣?然而,當(dāng)看到 OpenAI 的賬單竟超過比特幣價(jià)格時(shí),或許就不會(huì)這么認(rèn)為。
為了解決這個(gè)問題,在Redis中設(shè)置了滑動(dòng)窗口速率限制。以下介紹在Vercel Edge Functions上的具體實(shí)現(xiàn)方法:
1 // src/app/api/generate-caption/route.ts
2 import { Ratelimit } from "@upstash/ratelimit";
3 import { Redis } from "@upstash/redis";
4
5 const ratelimit = new Ratelimit({
6 redis: Redis.fromEnv(),
7 limiter: Ratelimit.slidingWindow(15, "86400s"), // 15 requests/day per IP
8 });
9
10 export async function POST(request: Request) {
11 const ip = request.headers.get("x-forwarded-for") ?? "127.0.0.1";
12 const { success } = await ratelimit.limit(ip);
13
14 if (!success) {
15 return new Response("Slow down, meme lord! Daily limit reached.", {
16 status: 429,
17 });
18 }
19
20 // Proceed with OpenAI call
21 }
節(jié)省成本的妙招
- 緩存流行的提示,例如“熱線來電”以及“拉取請(qǐng)求獲得批準(zhǔn)”等。
- 使用CloudFlare緩存生成的圖像。
由DALL-E 3生成的人工智能梗圖
有時(shí)候,人們會(huì)認(rèn)識(shí)到選擇完美的梗圖模板是一項(xiàng)不可能完成的任務(wù)。
1 // src/services/aiService.ts
2 const generateCustomMemeImage = async (prompt: string) => {
3 const response = await openai.images.generate({
4 model: "dall-e-3",
5 prompt: `
6 A meme template about "${prompt}".
7 Style: Flat vector, bold outlines, no text.
8 Background: Solid pastel color.
9 `,
10 size: "1024x1024",
11 quality: "hd",
12 });
13
14 return response.data[0].url;
15 }
更改輸出
- 提示:“兩個(gè)開發(fā)人員就采用Redux和Zustand框架進(jìn)行爭(zhēng)辯?!?/li>
- 最終產(chǎn)品:將Redux和Zustand這兩個(gè)卡通人物的爭(zhēng)論,以兩個(gè)在紫色背景上動(dòng)態(tài)呈現(xiàn)的圖標(biāo)形式進(jìn)行展示。
梗圖的歷史記錄功能(Zustad + LocalStorage)
為了讓用戶能夠保存梗圖,在Zustand的幫助下添加了梗圖的歷史記錄功能。
1 // src/stores/memeHistory.ts
2 import { create } from "zustand";
3 import { persist } from "zustand/middleware";
4
5 type Meme = {
6 id: string;
7 imageUrl: string;
8 caption: string;
9 timestamp: number;
10 };
11
12 interface MemeHistoryState {
13 memes: Meme[];
14 saveMeme: (meme: Omit<Meme, "id" | "timestamp">) => void;
15 }
16
17 export const useMemeHistory = create<MemeHistoryState>()(
18 persist(
19 (set, get) => ({
20 memes: [],
21 saveMeme: (meme) => {
22 const newMeme = {
23 ...meme,
24 id: crypto.randomUUID(),
25 timestamp: Date.now(),
26 };
27 set({ memes: [newMeme, ...get().memes].slice(0, 100) });
28 },
29 }),
30 { name: "meme-history" }
31 )
32);
用戶操作指引
- 首先創(chuàng)建一個(gè)梗圖,然后點(diǎn)擊保存。
- 梗圖將在本地保存,并將以網(wǎng)絡(luò)格式呈現(xiàn)。
- 已經(jīng)保存的梗圖可以通過點(diǎn)擊在編輯器中重新加載。
結(jié)束語
構(gòu)建人工智能梗圖生成器,不僅可以幫助開發(fā)人員加深對(duì)編程的理解,還可以幫助他們掌握應(yīng)對(duì)各類突發(fā)狀況的技巧。然而,從實(shí)施嚴(yán)格的速率限制到承受Reddit網(wǎng)站的流量激增,這一過程并不輕松。
因此,開發(fā)人員可以從零基礎(chǔ)起步,根據(jù)收到的反饋不斷改進(jìn)人工智能梗圖生成器。也許他們制作的梗圖會(huì)大受歡迎,并從中獲得令人滿意的回報(bào)。
原文標(biāo)題:??From Zero to Meme Hero: How I Built an AI-Powered Meme Generator in React??,作者:Mohit Menghnani
