ساخت یک موتور جستجوگر معنایی

بیایید با آن روبرو شویم – همه ما آنجا بوده ایم. شما در حال تلاش برای یافتن آن یادگار مناسب برای خلاصه ترس وجودی خود در مورد قهوه صبح دوشنبه هستید ، اما مهم نیست که چه کلمات کلیدی را امتحان می کنید ، فقط عکس های گربه نامربوط دریافت می کنید. جستجوی معنایی را وارد کنید: فناوری که در نهایت نمایش داده های جستجوی هرج و مرج شما را درک می کند. در این پست ، من به شما نشان خواهم داد که چگونه یک موتور جستجوگر Meme ساخته ام که در واقع با استفاده از برخی جادوی جالب فناوری (و ابزارهای عالی عالی Upstash) شوخی می کند.
Upstash یک پایگاه داده بردار ارائه می دهد که جستجوهای شباهت مقیاس پذیر را در بین بردارها امکان پذیر می کند ، با ویژگی هایی مانند فیلتر ابرداده و مدلهای تعبیه شده داخلی. علاوه بر این ، Upstash Redis قابلیت های ذخیره سازی قدرتمند و محدود کننده نرخ را فراهم می کند ، و آن را به پایه ای ایده آل برای برنامه های مدرن با قدرت هوش مصنوعی تبدیل می کند.
در این پست ، من شما را طی می کنم که چگونه یک موتور جستجوگر Meme معنایی را با استفاده از بردار Upstash و Redis ایجاد کردیم که نشان می دهد چگونه می توان از این ابزارهای قدرتمند برای ساختن یک تجربه جستجوی کارآمد و کاربر پسند استفاده کرد.
می توانید پروژه را در GitHub کاوش کنید یا آن را مستقیماً به حساب Vercel خود مستقر کنید.
هنگامی که کلمات کلیدی شکست می خورند: معضل جستجوی Meme
جستجوی سنتی وقتی دقیقاً می دانید که به دنبال چه چیزی هستید. “گربه گریم” → ممتاز گربه های بدخیم. آسان اما الگوهای رفتاری در فضای عجیب و غریب بین معنای تحت اللفظی و زمینه فرهنگی زندگی می کنند. سعی کنید “این احساس را وقتی WiFi شما در حین فراخوان بزرگنمایی می میرد” جستجو کنید و به سرعت در محدودیت های تطبیق کلمات کلیدی قرار خواهید گرفت.
اینجاست که جستجوی معنایی می درخشد. این مثل این است که دوستی داشته باشید که عمیق در فرهنگ Meme باشد. به آنها بگویید “من به چیزی احتیاج دارم که بزرگسالی خیلی سخت باشد” و آنها 10 نوع از الگوهای سگ “این خوب است” را بدون چشمک زدن به شما تحویل می دهند. این دقیقاً همان چیزی است که ما در اینجا می سازیم.
Tech Stack/Toolkit: چرا این گزینه ها مهم هستند
next.js 15: زیرا چه کسی می خواهد با تنظیمات پیچیده آشفتگی کند؟ مسیرهای API فوری + React Goodness = Developer Happy.
بردار Upstash: مانند مغزی که روابط بین مفاهیم را به یاد می آورد (اما ارزان تر از یک مغز واقعی).
upstash redis: چاقوی دیتابیس های ارتش سوئیس – از حافظه پنهان استفاده می کند و کاربران را از اسپم کردن API ما باز می دارد.
آتش: برای تولید داده های متا تصاویر Meme. بسیاری از مدل های LLM مختلف در دسترس هستند.
تعبیه Openai: ضروری نیست ، ما می توانیم از مدل های مختلف هوش مصنوعی استفاده کنیم (مانند مدل های بغل کردن صورت).
Vercel Blob: برای ذخیره تمام آن الگوهای ادویه ای بدون غرق شدن در پیکربندی های S3.
تولید داده های متا برای تصاویر Meme
role: "user",
content: [
{ type: "text", text: "Describe the image in detail." },
{
type: "image",
image: file,
},
],
},
],
});
images.push({ path: file, metadata: result.object.image });
قبل از تولید بردارهای جاسازی شده برای تصاویر Meme در مجموعه داده های خود ، ما برای ایجاد ابرداده جامع برای هر تصویر نیاز داشتیم. برای دستیابی به این هدف ، من API LLM OpenAI را به طور خودکار با 9 پارامتر اصلی برچسب گذاری کردم: PATH (میزبان در Vercel) ، عنوان ، توضیحات ، برچسب ها ، Memecontext ، Humor ، Format ، TextContent ، شخص و اشیاء. چالش این بود که هم محتوای بصری تصویر و هم متن تعبیه شده در الگوی رفتاری را ضبط کنیم. در زیر نمونه ای از برچسب های ابرداده وجود دارد:
{
"path": "https://kqznk1deju1f3qri.public.blob.vercel-storage.com/another_%20Bugs%20Bunny_s%20_No_-D6ht580Nj0I8mri3hYLVIHnRx4DNIB.jpg",
"metadata": {
"title": "Bugs Bunny 'No' Meme with Defiant Expression",
"description": "This meme features Bugs Bunny, a classic cartoon character from the Looney Tunes series, with a defiant expression. The image is a close-up of Bugs Bunny's face, showing him with half-closed eyes and a slightly open mouth, conveying disinterest or refusal. The word 'no' is displayed in bold white text, emphasizing his dismissive attitude. This meme is often used to humorously express firm rejection. The mid-20th-century animation style adds nostalgia.",
"tags": [
"Bugs Bunny",
"Looney Tunes",
"cartoon",
"meme",
"no",
"refusal",
"defiance",
"humor",
"animation",
"classic",
"nostalgia",
"expression",
"dismissive",
"rejection",
"bold text"
],
"memeContext": "A frame from Looney Tunes repurposed to convey humorous refusal in online conversations, leveraging Bugs Bunny's witty demeanor.",
"humor": "Exaggerated refusal using a beloved character, with simplicity enhancing comedic effect.",
"format": "Bugs Bunny 'No' Meme",
"textContent": "no",
"person": "Bugs Bunny, animated character",
"objects": "Bugs Bunny, text 'no'"
}
}
LLM هر دو عنصر بصری (به عنوان مثال ، عبارات شخصیت ، قرار دادن متن) و زمینه فرهنگی (به عنوان مثال ، جذابیت نوستالژیک Bugs Bunny) را برای تولید ابرداده های غنی و دوستانه دوستانه تجزیه و تحلیل کرد.
آماده سازی تعبیه
برای موتور جستجوگر Meme ، ما خودمان را با استفاده از مدل کوچک Text-Embedding-3-Small OpenAi تعبیه کردیم ، اما توصیه می کنم مدل های تعبیه دیگری را برای دیدن تنوع امتحان کنید. هر یادداشت با تعبیه عنوان و توضیحات خود نشان داده شده است:
export const generateEmbedding = async (value: string): Promise<number[]> => {
const input = value.replaceAll("\n", " ");
const { embedding } = await embed({
model: embeddingModel,
value: input,
});
return embedding;
};
نمایه سازی بردارها
برای موتور جستجوی Meme ، ما یک ساختار شاخص ساده ایجاد کردیم که در آن هر الگوی رفتاری توسط:
- تعبیه شده از عنوان و توضیحات آن
- ابرداده از جمله عنوان ، توضیحات و مسیر تصویر
- یک شناسه منحصر به فرد برای بازیابی
فرآیند نمایه سازی واقعی از طریق API بردار Upstash انجام می شود:
// Initialize Upstash Vector client with the memes index
export const memesIndex = new Index({
url: process.env.UPSTASH_VECTOR_URL!,
token: process.env.UPSTASH_VECTOR_TOKEN!,
});
بر خلاف پروژه های بزرگتر که ممکن است نیاز به پردازش دسته ای داشته باشد ، پایگاه داده Meme ما به اندازه کافی کوچک است که بدون بهینه سازی های ویژه به طور مؤثر نمایه می شود. سادگی API وکتور Upstash این روند را مستقیماً جلوه داد.
ساخت موتور جستجو Meme
عملکرد جستجو هر دو جستجوی شباهت بردار و تطبیق متن مستقیم را ترکیب می کند (و اگر کپی کند آن را رها می کند):
// For longer queries, perform both direct and semantic search
const directMatches = await findMemesByQuery(query);
const semanticMatches = await findSimilarMemes(query);
// Combine results, removing duplicates
const allMatches = uniqueItemsByTitle([...directMatches, ...semanticMatches]);
جستجوی وکتور محتوای معنایی مشابه را پیدا می کند:
export const findSimilarMemes = async (description: string): Promise<Meme[]> => {
const embedding = await generateEmbedding(description);
const results = await memesIndex.query({
vector: embedding,
includeMetadata: true,
topK: 60,
includeVectors: false,
});
return results.map((result: any) => ({
id: String(result.id),
title: result.metadata?.title || '',
description: result.metadata?.description || '',
path: result.metadata?.path || '',
embedding: [],
similarity: result.score,
}));
};
برای نتایج جستجو ، ما هم ابرداده (عنوان و توضیحات) و هم مسیر تصویر را درج می کنیم و به ما امکان می دهد نتایج غنی را به کاربر نمایش دهیم.
اجرای ذخیره و محدود کردن نرخ
برای بهینه سازی عملکرد و کنترل API ، ما با استفاده از upstash redis ، ذخیره و محدود کردن نرخ را اجرا کردیم.
بعد از اینکه از URL و نشانه خود گرفتیم ، می توانیم با کد ما ادغام شویم:
ذخیره سازی
// Create cache key
const cacheKey = query
? `q:${query.toLowerCase().replaceAll(" ", "_")}`
: "all_memes";
// Check cache first
const cached = await redis.get(cacheKey);
if (cached) {
// Return cached results
return { memes: JSON.parse(cached as string) };
}
// ... perform search ...
// Cache the results
await redis.set(cacheKey, JSON.stringify(allMatches), { ex: REDIS_CACHE_TTL });
محدود کردن نرخ با مجموعه های مرتب شده Redis
بیایید محدودیت نرخ را برای جلوگیری از استفاده از API ادغام کنیم:
const RATE_LIMIT = {
window: 60, // 1 minute
requests: 10 // Max 10 requests per minute
};
async function checkRateLimit(ip: string): Promise<boolean> {
const now = Math.floor(Date.now() / 1000);
const windowStart = now - RATE_LIMIT.window;
// Remove old entries
await redis.zremrangebyscore(`ratelimit:${ip}`, 0, windowStart);
// Count requests in the current window
const requestCount = await redis.zcount(`ratelimit:${ip}`, windowStart, '+inf');
// If under limit, add this request and return true
if (requestCount < RATE_LIMIT.requests) {
await redis.zadd(`ratelimit:${ip}`, { score: now, member: now.toString() });
return true;
}
return false;
}
مجموعه های مرتب شده Redis روشی زیبا و کارآمد برای اجرای محدود کننده نرخ پنجره کشویی ارائه می دهند. این رویکرد چندین مزیت ارائه می دهد:
- انقضاء دقیق مبتنی بر زمان درخواست های قدیمی
- شمارش کارآمد درخواست ها در پنجره فعلی
- استفاده از حافظه کم حتی با ترافیک زیاد
یک انتخاب جالب پایگاه داده: Vercel Blob
Vercel Blob ذخیره سازی پرونده را با ادغام بی دردسر ساده می کند. بر خلاف بانکهای اطلاعاتی سنتی ، تنظیم فقط به سه مرحله نیاز دارد:
- ایجاد یک پروژه
- فضای ذخیره سازی Vercel را وصل کنید
- بارگذاری پرونده ها را شروع کنید – بدون تنظیمات پیچیده.
داشبورد بصری آن ، مدیریت ذخیره سازی را ساده می کند و سازمان دارایی در زمان واقعی ، تجزیه و تحلیل استفاده و مقیاس پذیری یکپارچه را ارائه می دهد-همه به طور مستقیم در اکوسیستم Vercel. ایده آل برای توسعه دهندگان اولویت بندی سرعت و سادگی.
رابط کاربری
در ابتدا ، من یک UI بصری متراکم با اجزای پیچیده طراحی کردم. اما بعد فهمیدم ، “AKIF ، این پروژه به طرحی نیاز دارد که سادگی را برجسته کند!” – بنابراین من به عقب برگشتم و یک رابط کاربری مینیمالیستی تمیزتر و مینیمالیستی ایجاد کردم.
- یک کادر جستجوی برجسته در بالای صفحه
- یک طرح شبکه پاسخگو برای نمایش نتایج MEME
- جستجوهای پیشنهادی برای کاربران جدید که وارد پرس و جو نشده اند
بسته شدن کلمات
ساختن یک موتور جستجوگر معنایی معنایی با وکتور Upstash و Redis به طرز چشمگیری ساده بود. ترکیبی از این خدمات تمام ابزارهای مورد نیاز ما را برای ایجاد یک تجربه جستجوی سریع ، کارآمد و کاربر پسند فراهم می کند.
ویژگی های موجود در وکتور Upstash ، مانند تعبیه وکتور اتوماتیک ، ذخیره سازی ابرداده و جستجوی شباهت کارآمد ، این روند را بسیار آسان کرده است. به طور مشابه ، Upstash Redis ابزارهای قدرتمندی را برای ذخیره سازی و محدود کردن نرخ با حداقل تلاش فراهم کرد.
ما با این پروژه سرگرم کننده زیادی داشتیم و معتقدیم که خدمات Upstash برای ساخت برنامه های کاربردی AI از همه نوع مناسب است. اگر بازخوردی در مورد پروژه دارید ، احساس راحتی کنید تا به GitHub دسترسی پیدا کنید. بیایید با هم کار کنیم تا آن را بهتر کنیم!