اگر امروز یک چتبات تولیدی را با ۵ میلیون سند تغذیه کنید، هر درخواست کاربر حدود ۳۰ ثانیه معطل میماند و سیستم شما عملاً در برابر ترافیک واقعی نابود میشود. برای بقا در محیطهای عملیاتی، توسعهدهندگان باید از جستوجوی نزدیکترین همسایه تقریبی (Approximate Nearest Neighbor یا ANN) استفاده کنند. این روش، مقدار اندکی از دقت را فدای سرعت میکند تا زمان پاسخدهی را به میلیثانیه برساند.
این چالش دقیقاً زمانی رخ میدهد که تولید بازیابیافزا (RAG) — شبیه دانشآموزی که قبل از جواب دادن، اول کتاب درسی را باز میکند و از آن نقل میآورد — از مرحله نمونه اولیه (Prototype) به مقیاس سازمانی میرسد. همانطور که در تحلیل قبلی ما دربارهی نحوه مدیریت بردارهای با کارایی بالا در FAISS و PostgreSQL 18 اشاره کردیم، صنعت به سمت مدلهای ذخیرهسازی ترکیبی حرکت میکند. اکثر توسعهدهندگان اکنون با یک انتخاب حیاتی روبرو هستند: حفظ یک پایگاهداده رابطهای ساده یا مهاجرت به یک ذخیرهساز برداری اختصاصی به medida که حجم دادههایشان رشد میکند. اگر جستوجوی برداری را مانند جادو ببینید، در تصمیمات مربوط به تخصیص حافظه، انتخاب ایندکس و صورتحساب بردار معنایی (Embedding) — که مثل کارت معرفی عددی برای هر واژه است تا همسایگانش را بشناسد — دچار اشتباهات هزینه بر خواهید شد. در واقع، درک دقیق اینکه چگونه مدلهای بردار معنایی دقت بازیابی را در سیستمهای RAG افزایش میدهند، اولین قدم برای بهینهسازی این هزینههاست. این جادو نیست؛ بلکه مجموعهای از موازنههای حسابشده بین سرعت، حافظه و دقت است.
معماری ذخیرهسازی
ذخیرهسازی مؤثر برداری به چیزی بیش از یک بردار نیاز دارد. یک بردار بهتنهایی بیفایده است؛ آنچه شما در واقعیت ذخیره میکنید یک ردیف کامل است که شامل خود بردار، متن خام و زمینهای است که مانع از پرداخت هزینه تکراری برای یک بردار معنایی شود.
در یک محیط عملیاتی، شما سیستمی میسازید که باید در برابر ترافیک واقعی و هزینههای واقعی دوام بیاورد. یک طرحواره (Schema) آماده برای تولید، مانند آنچه در pgvector پیاده میشود، باید شامل موارد زیر باشد:
id: یک شناسه یکتا (برای مثال BIGSERIAL).chunk_text: متن اصلی که به عنوان بیمه نگهداری میشود تا اگر مدل Embedding تغییر کرد، مجبور به استخراج مجدد متن نباشید.embedding: خودِ بردار، که برای مدلهای مدرن معمولاًvector(1536)است.embedding_model: یک رشته متنی (مانند 'text-embedding-3-small') زیرا مدلهای مختلف فضاهای برداری ناسازگاری تولید میکنند.content_hash: یک هش SHA-256 از متن که برای حذف دادههای تکراری (Deduplication) استفاده میشود.metadata: ستونی از نوعjsonbبرای تعریف ویژگیهای منعطف.created_at: یک برچسب زمانی برای اعمال فیلترهای زمانی.
استفاده از ستون jsonb برای متادیتا اجازه میدهد انواع مختلف اسناد — مثلاً یک تکه از سیاستهای منابع انسانی با { department: "HR", doc_type: "policy" } یا یک تیکت پشتیبانی با { customer_id: 4521, priority: "high" } — بدون نیاز به بازطراحی مداوم ساختار جدول ذخیره شوند. اگر برای هر فیلد احتمالی یک ستون سختگیرانه تعریف میکردید، مجبور بودید هر هفته جدول خود را تغییر دهید. این انعطافپذیری حیاتی است چون ایندکسگذاری متادیتا تنها راه مدیریت کنترل دسترسی (Access Control) و فیلترهای سخت پیش از شروع جستوجوی برداری است که از نظر محاسباتی بسیار گران است.
شکستن دیوار محاسباتی
یک جستوجوی جامع یاexhaustive (Flat Index) میلیاردها عملیات را در هر پرسوجو انجام میدهد. برای ۵ میلیون بردار با ۱۵۳۶ بُعد، سیستم باید تقریباً ۷.۶۸ میلیارد عملیات ضرب و جمع را برای هر تک پرسوجو اجرا کند (۵,۰۰۰,۰۰۰ بردار × ۱۵۳۶ بُعد).
در یک ماشین معمولی، این حجم از محاسبات منجر به همان تأخیر ۳۰ ثانیهای میشود که ذکر شد. جستوجوی جامع (Brute Force) فقط در مقیاسهای کوچک — شاید در حد چند هزار بردار — پاسخگو است. فراتر از آن، شما به یک استراتژی بنیاداً متفاوت نیاز دارید: هر چیزی را چک نکنید، بلکه فقط کاندیداهای احتمالی را بررسی کنید.
تکنیکهای ANN با تضمین اینکه سیستم هرگز تمام بردارها را چک نمیکند، این مشکل را حل میکنند. بر خلاف ایندکسهای B-tree که به دلیل وجود ترتیب طبیعی مقادیر (مثلاً «الف» قبل از «ب» است) کار میکنند، بردارها در فضای ۱۵۳۶ بُعدی نمیتوانند بهطور معناداری مرتب شوند. در این فضا هیچ مفهوم «کمتر از» برای دو جهت مختلف وجود ندارد. در عوض، ANN یکی از دو کار را انجام میدهد: یا بردارهای مشابه را در «محلهها» گروهبندی میکند یا یک نقشه میانبر میسازد تا بهسرعت به سمت خوشه دادههای صحیح بپرد.
IVF: رویکرد محلهای
ایندکس فایل معکوس (Inverted File Index یا IVF) مانند نقشهای عمل میکند که محدوده جستوجو را بهصورت سلسلهمراتبی محدود میکند (مثلاً از کشور $\rightarrow$ استان $\rightarrow$ شهر). این فرآیند از دو مرحله تشکیل شده است:
۱. آموزش (Training): سیستم بردارها را با استفاده از الگوریتمهایی مانند k-means در K خوشه گروهبندی میکند. هر خوشه یک «مرکز» (Centroid) دارد که در واقع میانگین بردار آن گروه است. برای مثال، اگر ۵ میلیون بردار در ۱,۰۰۰ خوشه گروهبندی شوند، یک مرکز ممکن است نماینده «موضوعات برنامهنویسی» باشد و مرکز دیگر نماینده «موضوعات حقوقی». یک قاعده کلی این است که تعداد لیستها (خوشهها) را تقریباً برابر با جذر تعداد ردیفها قرار دهید (مثلاً برای ۵ میلیون ردیف، $\sqrt{5,000,000} \approx 2,236$، پس حدود ۲,۰۰۰ تا ۲,۵۰۰ لیست مناسب است).
۲. زمان پرسوجو (Query Time): سیستم بردار پرسوجو را «فقط» با مراکز مقایسه میکند (مثلاً ۱,۰۰۰ تا ۲,۰۰۰ مقایسه). پس از یافتن نزدیکترین مراکز (مثلاً ۵ مورد اول)، سیستم تنها بردارهای موجود در آن خوشههای خاص را جستوجو میکند.
اگر سیستم ۵ مرکز نزدیک را شناسایی کند، ممکن است بهجای ۵ میلیون بردار، تنها حدود ۲۵,۰۰۰ بردار را بررسی کند. این کار تعداد عملیات را به حدود ۲۶,۰۰۰ کاهش میدهد که به معنای افزایش سرعت ۲۰۰ برابری است.
تنظیمات و موازنههای IVF
- پیچک nProbe: این پارامتر تعیین میکند که چند خوشه جستوجو شوند. حالت
nProbe = 1سریعترین است اما ممکن است بهترین تطابق را از دست بدهد (Recall پایین). حالتnProbe = 10تعادل خوبی ایجاد میکند و حدود ۹۵٪ از تطابقهای واقعی را مییابد. در یک سیستم ۱۰۰۰ خوشهای،nProbe = 1000عملاً معادل همان جستوجوی جامع (Brute force) است. - اندازه خوشه: تعداد خیلی کمِ لیستها باعث ایجاد خوشههای غولپیکری میشود که بهسختی سریعتر از جستوجوی جامع هستند. تعداد خیلی زیاد نیز خوشهها را بیش از حد کوچک میکند و منجر به ایجاد مراکز غیرقابلاعتماد به دلیل نبود داده کافی در هر خوشه میشود.

