AI Prompt Engineering · درس

إعادة ترتيب المقاطع المسترجعة

إعادة الترتيب باستخدام Cross-encoder.

الدرس 2 من 413 خطوة

إعادة ترتيب المقاطع المسترجعة درس مجاني في AI Prompt Engineering على CoddyKit. هذا هو الدرس 2 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في AI Prompt Engineering، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة AI Prompt Engineering 4 دروس في المجموع.

لماذا نعيد الترتيب أصلًا

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

ويُعد نمط الاسترجاع على نطاق واسع ثم إعادة الترتيب بدقة العمود الفقري لـ RAG المتقدم.

def two_stage(query, k_retrieve=50, k_final=5):
    candidates = first_stage_retrieve(query, k_retrieve)  # high recall
    reranked = rerank(query, candidates)                  # high precision
    return reranked[:k_final]

bi-encoder مقابل cross-encoder

يحوّل bi-encoder الاستعلام والمستند إلى تمثيلات متجهية بشكل منفصل، ثم يقارنهما باستخدام جيب التمام؛ وهو سريع وقابل للفهرسة، لكنه يفقد التفاعل بين الاستعلام والمستند. أما cross-encoder فيمرر الاستعلام والمرشح معًا عبر النموذج، ويُخرج درجة صلة تلتقط التفاعل الدقيق.

وتتميز cross-encoders بدقة أعلى بكثير، لكن لا يمكن حسابها مسبقًا، ولذلك لا تُشغّل إلا على القائمة المختصرة.

# Bi-encoder: score = cos(enc(q), enc(d))     -> precomputable
# Cross-encoder: score = model(q, d) -> 0..1   -> per-pair, no index
def cross_encode(query, doc):
    return cross_encoder.predict([(query, doc)])[0]  # joint attention

جولة إعادة الترتيب باستخدام cross-encoder

تسجّل أداة إعادة الترتيب كل مرشح مقارنةً بالاستعلام، ثم ترتب النتائج تنازليًا. وبما أن cross-encoder ينتبه إلى الاستعلام والمستند معًا، فإنه يحسم جوانب دقيقة من الصلة أخفق bi-encoder في التقاطها، مثل النفي، واحتياجات المطابقة الحرفية، والتمييز بين الإجابة والموضوع.

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

def rerank(query, candidates):
    pairs = [(query, c.text) for c in candidates]
    scores = cross_encoder.predict(pairs)        # batched
    for c, s in zip(candidates, scores):
        c.rerank_score = s
    return sorted(candidates, key=lambda c: c.rerank_score, reverse=True)

استخدام LLM لإعادة الترتيب

عندما لا تتوفر cross-encoder مدرّبة تلائم مجالكم، يمكن لـ LLM إعادة الترتيب. تطلب المطالبة Listwise من النموذج ترتيب قائمة من المقاطع بحسب الصلة في استدعاء واحد، بينما تقيّم pointwise كل مقطع بشكل مستقل.

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

def llm_listwise(query, candidates):
    passages = '\n'.join(
        '[' + str(i) + '] ' + c.text for i, c in enumerate(candidates)
    )
    prompt = (
        'Rank the passages by relevance to the query. '
        'Return only IDs, most relevant first.\nQuery: ' + query +
        '\n' + passages
    )
    order = parse_ids(llm(prompt, temperature=0))
    return [candidates[i] for i in order]

زمن الاستجابة وحجم القائمة المختصرة

تتدرج تكلفة إعادة الترتيب مع حجم القائمة المختصرة. فتشغيل cross-encoder على 50 مرشحًا أرخص بكثير من تشغيله على 500. اختاروا قيمة k في المرحلة الأولى تكون كبيرة بما يكفي لالتقاط المقطع المرجعي، مع التحقق من recall@k، وصغيرة بما يكفي لإعادة الترتيب ضمن ميزانية زمن الاستجابة لديكم.

نفّذوا تقييم أزواج المرشحين على دفعات، وشغّلوه على عتاد مُسرّع؛ إذ تتوازى cross-encoders جيدًا عبر الأزواج.

def tune_shortlist(eval_set, ks=(20, 50, 100, 200)):
    # find smallest k where recall@k saturates -> rerank fewer pairs
    return {k: (recall_at_k(eval_set, k), rerank_latency(k)) for k in ks}

المرحلة الأولى الهجينة مع إعادة الترتيب

يأتي أقوى استدعاء من مرحلة أولى هجينة (دمج dense وBM25)، تُمرِّر قائمة مختصرة واحدة منزوعة التكرار إلى مُعيد الترتيب. يستعيد dense الصياغات المعادَة، بينما يستعيد sparse المعرّفات المطابقة تمامًا؛ ثم يرتّب cross-encoder اتحاد النتائج حسب الملاءمة الحقيقية.

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

def hybrid_then_rerank(query, k_final=6):
    dense = dense_retrieve(query, 50)
    sparse = bm25_retrieve(query, 50)
    fused = dedup(rrf(dense, sparse))     # reciprocal rank fusion
    return rerank(query, fused)[:k_final]

عتبات الدرجات وحدود القطع

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

اضبط العتبة باستخدام مجموعة تحقق لتحقيق توازن بين تغطية الأسئلة القابلة للإجابة وإدراج العناصر المشتتة.

def threshold_select(reranked, tau=0.3, max_n=8):
    kept = [c for c in reranked if c.rerank_score >= tau][:max_n]
    if not kept:
        return None        # signal: no sufficiently relevant context
    return kept

التنوع بعد إعادة الترتيب

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

هذا مهم للأسئلة متعددة القفزات التي تمتد إجاباتها عبر عدة وثائق.

