الاسترجاع لا يزال يتفوق على نافذة مليون رمز لقاعدة الكود الخاصة بك
قبل أسابيع قليلة، كنت أصحح خطأ في المصادقة في مشروع جانبي — واجهة خلفية بـ TypeScript، نحو 140 ملفًا، مريحًا تحت حاجز المليون رمز. كان لدي نموذج بمليون رمز أمامي، فاخترت الطريق الكسول: حشرت شجرة src/ بأكملها في موجه واحد وطلبت من العميل أن يجد أين يتم تحديث رمز الجلسة ولماذا يفشل أحيانًا.
قرأ كل شيء. كلّفني ذلك ثروة صغيرة في رموز الإدخال لتلك الدورة الواحدة. وأشار بثقة إلى مساعد refreshToken بدا صحيحًا لكنه كان خاطئًا — كان هناك اثنان في وحدتين مختلفتين، والذي اختاره لم يكن على المسار الذي يُنفَّذ فعلًا. أعطيته كامل كومة القش فالتقط إبرة معقولة من وسطها.
ثم أغلقت تلك الجلسة، وفتحت جلسة جديدة بنفس النموذج، وتركته يعمل بالطريقة الطبيعية: سرد الشجرة، البحث بـ grep عن refreshToken، قراءة الملفات الثلاثة المطابقة، تتبع الاستيرادات. وجد الخطأ الحقيقي بنحو ربع الرموز وثلث وقت التنفيذ. نفس النموذج. نفس المستودع. الفرق كان كليًا في كيفية تزويده بالكود.
الخلاصة المختصرة: بالنسبة لقاعدة كود حقيقية، إلقاء كل شيء في نافذة سياق ضخمة هو عادةً التحرك الأسوأ — ليس لأن النافذة صغيرة جدًا، بل لأن النماذج تصبح أقل موثوقية بشكل قابل للقياس كلما ملأتها، والكود هو أسوأ مدخل لهذا الفشل. الاسترجاع المستهدف — grep، فهرسة الرموز، القراءة الواعية بالتبعيات — يفوز في التكلفة والتأخير والدقة في معظم مهام الكود. يفوز السياق الطويل في حالات محددة قليلة، وسأعطيك القاعدة في النهاية.
رقم النافذة حقيقي؛ الجزء القابل للاستخدام ليس كذلك
أولًا، الجزء الصحيح: الأرقام الكبيرة موجودة. اعتبارًا من يونيو 2026، توفر النماذج الثلاثة الرائدة في البرمجة — Claude Opus 4.8 وGPT-5.5 وGemini 3.1 Pro — جميعها نافذة بمليون رمز، وأكثر من ثلاثة عشر نموذجًا حدوديًا مستضافًا يُعلن عن مليون رمز أو أكثر (مقارنة نوافذ السياق من Morph). بل إن Anthropic وOpenAI ألغتا رسوم السياق الطويل الإضافية، إذ تُحتسب الفاتورة الآن بنفس السعر لكل رمز عبر النافذة كاملة.
المشكلة أن المُعلَن والقابل للاستخدام رقمان مختلفان. تستمر اختبارات الاستدعاء المستقلة في 2026 في إظهار أن الطاقة الفعلية تقع أقل بكثير من الملصق — تضع إحدى الدراسات الشاملة السياق الفعلي عند نحو 50–65% من الاسمي، وتُبلّغ عن درجات MRCR متعددة الإبر تنهار في النصف الخلفي من النافذة: تحتفظ Gemini 3.1 Pro بنحو 84.9% استدعاء في النطاق 128K–256K وتنهار إلى 26.3% في نطاق 512K–1M (تحليل "خدعة نافذة السياق" من CodingFleet). تعامل مع النسب المئوية الدقيقة باعتبارها دليلًا اتجاهيًا — فهي أرقام معايير من الموردين لا دراسة خاضعة للضبط — لكن الشكل متسق في كل مكان: يتراجع الاستدعاء قبل اكتمال ملء النافذة.
لهذا التراجع اسم ومسار بحثي، وهو أكثر أهمية للكود منه لأي شيء آخر تقريبًا.
لماذا الكود هو أسوأ مدخل لنافذة ممتلئة
تعمل تأثيران موثقان جيدًا ضدك حين تحشر مستودعًا في موجه واحد.
ضائع في المنتصف. أظهرت ورقة Liu et al. (المنشورة في TACL) أن دقة النموذج تمثل دالة على شكل U تبعًا لـمكان المعلومات ذات الصلة: أعلى ما تكون حين تقع في بداية السياق أو نهايته، وتتراجع بأكثر من 30% حين تكون مدفونة في المنتصف — وقد صمد هذا عبر ست عائلات نماذج في الإجابة على أسئلة متعددة المستندات واسترجاع القيم المفتاحية. قاعدة الكود ليست سوى "معلومات ذات صلة في المنتصف". نادرًا ما تكون الدالة التي تحتاجها هي الملف الأول أو الأخير الذي لصقته.
تعفن السياق. اختبرت دراسة Chroma لعام 2025 ثمانية عشر نموذجًا حدوديًا — Claude Opus 4 / Sonnet 4 / 3.7 / 3.5 / Haiku 3.5، وOpenAI o3 وعائلة GPT-4.1، وGemini 2.5 Pro/Flash، وثلاثة أحجام من Qwen3 — ووجدت أن الأداء يتراجع مع نمو طول المدخل حتى في المهام التافهة كالعثور على سلسلة نصية أو تكرار كلمات متكررة (تقرير "تعفن السياق" من Chroma). ما ينبغي أن يُزعج كل مطور عميل كود: في مهمة LongMemEval، أدّى كل نموذج أداءً أسوأ مع السجل الكامل البالغ ~113 ألف رمز مقارنةً بمقتطف مركّز من ~300 رمز من نفس المحتوى ذي الصلة. رموز أكثر من المادة الصحيحة، مقدَّمة مجمّعة، حصلت على درجات أدنى من شريحة صغيرة منتقاة.
ثمة تفصيل في ذلك التقرير مثير للضحك تقريبًا لمن يكتب كودًا: سجّل النموذج أداءً أسوأ حين كانت كومة القش "تحافظ على تدفق منطقي للأفكار" مقارنةً بحالة اختلاطها. المستودع هو الأكثر تنظيمًا منطقيًا — استيرادات، ورسوم بيانية للاستدعاء، وتعريفات للأنواع، ووحدات مرتبة. أنت تعطي النموذج بالضبط شكل المدخل الذي يتعامل معه بأسوأ صورة.
والكود يعاقب الفشل بقسوة أشد من النثر. إن أخطأ برنامج الدردشة جملةً واحدة في مستند طويل، أعطى إجابة أقل دقة بعض الشيء. أما إذا أخطأ عميل الكود تعريف refreshToken الحقيقي، فسيكتب كودًا يستدعي دالة غير موجودة أو يُصلح الدالة الخاطئة — وبثقة تامة. كما أشار أحد تحليلات استرجاع العملاء: "نمط فشل RAG في سياقات البرمجة صامت ومتراكم" (MindStudio). الاسترجاع الخاطئ لا يُعطي خطأً. يُطرح في الإنتاج.
ما يعنيه "الاسترجاع" للكود (في معظمه ليس embeddings)
حين يسمع الناس "استرجاع" يتخيلون قاعدة بيانات متجهية. بالنسبة للكود، العملاء الذين يُشحَن فعلًا — Claude Code وCursor وDevin — في معظم الأحيان لا يلجؤون إليها أولًا. يتنقلون كما يفعل مطور متمرس: النظر في شجرة الملفات، البحث بـ grep عن رمز، قراءة الملفات المحددة المطابقة، تتبع سلسلة الاستيرادات (MindStudio). الأداة الأساسية للبحث في Claude Code هي ripgrep — مطابقة نصية دقيقة وسريعة مرقّمة بالأسطر تعمل على أي مستودع دون أي معالجة مسبقة.
ثمة سبب وجيه لعدم كون المتجهات الخيار الافتراضي للكود، ولا علاقة له بالحنين. إطار مفيد هو ثلاث طبقات، يُصعَّد فقط عند الحاجة (البحث في الكود لعملاء الذكاء الاصطناعي):
- المعجمية (ripgrep): مطابقة دقيقة، ميلي ثانية، واعية بـ gitignore. خيارك الافتراضي.
- الهيكلية (ast-grep): مطابقة أنماط AST — "ابحث عن كل دالة async"، "كل catch-and-rethrow" — أشياء لا يمكن لـ regex التعبير عنها بوضوح. صعّد هنا حين يكون الاستعلام عن الشكل لا النصوص.
- الدلالية (embeddings / repo-map): استعلامات مفاهيمية بالغة الطبيعية. الملاذ الأخير.
لماذا الدلالية هي الملاذ الأخير لا الأول؟ لأن الاستعلامات التي يولّدها عميل الكود فعليًا قصيرة وتأخذ شكل الرموز — auth flow، user service، refreshToken — وهذا بالضبط التنسيق الذي يكسر البحث بـ embeddings. وجد معيار CoREB أن الاستعلامات القصيرة بالكلمات المفتاحية "تُنهار تقريبًا كل نموذج دلالي جُرِّب إلى nDCG@10 قريب من الصفر". الـ embeddings تلتقط تشابه المعنى؛ الكود رسم بياني من التبعيات والاستيرادات وتعريفات الأنواع، والتشابه ليس العلاقة التي تستعلم عنها. دالتان قد تكونان متطابقتين دلاليًا وغير مترابطتين هيكليًا؛ التي تحتاجها هي التي تقع على مسار الاستدعاء، لا التي تتشابه في القراءة.
هذا لا يجعل الفهرسة الدلالية عديمة الفائدة — بل يجعلها متخصصة. حين تثبت جدارتها، التطبيقات الجيدة لا تُضمِّن نوافذ نصية اعتباطية. تُحلِّل بـ tree-sitter إلى وحدات حقيقية — دوال وفئات وواجهات مع docstrings سليمة — تحافظ على تكامل تلك القطع، وتقرن تشابه المتجهات بمطابقة كلمات BM25 مضافًا إليها أدوات رسم بياني للاستدعاء لتتبع التبعيات وتحليل نطاق التأثير (opencode-codebase-index). هذه هي اللعبة كاملة لاسترجاع الكود: تقطيع واعٍ بالتبعيات عند حدود الرموز مع احتياطي الكلمات المفتاحية — لا "قسّم الملف كل 512 رمزًا وتفاءل".
حسابات التكلفة والتأخير التي لا يجريها أحد
ضع الدقة جانبًا لحظة واحسب الفاتورة فقط. لنفترض أن مستودعًا متوسطًا يبلغ 600 ألف رمز. تفريغه في دورة واحدة يعني الدفع مقابل 600 ألف رمز إدخال في كل دورة من المحادثة ما لم تكن تخزّن مؤقتًا بعناية — وحتى مع التخزين المؤقت، تتحمل تأخير النموذج وهو يوجّه انتباهه عليها كلها. مرور الاسترجاع بدلًا من ذلك يرسل الشجرة (كيلوبايتات قليلة)، ونتيجة grep (بضع مئات من الرموز)، وربما أربعة ملفات تقرأها فعليًا (~15 ألف رمز). هذا ليس فارق تقريب؛ بل هو فارق من رتبة إلى رتبتين من حيث الحجم لكل دورة، مضروبًا في كل دورة في جلسة تصحيح أخطاء.
التأخير يتبع نفس المنحنى. نموذج يستدل على 600 ألف رمز أبطأ في الرمز الأول وإجمالًا من النموذج ذاته يستدل على 18 ألف. في حلقة تفاعلية تجري فيها عشرين دورة لتتبع خطأ، يتراكم ذلك في دقائق من الانتظار الضائع. الاسترجاع يُبقي كل دورة صغيرة، فكل دورة سريعة، فتبقى الحلقة محكمة.
وثمة تكلفة أكثر خفاءً: ميزانية الانتباه. كل ملف غير ذي صلة في النافذة هو عامل تشتيت، وأثبتت Chroma أن عوامل التشتيت تُنزل الدقة بشكل قابل للقياس — عامل تشتيت واحد أضرّ، وأربعة ضاعفت الضرر. مستودع كامل في السياق يمثل آلاف عوامل التشتيت لأي سؤال بعينه. أنت لا تدفع أكثر لتكون أبطأ فحسب؛ بل تدفع أكثر لتكون أبطأ وأقل دقة في الوقت ذاته.
متى يفوز السياق الطويل فعلًا
هذا لا يعني "الاسترجاع دائمًا". ثمة نطاق حقيقي حيث تفريغ كل شيء هو الخيار الصحيح، وهو محدد بحجم المستودع وشكل المهمة، لا بحجم النافذة التي تدفع مقابلها.
يفوز السياق الطويل حين:
- يتسع المستودع بأكمله بهامش مريح — لنقل أقل من ~150 ألف رمز، في عمق الجبهة عالية الاستدعاء للنافذة — وتتطلب المهمة استدلالًا متقاطعًا بين الملفات يصعب استرجاعه قطعة قطعة. المستودع الصغير حيث كل شيء مهم هو بالضبط الحالة التي يُعقّد فيها الاسترجاع الأمور.
- تحتاج إلى استدلال على ملف كامل أو وحدة كاملة دفعة واحدة — ملف كبير واحد تريد إعادة هيكلته بالكامل، قراءة أمنية من البداية للنهاية لوحدة واحدة، مراجعة معمارية يحتاج فيها النموذج للإمساك بالعلاقات دفعة واحدة. الاسترجاع يُجزّء ذلك؛ السياق يحفظه.
- هو مرور واحد للقراءة فقط، لا حلقة متكررة. استدعاء واحد من نوع "راجع هذا الـ PR" أو "لخّص كيف تعمل هذه الخدمة" يُوزّع التكلفة على دورة واحدة. الاقتصاد الذي يقتل السياق الطويل — الدفع مقابل النافذة كاملة في كل دورة — لا يلدغ حين لا توجد سوى دورة واحدة.
يفوز الاسترجاع في كل ما عدا ذلك، وهو في الواقع معظم العمل: أي مستودع أكبر من أن يتسع بارتياح، وأي شيء متكرر، وأي شيء تحتاج فيه إلى تعريف محدد بدلًا من فهم عام، وأي شيء يُفضي فيه استرجاع خاطئ صامت إلى شحن كود مكسور.
قاعدة تقريبية أستخدمها فعليًا: إن كانت الشريحة ذات الصلة بالمهمة أصغر من المستودع، استرجع. وإن كانت المهمة هي المستودع كله والمستودع يتسع في أول ~15% من النافذة، حمّله. في معظم الأيام، الشريحة أصغر من المستودع.
الخلاصة
نافذة مليون رمز قدرة حقيقية، ويستحق استغلال تسعيرها الخالي من الرسوم الإضافية في المهام التي تُبدع فيها — مراجعات دورة واحدة للكود الذي يتسع، استدلال على وحدة كاملة، المستودع الصغير العرضي حيث كل شيء ذو صلة. لكنها ليست بديلًا عن معرفة أين يعيش كودك. بالنسبة لعميل كود فعّال، المجموعة المملة — سرد الشجرة، grep للرمز، قراءة الملفات الأربعة المطابقة، تتبع الاستيرادات، الصعود إلى AST أو embeddings فقط حين تعود نتيجة البحث بالكلمات المفتاحية فارغة — أرخص وأسرع وأدق من المثيرة للإعجاب. الرقم الكبير في صفحة المواصفات خيار، لا تعليمة. في معظم الأوقات، لا تملأه.
