İstehsalat Səviyyəli RAG Arxitekturası: Next.js App Router, PostgreSQL HNSW İndeksləri və Yarı-Dəqiqlikli Vektor Axını
PostgreSQL səhifə limitləri daxilində qalmaq üçün yarı-dəqiqlikli vektorlar, SQL daxili RRF və tərs-proksi tamponlamasını keçən axın.
Müəssisə mühitlərində Axtarışla Genişləndirilmiş Generasiya (RAG) arxitekturaları təcrid olunmuş skript yazmadan imtina edib yüksək paralelliyə və xətalara dözümlü veb infrastrukturuna keçidi tələb edir. Ağır statistik axtarış mexanizmlərini asinxron istifadəçi interfeysləri ilə birləşdirərkən məlumatlar bazasının yaddaş limitləri və şəbəkə ötürülməsi gecikmələri əsas mühəndislik maneələrinə çevrilir. Müasir tətbiqlər üçün arxitektura bazası PostgreSQL-i vahid məlumat ekosistemi kimi qəbul edir, yüksək ölçülü vəziyyət (state) davamlılığı üçün pgvector genişlənməsini və cavabların axınla (streaming) ötürülməsi üçün Next.js App Router-i istifadə edir.
Bu analiz istehsalata hazır RAG konveyerinin qurulmasını təfərrüatlandırır. Diqqət mərkəzində yarı-dəqiqlikli (half-precision) vektorların saxlanması vasitəsilə PostgreSQL məlumat səhifəsi (memory page) məhdudiyyətlərinin optimallaşdırılması, hibrid axtarış mexanizmləri üçün SQL daxili Qarşılıqlı Dərəcə Birləşməsinin (RRF) tətbiqi və Next.js ekosistemində Server Tərəfindən Göndərilən Hadisələrin (SSE) tərs-proksi tamponlama (buffering) məhdudiyyətlərinin aradan qaldırılması dayanır.
Çoxölçülü Məlumat Davamlılığı və Yaddaş Səhifəsinin Optimallaşdırılması
PostgreSQL daxilində vektor oxşarlığı axtarışı verilənlər bazasının yaddaşa müraciət nümunələrini kökündən dəyişir. Standart B-ağacı (B-tree) indeksləri yüksək seçicilikli disk oxumaları üçün optimallaşdırıldığı halda, Təxmini Ən Yaxın Qonşu (ANN) indeksləri, xüsusilə də Hierarchical Navigable Small World (HNSW) qrafları, millisaniyədən az sorğu gecikməsinə nail olmaq üçün aktiv iş dəstinin tamamilə shared_buffers və ya əməliyyat sisteminin səhifə keşində yerləşməsini tələb edir.
Müasir modellər (məsələn, OpenAI-ın text-embedding-3-small modeli) tərəfindən yaradılan standart bir embedding 1,536 ölçüdən ibarətdir. Standart 32-bitlik sürüşən nöqtəli ədədlər (float32) kimi saxlandıqda, tək bir vektor təxminən 6,144 bayt yük sahəsi tutur. PostgreSQL məlumatların saxlanması üçün sabit ölçülü 8KB-lıq heap səhifələrindən istifadə edir; bir sətir TOAST mexanizmləri ilə saxlanmadıqca bir neçə səhifəyə yayıla bilməz, TOAST isə indeks skan sürətlərinə mənfi təsir göstərir. 8 baytlıq sətir başlığını nəzərə alsaq, 6,152 baytlıq bir qeyd 8KB-lıq səhifədə yalnız bir tam dəqiqlikli vektorun yerləşməsini diktə edir.
Bu arxitektur məhdudiyyəti aradan qaldırmaq üçün tətbiq pgvector 0.7.0 versiyasında təqdim edilmiş 16-bitlik sürüşən nöqtəli format olan halfvec-in istifadəsini tələb edir. Ölçüləri 16 bitə sıxmaq yükü 3,072 bayta endirir. Başlıqla birlikdə 3,080 baytlıq həcm iki vektorun tam olaraq bir 8KB səhifəyə sığmasına imkan verir. Bu sıxılma yığılma sıxlığını iki dəfə artırır, disk G/Ç əməliyyatlarını 50% azaldır və HNSW indeksinin ölçüsünü yarıya endirir, özü də axtarış dəqiqliyində (recall) nəzərəçarpmaz itki ilə.
| Metrik | Tam Dəqiqlik (vector, 32-bit) |
Yarı Dəqiqlik (halfvec, 16-bit) |
Fərq |
|---|---|---|---|
| Sətir başına yük (1536 ölçü) | 6,144 bayt | 3,072 bayt | -50% yaddaş |
| 8KB səhifədə vektor sayı | 1 | 2 | +100% sıxlıq |
| İndeksin qurulma vaxtı (1M sətir) | 377 saniyə | 163 saniyə | -57% hesablama |
| P99 sorğu gecikməsi | 2.7 ms | 1.9 ms | -30% gecikmə |
| Recall @ K=10 | 0.945 | 0.945 | 0% dəyişiklik |
Məlumatlar dbpedia-openai-1000k-angular məlumat dəstlərini təmsil edən 1,000,000 vektor üzərində icra edilmiş benchmark nəticələrini əks etdirir.
Sxemin Təyini və HNSW Parametrlərinin Tənzimlənməsi
PostgreSQL sxemini qurmaq bu optimallaşdırmaları qarşılamaq üçün dəqiq konfiqurasiyalar tələb edir. HNSW alqoritmi hər bir qovşağın bir vektoru təmsil etdiyi çoxtəbəqəli bir qraf qurur. Sorğunun marşrutlaşdırılması yuxarıdakı daha seyrək təbəqələrdən başlayır və ən yaxın qonşuları loqarifmik olaraq tapmaq üçün daha sıx təbəqələrə doğru enir.
İndeksi təyin edərkən m (qovşaq başına maksimum əlaqə) və ef_construction (indeksin qurulması zamanı dinamik namizəd siyahısının ölçüsü) parametrləri qrafın sıxlığını və dəqiqliyini müəyyən edir. m = 16 və ef_construction = 128 təyin etmək 1,536 ölçülü fəzalar üçün struktur bütövlüyü ilə qurulma performansı arasında riyazi cəhətdən əsaslandırılmış tarazlıq yaradır.
Milyonlarla sətir üzərində HNSW indeksi qurmaq təbiəti etibarilə yaddaş tələbkar bir əməliyyatdır. Əgər ayrılmış maintenance_work_mem qurulma zamanı bütün qraf strukturunu saxlamaq üçün kifayət etmirsə, PostgreSQL disk əsaslı çeşidləmə yoluna keçir ki, bu da qurulma vaxtlarını 10-50 dəfə artıra bilər. Bundan əlavə, qurulmanın paralelləşdirilməsi kritikdir; max_parallel_maintenance_workers parametrinin tənzimlənməsi qrafın qurulması zamanı bütün əlçatan CPU nüvələrinin istifadə edilməsini təmin edir.
-- Vektor əməliyyatları genişlənməsini aktivləşdir.
CREATE EXTENSION IF NOT EXISTS vector;
-- Sənəd saxlama sxemini işə sal.
CREATE TABLE documents (
id BIGSERIAL PRIMARY KEY,
content TEXT NOT NULL,
-- Yaddaşın optimallaşdırılması üçün 16-bitlik sürüşən nöqtəli ədədlər.
embedding HALFVEC(1536),
-- Tam mətn axtarışı təmsilini sinxronlaşdıran generasiya olunmuş sütun.
fts TSVECTOR GENERATED ALWAYS AS (to_tsvector('english', content)) STORED,
created_at TIMESTAMPTZ DEFAULT now()
);
-- Paralel hesablamanı və texniki xidmət yaddaşını optimallaşdır.
SET maintenance_work_mem = '4GB';
SET max_parallel_maintenance_workers = 4;
-- Cədvəlin kilidlənməsinin qarşısını almaq üçün HNSW indeksini paralel qur.
CREATE INDEX CONCURRENTLY idx_docs_hnsw
ON documents
USING hnsw (embedding halfvec_cosine_ops)
WITH (m = 16, ef_construction = 128);
-- Leksik axtarış üçün Ümumiləşdirilmiş Tərs İndeksi (GIN) qur.
CREATE INDEX CONCURRENTLY idx_docs_fts
ON documents
USING gin (fts);
Sorğunun icrası zamanı PostgreSQL-in sorğu planlayıcısı (query planner) HNSW indeksindən istifadə etməyi və ya ardıcıl skan (sequential scan) etməyi qiymətləndirir. hnsw.ef_search parametri axtarış mərhələsində namizəd siyahısının ölçüsünü idarə edir. Daha yüksək ef_search daha yaxşı dəqiqlik versə də, həddindən artıq yüksək dəyər sorğu planlayıcısının xərc modelini indeksdən tamamilə imtina etməyə vadar edir və tam cədvəl skanına qayıdış icra vaxtlarını 2.5 millisaniyədən 360 millisaniyədən yuxarı qaldırır. ef_search-i məhdud bir aralıqda (məsələn, 40-100) tənzimləmək bu tərəddüdün qarşısını alır.
Qarşılıqlı Dərəcə Birləşməsi (RRF) vasitəsilə Hibrid Axtarış
Semantik vektor axtarışı konseptual eşləşmədə mükəmməldir, lakin konkret hərf-rəqəm xəta kodlarının, UUID-lərin və ya yüksək ixtisaslaşmış domen nomenklaturasının təhlili kimi dəqiq açar söz axtarışlarında tez-tez uğursuz olur. Etibarlı istehsalat sistemi bunu sıx vektor embedding-lərini seyrək leksik təmsillərlə (BM25 və ya tsvector) birləşdirən hibrid axtarış konveyeri qurmaqla yüngülləşdirir.
Ənənəvi yanaşma iki ayrı sorğunun icrasını, nəticələrin kənar tətbiq serverinə ötürülməsini və siyahıların proqramatik olaraq birləşdirilməsini ehtiva edir. Lakin bu məntiqin birbaşa PostgreSQL daxilində icra edilməsi şəbəkə serializasiyası yükünü aradan qaldırır və tətbiq tərəfindəki yaddaş təzyiqini azaldır.

