توسيع الاستدلال لتحسين RAG متعدد الوسائط في Granite

توسيع الاستدلال لتحسين RAG متعدد الوسائط في Granite 

يشير توسيع الاستدلال في الذكاء الاصطناعي إلى تقنيات تعمل على تحسين أداء النموذج عن طريق تخصيص الموارد الحاسوبية أثناء مرحلة الاستدلال (عند توليد النموذج للمخرجات) بدلًا من الاعتماد على مجموعات بيانات تدريب أكبر أو بنى نماذج أكبر. مع استمرار توسُّع النماذج اللغوية الكبيرة من حيث عدد المَعلمات وحجم مجموعات البيانات، أصبح تحسين وقت الاستدلال وإدارة توسيع قدرة الحوسبة أثناء الاستدلال -وخاصةً على أجهزة GPU- من التحديات الأساسية لنشر أنظمة RAG متعددة الوسائط عالية الأداء. 

مقدمة حول توسيع الاستدلال

التطورات الحديثة في استراتيجيات الاستدلال، التي تزيد من الموارد الحاسوبية وتستخدم خوارزميات معقدة أثناء وقت الاختبار، تُعيد تعريف طريقة تعامل النماذج اللغوية الكبيرة مع المهام المعقدة في الاستدلال وتقديم مخرجات عالية الجودة عبر وسائط إدخال متنوعة. يعمل توسيع الاستدلال على تحسين سلسلة التفكير (CoT) من خلال زيادة عمق الاستدلال. يُتيح هذا التوسيع للنماذج إنتاج سلاسل تفكير أطول وأكثر تفصيلًا من خلال المطالبات المتكررة أو التوليد متعدد الخطوات. يمكن الاستفادة من توسيع الاستدلال لتحسين RAG متعدد الوسائط، مع التركيز على التوازن بين حجم النماذج وميزانيات الحوسبة والتحسين العملي لوقت الاستدلال في التطبيقات الواقعية.

علاوةً على ذلك، تؤكِّد قوانين التوسيع ونتائج الاختبارات المرجعية على المقايضات بين مرحلة ما قبل التدريب والضبط الدقيق واستراتيجيات وقت الاستدلال والخوارزميات المتقدمة لاختيار المخرجات. تستفيد النماذج الكبيرة والصغيرة على حد سواء من توسيع الاستدلال، إذ يُتيح أيضًا للأنظمة محدودة الموارد الاقتراب من أداء أحدث النماذج اللغوية الكبيرة. يوضِّح هذا البرنامج التعليمي تأثير تقنيات التحسين في أداء النماذج، ويقدِّم إرشادات عملية لموازنة الدقة وزمن الانتقال والتكلفة في عمليات نشر RAG متعدد الوسائط.

تم تصميم هذا البرنامج التعليمي للمطورين والباحثين وهواة الذكاء الاصطناعي الذين يريدون تعزيز معرفتهم بإدارة المستندات وتقنيات معالجة اللغة الطبيعية (NLP) المتقدمة. ستتعرَّف على كيفية استغلال قوة توسيع الاستدلال لتحسين مسار RAG متعدد الوسائط الذي تم إنشاؤه في الوصفة السابقة. بينما يركِّز هذا البرنامج التعليمي على استراتيجيات قابلية التوسع في RAG متعدد الوسائط مع نماذج IBM Granite الكبيرة، فإن المبادئ نفسها تنطبق على معظم النماذج الشائعة بما في ذلك نماذج OpenAI (مثل GPT-4 وGPT-4o وChatGPT) وDeepMind.

يرشدك هذا البرنامج التعليمي خلال العمليات التالية:

  • المعالجة المسبقة للمستندات: ستتعرَّف على كيفية التعامل مع المستندات من مصادر مختلفة وتحليلها وتحويلها إلى تنسيقات قابلة للاستخدام وتخزينها في قاعدة بيانات المتجهات باستخدام Docling. تُعَد Docling مجموعة أدوات مفتوحة المصدر من IBM تُستخدم لتحليل صيغ المستندات بكفاءة - مثل PDF وDOCX وPPTX وXLSX والصور وHTML وAsciiDoc وMarkdown. ثم يقوم بتصدير محتوى المستند إلى صيغ قابلة للمعالجة آليًا مثل Markdown أو JSON. ستستخدم نموذج التعلم الآلي Granite لإنشاء أوصاف صور للصور في المستندات. في هذا البرنامج التعليمي، سيقوم Docling بتحميل مستندات PDF ومعالجتها لنتمكن من استخراج النصوص والصور الموجودة فيها. في هذا البرنامج التعليمي، سيقوم Docling بتحميل مستندات PDF ومعالجتها لنتمكن من استخراج النصوص والصور الموجودة فيها.

  • التوليد المعزز بالاسترجاع (RAG): تعرَّف على كيفية ربط النماذج اللغوية الكبيرة مثل Granite بقاعدة معرفة خارجية لتعزيز استجابات الاستعلامات وتوليد رؤًى قيّمة. يُعَد RAG تقنية للنماذج اللغوية الكبيرة تُستخدَم لربط النماذج بقاعدة معرفة تحتوي على معلومات خارج نطاق البيانات التي تم تدريب النموذج عليها. يتم تطبيق هذه التقنية على النماذج اللغوية الكبيرة دون الحاجة إلى الضبط الدقيق. يقتصر استخدام RAG التقليدي على حالات الاستخدام القائمة على النصوص مثل تلخيص النصوص وروبوتات المحادثة.

  • RAG متعدد الوسائط: تعرَّف على كيفية استخدام RAG متعدد الوسائط للنماذج اللغوية الكبيرة متعددة الوسائط (MLLMs) لمعالجة المعلومات من أنواع بيانات مختلفة. يمكن بعد ذلك إدراج هذه البيانات كجزء من قاعدة المعرفة الخارجية المستخدمة في RAG. يمكن أن تتضمن البيانات متعددة الوسائط نصوصًا أو صورًا أو مقاطع صوتية أو مقاطع فيديو أو أشكالًا أخرى. في هذا البرنامج التعليمي، نستخدم أحدث نموذج رؤية متعدد الوسائط من IBM، وهو Granite 3.2 Vision.

  • تنفيذ RAG القائم على العروض التوضيحية (DRAG) وRAG القائم على العروض التوضيحية التكرارية (IterDRAG): تطبيق تقنيات توسيع الاستدلال من الورقة البحثية لتحسين أداء RAG بشكل كبير عند التعامل مع سياق طويل. تستفيد طريقة DRAG من التعلم في السياق لتحسين أداء RAG. من خلال تضمين أمثلة RAG متعددة كعروض توضيحية، يساعد DRAG النماذج على تعلُّم تحديد موقع المعلومات ذات الصلة في سياقات طويلة. على عكس RAG التقليدي الذي قد يتوقف عن التحسن مع زيادة عدد المستندات، يُظهر DRAG تحسُّنًا خطيًا مع زيادة طول السياق. يُعَد IterDRAG امتدادًا لـ DRAG يعالج الاستعلامات المعقدة متعددة القفزات عن طريق تقسيمها إلى استعلامات فرعية أبسط. متعدد القفزات (Multihop) هو عملية يتم فيها تقسيم الاستعلام المعقد والإجابة عنه من خلال أسئلة فرعية بسيطة. قد يتطلب كل سؤال فرعي معلومات يتم استرجاعها و/أو توليفها من مصادر مختلفة. يدمج IterDRAG خطوات الاسترجاع والتوليد، لإنشاء سلاسل استدلالية تسدّ الفجوات التركيبية. هذا النهج فعَّال بشكل خاص للتعامل مع الاستعلامات المعقدة عبر سياقات طويلة.

  • LangChain لتكامل سير العمل: اكتشِف كيفية استخدام LangChain لتبسيط وتنظيم سير عمل معالجة المستندات واسترجاعها، ما يُتيح تفاعلًا سلسًا بين مختلف عناصر النظام.

