RAG — وقتی LLM حافظه پیدا می‌کنه

قسمت ۵ ۲۲ دقیقه

مرور سریع: کجای مسیریم؟

اپیزود قبلی با مدل‌های اوپن‌سورس آشنا شدیم — Llama، Qwen، DeepSeek — و یاد گرفتیم چطور انتخاب کنیم. ولی یه مشکل بزرگ هنوز حل نشده: این مدل‌ها فقط چیزایی رو می‌دونن که تو آموزششون دیدن. از اطلاعات جدید خبر ندارن. از داده‌های شرکت تو بی‌خبرن. از اون PDF ای که دیروز گرفتی هیچی نمی‌دونن.

امروز می‌خوایم این مشکل رو حل کنیم. با تکنیکی که شاید مهم‌ترین تکنیک عملی دنیای AI باشه: RAG.

چهار محدودیت بزرگ LLM به تنهایی

قبل از اینکه بریم سراغ راه‌حل، بیا مشکل رو دقیق بشناسیم. یه LLM به تنهایی چهار تا محدودیت اساسی داره:

۱. اطلاعات قدیمی (Knowledge Cutoff)

هر مدل یه تاریخ آخرین آموزش داره. مثلاً اگه یه مدل تا ژانویه ۲۰۲۵ آموزش دیده، از هر اتفاقی بعد از اون تاریخ بی‌خبره. فرقی نمی‌کنه چقدر باهوش باشه — اگه اطلاعات نداشته باشه، نمی‌تونه جواب بده.

۲. توهم (Hallucination)

وقتی مدل جواب سوالی رو نمی‌دونه، به‌جای گفتن «نمی‌دونم»، جواب من‌درآوردی می‌ده! به این می‌گن Hallucination (توهم). مثلاً ازش بپرس آخرین گزارش مالی یه شرکت چی بود — با کمال اعتماد به نفس یه عدد الکی بهت می‌ده.

۳. بدون دسترسی به داده‌های اختصاصی

مدل از اسناد داخلی، دیتابیس، ایمیل‌ها، و فایل‌های شرکتت هیچی نمی‌دونه. تو نمی‌تونی بهش بگی «طبق قرارداد شماره ۱۲۳ چی نوشته؟» چون اصلاً اون قرارداد رو ندیده.

۴. محدودیت حافظه (Context Window)

حتی اگه بخوای همه اطلاعات رو تو prompt بذاری، یه سقف وجود داره. هر مدل یه Context Window (پنجره زمینه) داره — یعنی حداکثر متنی که می‌تونه یه‌جا پردازش کنه. مثلاً اگه ۱۰۰ صفحه سند داری، نمی‌تونی همه رو تو prompt بچپونی.

RAG چیه؟ — تشبیه دکتر

RAG مخفف Retrieval-Augmented Generation هست. بیا با یه تشبیه ساده شروع کنیم.

فرض کن یه دکتر عمومی خیلی باهوش داری (LLM). این دکتر ۱۰ سال درس خونده و کلی چیز بلده. ولی وقتی مریض خاصی میاد پیشش، باید پرونده پزشکیش رو ببینه تا تشخیص دقیق بده. دکتر بدون پرونده فقط می‌تونه حدس بزنه.

RAG یعنی قبل از اینکه دکتر جواب بده، پرونده مربوطه رو از آرشیو بیاری و بذاری جلوش.

به زبان فنی: وقتی کاربر سوالی می‌پرسه، قبل از اینکه سوال به LLM برسه، سیستم اول می‌ره اطلاعات مرتبط رو از یه منبع داده (مثل دیتابیس اسناد) پیدا می‌کنه و همراه سوال به مدل می‌ده. مدل با این اطلاعات اضافی، جواب دقیق‌تری تولید می‌کنه.

معماری پایه RAG

یه سیستم RAG سه مرحله اصلی داره. بیا قدم‌به‌قدم بریم:

مرحله ۱: آماده‌سازی اسناد (Indexing)

قبل از هر چیز باید اسناد و داده‌هات رو آماده کنی. این مرحله یه بار انجام می‌شه (نه هر دفعه که کاربر سوال می‌پرسه).

  1. جمع‌آوری اسناد: PDF، Word، صفحات وب، دیتابیس — هر منبعی که داری
  2. تبدیل به متن: همه رو به متن خام تبدیل کن
  3. Chunking (تکه‌تکه کردن): متن رو به قطعات کوچک‌تر تقسیم کن
  4. Embedding (بردارسازی): هر تکه رو به یه بردار عددی تبدیل کن
  5. ذخیره در Vector Database: بردارها رو تو یه پایگاه داده مخصوص ذخیره کن

مرحله ۲: جستجو (Retrieval)

وقتی کاربر سوال می‌پرسه:

  1. سوال کاربر هم Embed می‌شه (تبدیل به بردار)
  2. بردار سوال با بردارهای ذخیره‌شده مقایسه می‌شه
  3. مرتبط‌ترین تکه‌ها پیدا می‌شن

