
در Next.js، کلید API و Query دیتابیس باید داخل Route Handler سمت سرور بمانند. مرورگر فقط سؤال را میفرستد و پاسخ Streamشده همراه منابع معتبر دریافت میکند. این مرز، کلید پاستا و اطلاعات دیتابیس را از Bundle کاربر دور نگه میدارد.
این آموزش فرض میکند سندها قبلاً Chunk و Embed شدهاند. برای Ingest از راهنمای ساخت RAG با pgvector استفاده کنید.
معماری برنامه
- فرم Client سؤال را به
/api/askمیفرستد. - Route Handler هویت، اندازه و نرخ درخواست را بررسی میکند.
- سؤال با API پاستا Embed میشود.
- pgvector نزدیکترین Chunkهای مجاز را برمیگرداند.
- مدل گفتگو پاسخ محدود به Context میسازد.
- Backend پاسخ و Citationهای تأییدشده را Stream میکند.
نصب SDK و متغیرهای محیطی
npm install openai pg
PAASTA_AI_KEY=...
EMBEDDING_MODEL=...
CHAT_MODEL=...
DATABASE_URL=...
متغیر کلید نباید پیشوند NEXT_PUBLIC_ داشته باشد. برای هر محیط کلید جدا با سقف مصرف و مدلهای مجاز بسازید.
Client سازگار با OpenAI
import OpenAI from "openai";
export const ai = new OpenAI({
apiKey: process.env.PAASTA_AI_KEY,
baseURL: "https://console.paasta.cloud/api/ai/v1",
});
Route Handler پرسش
export async function POST(request: Request) {
const { question } = await request.json();
if (typeof question !== "string" || question.length > 2000) {
return Response.json({ error: "invalid_question" }, { status: 400 });
}
const embedded = await ai.embeddings.create({
model: process.env.EMBEDDING_MODEL!,
input: question,
});
const vector = embedded.data[0].embedding;
const sources = await retrieveSources(vector, currentTenantId());
if (!sources.length) {
return Response.json({ answer: "در منابع موجود پیدا نشد.", sources: [] });
}
return streamGroundedAnswer(question, sources);
}
currentTenantId() باید از Session معتبر سمت سرور بیاید. شناسهای را که مرورگر آزادانه میفرستد مبنای دسترسی قرار ندهید.