خلال هذا البرنامج التعليمي، ستستخدم أيضًا ثلاث تقنيات متطورة:

  1. Docling: مجموعة أدوات مفتوحة المصدر يتم استخدامها لتحليل المستندات وتحويلها.

  2. Granite: مجموعة متقدمة من النماذج اللغوية الكبيرة توفِّر قدرات قوية في معالجة اللغة الطبيعية، بالإضافة إلى نموذج رؤية لغوي يُتيح توليد نصوص من الصور.
  3. LangChain: إطار عمل قوي يُستخدَم لبناء التطبيقات المدعومة بنماذج اللغة، وهو مصمم لتبسيط مهام سير العمل المعقدة ودمج الأدوات الخارجية بسلاسة.

بحلول نهاية هذا البرنامج التعليمي، ستتمكن من إنجاز ما يلي:

  • اكتساب الكفاءة في معالجة المستندات مسبقًا وتقسيمها وفهم الصور.

  • دمج قواعد بيانات المتجهات لتعزيز القدرات.

  • تنفيذ DRAG وIterDRAG لإجراء استرجاع البيانات بكفاءة ودقة مع توسيع الاستدلال.

  • جرّب بنفسك كيف يمكن لتوسيع قدرة الاستدلال الحاسوبي أن يؤدي إلى تحسينات أداء شبه خطية في أداء RAG.

فهم تحديات السياق الطويل

تواجه النماذج اللغوية التقليدية صعوبات عند التعامل مع السياقات الطويلة لعدة أسباب:

  • آليات الانتباه التقليدية مثل المحوِّلات تتوسع بشكل تربيعي، ما قد يستهلك موارد حاسوبية هائلة. 

  • صعوبة في تحديد المعلومات ذات الصلة في تسلسلات طويلة جدًا. 

  • تحديات في الحفاظ على التماسك عبر الأجزاء البعيدة من الإدخال. 

  • زيادة المتطلبات الحوسبية لمعالجة التسلسلات الطويلة.

تعالج التقنيات الواردة في هذا البرنامج التعليمي هذه التحديات من خلال التخصيص الاستراتيجي لقدرات الحوسبة أثناء الاستدلال.

طرق قياس الاستدلال: DRAG وIterDRAG

DRAG وIterDRAG
DRAG وIterDRAG

يمكن العثور على المزيد من المعلومات حول هاتين التقنيتين المتقدمتين لقياس الاستدلال (DRAG وIterDRAG) في الورقة البحثية "Inference Scaling for Long-Context Retrieval Augmented Generation".

تُظهر هذه الأساليب أن توسيع قدرات الحوسبة أثناء الاستدلال يمكن أن يحسِّن أداء RAG بشكل شبه خطي عند تخصيصه بشكل مثالي، ما يُتيح لأنظمة RAG الاستفادة بشكل أفضل من قدرات السياق الطويل في النماذج اللغوية الحديثة. لهذا التنفيذ، سنستخدم نموذج IBM Granite القادر على معالجة الوسائط المختلفة. ستُنشئ نظام ذكاء اصطناعي للإجابة عن استفسارات المستخدم في الوقت الفعلي من البيانات غير المنظمة، وذلك من خلال تطبيق المبادئ المذكورة في الورقة البحثية.

المتطلبات الأساسية

  • المعرفة ببرمجة Python.

  • فهم أساسي لمفاهيم النماذج اللغوية الكبيرة ومعالجة اللغة الطبيعية ورؤية الكمبيوتر.

الخطوات

تأكد من تشغيل Python 3.10 أو 3.11 أو 3.12 في بيئة افتراضية تم إنشاؤها حديثًا. ملاحظة، يمكنك أيضًا الوصول إلى هذا البرنامج التعليمي على GitHub.

الخطوة 1: إعداد البيئة.

import sys
assert sys.version_info >= (3, 10) and sys.version_info < (3, 13), "Use Python 3.10, 3.11, or 3.12 to run this notebook."

الخطوة 2: تثبيت التبعيات.

! pip install "git+https://github.com/ibm-granite-community/utils.git" \
    transformers \
    pillow \
    langchain_community \
    langchain_huggingface \
    langchain_milvus \
    docling \
    replicate

التسجيل

لرؤية بعض معلومات التسجيل، يمكننا تكوين مستوى سجل INFO.

ملاحظة: يمكن تخطي تنفيذ هذه الخلية دون مشكلة.

import logging

logging.basicConfig(level=logging.INFO)

الخطوة 3: اختيار نماذج الذكاء الاصطناعي.

تحميل نماذج Granite

تحديد نموذج التضمينات لاستخدامه في إنشاء متجهات تضمين النص. هنا سنستخدم أحد نماذج Granite Embeddings.

لاستخدام نموذج تضمينات مختلف، استبدِل هذه الخلية البرمجية بأخرى من وصفة نماذج Embeddings.

from langchain_huggingface import HuggingFaceEmbeddings
from transformers import AutoTokenizer

embeddings_model_path = "ibm-granite/granite-embedding-30m-english"
embeddings_model = HuggingFaceEmbeddings(
    model_name=embeddings_model_path,
)
embeddings_tokenizer = AutoTokenizer.from_pretrained(embeddings_model_path)

حدد MLLM لاستخدامه لفهم الصور. سنستخدم نموذج الرؤية من Granite.

from ibm_granite_community.notebook_utils import get_env_var
from langchain_community.llms import Replicate
from transformers import AutoProcessor

vision_model_path = "ibm-granite/granite-vision-3.2-2b"
vision_model = Replicate(
    model=vision_model_path,
    replicate_api_token=get_env_var("REPLICATE_API_TOKEN"),
    model_kwargs={
        "max_tokens": embeddings_tokenizer.max_len_single_sentence, # Set the maximum number of tokens to generate as output.
        "min_tokens": 100, # Set the minimum number of tokens to generate as output.
        "temperature": 0.01,
    },
)
vision_processor = AutoProcessor.from_pretrained(vision_model_path)

حدِّد نموذج اللغة الذي سيتم استخدامه في عملية توليد RAG. هنا نستخدم عميل LangChain من Replicate للاتصال بنموذج Granite من ibm-granite org على Replicate.

لإعداد حسابك على Replicate، اطَّلِع على البدء باستخدام Replicate.

