0Pricing
AI Prompt Engineering · درس

ضغط السياق

اختصار السياق إلى المعلومات المهمة.

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

لماذا نضغط السياق

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

تتراكم الفوائد: انخفاض تكلفة الرموز، وتقليل زمن الاستجابة، وتقليل العناصر المشتتة، والحد من مشكلة lost-in-the-middle عبر تقليص السياق الذي يتعين على النموذج اجتيازه.

def compress(query, chunks):
    # Goal: keep only spans that bear on the query,
    # dropping boilerplate, navigation, and off-topic sentences.
    return [extract_relevant(query, c) for c in chunks]

الضغط الاستخلاصي مقابل التجريدي

يختار الضغط الاستخلاصي مقاطع حرفية (جملًا أو فقرات) ذات صلة بالاستعلام، مع الحفاظ على الصياغة الأصلية وإمكانية تتبّع المصدر. أما الضغط التجريدي فيعيد صياغة النص أو يلخّصه، محققًا ضغطًا أكبر لكنه معرّضًا لفقدان المعلومات وإدخال أخطاء جديدة.

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

def extractive(query, chunk):
    sents = split_sentences(chunk.text)
    scored = [(s, relevance(query, s)) for s in sents]
    kept = [s for s, r in scored if r > TAU]
    return ' '.join(kept) or top1(scored)   # never return empty

التصفية على مستوى الجملة

هذه تقنية خفيفة ومتينة: احسب درجة كل جملة في كل مقطع مقابل الاستعلام باستخدام cross-encoder صغير أو تشابه التضمينات، ثم احذف الجمل منخفضة الدرجة. بذلك تحافظ على الجمل عالية القيمة وتتخلص من الحشو.

احتفظ بحد أدنى من السياق لكل مقطع، حتى لا تحذف الجملة المحيطة التي تمنح الحقيقة معناها.

def sentence_filter(query, chunk, keep_ratio=0.4):
    sents = split_sentences(chunk.text)
    scores = embed_sim_batch(query, sents)
    n_keep = max(1, int(len(sents) * keep_ratio))
    idx = sorted(range(len(sents)), key=lambda i: -scores[i])[:n_keep]
    return ' '.join(sents[i] for i in sorted(idx))  # keep original order

الضغط السياقي المعتمد على LLM

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

هذا هو نمط LangChain ContextualCompressionRetriever. وهو أدق من المرشحات المعتمدة على التضمينات، لكنه يضيف استدعاءً إلى LLM لكل مقطع؛ لذلك يُفضَّل حجزه لخطوط المعالجة عالية الأهمية أو تشغيله على دفعات.

def llm_compress(query, chunk):
    prompt = (
        'Extract ONLY sentences from the passage relevant to the query. '
        'If none are relevant, output NONE. Do not paraphrase.\n'
        'Query: ' + query + '\nPassage: ' + chunk.text
    )
    out = llm(prompt, temperature=0).strip()
    return None if out == 'NONE' else out

التشذيب على مستوى الرموز (LLMLingua)

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

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

def token_prune(context, target_ratio=0.3):
    # small LM estimates per-token information (perplexity-based);
    # drop the least informative tokens down to target_ratio length
    scores = small_lm_token_importance(context)
    return keep_top_fraction(context, scores, target_ratio)

الضغط المراعي للاستعلام مقابل غير المراعي للاستعلام

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

يخزّن الأسلوب الهجين نسخًا مكثفة من المقاطع دون اتصال، ثم يطبّق اقتطاعًا خفيفًا مراعيًا للاستعلام أثناء المعالجة الفورية.

# Offline: precompute a dense summary per chunk (query-agnostic)
chunk.summary = summarize(chunk.text)
# Online: query-aware trim over summaries (cheap) then verify on full
ctx = [sentence_filter(query, Chunk(c.summary)) for c in top_chunks]

الحماية من فقدان المعلومات

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

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

def safe_compress(query, chunk):
    out = llm_compress(query, chunk)
    if out is None and chunk.rerank_score > 0.7:
        return chunk.text          # trust re-ranker over compressor
    return out or chunk.text

الحفاظ على إمكانية تتبّع المصدر

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

مرّر المعرّفات عبر خط المعالجة كحقول منظمة، ولا تمررها أبدًا كنص حر قد يحذفه الضغط.

def compress_with_ids(query, chunks):
    out = []
    for c in chunks:
        span = safe_compress(query, c)
        out.append({'id': c.id, 'doc': c.meta['doc_id'], 'text': span})
    return out   # generator cites id; provenance preserved

الضغط وميزانية الرموز

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

يفصل ذلك بين مقدار ما تسترجعه ومقدار ما تنفقه على التوليد، وهو عامل أساسي لرفع الكفاءة.

def pack(spans, budget_tokens, tok):
    spans = sorted(spans, key=lambda s: -s['score'])
    used, packed = 0, []
    for s in spans:
        t = tok(s['text'])
        if used + t > budget_tokens:
            continue
        packed.append(s); used += t
    return packed

قياس جودة الضغط

تتبّع ثلاثة أرقام: نسبة الضغط (الرموز التي جرى توفيرها)، ودقة الإجابة (هل بقيت الجودة كما هي؟)، والأمانة (هل تجنّب الضاغط تغيير الحقائق؟). الهدف هو تحقيق أعلى نسبة ضغط دون التأثير في دقة الإجابة.

اختبر مستويات مختلفة من شدة الضغط، واختر نقطة Pareto التي تحقق هدف التكلفة دون خسارة في الجودة.

def eval_compression(eval_set, levels):
    return {
        lvl: {
            'ratio': mean_ratio(eval_set, lvl),
            'acc':   answer_accuracy(eval_set, lvl),
            'faith': faithfulness(eval_set, lvl),
        } for lvl in levels
    }

خط أنابيب يراعي الضغط

من البداية إلى النهاية: استرجع نطاقًا واسعًا، وأعد الترتيب، واضغط كل مقطع باقٍ (استخلاصيًا أو باستخدام LLM) مع الحفاظ على مصدره، واحمِ من الاقتطاع المفرط، واحشد المحتوى ضمن ميزانية الرموز، ثم ولّد الإجابة مع الاستشهادات.

الضغط هو الطبقة التي تجعل الاسترجاع عالي الاستدعاء ميسور التكلفة، وتحافظ على تركيز المولّد فيما يهم.

def pipeline(query):
    top = rerank(query, hybrid_retrieve(query, 50))[:12]
    spans = compress_with_ids(query, top)
    spans = [s for s in spans if s['text']]
    packed = pack(spans, budget_tokens=2000, tok=count_tokens)
    return generate_with_citations(query, packed)

اختبار سريع

اختر أسلوب الضغط المناسب لنظام RAG واقعي يعتمد على الحقائق ويستخدم الاستشهادات.

خلاصة

أهم النقاط:

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

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

هل درس «ضغط السياق» مجاني؟

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

ماذا ستتعلم في «ضغط السياق»؟

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

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

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

كم من الوقت يستغرق درس «ضغط السياق»؟

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

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

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

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

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