HNSW: سیستم بزرگراهها
جهان کوچک قابلناوبرد سلسلهمراتبی (Hierarchical Navigable Small World یا HNSW) یک گراف چندلایه میسازد. آن را مانند یک سیستم جادهای تصور کنید: شما از یک بزرگراه پرسرعت استفاده میکنید تا به نزدیکی مقصد برسید، سپس وارد جادههای استانی میشوید و در نهایت برای رسیدن به نقطه دقیق، از کوچههای محلی استفاده میکنید.
- لایههای بالایی (بزرگراهها): شبکههای پراکنده با اتصالات «دوربرد» بسیار کم برای پرشهای عظیم در فضای برداری.
- لایههای میانی (جادههای استانی): تعداد گرههای بیشتر با اتصالات متوسط برای دقیقتر کردن موقعیت.
- لایه صفر (کوچههای محلی): شامل تمام گرهها با اتصالات کوتاه و محلی برای دستیابی به حداکثر دقت.
جستوجو در HNSW تقریباً به $\log(N)$ پرش نیاز دارد. برای ۵ میلیون بردار، این یعنی حدود ۲۲ پرش بهجای ۵ میلیون مقایسه. میتوانید HNSW را به عنوان یک «لیست پرشی» (Skip List) تصور کنید که از یک لیست تک-بعدی مرتب به یک گراف در فضای ۱۵۳۶ بُعدی تعمیم یافته است.
پیادهسازی HNSW در pgvector
در pgvector، مدل HNSW از طریق m و ef_construction تنظیم میشود:
m: تعیین میکند که هر گره چند اتصال داشته باشد. مقادیر بالاتر، دقت را افزایش میدهد اما حافظه RAM بیشتری میطلبد.ef_construction: تعیین میکند که فرآیند ساخت ایندکس چقدر جامع باشد. مقادیر بالاتر ایندکس بهتری میسازند اما زمان ساخت را طولانیتر میکنند.ef_search: این یک تنظیم مربوط به زمان پرسوجو است. در حالی کهmوef_constructionهزینههای یکبارهای هستند که هنگام ساخت ایندکس پرداخت میشوند،ef_searchدر هر پرسوجو هزینه دارد. افزایش آن، میزان بازیابی (Recall) را برای پرسوجوهای حساس (مانند تطبیق قوانین compliance) بهبود میبخشد اما تأخیر (Latency) را زیاد میکند. مقدار پیشفرض معمولاً ۴۰ است.
مقایسه IVF و HNSW
- زمان ساخت: IVF سریعتر ساخته میشود؛ HNSW بهدلیل پیچیدگی ساخت گراف، کندتر است.
- حافظه: IVF از نظر حافظه بهینهتر است؛ HNSW برای ذخیره اتصالات گراف به مقدار RAM بیشتری نیاز دارد.
- دقت: HNSW عموماً تعادل فوقالعادهای بین سرعت و دقت ارائه میدهد و در حال حاضر انتخاب پیشفرض مدرن است.
- درج داده: IVF درج دادههای جدید را راحتتر مدیریت میکند؛ درج در گراف HNSW هزینهبرتر است.
فشردهسازی و معیارهای فاصله
در مقیاسهای عظیم (بیش از ۱۰۰ میلیون بردار)، حتی HNSW هم میتواند حافظه سیستم را بهطور کامل مصرف کند. کوانتایزیشن محصول (Product Quantization یا PQ) با فشردهسازی خودِ بردارها این مشکل را حل میکند. PQ جایگزین IVF یا HNSW نیست، بلکه در کنار آنها عمل میکند.
PQ ابعاد ۱۵۳۶ را به زیر-بردارهای کوچکتر تقسیم میکند (مثلاً ۹۶ زیر-بردار با ۱۶ بُعد). بهجای ذخیره اعداد خام، نزدیکترین تطابق را از یک «کتاب کد» (Codebook) یادگرفتهشده پیدا میکند و فقط کد (یک ایندکس) را ذخیره میکند. این کار میتواند یک بردار را از ۶,۱۴۴ بایت (۱۵۳۶ بُعد × ۴ بایت) به ۹۶ بایت (۹۶ کد × ۱ بایت) کاهش دهد؛ یعنی یک کاهش حجم ۶۴ برابری. موازنه این کار، پذیرش کاهش اندکی در دقت است. سیستمهای مقیاسبزرگ اغلب هر سه روش را ترکیب میکنند: IVF برای محدود کردن محله، PQ برای فشردهسازی حافظه و در نهایت یک مرحله رتبهبندی دقیق (Re-ranking) برای بازیابی دقت.
انتخاب معیار فاصله (Distance Metric) نیز به همان اندازه حیاتی است. سه معیار اصلی استفاده میشوند:
- شباهت کسینوسی (Cosine Similarity): زاویه بین بردارها را میسنجد و اندازه (Magnitude) را نادیده میگیرد. این استاندارد مدلهای OpenAI و Cohere است زیرا این مدلها بهگونهای آموزش دیدهاند که معنا در جهت بردار نهفته است.
- فاصله اقلیدسی (L2): فاصله خط مستقیم را میسنجد (
sqrt(sum((A[i] - B[i])²)))؛ این معیار برای بردارهای تصویری یا دادههای مکانی مناسبتر است. - حاصلضرب نقطهای (Dot Product): هم زاویه و هم اندازه را در نظر میگیرد (
A · B)؛ زمانی استفاده میشود که خودِ اندازه بردار معنای خاصی داشته باشد.
استفاده از معیار غلط (به عنوان مثال استفاده از L2 برای مدلی که برای Dot Product آموزش دیده) نتایج را بهصورت خاموش تخریب میکند، بدون اینکه سیستم هیچ خطایی صادر کند.
تلهی فیلترینگ متادیتا
شباهت برداری نمیتواند مشکلات فیلترینگ را حل کند؛ مثلاً تضمین اینکه کاربر فقط اسناد بخش «مالی» را ببیند یا نتایجی از ۶ ماه گذشته را دریافت کند. این یک مشکل «فیلترینگ» است، نه یک مشکل «معنایی». دو راه برای مدیریت این موضوع وجود دارد:
- فیلترینگ پساز-جستوجو (Post-filtering - اشتباه): ابتدا جستوجوی برداری اجرا شود و سپس نتایج فیلتر شوند. اگر ۵ نتیجه اول (Top-5) مربوط به بخشهای HR و مهندسی باشند، فیلتر «مالی» هیچ نتیجهای برنمیگرداند، حتی اگر تکههای مالی بسیار مرتبطی در رتبههای پایینتر لیست وجود داشته باشند.
- فیلترینگ پیش-جستوجو (Pre-filtering - درست): ابتدا فیلتر متادیتا (مثلاً
WHERE department = 'Finance') اعمال شود تا مجموعه کاندیدها کوچک شود و سپس جستوجوی برداری فقط در آن مجموعه محدود اجرا شود.
برای سرعت بخشیدن به پیش-فیلترینگ، متادیتا نیاز به ایندکس جداگانه از ایندکس برداری دارد. در Postgres، این کار با استفاده از ایندکسهای GIN روی ستونهای jsonb یا ایجاد ستونهای ذخیرهشده تولیدی (Generated stored columns) برای فیلدهایی که زیاد فیلتر میشوند، انجام میشود. الگوی عملیاتی این است: با متادیتا «سخت» فیلتر کنید و با بردارها «هوشمندانه» جستوجو کنید.
اقتصاد توکنها و کنترل هزینه
هزینههای Embedding اگر یک متن چندین بار بردار شود، میتواند پروژه را ورشکست کند. دو نوع هزینه مجزا وجود دارد: توکنهای Embedding (که هر بار API فراخوانی شود پرداخت میشود) و توکنهای تولید LLM (که هر بار یک تکه بازیابی شده در پرامپت قرار میگیرد).
با استفاده از هشهای محتوایی SHA-256، توسعهدهندگان میتوانند از فراخوانی API برای پاراگرافهای تکراری که در اسناد مختلف یافت میشوند، اجتناب کنند. این موضوع حیاتی است زیرا متون کلیشهای، بندهای حقوقی یا معرفی شرکتها اغلب در صدها فایل PDF تکرار میشوند. برخی مجموعههای داده واقعی ۲۰ تا ۳۰ درصد همپوشانی محتوایی دارند؛ حذف تکراریها میتواند هزینههای Embedding را به همین میزان کاهش دهد.
سایر مکانیسمهای کاهش هزینه شامل موارد زیر است:
- دستهبندی (Batching): ارسال آرایهای از متون در یک فراخوانی API بهجای درخواستهای تکتک. این کار تعداد درخواستها را کاهش میدهد که برای مدیریت محدودیتهای نرخ (Rate Limits) و کاهش سربار شبکه حیاتی است.
- انتخاب آگاهانه Top-K: کم نگه داشتن تعداد تکههای بازیابی شده. بازیابی ۱۰ تکه بهجای ۵ تکه، نه تنها هزینه پرامپت LLM را دو برابر میکند، بلکه اغلب نویزی اضافه میکند که کیفیت پاسخ را کاهش میدهد.
- کش کردن پرسوجو (Query Caching): ذخیره پاسخهای نهایی یا تکههای بازیابی شده برای سوالات کاملاً یکسانی (مثلاً «دوره اعلان استعفا چقدر است؟») که صدها بار در ماه پرسیده میشوند تا از اجرای مجدد زنجیره «بردار-جستوجو-تولید» جلوگیری شود.
انتخاب ابزار مناسب
برای اکثر کاربرانی که کمتر از ۱۰ میلیون بردار دارند، pgvector بهدلیل هزینه عملیاتی پایین (Low operational overhead) توصیه میشود، زیرا صرفاً افزونهای برای پایگاهدادهای است که از قبل دارید. ذخیرهسازهای اختصاصی موازنههای متفاوتی دارند:
- pgvector: بهترین برای کسانی که از Postgres استفاده میکنند و مقیاس متوسط (<۱۰ میلیون بردار) دارند. سربار کم و پشتیبانی از HNSW و IVFFlat.
- Qdrant/Weaviate: مناسب برای کنترل بیشتر در حالت Self-hosted، فیلترینگ قوی و جستوجوی ترکیبی پیشرفته (ترکیب جستوجوی کلمات کلیدی و برداری).
- Pinecone: یک راهکار کاملاً مدیریتشده (Zero-ops) که برای مقیاسهای عظیم مناسب است و مدل هزینه آن بر اساس تعداد بردارها و حجم پرسوجوهاست.
این تغییر در معماری به این معناست که یک جستوجوی برداری «کند»، بهندرت تقصیر خودِ ابزار است. معمولاً این کندی ناشی از پارامترهای اشتباه ایندکس، یک فیلتر متادیتای بدون ایندکس یا تنظیم ناکارآمد ef_search در پیکربندی HNSW است. انتخاب درست بستگی به این دارد که آیا نیاز دارید جستوجوی برداری را بهطور مستقل از دادههای رابطهای اصلی خود مقیاسبندی کنید یا خیر.
تکامل بعدی در این مسیر، مربوط به ایمنی در زمان پرسوجو است؛ شامل بررسیهای «مبنیسازی» (Groundedness checks) برای جلوگیری از توهمات LLM زمانی که بازیابی هیچ تکه مرتبطی پیدا نمیکند، در کنار مهاجرتهای امن مدلهای Embedding و محدود کردن نرخ (Rate limiting) برای یک API واقعی در محیط تولید.
گام بعدی شما
- اگر از pgvector استفاده میکنید، مقدار
ef_searchرا بر اساس حساسیت دادههایتان (دقت در برابر سرعت) بازتنظیم کنید. - برای کاهش هزینههای API، یک لایه هشینگ SHA-256 برای متون ورودی قبل از مرحله Embedding پیاده کنید.
- استراتژی فیلترینگ خود را از Post-filtering به Pre-filtering تغییر دهید تا از دست رفتن نتایج مرتبط جلوگیری کنید.
اما تکامل بعدی در این مسیر، مربوط به ایمنی زمان پرسوجو و جلوگیری از توهمات LLM است؛ در تحلیل ما دربارهی «مبنیسازی» (Grounding) این موضوع را بررسی کنید.




گفتگو