للاتصال بنموذج من مزوِّد آخر غير Replicate، استبدِل هذه الخلية البرمجية بأخرى من وصفة مكون النموذج اللغوي الكبير.

model_path = "ibm-granite/granite-3.3-8b-instruct"
model = Replicate(
    model=model_path,
    replicate_api_token=get_env_var("REPLICATE_API_TOKEN"),
    model_kwargs={
        "max_tokens": 1000, # Set the maximum number of tokens to generate as output.
        "min_tokens": 100, # Set the minimum number of tokens to generate as output.
        "temperature": 0.01
    },
)
tokenizer = AutoTokenizer.from_pretrained(model_path)

الخطوة 4: إعداد المستندات لقاعدة بيانات المتجهات باستخدام Docling.

from docling.document_converter import DocumentConverter, PdfFormatOption
from docling.datamodel.base_models import InputFormat
from docling.datamodel.pipeline_options import PdfPipelineOptions

pdf_pipeline_options = PdfPipelineOptions(
    do_ocr=False,
    generate_picture_images=True,
)
format_options = {
    InputFormat.PDF: PdfFormatOption(pipeline_options=pdf_pipeline_options),
}
converter = DocumentConverter(format_options=format_options)

sources = [
    "https://midwestfoodbank.org/images/AR_2020_WEB2.pdf",
]
conversions = { source: converter.convert(source=source).document for source in sources }

بعد معالجة المستندات، نعمل على إجراء المزيد من معالجة عناصر النص فيها وتقسيمها إلى مقاطع بالحجم المناسب لنموذج التضمينات الذي نستخدمه. يتم إنشاء قائمة بمستندات LangChain من مقاطع النص.

from docling_core.transforms.chunker.hybrid_chunker import HybridChunker
from docling_core.types.doc import DocItem, TableItem
from langchain_core.documents import Document

doc_id = 0
texts: list[Document] = []
for source, docling_document in conversions.items():
    for chunk in HybridChunker(tokenizer=embeddings_tokenizer).chunk(docling_document):
        items: list[DocItem] = chunk.meta.doc_items # type: ignore
        if len(items) == 1 and isinstance(items[0], TableItem):
            continue # we will process tables later
        refs = " ".join(map(lambda item: item.get_ref().cref, items))
        print(refs)
        text = chunk.text
        document = Document(
            page_content=text,
            metadata={
                "doc_id": (doc_id:=doc_id+1),
                "source": source,
                "ref": refs,
            },
        )
        texts.append(document)

print(f"{len(texts)} text document chunks created")

بعد ذلك، نعمل على معالجة أي جداول في المستندات. ثم تحويل بيانات الجداول إلى صيغة Markdown ليتمكن نموذج اللغة من معالجتها. يتم إنشاء قائمة من مستندات LangChain من تمثيلات الجداول بصيغة Markdown.

from docling_core.types.doc import DocItemLabel

doc_id = len(texts)
tables: list[Document] = []
for source, docling_document in conversions.items():
    for table in docling_document.tables:
        if table.label in [DocItemLabel.TABLE]:
            ref = table.get_ref().cref
            print(ref)
            text = table.export_to_markdown(docling_document)
            document = Document(
                page_content=text,
                metadata={
                    "doc_id": (doc_id:=doc_id+1),
                    "source": source,
                    "ref": ref
                },
            )
            tables.append(document)


print(f"{len(tables)} table documents created")

أخيرًا، نعمل على معالجة أي صور في المستندات. هنا نستخدم نموذج الرؤية اللغوي لفهم محتوى الصور. في هذا المثال، نركِّز على أي معلومات نصية موجودة في الصورة.

يُعَد اختيار مطالبة مناسبة للصورة أمرًا بالغ الأهمية لأنه يحدِّد الجوانب التي سيركِّز عليها النموذج في الصورة. على سبيل المثال:

  • مطالبة مثل "قدِّم وصفًا تفصيليًا لما هو موضَّح في الصورة" (المستخدم أدناه) ستقدِّم معلومات عامة عن جميع العناصر المرئية.

  • مطالبة مثل "ما النص الذي يظهر في هذه الصورة؟" ستركِّز بشكل خاص على استخراج المحتوى النصي.

  • مطالبة مثل "صِف التصوير البياني للبيانات في هذه الصورة" ستكون أفضل للمخططات والرسوم البيانية.

  • يجب أن تجرّب مطالبات مختلفة اعتمادًا على نوع الصور في مستنداتك والمعلومات التي تحتاج لاستخراجها منها.

ملاحظة: قد تتطلب معالجة الصور وقتًا كبيرًا حسب عدد الصور والخدمة المستخدمة لتشغيل نموذج الرؤية اللغوي.

import base64
import io
import PIL.Image
import PIL.ImageOps

def encode_image(image: PIL.Image.Image, format: str = "png") -> str:
    image = PIL.ImageOps.exif_transpose(image) or image
    image = image.convert("RGB")

    buffer = io.BytesIO()
    image.save(buffer, format)
    encoding = base64.b64encode(buffer.getvalue()).decode("utf-8")
    uri = f"data:image/{format};base64,{encoding}"
    return uri

# Feel free to experiment with this prompt
image_prompt = "Give a detailed description of what is depicted in the image"
conversation = [
    {
        "role": "user",
        "content": [
            {"type": "image"},
            {"type": "text", "text": image_prompt},
        ],
    },
]
vision_prompt = vision_processor.apply_chat_template(
    conversation=conversation,
    add_generation_prompt=True,
)
pictures: list[Document] = []
doc_id = len(texts) + len(tables)
for source, docling_document in conversions.items():
    for picture in docling_document.pictures:
        ref = picture.get_ref().cref
        print(ref)
        image = picture.get_image(docling_document)
        if image:
            text = vision_model.invoke(vision_prompt, image=encode_image(image))
            document = Document(
                page_content=text,
                metadata={
                    "doc_id": (doc_id:=doc_id+1),
                    "source": source,
                    "ref": ref,
                },
            )
            pictures.append(document)

print(f"{len(pictures)} image descriptions created")

يمكننا بعد ذلك عرض مستندات LangChain التي تم إنشاؤها من مستندات الإدخال.

import itertools
from docling_core.types.doc import RefItem
from IPython.display import display

# Print all created documents
for document in itertools.chain(texts, tables):
    print(f"Document ID: {document.metadata['doc_id']}")
    print(f"Source: {document.metadata['source']}")
    print(f"Content:\n{document.page_content}")
    print("=" * 80)  # Separator for clarity

for document in pictures:
    print(f"Document ID: {document.metadata['doc_id']}")
    source = document.metadata['source']
    print(f"Source: {source}")
    print(f"Content:\n{document.page_content}")
    docling_document = conversions[source]
    ref = document.metadata['ref']
    picture = RefItem(cref=ref).resolve(docling_document)
    image = picture.get_image(docling_document)
    print("Image:")
    display(image)
    print("=" * 80)  # Separator for clarity

ملء قاعدة بيانات المتجهات

باستخدام نموذج التضمين، نقوم بتحميل المستندات من مقاطع النص والتعليقات التوضيحية للصور التي تم إنشاؤها في قاعدة بيانات المتجهات. يُتيح لنا إنشاء قاعدة بيانات المتجهات هذه إجراء بحث تشابُه دلالي بسهولة عبر مستنداتنا.

