
بقلم: ليلى | محررة أدوات المطورين · صوت تحريري بإشراف بشري
تخيّل أنك تسأل نظام RAG عن الحد المالي المطبّق في بوليصة تأمين، فيجيبك: “الحد المعمول به موضح في القسم 7.2 من الوثيقة.” والآن؟ ماذا يقول القسم 7.2؟ ما الرقم الفعلي؟ الإجابة ليست خاطئة، لكنها ناقصة — المقطع المسترجع يُشير إلى الجواب بدلاً من أن يحتويه، ولا شيء في الأنبوب ذهب لجلبه. العقود والمعايير والأوراق البحثية مليئة بهذه المؤشرات الداخلية، والسؤال كيف تتبعها بشكل منهجي بدلاً من الأمل أن الصفحة الصحيحة جُلبت في المحاولة الأولى.
المقال الذي نعرضه هنا جزء من سلسلة Enterprise Document Intelligence، وتحديداً مقال رقم 11 في الجزء الثالث، الذي يبني نظام RAG مؤسسياً من أربعة مكوّنات: تحليل المستندات، وتحليل الأسئلة، والاسترجاع، والتوليد. الحل يعمل على ثلاثة مستويات: المحلل يُعلّم المراجع بتكلفة منخفضة، والتوليد يُبلّغ عن المراجع غير المحلولة، والمنسّق يعود للهدف في حلقة ثانية.