مرحله ۳: تولید پاسخ (Generation)

تکه‌های مرتبط + سوال کاربر به LLM داده می‌شه و مدل جواب رو تولید می‌کنه.

# شبه‌کد ساده RAG
def answer_question(user_query):
    # مرحله ۱: بردارسازی سوال
    query_vector = embed(user_query)
    
    # مرحله ۲: جستجوی مشابه
    relevant_chunks = vector_db.search(query_vector, top_k=5)
    
    # مرحله ۳: ساخت prompt و تولید جواب
    context = "\n".join(relevant_chunks)
    prompt = f"""بر اساس اطلاعات زیر به سوال پاسخ بده:
    
    اطلاعات: {context}
    
    سوال: {user_query}"""
    
    return llm.generate(prompt)

Embedding — قلب RAG

یکی از مفاهیم کلیدی RAG، مفهوم Embedding (بردارسازی) هست. بیا ساده توضیح بدم.

فرض کن می‌خوای کلمه «گربه» رو به کامپیوتر بفهمونی. کامپیوتر فقط عدد می‌فهمه. پس باید کلمه رو تبدیل به عدد کنی. ولی نه هر عددی — عددی که معنی کلمه رو حفظ کنه.

Embedding دقیقاً همینه: تبدیل متن به یه لیست اعداد (بردار) که معنی متن رو نشون می‌ده.

💡 نکته مهم: تو فضای Embedding، کلمات با معنی نزدیک، بردارهای نزدیکی دارن. مثلاً بردار «گربه» به بردار «بچه‌گربه» نزدیکه، ولی از بردار «ماشین» دوره. این ویژگی باعث می‌شه بتونیم جستجوی معنایی (Semantic Search) انجام بدیم — نه بر اساس تطابق کلمات، بلکه بر اساس معنی.

مدل‌های Embedding محبوب

  • OpenAI text-embedding-3-small: ساده و خوب، ولی API هزینه داره
  • BGE-M3 (BAAI): اوپن‌سورس و چندزبانه — برای فارسی خوبه
  • Multilingual-E5: مایکروسافت ساخته، عملکرد خوب روی فارسی
  • Nomic Embed: اوپن‌سورس، سبک و سریع

Vector Database — حافظه RAG

بعد از اینکه متن‌ها رو Embed کردی، باید بردارها رو یه جایی ذخیره کنی. اینجاست که Vector Database (پایگاه داده برداری) وارد می‌شه.

تفاوت Vector Database با دیتابیس معمولی: دیتابیس معمولی (مثل MySQL) برای جستجوی دقیق طراحی شده — مثلاً «رکوردهایی که شهر = تهران». ولی Vector Database برای جستجوی شباهت (Similarity Search) طراحی شده — مثلاً «بردارهایی که به این بردار نزدیکن».

گزینه‌های محبوب

  • ChromaDB: ساده‌ترین برای شروع. نصب یه خطه. عالی برای پروتوتایپ.
  • Qdrant: سریع و مناسب پروداکشن. API خوبی داره.
  • Weaviate: قابلیت‌های پیشرفته مثل فیلتر ترکیبی.
  • Pinecone: سرویس ابری (Managed). خودت نگهداری نمی‌کنی.
  • pgvector: اگه از PostgreSQL استفاده می‌کنی، یه افزونه بهش اضافه کن و تمام!
# مثال ساده با ChromaDB
import chromadb

# ساخت دیتابیس
client = chromadb.Client()
collection = client.create_collection("my_docs")

# اضافه کردن اسناد
collection.add(
    documents=["گربه‌ها حیوانات خانگی محبوبی هستن", 
               "پایتون یه زبان برنامه‌نویسی محبوبه"],
    ids=["doc1", "doc2"]
)

# جستجو
results = collection.query(
    query_texts=["حیوانات خانگی"],
    n_results=1
)
# خروجی: "گربه‌ها حیوانات خانگی محبوبی هستن"

Chunking — هنر تکه‌تکه کردن

یکی از مهم‌ترین و ظریف‌ترین بخش‌های RAG، نحوه Chunking (تکه‌تکه کردن) اسناده. اگه بد انجام بدی، کل سیستم خراب می‌شه.

چرا Chunking لازمه؟

فرض کن یه کتاب ۳۰۰ صفحه‌ای داری. نمی‌تونی کل کتاب رو یه‌جا Embed کنی — هم سنگینه هم معنی کلی از بین می‌ره. باید تکه‌تکه‌ش کنی. ولی اگه خیلی ریز تکه کنی (مثلاً هر جمله جدا)، اون تکه‌ها به تنهایی معنی ندارن.