ملاحظة: قد تتطلب عملية تجميع قاعدة بيانات المتجهات وقتًا كبيرًا في المعالجة اعتمادًا على نموذج التضمين والخدمة.

اختيار قاعدة بيانات المتجهات

حدِّد قاعدة البيانات لاستخدامها لتخزين واسترجاع متجهات التضمين. لغرض هذا البرنامج التعليمي، سنستخدم Milvus عبر LangChain. بصفتها قاعدة بيانات متجهات، ستعمل Milvus على تخزين وفهرسة وإدارة المتجهات العددية التي تولِّدها الشبكات العصبية وخوارزميات التعلم الآلي المختلفة.

للاتصال بقاعدة بيانات متجهات غير Milvus، استبدِل هذه الخلية البرمجية بأخرى من وصفة Vector Store هذه.

import tempfile
from langchain_core.vectorstores import VectorStore, VectorStoreRetriever
from langchain_milvus import Milvus

db_file = tempfile.NamedTemporaryFile(prefix="vectorstore_", suffix=".db", delete=False).name
print(f"The vector database will be saved to {db_file}")

vector_db: VectorStore = Milvus(
    embedding_function=embeddings_model,
    connection_args={"uri": db_file},
    auto_id=True,
    enable_dynamic_field=True,
    index_params={"index_type": "AUTOINDEX"},
)

الآن، نضيف جميع مستندات LangChain للنص والجداول وأوصاف الصور إلى قاعدة بيانات المتجهات.

import itertools

documents = list(itertools.chain(texts, tables, pictures))
ids = vector_db.add_documents(documents)
print(f"{len(ids)} documents added to the vector database")
retriever: VectorStoreRetriever = vector_db.as_retriever(search_kwargs={"k": 10})

الخطوة 5: RAG مع Granite.

الآن بعد أن نجحنا في تحويل مستنداتنا وتوجيهها، يمكننا إعداد مسار RAG الخاص بنا.

التحقق من جودة الاسترجاع

هنا نختبر قاعدة بيانات المتجهات من خلال البحث عن المقاطع التي تحتوي على معلومات ذات صلة باستعلامنا في مساحة المتجه. نعرض المستندات المرتبطة بوصف الصورة المسترجَعة.

تُعَد خطوة التحقق من الصحة هذه مهمة للمساعدة على ضمان عمل نظام الاسترجاع الخاص بنا بشكل صحيح قبل بناء مسار RAG الكامل. نريد معرفة إذا ما كانت المستندات التي تم إرجاعها ذات صلة باستعلامنا.

لا تتردد في تجربة استعلامات مختلفة.

query = "Analyze how Midwest Food Bank's financial efficiency changed during the pandemic by comparing their 2019 and 2020 performance metrics. What specific pandemic adaptations had the greatest impact on their operational capacity, and how did their volunteer management strategy evolve to maintain service levels despite COVID-19 restrictions? Provide specific statistics from the report to support your analysis."
for doc in vector_db.as_retriever().invoke(query):
    print(doc)
    print("=" * 80)  # Separator for clarity

يجب أن تكون المستندات المسترجَعة متوافقة مع الاستعلام. لنبدأ ببناء مسار RAG الخاص بنا.

يجب أن تكون المستندات المسترجَعة متوافقة مع الاستعلام. لنبدأ ببناء مسار RAG الخاص بنا.

بناء مسار RAG لنموذج Granite

أولًا، نُنشئ مطالبات لنموذج Granite لتنفيذ استعلام RAG. نستخدم قالب دردشة Granite ونوفر قيم العناصر النائبة التي سيحل محلها مسار LangChain RAG.

سيحتفظ {context} بالأجزاء المسترجَعة، كما هو موضَّح في البحث السابق، وسيتم تمريرها إلى النموذج كسياق مستند للإجابة عن سؤالنا.

بعد ذلك، نُنشئ مسار RAG باستخدام قوالب مطالبات Granite التي أنشأناها.

from ibm_granite_community.notebook_utils import escape_f_string
from langchain.prompts import PromptTemplate
from langchain.chains.retrieval import create_retrieval_chain
from langchain.chains.combine_documents import create_stuff_documents_chain

# Create a Granite prompt for question-answering with the retrieved context
prompt = tokenizer.apply_chat_template(
    conversation=[{
        "role": "user",
        "content": "{input}",
    }],
    documents=[{
        "doc_id": "0",
        "text": "{context}",
    }],
    add_generation_prompt=True,
    tokenize=False,
)
prompt_template = PromptTemplate.from_template(template=escape_f_string(prompt, "input", "context"))

# Create a Granite document prompt template to wrap each retrieved document
document_prompt_template = PromptTemplate.from_template(template="""\
<|end_of_text|>
<|start_of_role|>document {{"document_id": "{doc_id}"}}<|end_of_role|>
{page_content}""")
document_separator=""

# Assemble the retrieval-augmented generation chain
combine_docs_chain = create_stuff_documents_chain(
    llm=model,
    prompt=prompt_template,
    document_prompt=document_prompt_template,
    document_separator=document_separator,
)
rag_chain = create_retrieval_chain(
    retriever=retriever,
    combine_docs_chain=combine_docs_chain,
)

إنشاء استجابة معززة بالاسترجاع لسؤال

يستخدم المسار الاستعلام لتحديد موقع المستندات من قاعدة بيانات المتجهات واستخدامها كسياق للاستعلام.

outputs = rag_chain.invoke({"input": query})
print(outputs['answer'])

قيود RAG القياسي ولماذا نحتاج إلى توسيع الاستدلال

على الرغم من أن أسلوب RAG القياسي يعمل بشكل جيد إلى حد ما، فإنه يعاني من عدة قيود رئيسية عند التعامل مع محتوى طويل أو معقد:

  1. إدارة السياق: عند التعامل مع العديد من المستندات، يواجه RAG القياسي صعوبة في الاستفادة بشكل فعَّال من السياق المتاح بأكمله.

  2. جودة الاسترجاع: دون إرشادات حول كيفية استخدام المعلومات المسترجَعة، غالبًا ما تركِّز النماذج على الأجزاء الخاطئة من المستندات.

  3. الاستدلال التركيبي: عملية فهم الاستفسارات المعقدة التي تتطلب التفكير متعدد الخطوات تمثِّل تحديًا بالنسبة إلى RAG القياسي.

  4. استقرار الأداء: إضافة مزيد من المستندات إلى RAG القياسي غالبًا ما يؤدي إلى عوائد متناقصة بعد حد معين.

تقنيات توسيع الاستدلال تعالج هذه القيود من خلال تخصيص المزيد من الموارد الحوسبية بشكل استراتيجي أثناء مرحلة الاستدلال.

تحسين RAG باستخدام DRAG (RAG القائم على العروض التوضيحية)

الآن سنطبِّق تقنية DRAG من الورقة البحثية "Inference Scaling for Long-Context Retrieval Augmented Generation" لتعزيز نظام RAG الخاص بنا.

