এই পোস্টে আমি ব্যাখ্যা করব কিভাবে Upstash, Next.js, LangChain এবং Fly.io ব্যবহার করে একটি ওপেন-সোর্স কাস্টম কন্টেন্ট AI চ্যাটবট তৈরি করা হয়েছে। Upstash মডেল ট্রেনিং শিডিউল করতে সাহায্য করল, উদার রেট লিমিটিং অফার করল এবং OpenAI API রেসপন্স ক্যাশ করল।

আমরা কী ব্যবহার করব
- Next.js (ফ্রন্টএন্ড এবং ব্যাকএন্ড)
- LangChain (ভাষা মডেল-চালিত অ্যাপ্লিকেশন বিকাশের ফ্রেমওয়ার্ক)
- Upstash (QStash দিয়ে ট্রেনিং মডেল শিডিউল, রেট লিমিটিং এবং OpenAI রেসপন্স ক্যাশিং)
- Tailwind CSS (স্টাইলিং)
- Fly.io (ডিপ্লয়মেন্ট)
আপনার কী প্রয়োজন
- Node.js 18
- Upstash অ্যাকাউন্ট
- OpenAI অ্যাকাউন্ট (OpenAI API Key-এর জন্য)
Upstash Redis সেটআপ করা
Upstash অ্যাকাউন্টে লগ ইন করে Redis ট্যাবে একটি ডেটাবেস তৈরি করুন। Details ট্যাবে Connect your database সেকশন থেকে কন্টেন্ট কপি করে রাখুন। REST API সেকশনে .env বাটনে ক্লিক করে কন্টেন্ট কপি করুন।
Upstash QStash সেটআপ করা
QStash ট্যাবে যান এবং QSTASH_URL, QSTASH_TOKEN, QSTASH_CURRENT_SIGNING_KEY, এবং QSTASH_NEXT_SIGNING_KEY প্রাপ্ত করুন। কন্টেন্ট কপি করে রাখুন।
প্রজেক্ট সেটআপ করা
রিপো ক্লোন করুন এবং টিউটোরিয়াল ফলো করুন:
git clone https://github.com/rishi-raj-jain/custom-content-ai-chatbot cd custom-content-ai-chatbot npm install
.env ফাইল তৈরি করুন:
# .env # উপরের ধাপগুলো থেকে প্রাপ্ত # Upstash Redis Secrets UPSTASH_REDIS_REST_URL="https://....upstash.io" UPSTASH_REDIS_REST_TOKEN="..." # Upstash QStash Secrets QSTASH_URL="https://qstash.upstash.io/v1/publish/" QSTASH_TOKEN="..." QSTASH_CURRENT_SIGNING_KEY="sig_..." QSTASH_NEXT_SIGNING_KEY="sig_..." # OpenAI Key OPENAI_API_KEY="sk-..." # Admin Access Key # ট্রেনিং রিকোয়েস্ট শুধু অ্যাডমিন করবে তা ভেরিফাই করতে ADMIN_KEY="..."
লোকাল এনভায়রনমেন্ট চালান:
npm run dev
রিপোজিটরি কাঠামো
প্রজেক্টের প্রধান ফোল্ডার কাঠামো। লাল চিহ্নিত ফাইলগুলো এই পোস্টে আলোচনা করা হয়েছে: ভেক্টর স্টোর ম্যানেজমেন্ট, কাস্টম কন্টেন্টে ট্রেনড AI-তে চ্যাট করার API Routes (রেসপন্স ক্যাশিংসহ), এবং মডেল ট্রেনিং প্রসেস শিডিউলিং।
উচ্চ-স্তরের ডেটা ফ্লো এবং অপারেশন
ডেটা কীভাবে ফ্লো করে এবং অপারেশনগুলো কীভাবে হয় সেটির হাই-লেভেল ডায়াগ্রাম:

- যখন ব্যবহারকারী চ্যাটবটে প্রশ্ন করেন, তাদের IP রেট লিমিটিংয়ের বিরুদ্ধে চেক করা হয়, এবং রেসপন্স (Upstash Redis-এ ক্যাশ না থাকলে) OpenAI API-থেকে সিক করা হয় (তারপর ক্যাশ) এবং ব্যবহারকারীর কাছে স্ট্রিম করা হয়।
- যখন অ্যাডমিন দেয়া URL সেটে এক্সিস্টিং মডেল ট্রেন করার রিকোয়েস্ট করেন, Upstash QStash-এর সাহায্যে একটি নির্দিষ্ট ডিলে (১০ সেকেন্ড) পরে সার্ভারলেসে POST রিকোয়েস্ট করা হয় দেয়া URL-গুলোর কন্টেন্ট ফেচ করে মডেল আপডেট করতে (ব্যাকগ্রাউন্ডে)।
Next.js-এ Chat এবং Train API Routes সেটআপ করা
এই সেকশনে আমরা আলোচনা করব কিভাবে pages/api/chat.js রাউট সেটআপ করা হয়েছে: Cross Origin Requests সক্ষম করা, Chat API কলে রেট লিমিট করা, রেসপন্স ক্যাশ ও স্ট্রিম করা, এবং নির্দিষ্ট URL-এ কন্টেন্ট ট্রেনিং শিডিউল করার মেথড এক্সপোজ করা। আর pages/api/train.js শুধু দেয়া URL-এ ট্রেনিং পারফর্ম করে কিন্তু ব্যাকগ্রাউন্ডে।
১. CORS সক্ষম করা
cors প্যাকেজ ব্যবহার করে অ্যাপ্লিকেশনে CORS সক্ষম করা হয়েছে যাতে চ্যাটবট একাধিক জায়গায় ব্যবহার করা যায় (যেমন আপনার ওয়েবসাইটে বট হিসেবে)। API Route ইনিশিয়ালাইজ হলে cors সেটআপ রান করা হয়:
// File: pages/api/chat.js import { runMiddleware } from '@/lib/cors' export default async function (req, res) { try { await runMiddleware(req, res) // ... catch (e) { console.log(e.message || e.toString()) } return res.end() } // Cors Function // File: lib/cors.js import Cors from 'cors' const cors = Cors({ methods: ['POST', 'OPTIONS', 'HEAD'], }) export function runMiddleware(req, res, fn = cors) { return new Promise((resolve, reject) => { fn(req, res, (result) => { if (result instanceof Error) return reject(result) return resolve(result) }) }) }
২. দেয়া URL-এ কন্টেন্ট ট্রেনিং রিকোয়েস্ট শিডিউল করা
Upstash QStash দিয়ে "fire and forget" টাইপ API তৈরি করা যায়। মেইন ফাংশন ফিনিশ হওয়ার জন্য এক্টিভলি ওয়েট করতে হয় না, বরং ব্যাকগ্রাউন্ডে (অপশনালি কিছু ডিলে পর) করা হয়। এটি ক্রন-জবের মত কিন্তু রেগুলার ইন্টারভালে না, প্রতিটি রিকোয়েস্ট অনুযায়ী রান করে।
সেইম চ্যাট API রাউটে, যদি রিকোয়েস্টে admin-key হেডার থাকে এবং সেটি সার্ভার সাইড সিক্রেট (ADMIN_KEY) সাথে মেলে, তবে রিকোয়েস্ট বডিতে পাস করা URL সেটে কন্টেন্ট ট্রেনিং কিছু ডিলে (এখানে 10s) পর শিডিউল করা হয়। ট্রেনিং রিকোয়েস্ট একটি দেয়া এন্ডপয়েন্টে (এখানে: https://custom-content-ai-chatbot.fly.dev/api/train) করা হয়।
// File: pages/api/chat.js if (req.headers['admin-key'] === process.env.ADMIN_KEY) { if (!req.body.urls) return res.status(400).send('No urls to train on.') await qstashClient.publishJSON({ delay: 10, body: { urls: req.body.urls }, url: 'https://custom-content-ai-chatbot.fly.dev/api/train' }) return res.status(200).end() }
এখন pages/api/train.js ট্রেন API রাউটে কী আছে সেটি দেখি:
// File: pages/api/train.js import train from '@/lib/train' import * as dotenv from 'dotenv' import { redis } from '@/lib/redis' import { runMiddleware } from '@/lib/cors' import { verifySignature } from '@upstash/qstash/nextjs' dotenv.process() export const config = { api: { bodyParser: false, }, } async function handler(req, res) { try { await runMiddleware(req, res) if (req.method !== 'POST') return res.status(403).send('No other methods allowed.') if (!req.body.urls) return res.status(400).send('No urls to train on.') await train(req.body.urls) let allKeys = await redis.keys('*') if (allKeys) { allKeys = allKeys.filter((i) => !i.includes('@upstash/ratelimit:')) const p = redis.pipeline() allKeys.forEach((i) => p.del(i)) await p.exec() console.log('Cleaned cached responses in Upstash.') } return res.status(200).end() } catch (e) { console.log(e.message || e.toString()) } return res.end() } export default verifySignature(handler)
উপরের কোডে তিনটি ক্রিটিক্যাল অ্যাকশন করা হয়েছে:
- QStash-এর
verifySignatureমেথড দিয়ে ইনকামিং রিকোয়েস্ট ভেরিফিকেশন (Upstash-Signature হেডার খুঁজে র বডি দিয়ে ভেরিফাই)। trainফাংশন কল করা যা URL কন্টেন্ট ফেচ করে এক্সিস্টিং ভেক্টর স্টোরে যোগ করে (এবং সেভ করে)।- রেট লিমিটিং এর কি-গুলোর বাদে বাকি সব কি Upstash Redis-এ ক্লিয়ার করা (Redis Transactions ব্যবহার করে)।
৩. রেট লিমিটিং
রেট লিমিটিং বাস্তবায়নের জন্য Upstash Redis ডেটাবেস ক্লায়েন্ট এবং @upstash/ratelimit লাইব্রেরি ব্যবহার করা হয়েছে।
// File: lib/redis.js import * as dotenv from 'dotenv' import { Redis } from '@upstash/redis' import { Ratelimit } from '@upstash/ratelimit' dotenv.process() export const redis = new Redis({ url: process.env.UPSTASH_REDIS_REST_URL, token: process.env.UPSTASH_REDIS_REST_TOKEN, }) export const ratelimit = { chat: new Ratelimit({ redis, limiter: Ratelimit.slidingWindow(30, '86400s'), }), }
রেট লিমিটিং ব্যবহার করে আমি সার্ভিসটি পুরোপুরি ফ্রি এবং পাবলিক করে ফেলতে পারলাম! কেউ ওয়েবসাইটের মাধ্যমে দিনে ৩০টি প্রশ্ন জিজ্ঞেস করতে পারে। IP address-কে কি হিসেবে ব্যবহার করে দিনে ৩০ প্রশ্নের রেট লিমিট এনফোর্স করা হয়েছে।
// File: pages/api/chat.js import requestIp from 'request-ip' import { ratelimit } from '@/lib/redis' const detectedIp = requestIp.getClientIp(req) if (!detectedIp) return res.status(400).send('Bad request.') const result = await ratelimit.chat.limit(detectedIp) if (!result.success) return res.status(400).send('Rate limit exceeded.') // চ্যাট রেসপন্স সার্ভ করুন
৪. সেভ করা ইন্ডেক্সড ভেক্টর স্টোর লোড করা এবং OpenAI-কে রেসপন্সের জন্য জিজ্ঞেস করা
সব চেক শেষ হলে মূল কাজে: কাস্টম কন্টেন্টসহ OpenAI API কল করা এবং রেসপন্স ইউজারকে পাঠানো।
- ৪.১: সেভ করা ভেক্টর স্টোর রিট্রিভ করা
// File: pages/api/chat.js import { loadVectorStore } from '@/lib/vectorStore' const vectorStore = await loadVectorStore() // ... // Vector Store Function // File: lib/vectorStore.js import { join } from 'path' import { existsSync } from 'fs' import { Document } from 'langchain/document' import { FaissStore } from 'langchain/vectorstores/faiss' import { OpenAIEmbeddings } from 'langchain/embeddings/openai' export async function loadVectorStore() { const directory = join(process.cwd(), 'loadedVectorStore') const docStoreJSON = join(process.cwd(), 'loadedVectorStore', 'docstore.json') if (existsSync(docStoreJSON)) { return await FaissStore.load(directory, new OpenAIEmbeddings()) } else { return await FaissStore.fromDocuments([new Document({ pageContent: 'Hey' })], new OpenAIEmbeddings()) } }
- ৪.২: ইউজার কোয়েরিতে প্রম্প্ট গাইডলাইন যোগ করা
LangChain-এর PromptTemplate ব্যবহার করে ইউজার কোয়েরির সাথে ইনস্ট্রাকশন পাস করা হয় কীভাবে এবং কোন ম্যানারে AI উত্তর দেবে:
import { z } from 'zod' import { PromptTemplate } from 'langchain/prompts' import { RetrievalQAChain } from 'langchain/chains' import { OutputFixingParser, StructuredOutputParser } from 'langchain/output_parsers' const outputParser = StructuredOutputParser.fromZodSchema( z.object({ answer: z.string().describe('answer to question in HTML friendly format, use all of the tags wherever possible and including reference links'), }), ) const outputFixingParser = OutputFixingParser.fromLLM(model, outputParser) const prompt = new PromptTemplate({ template: `Answer the user's question as best and be as detailed as possible:\n{format_instructions}\n{query}`, inputVariables: ['query'], partialVariables: { format_instructions: outputFixingParser.getFormatInstructions(), }, }) const chain = RetrievalQAChain.fromLLM(model, vectorStore.asRetriever(), prompt)
- ৪.৩: রেসপন্স স্ট্রিম এবং ক্যাশ করা
Upstash Redis-এ রেসপন্স ক্যাশ করার জন্য LangChain-এর UpstashRedisCache ক্যাশ লাইব্রেরি ব্যবহার করা হয়েছে। এক্সিস্টিং Redis ইন্সট্যান্স ক্লায়েন্ট হিসেবে পাস করা হয় এবং ক্যাশিং হ্যান্ডলার ChatOpenAI র্যাপারে পাস করা হয় যাতে রেসপন্স ডেলিভার হওয়ার পর ক্যাশ হয়।
import { redis } from '@/lib/redis' import { ChatOpenAI } from 'langchain/chat_models/openai' import { UpstashRedisCache } from 'langchain/cache/upstash_redis' const upstashRedisCache = new UpstashRedisCache({ client: redis }) let doesToken = false const model = new ChatOpenAI({ streaming: true, cache: upstashRedisCache, callbacks: [ { handleLLMNewToken(token) { doesToken = true res.write(token) }, }, ], }) const chainOutput = await chain.call({ query: req.body.input }) if (!doesToken) return res.status(200).send(chainOutput.text)
অনেক কিছু শিখলাম! এখন কাজ শেষ।
Fly.io-এ ডিপ্লয় করা
রিপোজিটরিতে Fly.io-এর জন্য বিল্ট-ইন সেটআপ আছে: Dockerfile, fly.toml, .dockerignore। Fly.io অ্যাকাউন্ট থাকলে রুট ফোল্ডারে:
fly launch fly deploy
ডিপ্লয়মেন্ট শেষ! হ্যাঁ, এতটুকুই ছিল।
উপসংহার
এই প্রজেক্ট থেকে OpenAI রেসপন্স ক্যাশিং, রেট লিমিটিং, এবং মডেল ট্রেন করার জন্য শিডিউলড API রিকোয়েস্ট বাস্তবায়ন করার মূল্যবান অভিজ্ঞতা লাভ হয়েছে, আপনার ডিমান্ড অনুযায়ী স্কেল হওয়া সার্ভিস (Upstash) ব্যবহার করে।
Next.js, Redis, TailwindCSS, LangChain, Serverless Scheduling