def diversify(reranked, max_per_doc=2, k=6):
    out, per_doc = [], {}
    for c in reranked:
        d = c.meta['doc_id']
        if per_doc.get(d, 0) < max_per_doc:
            out.append(c)
            per_doc[d] = per_doc.get(d, 0) + 1
        if len(out) == k:
            break
    return out

ترتيب العناصر للمولّد

بعد اختيار المقاطع الأعلى ترتيبًا، ضعها بطريقة تستفيد من آلية الانتباه. نظرًا إلى مشكلة lost-in-the-middle، ضع المقطع صاحب أعلى درجة في بداية السياق أو نهايته، لا مدفونًا بين المقاطع الأخرى.

ترتّب بعض خطوط المعالجة المقاطع تصاعديًا حسب الملاءمة، بحيث يكون الأفضل أقرب إلى السؤال، محاكاةً لاستراتيجية الحداثة في few-shot.

def order_for_llm(chunks):
    chunks = sorted(chunks, key=lambda c: c.rerank_score)  # ascending
    return chunks                 # most relevant chunk ends up last,
                                  # nearest the trailing question

تقييم مُعيد الترتيب

قِس أداء مُعيد الترتيب باستخدام مقاييس الترتيب، وعلى رأسها NDCG وMRR، على بيانات معنونة لملاءمة أزواج الاستعلام والمقطع، ثم قِس دقة الإجابة في المرحلة اللاحقة. قد يكون مُعيد الترتيب الذي يحسّن NDCG دون تحسين الإجابات يعيد ترتيب المقاطع بطريقة كان المولّد يتعامل معها أصلًا.

احرص دائمًا على ربط التقييم بجودة المهمة النهائية، لا بمقاييس الترتيب وحدها.

import math

def ndcg_at_k(relevances, k):
    dcg = sum(r / math.log2(i + 2) for i, r in enumerate(relevances[:k]))
    ideal = sorted(relevances, reverse=True)
    idcg = sum(r / math.log2(i + 2) for i, r in enumerate(ideal[:k]))
    return dcg / idcg if idcg else 0.0

خط أنابيب إعادة ترتيب للإنتاج

من البداية إلى النهاية: نفّذ استرجاعًا هجينًا لقائمة مختصرة تضم 50 مرشحًا، وأزل التكرار، ثم أجرِ إعادة ترتيب باستخدام cross-encoder، وطبّق عتبة للدرجة، ونوّع النتائج حسب الوثيقة، ورتّبها بما يلائم الانتباه، ثم ولّد الإجابة مع الاستشهادات. استخدم العتبة لاتخاذ قرار بإرجاع إجابات صادقة تفيد بعدم وجود جواب.

خزّن التضمينات ودرجات مُعيد الترتيب مؤقتًا لكل زوج (query, chunk) عندما تتكرر الطلبات، لتقليل التكلفة.

def pipeline(query):
    shortlist = hybrid_then_rerank(query, k_final=20)
    kept = threshold_select(shortlist, tau=0.3, max_n=8)
    if kept is None:
        return 'No relevant information found.'
    ctx = order_for_llm(diversify(kept))
    return generate_with_citations(query, ctx)

اختبار سريع

اختر بنية إعادة الترتيب المناسبة.

خلاصة

أهم النقاط:

  • استرجع نطاقًا واسعًا لتحقيق الاستدعاء، ثم أعد ترتيب القائمة المختصرة لتحقيق الدقة.
  • يقيّم cross-encoder أزواج الاستعلام والوثيقة معًا (دقيق لكنه غير قابل للفهرسة)، بينما bi-encoder سريع لكنه تقريبي.
  • إعادة الترتيب باستخدام LLM بأسلوبي listwise وpointwise خيار احتياطي؛ انتبه إلى تحيز الموضع ومشكلات التحليل.
  • اضبط حجم القائمة المختصرة للوصول إلى تشبّع الاستدعاء ضمن ميزانية زمن الاستجابة، وادمجه مع الاسترجاع الهجين في المرحلة الأولى.
  • طبّق عتبات للدرجات، ونوّع النتائج حسب الوثيقة، ورتّب المقاطع بما يلائم الانتباه، وقيّم الأداء باستخدام NDCG إلى جانب دقة الإجابة في المرحلة اللاحقة.
البدء مجانًا

تعلم AI Prompt Engineering مع معلم ذكاء اصطناعي — مجانًا

اكتب وقم بتشغيل أكوادك الفعلية في المتصفح، واحصل على مساعدة فورية من معلم ذكاء اصطناعي متاح 24/7، واستمر من حيث توقفت على الويب أو في التطبيق.

الدورات
53
الدروس
199

الأسئلة الشائعة

هل درس «إعادة ترتيب المقاطع المسترجعة» مجاني؟

نعم — نص درس «إعادة ترتيب المقاطع المسترجعة» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة AI Prompt Engineering، انتقل إلى CoddyKit PRO. تتضمن دورة AI Prompt Engineering 4 دروس في المجموع.

ماذا ستتعلم في «إعادة ترتيب المقاطع المسترجعة»؟

إعادة الترتيب باستخدام Cross-encoder. تتمرن على AI Prompt Engineering مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ AI Prompt Engineering؟

لا تُشترط خبرة سابقة. AI Prompt Engineering على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 2 من أصل 4.

كم من الوقت يستغرق درس «إعادة ترتيب المقاطع المسترجعة»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس AI Prompt Engineering هذا؟

نعم. كل درس في AI Prompt Engineering يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. ما بعد RAG الساذج
  2. إعادة ترتيب المقاطع المسترجعة
  3. ضغط السياق
  4. إعادة صياغة الاستعلام وHyDE
← العودة إلى AI Prompt Engineering