يستخدم DRAG أمثلة ضمن السياق لتوضيح كيفية استخراج المعلومات من المستندات واستخدامها للنموذج، ما يحسِّن الأداء في سيناريوهات السياق الطويل.

الخطوة 1: إنشاء عيّنة من العروض التوضيحية داخل السياق

عادةً ما تأتي هذه الأمثلة من مجموعة بيانات مُختارة بعناية تحتوي على أزواج سؤال وجواب عالية الجودة. لهذا الغرض، سننشئ بعض الأمثلة الاصطناعية التي تتوافق مع المجال المتوقع.

هنا، نحدِّد فئة بيانات لتمثيل عرض فردي ثم نُنشئ بعض العروض التوضيحية.

from dataclasses import dataclass, field, InitVar
from langchain_core.documents import Document

@dataclass
class DRAG_Demonstration:
    query: str
    answer: str
    retriever: InitVar[VectorStoreRetriever] = field(kw_only=True)
    documents: list[Document] = field(default_factory=list, kw_only=True)

    def __post_init__(self, retriever: VectorStoreRetriever):
        if not self.documents:
            self.documents = retriever.invoke(self.query)

    def __format__(self, format_spec: str) -> str:
        formatted_documents = "\n".join(
            f"Document {i+1}:\n{document.page_content}"
            for i, document in enumerate(self.documents)
        )
        return f"""\
{formatted_documents}
Question: {self.query}
Answer: {self.answer}
"""

def create_enhanced_drag_demonstrations(vector_db: VectorStore) -> list[DRAG_Demonstration]:
    """Create high-quality demonstrations for DRAG technique that showcase effective document analysis"""
    demonstration_retriever: VectorStoreRetriever = vector_db.as_retriever(search_kwargs={"k": 5})
    demonstrations = [
        DRAG_Demonstration(
            query="How did the COVID-19 pandemic impact Midwest Food Bank's operations in 2020?",
            answer="The COVID-19 pandemic significantly impacted Midwest Food Bank's operations in 2020. Despite challenges, MFB remained open and responsive to increased needs. They implemented safety protocols, reduced volunteer numbers for social distancing, and altered their distribution model to allow partner agencies to receive food safely. The pandemic created unprecedented food insecurity, with many people seeking assistance for the first time. MFB distributed 37% more food than in 2019, with a record 179 semi-loads of Disaster Relief family food boxes sent nationwide. The organization also faced supply chain disruptions and food procurement challenges in the early months but continued to find and distribute food. Community, business, and donor support helped fund operations and food purchases. Additionally, MFB began participating in the USDA Farmers to Families Food Box program in May 2020, distributing over $52 million worth of nutritious produce, protein, and dairy products.",
            retriever=demonstration_retriever
        ),
        DRAG_Demonstration(
            query="What role did volunteers play at Midwest Food Bank during 2020, and how were they affected by the pandemic?",
            answer="Volunteers were described as 'the life-blood of the organization' in the 2020 annual report. Despite the pandemic creating safety challenges, volunteers demonstrated courage and dedication by increasing their hours to meet growing needs. MFB implemented safety protocols at each location and limited volunteer group sizes to allow for social distancing. This created a challenge as food needs increased while fewer volunteers were available to help. To address this gap, multiple MFB locations received assistance from the National Guard, who filled vital volunteer positions driving trucks, operating forklifts, and helping with food distributions. In 2020, 17,930 individuals volunteered 300,898 hours of service, equivalent to 150 full-time employees. The volunteer-to-staff ratio was remarkable with 450 volunteers for every 1 paid MFB staff member, highlighting the volunteer-driven nature of the organization during the crisis.",
            retriever=demonstration_retriever
        ),
        DRAG_Demonstration(
            query="How did Midwest Food Bank's international programs perform during 2020, particularly in Haiti and East Africa?",
            answer="In 2020, Midwest Food Bank's international operations in East Africa and Haiti faced unique challenges but continued to serve communities. In East Africa (operated as Kapu Africa), strict lockdowns led to mass hunger, especially in slum areas. Kapu Africa distributed 7.2 million Tender Mercies meals, working with partner ministries to share food in food-insecure slums. A notable outcome was a spiritual awakening among recipients, with many asking why they were receiving help. In Haiti, the pandemic added to existing challenges, closing airports, seaports, factories, and schools. MFB Haiti more than doubled its food shipments to Haiti, delivering over 160 tons of food relief, nearly three-quarters being Tender Mercies meals. As Haitian children primarily receive nourishment from school lunches, MFB Haiti distributed Tender Mercies through faith-based schools and also partnered with over 20 feeding centers serving approximately 1,100 children daily. Nearly 1 million Tender Mercies meals were distributed in Haiti during 2020.",
            retriever=demonstration_retriever
        ),
    ]

    return demonstrations

الخطوة 2: تنسيق العروض التوضيحية لتضمينها في المطالبة.

بعد ذلك، نعمل على تنسيق جميع العروض التوضيحية معًا لاستخدامها في المطالبة.

# Format all demonstrations together
demonstrations = create_enhanced_drag_demonstrations(vector_db)

formatted_demonstrations = "\n\n".join(
    f"Example {i+1}:\n{demo}"
    for i, demo in enumerate(demonstrations)
)

الخطوة 3: إنشاء قالب مطالبة DRAG.

بعد ذلك، نُنشئ مطالبة DRAG للنموذج، والتي تتضمن الأمثلة التوضيحية المنسّقة.

drag_prompt = tokenizer.apply_chat_template(
    conversation=[{
        "role": "user",
        "content": f"""\
Here are examples of effectively extracting information from documents to answer questions.

{formatted_demonstrations}

Follow these examples when answering the user's question:

{{input}}""",
    }],
    documents=[{
        "doc_id": "0",
        "text": "Placeholder{context}",
    }],
    add_generation_prompt=True,
    tokenize=False,
)

# Convert to prompt template
drag_prompt_template = PromptTemplate.from_template(template=escape_f_string(drag_prompt, "input", "context"))

الخطوة 4: إنشاء مسترجع مخصص لإعادة ترتيب المستندات.

عادةً، يعمل المسترجع على إرجاع المستندات وفق ترتيب التشابه، بحيث يكون المستند الأكثر تشابهًا في البداية. نحدِّد مسترجعًا لإعادة ترتيب النتائج لعكس تسلسلها. الآن، يعرض الترتيب المستند الأكثر تشابهًا في النهاية، وبالتالي يكون أقرب إلى نهاية المطالبة.

import typing
from langchain_core.retrievers import BaseRetriever, RetrieverInput, RetrieverOutput
from langchain_core.callbacks.manager import CallbackManagerForRetrieverRun

class ReorderingRetriever(BaseRetriever):
    base_retriever: BaseRetriever

    def _get_relevant_documents(
        self, query: RetrieverInput, *, run_manager: CallbackManagerForRetrieverRun, **kwargs: typing.Any
    ) -> RetrieverOutput:
        docs = self.base_retriever._get_relevant_documents(query, run_manager=run_manager, **kwargs)
        return list(reversed(docs))  # Reverse the order so higher-ranked docs are closer to query in prompt

reordering_retriever = ReorderingRetriever(base_retriever=retriever)