المشكلة تظهر في ثلاث لحظات مختلفة داخل الأنبوب: عند التحليل يكتبها مؤلف الوثيقة، وعند الاسترجاع تُطابق المقطع الذي يحتوي المرجع الاستعلامَ، وعند التوليد يكتب النموذج “انظر القسم X” بدلاً من جلب القسم X. المؤشر يمكن حلّه في أي من هذه اللحظات الثلاث، لكنه يُسقَط بصمت في كل واحدة منها. الإصلاح يعتمد على حقل تغذية راجعة في مخرجات التوليد يُعلّم بوجود مرجع غير محلول، ومنسّق يُمسك الإشارة ويحل المرجع عبر جداول المحلل العلائقية، ثم يسترجع المنطقة المرتبطة ويُعيد التوليد مع السياق الجديد.
المثال التطبيقي يعمل على ورقة Attention Is All You Need لـ Vaswani وآخرين (2017). السؤال: “هل تُعطي التضمينات الموضعية المتعلَّمة نتائج مشابهة للتضمينات الجيبية في ورقة Transformer؟” النص في الصفحة 6 (القسم 3.5 Positional Encoding) يُعطي إجابة نوعية “أنتج الإصداران نتائج متطابقة تقريباً”، لكنه يضيف “انظر Table 3 row (E)”. الأرقام الفعلية موجودة في الصفحة 9. مع سياسة استرجاع top-1، يجلب المرور الأول الصفحة 6 فقط، وتُفوَّت الجدول.
الحل يمر بسبع خطوات متسلسلة:
- التحليل الرخيص للمراجع عند بناء المستند: المحلل ينتج جداول المعتادة — line_df وpage_df وtoc_df — بالإضافة إلى cross_ref_df. هذا الجدول يُسجّل في مرحلة التحليل فقط المراجع ذات التكلفة الصفرية أو شبه الصفرية: روابط PDF النصية (المدمجة بـ LaTeX \hyperref أو ميزة الروابط في Word)، ومراسي الأقسام من toc_df، والكائنات المسماة (الأشكال والجداول والملاحق) التي يُحددها المحلل مسبقاً. ما لا يدخل الجدول في هذه المرحلة: عشرات الإشارات النثرية مثل “see Figure 2″ أو “as described in section 3.2” — المفردات متنوعة جداً (cf. / see / per / as defined in) والحجم كبير جداً لاستخراجها قبل معرفة ما سيُحتاج فعلياً.
- هيكل بيانات المرجع عبر ReferenceRow: كل صف في cross_ref_df يحمل: origin_page (الصفحة التي تحتوي المرجع)، وorigin_line (السطر المحدد)، وanchor_text (النص الخام مثل “see Section 5.2” أو “Table 3 row (E)”)، وref_type (“section” أو “table” أو “figure” أو “annex” أو “bib” أو “external”)، وref_target (معرّف polymorphic — إذا كان النوع “section” يُشير إلى toc_df.toc_id كـ”5.2″، وفي أي حالة أخرى يُشير إلى object_registry.object_id)، وtarget_page (الصفحة المحلولة إن كانت رخيصة، وإلا null)، وsource (“native_link” أو “object_registry” أو “toc_anchor” أو “regex” أو “llm”).
- تحليل السؤال والاسترجاع في المرور الأول: تحليل السؤال (المقال 6 من السلسلة) يستخرج الكلمات المفتاحية: learned positional embeddings وsinusoidal وpositional encoding، ويُحدد شكل الإجابة المتوقع كـ”مقارنة”. الاسترجاع يعمل بالمعيار المعتاد (صفحات جدول المحتويات + صفحات الكلمات المفتاحية، مدمجة)، ويأخذ فقط أعلى صفحة واحدة — الصفحة 6. الصفحة 9 (Table 3) هي المرشحة الثانية لكنها لا تُجلب تحت سياسة top-1.
- التوليد يُعلّم بالمراجع غير المحلولة: المخطط يمتد بحقلين جديدين — pending_references (قائمة المراجع التي اكتشفها LLM في المقاطع وتبدو مؤثرة على الإجابة)، وanswer_completeness بثلاثة قيم: “complete” أو “references_unresolved” أو “partial”. في مثالنا، ينتج LLM إجابة نثرية صحيحة نوعياً (“أنتج الإصداران نتائج متطابقة تقريباً”)، لكنه يُعيّن answer_completeness إلى “references_unresolved” ويضع في pending_references: raw_text = “Table 3 row (E)”، وref_type = “table”، وorigin_page = 6، وorigin_line = 28. الإجابة النثرية هي ما كان الأنبوب الساذج سيُعيده ويتوقف عنده.
- المنسّق يقرأ الإشارة ويُقرر الحلقة الثانية: المنسّق يقرأ answer_completeness أولاً. إذا كانت “complete” تعود الإجابة للمستخدم. إذا كانت “references_unresolved” وعدد الحلقات أقل من الحد (1 بالإعداد الافتراضي) يُطلق resolve_references. دالة decide_next_pass تملك ثلاثة مخارج: return_to_user وresolve_references وgive_up_partial. نقطة أساسية: المُشغّل يعمل على حقل مكتوب (answer_completeness == “references_unresolved” مع pending_references غير فارغة)، لا على درجة ثقة غامضة. هذا يجعل السلوك قابلاً للتتبع والتصحيح.
- الحل عند الطلب لا عند البناء: المرور الثاني يُشغّل regex فقط على المقاطع التي أعادها الاسترجاع وعلّم عليها LLM — لا على المستند كله. المحلل يُضيف صفوف regex إلى cross_ref_df فقط الآن. المحلل يُشغّل الربط في اتجاهين: إذا كان ref_type = “section” يربط ref_target بـtoc_df.toc_id، وإذا كان أي نوع آخر يربط بـobject_registry.object_id. النتيجة: target_page = 9 لـ”Table 3 row (E)”.
- المرور الثاني يُكمل الإجابة: الاسترجاع يُعاد مع الصفحة 9 مضافةً إلى السياق، والتوليد يعمل على المقطعين معاً (الصفحة 6 + الصفحة 9). الإجابة تعود مع الأرقام الفعلية من الجدول، وanswer_completeness = “complete”، وpending_references فارغة. كود المستودع المرافق على GitHub: doc-intel/notebooks-vol1 يمكّنك من تشغيل الحلقتين بنفسك على ورقة Attention.
GitHub“>ما يجعل هذا النهج قابلاً للصيانة هو تماسكه المعماري. نفس الآلية التي استخدمها المقال 10 لجودة التحليل تعمل هنا للمراجع، والمقال 12 يطبّقها على بُعد ثالث (اكتمال القوائم)، والمقال 13 يُسمّي النمط صراحةً ويوضح كيف تتألف هذه الأنماط مع بعضها. الفكرة المركزية: المنسّق يدور على إشارات من المخرجات المهيكلة، لا على درجات الثقة. هذا يُغيّر طبيعة تصحيح الأخطاء — بدلاً من تتبع عتبة رقمية غامضة، تتتبع حقلاً مكتوباً بقيم محددة. في RAG المؤسسي الذي يعمل على عقود وأوراق بحثية ومعايير تقنية، الفرق بين “الحد موضّح في القسم 7.2″ و”الحد هو 50,000 دولار” ليس تفصيلاً — إنه الإجابة بأكملها.