روش‌های Chunking

  • Fixed-size (اندازه ثابت): هر ۵۰۰ کاراکتر یه تکه. ساده ولی ممکنه وسط جمله ببره.
  • Sentence-based (جمله‌ای): هر ۵ جمله یه تکه. بهتره ولی اندازه‌ها متفاوته.
  • Semantic (معنایی): بر اساس تغییر موضوع تکه‌بندی کن. بهترین نتیجه، ولی پیچیده‌تر.
  • Recursive: اول بر اساس پاراگراف، بعد اگه بزرگ بود بر اساس جمله. یه رویکرد عملی و خوب.
⚡ نکته مهم — Overlap: وقتی تکه‌تکه می‌کنی، یه مقدار هم‌پوشانی (Overlap) بذار. مثلاً اگه هر تکه ۵۰۰ کاراکتره، ۵۰ کاراکتر آخر هر تکه رو تو اول تکه بعدی هم تکرار کن. این کار باعث می‌شه اطلاعاتی که روی مرز دو تکه‌ان، گم نشن.
# مثال Chunking با LangChain
from langchain.text_splitter import RecursiveCharacterTextSplitter

splitter = RecursiveCharacterTextSplitter(
    chunk_size=500,      # حداکثر اندازه هر تکه
    chunk_overlap=50,    # مقدار هم‌پوشانی
    separators=["\n\n", "\n", ".", " "]  # اولویت جدا کردن
)

chunks = splitter.split_text(long_document)
print(f"تعداد تکه‌ها: {len(chunks)}")
print(f"اندازه تکه اول: {len(chunks[0])} کاراکتر")

یه مثال عملی کامل

بیا تصور کنیم می‌خوای یه چت‌بات بسازی که به سوالات کاربران درباره محصولاتت جواب بده. مراحل کار:

  1. جمع‌آوری: همه مستندات محصول، FAQ، و راهنماها رو جمع کن
  2. پاکسازی: HTML و فرمت‌های اضافی رو حذف کن، فقط متن خالص نگه دار
  3. Chunking: متن‌ها رو به تکه‌های ۵۰۰ کاراکتری با ۵۰ کاراکتر Overlap تبدیل کن
  4. Embedding: هر تکه رو با یه مدل Embedding (مثلاً BGE-M3) تبدیل به بردار کن
  5. ذخیره: بردارها رو تو ChromaDB یا Qdrant ذخیره کن
  6. سرویس: وقتی کاربر سوال پرسید، سوال رو Embed کن، ۵ تکه مرتبط رو پیدا کن، و همراه سوال به LLM بده

RAG در مقابل Fine-tuning

یه سوال رایج: «چرا Fine-tune نکنم؟ چرا RAG؟» جواب کوتاه: هر کدوم کار خودشو داره.

  • RAG: وقتی داده‌ها مرتب عوض می‌شن (مثل اخبار، محصولات، قیمت‌ها). داده جدید اضافه می‌کنی بدون اینکه مدل رو دوباره آموزش بدی.
  • Fine-tuning: وقتی می‌خوای رفتار و سبک مدل رو عوض کنی (مثلاً فارسی بهتر حرف بزنه). یه بار آموزش می‌دی و تمام.
  • ترکیب هر دو: بهترین حالت! مدل رو Fine-tune کن که فارسی خوب حرف بزنه، بعد RAG اضافه کن که از داده‌های اختصاصیت استفاده کنه.
🎯 تمرین عملی: یه فولدر از فایل‌های متنی درست کن (مثلاً ۵ تا فایل درباره موضوعات مختلف). بعد با ChromaDB و یه مدل Embedding ساده، یه سیستم جستجوی معنایی بساز. سوال بپرس و ببین آیا تکه‌های مرتبط رو پیدا می‌کنه یا نه.

جمع‌بندی

تو این اپیزود یاد گرفتی:

  1. چهار محدودیت اساسی LLM به تنهایی: اطلاعات قدیمی، توهم، نبود داده اختصاصی، محدودیت حافظه
  2. RAG چیه و چطور این مشکلات رو حل می‌کنه
  3. معماری سه‌مرحله‌ای: آماده‌سازی، جستجو، تولید
  4. Embedding چیه و چرا جستجوی معنایی ممکن می‌کنه
  5. Vector Database چیه و گزینه‌های محبوب کدومن
  6. Chunking و اهمیتش

اپیزود بعدی: Fine-tuning — مدل رو به سبک خودت در بیار

تا اینجا یاد گرفتی چطور به مدل اطلاعات بدی (RAG). ولی اگه بخوای رفتار خود مدل رو عوض کنی چی؟ مثلاً بخوای فارسی بهتر حرف بزنه، یا سبک نوشتنش عوض بشه؟ اپیزود بعدی می‌ریم سراغ Fine-tuning و تکنیک‌های مدرنی مثل LoRA که با سخت‌افزار معمولی هم قابل انجامن!

نظرات

هنوز نظری ثبت نشده. اولین نفر باشید!

نظر خود را بنویسید