الخطوة 5: إنشاء مسار DRAG.

نُنشئ مسارًا لاستعلام DRAG باستخدام قالب مطالبة DRAG والمسترجع المُعاد ترتيبه.

drag_combine_docs_chain = create_stuff_documents_chain(
    llm=model,
    prompt=drag_prompt_template,
    document_prompt=document_prompt_template,
    document_separator=document_separator,
)

drag_chain = create_retrieval_chain(
    retriever=reordering_retriever,
    combine_docs_chain=drag_combine_docs_chain,
)

الخطوة 6: توليد إجابة محسَّنة من DRAG على سؤال ما.

drag_outputs = drag_chain.invoke({"input": query})
print("\n=== DRAG-Enhanced Answer ===")
print(drag_outputs['answer'])

ممتاز، يبدو أننا حصلنا على تحسينات في الإجابة من خلال تزويد النموذج ببعض الأمثلة. دعنا نجرّب تقنية RAG أكثر شمولية في الخطوة التالية!

تنفيذ IterDrag (RAG القائم على العرض التوضيحي التكراري)

يوسِّع IterDRAG تقنية DRAG عن طريق تفكيك الاستعلامات المعقدة إلى استعلامات فرعية أبسط وتنفيذ استرجاع متداخل. يُعَد هذا النهج فعَّالًا بشكل خاص للأسئلة المعقدة متعددة الخطوات التي تتطلب دمج المعلومات من مصادر متعددة أو إجراء استنتاجات عبر عدة مراحل.
 
الفوائد الرئيسية للنهج التكراري:

  • تقسيم الأسئلة المعقدة إلى أجزاء يسهُل التعامل معها.

  • استرجاع المزيد من المعلومات ذات الصلة بكل سؤال فرعي.

  • إنشاء سلاسل استدلال واضحة ومباشرة.

  • إمكانية التعامل مع الأسئلة التي قد يكون من الصعب الإجابة عنها في خطوة واحدة.

الخطوة 1: إنشاء سلسلة لتفكيك الاستعلامات.

خطوة التفكيك مهمة جدًا لأنها تعمل على تحويل الاستعلام المعقَّد إلى استعلامات فرعية أبسط وأكثر تركيزًا يمكن الإجابة عنها بشكل فردي.

decompose_prompt = tokenizer.apply_chat_template(
    conversation=[{
        "role": "user",
        "content": """\
You are a helpful assistant that breaks down complex questions into simpler sub-questions.
For multi-part or complex questions, generate 1-3 sub-questions that would help answer the main question.

Here are examples of how to decompose complex questions:
{demonstrations}

Follow the above examples when breaking down the user's question.
If the following question is already simple enough, just respond with "No follow-up needed."

Otherwise, break down the following question into simpler sub-questions. Format your response as:
Follow up: [sub-question]

Question: {input}"""
    }],
    add_generation_prompt=True,
    tokenize=False,
)

decompose_prompt_template = PromptTemplate.from_template(template=escape_f_string(decompose_prompt, "input", "demonstrations"))
decompose_chain = decompose_prompt_template | model

الخطوة 2: إنشاء سلسلة للإجابة عن الاستعلامات الفرعية.

يتعامل عنصر الإجابة عن الاستعلامات الفرعية مع كل سؤال فرعي على حدة عن طريق استرجاع المستندات ذات الصلة وإنتاج إجابات وسيطة مركَّزة.

intermediate_prompt = tokenizer.apply_chat_template(
    conversation=[{
        "role": "user",
        "content": """\
You are a helpful assistant that answers specific questions based on the provided documents.

Focus only on the sub-question and provide a concise intermediate answer.
Please answer the following sub-question based on the provided documents.
Format your response as:
Intermediate answer: [your concise answer to the sub-question]

Sub-question: {input}
"""
    }],
    documents=[{
        "doc_id": "0",
        "text": "Placeholder{context}",
    }],
    add_generation_prompt=True,
    tokenize=False,
)

intermediate_prompt_template = PromptTemplate.from_template(template=escape_f_string(intermediate_prompt, "input", "context"))
intermediate_combine_docs_chain = create_stuff_documents_chain(
    llm=model,
    prompt=intermediate_prompt_template,
    document_prompt=document_prompt_template,
    document_separator=document_separator,
)
intermediate_chain = create_retrieval_chain(
    retriever=reordering_retriever,
    combine_docs_chain=intermediate_combine_docs_chain,
)

الخطوة 3: إنشاء سلسلة توليد إجابات نهائية.

يتولى عنصر توليد الإجابة النهائية دمج جميع الإجابات الوسيطة لإنتاج رد شامل على السؤال الأصلي.

final_prompt = tokenizer.apply_chat_template(
    conversation=[{
        "role": "user",
        "content": """\
You are a helpful assistant that provides comprehensive answers to questions.
Use the intermediate answers to sub-questions to formulate a complete final answer.
Please provide a final answer to the main question based on the intermediate answers to sub-questions.
Format your response as:
So the final answer is: [your comprehensive answer to the main question]

Main question: {input}

Sub-questions and intermediate answers:
{context}"""
    }],
    add_generation_prompt=True,
    tokenize=False,
)

final_prompt_template = PromptTemplate.from_template(template=escape_f_string(final_prompt, "input", "context"))
final_chain = final_prompt_template | model

الخطوة 4: إنشاء أمثلة توضيحية لـ IterDRAG.

يُعَد إنشاء عروض توضيحية فعَّالة أمرًا بالغ الأهمية لأداء IterDrag. توضِّح هذه الأمثلة للنموذج كيفية:

  1. تقسيم الأسئلة المعقدة إلى أسئلة فرعية أبسط.

  2. توليد إجابات وسيطة ذات صلة.

  3. جمع هذه الإجابات في إجابة نهائية متماسكة.
@dataclass
class IterDRAG_Demonstration_Base:
    query: str
    answer: str

@dataclass
class IterDRAG_Demonstration(IterDRAG_Demonstration_Base):
    intermediate: list[IterDRAG_Demonstration_Base]

    def __format__(self, format_spec: str) -> str:
        sub_questions="\n".join(
            f"Follow up: {sub.query}"
            for sub in self.intermediate
        )

        return f"Question: {self.query}\n{sub_questions}"