Bu birləşdirmə üçün optimal riyazi çərçivə Qarşılıqlı Dərəcə Birləşməsidir (RRF). RRF bir sənədin birdən çox müstəqil axtarış metodologiyası üzrə dərəcəsinin tərslərini toplayaraq vahid bir xal hesablayır. Formulyasiya belə müəyyən edilir:
Bu tənlikdə sənədinin axtarış metodunun çeşidlənmiş nəticə siyahısındakı 1-əsaslı mövqeyini təmsil edir, isə hamarlaşdırma (smoothing) sabiti kimi çıxış edir. Bu sabit empirik olaraq kalibrlənir və sənaye standartı sayılır. Həmin dəyər qeyri-xətti azalan gəlir təmin edir: yuxarı sıralarda yerləşən sənədlərə güclü çəki verilir, aşağı sıralardakılar isə marjinal töhfə verir, beləliklə heç bir axtarış metodologiyasının son nəticəyə tək başına hakim olmasına imkan vermədən bərabər hallar həll olunur.
SQL icra nümunəsi RRF matrisini dinamik şəkildə hesablamaq üçün Ortaq Cədvəl İfadələrinə (CTE) və FULL OUTER JOIN əməliyyatına əsaslanır.
WITH vector_search AS (
SELECT id,
content,
ROW_NUMBER() OVER (ORDER BY embedding <=> $1::halfvec(1536)) AS rank
FROM documents
ORDER BY embedding <=> $1::halfvec(1536)
LIMIT 20
),
text_search AS (
SELECT id,
content,
ROW_NUMBER() OVER (ORDER BY ts_rank(fts, query) DESC) AS rank
FROM documents, plainto_tsquery('english', $2) query
WHERE fts @@ query
ORDER BY ts_rank(fts, query) DESC
LIMIT 20
),
combined AS (
SELECT COALESCE(v.id, t.id) AS id,
COALESCE(v.content, t.content) AS content,
-- k = 60 ilə RRF hesablamasının tətbiqi.
(COALESCE(1.0 / (60 + v.rank), 0.0) +
COALESCE(1.0 / (60 + t.rank), 0.0)) AS rrf_score
FROM vector_search v
FULL OUTER JOIN text_search t ON v.id = t.id
)
SELECT id, content, rrf_score
FROM combined
ORDER BY rrf_score DESC
LIMIT 5;
Bu sorğu sənədin yalnız vektor axtarışı tərəfindən tapılıb, leksik axtarış tərəfindən tamamilə qaçırıldığı halda belə, qismən xalın düzgün qiymətləndirilməsinə zəmanət verir və sistemin zərif şəkildə geri çəkilməsinə imkan yaradır.
Şəbəkə Axını Məhdudiyyətləri və Next.js App Router
PostgreSQL-dən yüksək uyğunluqlu kontekst matrisini əldə etdikdən sonra, arxitekturanın növbəti mərhələsi bu məlumatların Böyük Dil Modelinə (LLM) ötürülməsini və generasiya edilmiş cavabın son istifadəçiyə çatdırılmasını nəzərdə tutur. LLM çıxarışları öz təbiəti etibarilə gecikməli olduğundan, cavabın tək bir blok şəklində ötürülməsi qəbuledilməz İlk Bayt Müddəti (TTFB) göstəricilərinə gətirib çıxarır. Standart həll yolu hissəvi (chunked) transfer kodlaması ilə birlikdə Server Tərəfindən Göndərilən Hadisələrdən (SSE) istifadə etməkdir.
Next.js App Router standart Web ReadableStream API-sindən istifadə edərək axın cavablarını yerli olaraq dəstəkləyir. Lakin axın son nöqtələrinin istehsalat mühitlərinə yerləşdirilməsi ciddi şəbəkə tamponlama fəsadları yaradır. Tərs proksilər (məsələn, Nginx) və serversiz kənar şəbəkələr (məsələn, Vercel) HTTP cavablarını tamponlamaq üçün standart olaraq konfiqurasiya edilir. Onlar çıxan hissələri kəsir, marşrut işləyicisi (route handler) funksiyasının bağlantını bağlamasını gözləyir və sonra bütün yükü eyni anda müştəriyə çatdıraraq real vaxt axınının bütün məqsədini boşa çıxarır.
Aralıq tamponlama qatlarından yan keçmək üçün HTTP cavabı açıq şəkildə dərhal çatdırılmanı tələb etməlidir. Bu, sərt Cache-Control direktivləri ilə birlikdə X-Accel-Buffering: "no" başlığının daxil edilməsi ilə əldə edilir.
Aşağıdakı tətbiq TypeScript dilində yazılmış etibarlı Next.js Route Handler-i təfərrüatlandırır. O, məlumatlar bazası əlaqələri üçün pg drayverini inteqrasiya edir, RRF sorğusunu icra edir, LLM tamamlamasını inisiallaşdırır və proksi tamponlarından aktiv şəkildə yan keçərək bayt axınını müştəriyə təslim edir.
// app/api/rag/route.ts
import { NextRequest } from "next/server";
import { Pool } from "pg";
import OpenAI from "openai";
// Resurslardan səmərəli istifadə üçün əlaqə hovuzu (connection pooling) qurulur.
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
max: 20,
idleTimeoutMillis: 30000,
});
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
export async function POST(req: NextRequest) {
const { query } = await req.json();
// 1. Sorğu vektorunun (embedding) generasiyası.
const embedRes = await openai.embeddings.create({
model: "text-embedding-3-small",
input: query,
});
const queryEmbedding = embedRes.data[0].embedding;
// 2. Təcrid olunmuş bir əlaqə daxilində RRF sorğusunun icrası.
const pgClient = await pool.connect();
let contextDocs: string[] = [];
try {
const { rows } = await pgClient.query(
`WITH vector_search AS (
SELECT id, content,
ROW_NUMBER() OVER (ORDER BY embedding <=> $1::halfvec(1536)) AS rank
FROM documents
ORDER BY embedding <=> $1::halfvec(1536)
LIMIT 20
), text_search AS (
SELECT id, content,
ROW_NUMBER() OVER (ORDER BY ts_rank(fts, q) DESC) AS rank
FROM documents, plainto_tsquery('english', $2) q
WHERE fts @@ q
ORDER BY ts_rank(fts, q) DESC
LIMIT 20
)
SELECT COALESCE(v.content, t.content) AS content
FROM vector_search v
FULL OUTER JOIN text_search t ON v.id = t.id
ORDER BY (COALESCE(1.0 / (60 + v.rank), 0.0) +
COALESCE(1.0 / (60 + t.rank), 0.0)) DESC
LIMIT 5;`,
[JSON.stringify(queryEmbedding), query]
);
contextDocs = rows.map((r) => r.content);
} finally {
pgClient.release(); // Əlaqənin hovuza qaytarılmasını təmin etmək.
}
// 3. Genişləndirilmiş sistem prompt-unun qurulması.
const systemPrompt =
`Təqdim edilmiş konteksti təhlil edin və istifadəçinin sorğusuna dəqiq cavab formalaşdırın.\n\n` +
`Kontekst:\n${contextDocs.join("\n\n")}`;
// 4. Asinxron LLM generasiya axınının inisializasiyası.
const llmStream = await openai.chat.completions.create({
model: "gpt-4o",
messages: [
{ role: "system", content: systemPrompt },
{ role: "user", content: query },
],
stream: true,
});
// 5. SSE üzərindən hissələri nəql etmək üçün ReadableStream-in qurulması.
const encoder = new TextEncoder();
const stream = new ReadableStream({
async start(controller) {
try {
for await (const chunk of llmStream) {
const text = chunk.choices[0]?.delta?.content || "";
if (text) {
// Protokola xas SSE formatlaşdırması.
controller.enqueue(
encoder.encode(`data: ${JSON.stringify({ text })}\n\n`)
);
}
}
controller.enqueue(encoder.encode("data: [DONE]\n\n"));
controller.close();
} catch (error) {
console.error("Axın konveyerində istisna baş verdi:", error);
controller.error(error);
}
},
});
// 6. Axının anti-tamponlama şəbəkə başlıqları ilə göndərilməsi.
return new Response(stream, {
headers: {
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache, no-transform",
Connection: "keep-alive",
// Nginx və kənar şəbəkə tamponlamasını söndürmək üçün kritik direktiv.
"X-Accel-Buffering": "no",
},
});
}
Cache-Control: 'no-cache, no-transform' direktivi X-Accel-Buffering başlığını tamamlayır və aralıq proksilərin yükün kodlamasını dəyişdirməsinin və ya qismən axın vəziyyətlərini keşləməsinin qarşısını alır. Bu sərt şəbəkə konfiqurasiyası tokenlərin generasiya olunan kimi müştərinin TCP soketinə ötürülməsinə zəmanət verir və RAG tətbiqləri üçün həyati əhəmiyyət daşıyan real vaxt istifadəçi təcrübəsini qoruyur.
Yaddaş sıxlığını 16-bitlik vektor kvantlaşdırması ilə həll edərək, hibrid axtarış məntiqini riyazi cəhətdən əsaslandırılmış dərəcə birləşməsi ilə verilənlər bazası təbəqəsində birləşdirərək və marşrutlaşdırma qatında tamponlanmamış axın ötürülməsini tətbiq edərək, sistemlər paylanmış mikroxidmətlərin kövrəkliyindən yan keçə bilər. Bu arxitektura davamlı məlumat anbarları üzərində mürəkkəb LLM qarşılıqlı təsirlərini icra etmək üçün müstəsna dərəcədə dayanıqlı, yüksək ötürücülüklü bir təməl təqdim edir.
Mənbələr
22
- halfvec: Half the Bits, Twice the speed? - DEV Communitydev.to
- pgvector, a guide for DBA - Part 2: Indexes (update march 2026) - dbi servicesdbi-services.com
- Hybrid Search with pgvector and PostgreSQL Full-Text Search - Rivestackrivestack.io
- Reciprocal Rank Fusion (RRF) explained in 4 mins — How to score results form multiple retrieval methods in RAG | by Deval Shah | Mediummedium.com
- pgvector/pgvector: Open-source vector similarity search for Postgres - GitHubgithub.com
- Scaling pgvector: Memory, Quantization, and Index Build Strategies | myDBA.devmydba.dev
- The pgvector extension - Neon Docsneon.com
- Guides: Streaming - Next.jsnextjs.org
- Guides: Self-Hosting - Next.jsnextjs.org
- Self-Host Next.js with Docker: Standalone, Nginx, Streaming - Easton Deveastondev.com
- FastAPI SSE working Locally but not in Azure Web App? - Stack Overflowstackoverflow.com
- Postgres vector search: what breaks first - Kubai Kevinkubaik.github.io
- How to Reduce Bloat in Large PostgreSQL Tables - Tiger Datatigerdata.com
- Deep dive into PostgreSQL VACUUM garbage collector | Google Cloud Blogcloud.google.com
- An early look at HNSW performance with pgvector | Jonathan Katzjkatz05.com
- Reciprocal rank fusion | Elasticsearch Referenceelastic.co
- LM-Kit.NET Reciprocal Rank Fusion (RRF): Merge Retrieval Results in C# .NETdocs.lm-kit.com
- Reciprocal Rank Fusion Algorithm - Emergent Mindemergentmind.com
- Implementing Server-Sent Events (SSE) in Node.js with Next.js: A Complete Guide - Mediummedium.com
- Fixing Slow SSE (Server-Sent Events) Streaming in Next.js and Vercel - Mediummedium.com
- Server-Sent Events don't work in Next API routes #48427 - GitHubgithub.com
- Streaming Layer - AI Business Maturity Modelaibmm.ai