def create_iterdrag_demonstrations() -> list[IterDRAG_Demonstration]:
    """Create examples showing how to decompose and answer complex questions"""

    demonstrations = [
        IterDRAG_Demonstration(
            query="What impact did the pandemic have on the food bank's operations and distribution?",
            answer="The pandemic had a profound impact on food bank operations and distribution. Distribution volume increased by 60% to over 100 million pounds of food in 2020. Operationally, the food bank faced supply chain disruptions, volunteer shortages, and safety protocol challenges. In response, they implemented contactless distribution, expanded mobile pantries, created emergency food boxes for vulnerable populations, and developed virtual nutrition education. Despite these challenges, they successfully scaled operations to meet the unprecedented community need during the crisis.",
            intermediate=[
                IterDRAG_Demonstration_Base(
                    query="How did food distribution volume change during the pandemic?",
                    answer="Food distribution volume increased by 60% during the pandemic, rising from approximately 62 million pounds in 2019 to over 100 million pounds in 2020.",
                ),
                IterDRAG_Demonstration_Base(
                    query="What operational challenges did the food bank face during the pandemic?",
                    answer="The food bank faced challenges including supply chain disruptions, volunteer shortages due to social distancing requirements, and the need to implement new safety protocols for food handling and distribution.",
                ),
                IterDRAG_Demonstration_Base(
                    query="What new programs were implemented in response to the pandemic?",
                    answer="New programs included contactless distribution methods, expanded mobile pantry operations, emergency food boxes for vulnerable populations, and virtual nutrition education classes.",
                ),
            ],
        ),
        IterDRAG_Demonstration(
            query="How does the food bank's financial management compare to industry standards for non-profits?",
            answer="The food bank demonstrates excellent financial management compared to industry standards. With 94% of its budget allocated to program services and only 6% to administrative and fundraising costs, it exceeds the industry benchmark of 85-90% for program spending. This financial efficiency places the food bank among the top-performing non-profits in terms of maximizing donor impact and minimizing overhead expenses.",
            intermediate=[
                IterDRAG_Demonstration_Base(
                    query="What percentage of the food bank's budget goes to program services versus administrative costs?",
                    answer="94% of the food bank's budget goes directly to program services, with only 6% allocated to administrative and fundraising costs.",
                ),
                IterDRAG_Demonstration_Base(
                    query="What are the industry standards for program spending versus overhead for food banks?",
                    answer="Industry standards suggest that well-run food banks typically allocate 85-90% of their budget to program services, with 10-15% for administrative and fundraising expenses.",
                ),
            ],
        ),
    ]
    return demonstrations

الخطوة 5: تنفيذ دالة IterDRAG.

تعمل هذه الوظيفة على تنسيق العملية التكرارية بأكملها:

  1. تفكيك السؤال الرئيسي إلى أسئلة فرعية.

  2. استرجاع المستندات ذات الصلة وإنشاء إجابة وسيطة لكل سؤال فرعي.

  3. الجمع بين جميع الإجابات الوسيطة لإنتاج الإجابة النهائية.
import re

def iterative_drag(main_question: str) -> dict[str, typing.Any]:
    """
    Implements IterDRAG: decomposing queries, retrieving documents for sub-queries,
    and generating a final answer based on intermediate answers.
    """
    print(f"\n=== Processing query with IterDRAG: '{main_question}' ===")

    # Step 1: Decompose the main question into sub-questions
    print("Step 1: Decomposing the query into sub-questions...")
    iterdrag_demonstrations = create_iterdrag_demonstrations()
    formatted_demonstrations = "\n\n".join(
        f"Example {i+1}:\n{demo}"
        for i, demo in enumerate(iterdrag_demonstrations)
    )
    decompose_result = decompose_chain.invoke({
        "input": main_question,
        "demonstrations": formatted_demonstrations,
    })
    decompose_answer = decompose_result

    # Extract sub-questions using regex
    sub_questions = re.findall(r"Follow up: (.*?)(?=Follow up:|\n|$)", decompose_answer, re.DOTALL)
    sub_questions = [sq.strip() for sq in sub_questions if sq.strip()]
    if not sub_questions:
        print("No decomposition needed or found. Using standard DRAG approach.")
        return drag_chain.invoke({"input": main_question})
    print(f"Decomposed into {len(sub_questions)} sub-questions")

    # Step 2: Answer each sub-question
    intermediate_pairs: list[dict[str, str]] = []
    for i, sub_question in enumerate(sub_questions):
        print(f"\nStep 2.{i+1}: Processing sub-question: '{sub_question}'")

        # Generate answer for this sub-question
        intermediate_result = intermediate_chain.invoke({"input": sub_question})
        intermediate_answer = intermediate_result["answer"]

        # Extract intermediate answer using regex
        intermediate_answer_match = re.search(r"Intermediate answer: (.*?)$", intermediate_answer, re.DOTALL)
        if intermediate_answer_match:
            intermediate_answer = intermediate_answer_match.group(1).strip()

        print(f"Generated intermediate answer: {intermediate_answer[:100]}...")

        # Store the sub-question and its answer
        intermediate_pairs.append({"input": sub_question, "answer": intermediate_answer})

    # Step 3: Generate the final answer based on sub-question answers
    print("\nStep 3: Generating final answer based on intermediate answers...")
    final_result = final_chain.invoke({
        "input": main_question,
        "context": "\n\n".join(
            f"Sub-question: {pair['input']}\nIntermediate answer: {pair['answer']}"
            for pair in intermediate_pairs
        ),
    })
    final_answer = final_result

    # Extract final answer
    final_answer_match = re.search(r"So the final answer is: (.*?)$", final_answer, re.DOTALL)
    if final_answer_match:
        final_answer = final_answer_match.group(1).strip()

    return {"input": main_question, "answer": final_answer, "intermediate": intermediate_pairs}

مقارنة طرق RAG

الآن بعد أن أعددنا جميع طرق RAG الثلاث، دعنا نقارن استجاباتها للاستعلام نفسه، وهذه المرة استعلام أكثر تعقيدًا لرصد الفروقات.

ستساعدنا هذه المقارنة على فهم فائدة كل طريقة ومتى يكون استخدام كل منها أكثر ملاءمة.

# Run all approaches on the same complex query
comparison_query = "What was the full impact chain of the National Guard's assistance during the pandemic? Specifically, how did their involvement affect volunteer operations, what specific tasks did they perform, and how did this ultimately translate to community impact in terms of food distribution capabilities and reach?"

print("\n=== Standard RAG ===")
standard_result = rag_chain.invoke({"input": comparison_query})
print(standard_result["answer"])

print("\n=== DRAG ===")
drag_result = drag_chain.invoke({"input": comparison_query})
print(drag_result["answer"])

print("\n=== IterDRAG ===")
iterdrag_result = iterative_drag(comparison_query)
print(iterdrag_result["answer"])

مقارنة النتائج وتحليلها

نلخص هنا اختلافات الأداء بين طرق RAG الثلاثة التي تم تنفيذها:

المنهج

 

نقاط القوة

 

القيود

 

أفضل حالات الاستخدام

 

RAG القياسي

  • تنفيذ بسيط
  • جيد للاستفسارات المباشرة
  • متطلبات حسابية أقل
  • استخدام سياق محدود
  • استقرار الأداء مع زيادة عدد المستندات
  • أداء محدود في الاستدلال المعقد 
  • استفسارات واقعية بسيطة
  • عندما تكون القدرة الحاسوبية محدودة
  • عندما يكون السياق صغيرًا 

DRAG

  • استخدام أفضل للسياق
  • أداء محسَّن مع المزيد من المستندات
  • جيد للاستعلامات المعقدة إلى حد ما
  • ما زال مقيدًا بالتوليد في خطوة واحدة
  • أقل فاعلية مع الأسئلة متعددة الخطوات
  • استعلامات متوسطة التعقيد
  • عند توفُّر المزيد من المستندات
  • عندما يمكن تقديم أمثلة في السياق

IterDRAG

  • الأفضل للاستعلامات المعقدة
  • سلاسل الاستدلال الصريح
  • الاستخدام الأكثر فاعلية للسياق
  • أعلى متطلبات القدرة الحوسبية
  • تنفيذ أكثر تعقيدًا
  • أسئلة متعددة الخطوات
  • تحليلات معقدة تتطلب استدلالًا مركبًا
  • عند الحاجة إلى أقصى أداء 
    

كما رأينا في تقنيات توسيع الاستدلال التي طبّقناها، مثل DRAG وIterDRAG، يمكن تحسين أداء RAG بشكل كبير. هذه الطريقة تنطبق بشكل خاص على الاستعلامات المعقدة التي تتطلب تحليلًا عميقًا لمستندات متعددة.

الخاتمة

في هذا البرنامج التعليمي، استكشفنا كيف يمكن لتوسيع الاستدلال أن يحسِّن أداء RAG بشكل كبير. من خلال تخصيص موارد حسابية إضافية بشكل استراتيجي أثناء الاستدلال باستخدام تقنيات مثل DRAG وIterDRAG، يمكننا تحقيق مكاسب كبيرة في جودة الإجابات للأسئلة المعقدة.

التحديات التي تواجه النماذج التقليدية القائمة على RAG والنماذج القائمة على المحوِّلات

الاستدلال المكلِّف: تتطلب النماذج القائمة على المحوِّلات، والتي تستخدم آليات الانتباه الذاتي، تكاليف استدلال تتناسب طرديًا مع طول الإدخال. تجعل هذه الطريقة معالجة السياقات الطويلة مكلِّفة حسابيًا، ما يقيّد الاستخدام العملي لتقنية RAG بالمستندات القصيرة أو يتطلب تقصيرًا شديدًا للمحتوى.

استخدام محدود للسياق: غالبًا ما تعمل أنظمة RAG القياسية على استرجاع ومعالجة عدد محدد من المستندات، ما قد يكون غير كافٍ للاستعلامات المعقدة متعددة الخطوات. يصل الأداء إلى حد الاستقرار مع زيادة طول السياق، خاصةً عند تجاوز 128,000 رمز مميز، لأن النموذج يواجه صعوبة في تجميع المعلومات عبر العديد من المقاطع المسترجعة.

تخصيص غير فعَّال للحوسبة: دون إدارة دقيقة للموارد، تؤدي إضافة المزيد من المستندات المسترجعة أو السياق إلى زيادة التكلفة الحسابية دون تحقيق مكاسب متناسبة في الدقة، ما يؤدي إلى تراجع الأداء أو حتى تدهوره بسبب كثرة المعلومات.

كيف يتعامل كلٌّ من DRAG وIterDRAG مع هذه التحديات

RAG القائم على العرض التوضيحي (DRAG):

يستفيد DRAG من عدة أمثلة مسترجعة من أسئلة وأجوبة كمُعطيات ضمن المطالبة، ما يمكِّن النموذج من تعلُّم كيفية تحديد المعلومات ذات الصلة واستخدامها ضمن السياق نفسه.

هذا النهج فعَّال بشكل خاص مع أطوال السياق الفعَّال الأقصر، إذ يسمح للنموذج باستخدام سياق غني دون إرباك آلية الانتباه، ما يحسِّن جودة الاسترجاع والتوليد على حد سواء.

RAG القائم على العرض التوضيحي التكراري (IterDRAG):

يعمل IterDRAG على تفكيك الاستعلامات المعقدة إلى استعلامات فرعية أبسط، مع استرجاع وتوليد الإجابات لكل خطوة فرعية بشكل تكراري.

من خلال تداخل الاسترجاع والتوليد، يبني IterDRAG سلاسل استدلالية تسدّ الفجوة في الاستعلامات متعددة الخطوات، ما يجعلها فعَّالة بشكل خاص مع السياقات الطويلة جدًا.

تُتيح هذه العملية للنموذج تخصيص الموارد الحسابية بشكل أكثر كفاءة، مع التركيز على المعلومات الأكثر صلة في كل خطوة وتجنُّب خطر إرهاق آلية الانتباه بالسياقات الطويلة. من خلال تطبيق تقنيات توسيع الاستدلال هذه على تطبيقات RAG الخاصة بك، يمكنك تحقيق أداء أفضل بشكل كبير في المهام المعرفية المكثفة دون الحاجة إلى تغيير النماذج الأساسية.

الخطوات التالية:

  • جرِّب نماذج استرجاع مختلفة وأساليب المعالجة المسبقة للمستندات.

  • حاوِل استخدام صيغ مطالبات مختلفة لفهم الصورة.

  • استكشِف تحسين مَعلمات النموذج للعثور على الإعدادات المثالية لحالة الاستخدام المحددة لديك.
حلول ذات صلة
IBM watsonx.ai

تدريب الذكاء الاصطناعي التوليدي والتحقق من صحته وضبطه ونشره، وكذلك قدرات نماذج الأساس والتعلم الآلي باستخدام IBM watsonx.ai، وهو استوديو الجيل التالي من المؤسسات لمنشئي الذكاء الاصطناعي. أنشئ تطبيقات الذكاء الاصطناعي بسرعة أكبر وببيانات أقل.

اكتشف watsonx.ai
حلول الذكاء الاصطناعي

استفد من الذكاء الاصطناعي في عملك بالاستعانة بخبرة IBM الرائدة في مجال الذكاء الاصطناعي ومحفظة حلولها المتوفرة لك.

استكشف حلول الذكاء الاصطناعي
الاستشارات والخدمات المتعلقة بالذكاء الاصطناعي

أعدّ ابتكار عمليات ومهام سير العمل الحساسة بإضافة الذكاء الاصطناعي لتعزيز التجارب وصنع القرارات في الوقت الفعلي والقيمة التجارية.

استكشف خدمات الذكاء الاصطناعي
اتخِذ الخطوة التالية

احصل على وصول شامل إلى القدرات التي تغطي دورة حياة تطوير الذكاء الاصطناعي. تمكَّن من إنتاج حلول ذكاء اصطناعي قوية بفضل الواجهات سهلة الاستخدام وعمليات سير العمل السلسة وإمكانية الوصول إلى واجهات برمجة التطبيقات ومجموعات تطوير البرامج القياسية في الصناعة.

استكشف watsonx.ai احجز عرضًا توضيحيًا مباشرًا
الحواشي

1. “A Survey of Frontiers in LLM Reasoning: Inference Scaling, Learning to Reason, and Agentic Systems,” Ke, Zixuan, Fangkai Jiao, Yifei Ming, Xuan-Phi Nguyen, Austin Xu, Do Xuan Long, Minzhi Li, et al.,  ArXiv.org, 2025.

2. “Reasoning in Granite 3.2 Using Inference Scaling,” Lastras, Luis. 2025,  IBM Research, IBM, February 26, 2025.

3. “Inference Scaling for Long-Context Retrieval Augmented Generation,” Zhenrui Yue, Honglei Zhuang, Aijun Bai, Kai Hui, Rolf Jagerman, Hansi Zeng, Zhen Qin, Dong Wang, Xuanhui Wang, Michael Bendersky, ArXiv.org, 2024.