البحث المدمَج متعدد الأنماط: اختيار المسترجِع الصحيح لكل استعلام
> استعلام مثل "أين يُعرَّف parseConfig" يحتاج إلى بحث مختلف عن "كيف تعمل المصادقة". يصنِّف Maguyva النية، ويُرجِّح أربعة أنماط استرجاع تبعًا لذلك، ثم يدمج النتائج عبر Reciprocal Rank Fusion الموزون.
استعلام البحث ليس شيئًا واحدًا.
“أين يُعرَّف parseConfig” يريد رمزًا دقيقًا — موقعًا واحدًا محدَّدًا، بسرعة. “كيف تعمل المصادقة” يريد المعنى — انتشار الكود ذي الصلة الذي يشرح مفهومًا. “ما الذي ينكسر إذا غيّرت هذه الدالة” يريد الرسم البياني للتبعيات. “ابحث عن السلسلة ECONNREFUSED” يريد مطابقة حرفية، لا شيء ذكيًا.
grep ممتاز للمطابقات الحرفية ومفيد لبعض عمليات البحث عن المراجع، لكنه ليس رسمًا بيانيًا للتبعيات ولا يفهم المعنى. تُغطِّي التضمينات الجانب الدلالي، لكنها الأداة الخطأ للسلاسل الدقيقة وتحليل التأثير. تختار معظم أدوات بحث الكود محركًا واحدًا وتُلزم كل استعلام بذلك الاختيار. Maguyva لا يختار. إنه يستنتج نوع السؤال الذي طرحته، ثم يمزج أربعة مسترجِعات بالنسبة التي يستحقها ذلك السؤال.
أربعة أنماط
تحت الغطاء، توجد أربع طرق مستقلة لإيجاد الكود:
- الدلالي (semantic) — بحث متجهي عبر تضمينات Voyage الثنائية (binary)؛ يجد الكود بالمعنى.
- النصي (text) — مطابقة ثلاثية الحروف (trigram)؛ يجد السلاسل الحرفية، وسلاسل الأخطاء، والمعرِّفات الدقيقة.
- البنيوي (structural) — استعلامات AST؛ يجد التعريفات، والتوقيعات، وبُنى اللغة.
- الرسم البياني (graph) — رسم بياني للتبعيات؛ يجد المُستدعِين، والمُستدعَين، ونطاق التأثير.
كل واحد قوي في فئة مختلفة من الأسئلة. الحيلة هي تحديد مقدار الثقة التي تُمنَح لكل واحد بالنسبة إلى الاستعلام الذي أمامك.
تصنيف النية
قبل تشغيل أي استرجاع، يُصنِّف مُصنِّف خفيف الوزن الاستعلام إلى واحدة من ست نوايا، بدرجة ثقة. إنه رخيص عمدًا — أساليب استدلال مرتَّبة، وتفوز أول مطابقة — لأنه يعمل على المسار الساخن ولا يضيف سوى ميلي ثانية أو اثنتين:
- يبدأ بـ
def،class،func،import… → find_definition (ثقة 0.95) - “من يستدعي”، “استخدامات لـ”، “مراجع إلى” → find_references (0.90)
- “التأثير”، “نطاق التأثير”، “ما الذي يعتمد على” → impact_analysis (0.90)
"string"مقتبَس أو رمز خطأ مثلtraceback→ exact_match (0.85–0.90)- معرِّف
CamelCaseأوsnake_case→ find_definition (0.60–0.80) - “كيف”، “لماذا”، “اشرح”، “البنية المعمارية” → understand_code (0.75)
- لا شيء يطابق → understand_code، ثقة منخفضة (0.40)
تحمل كل نية ملف ترجيح عبر الأنماط الأربعة. هذه هي الأرقام الحقيقية:
| النية | الدلالي | النصي | البنيوي (AST) | الرسم البياني |
|---|---|---|---|---|
| find_definition | 0.2 | 0.1 | 0.6 | 0.1 |
| find_references | 0.1 | 0.2 | 0.2 | 0.5 |
| understand_code | 0.5 | 0.2 | 0.2 | 0.1 |
| find_similar | 0.4 | 0.3 | 0.2 | 0.1 |
| impact_analysis | 0.1 | 0.1 | 0.1 | 0.7 |
| exact_match | 0.0 | 0.9 | 0.1 | 0.0 |
إذًا “أين يُعرَّف parseConfig” يعتمد بشدة على AST (0.6). “كيف تعمل المصادقة” يعتمد على المتجهات الدلالية (0.5). “ما الذي يعتمد على هذا” هو تقريبًا كله رسم بياني (0.7). “ابحث عن ECONNREFUSED” هو تقريبًا كله مطابقة ثلاثية الحروف (0.9)، مع إيقاف نموذج التضمين تمامًا — لأن التشابه الدلالي هو بالضبط الأداة الخطأ لسلسلة حرفية دقيقة.
المسار السريع، والمسار المدمَج
عندما يكون المصنِّف واثقًا — درجة ≥ 0.85 — ويكون الاستعلام عاديًا، يتخطى Maguyva الدمج تمامًا ويوجِّه مباشرة إلى النمط المهيمِن الواحد. “أين يُعرَّف X” لا يحتاج إلى أربعة مسترجِعات؛ إنه يحتاج إلى فهرس AST، الآن. يُبلَّغ عن هذا المسار المباشر باسم fusion_strategy: "direct".
يمر كل ما هو غامض عبر الدمج. تعمل الأنماط الأربعة (أو الثلاثة، في الإعداد الافتراضي) بالتوازي، ويُعيد كل واحد قائمته المُرتَّبة الخاصة، وندمجها معًا.
دمج الرتب التبادلي الموزون (Weighted Reciprocal Rank Fusion)
دمج مسترجِعات متغايرة أصعب مما يبدو: تشابه جيب تمام بقيمة 0.82، ودرجة ثلاثية حروف بقيمة 137، ومركزية رسم بياني بقيمة 0.004 ليست على المقياس نفسه، لذا لا يمكنك ببساطة جمعها. يتفادى دمج الرتب التبادلي (RRF) المشكلة بتجاهل الدرجات الخام والاحتفاظ فقط بـالرتبة التي منحها كل محرك. مساهمة نتيجة من نمط واحد هي:
contribution = weight × 1 / (k + rank + 1)
حيث rank هي موضعها في قائمة ذلك النمط وk ثابت تنعيم. تُجمَع المساهمات عبر الأنماط لأي نتيجة وجدها أكثر من محرك — يطفو الاتفاق بين المسترجِعات طبيعيًا إلى القمة. نستخدم k = 40 في الإعداد الافتراضي و60 في thorough (quick يعمل دلاليًا فقط، لذا لا يتدخل الدمج هناك أبدًا). استقر عمل RRF الأصلي على k = 60 للاسترجاع العام؛ نحن نفترض قيمة أحدّ قليلًا افتراضيًا، مما يمنح الاتفاق الأعلى رتبة بين الأنماط وزنًا أكبر قليلًا — ولا نوصي بضبطها يدويًا.
فوق ذلك، تحمل النتائج تعزيزًا لأهمية الرسم البياني. يجب أن تتفوق عقدة محورية — دالة تعتمد عليها قاعدة الكود بأكملها — على ورقة غامضة حتى عند التساوي في الأهمية النصية، لذا نضرب كل مساهمة في:
boost = min(1 + 0.3 × ln(1 + centrality), 1.5)
تأتي المركزية من مقاييس PageRank/الدرجة المحسوبة مسبقًا لخط الأنابيب، ويُقيَّد التعزيز عند 1.5× كي لا تستطيع دالة شائعة أن تدفن تمامًا دالة غامضة أكثر صلة. أخيرًا، نُخفِّض رتبة النتائج من مسارات البائعين (vendor)، والبناء (build)، والأرشيف، ونزيل التكرار إلى أفضل جزء لكل ملف.
ما لا يزال غير كامل
مُصنِّف النية هو كومة من التعبيرات النمطية، لا نموذجًا مُتعلَّمًا. يغطي الأشكال الشائعة للاستعلام جيدًا — سجَّل القرار الذي أدخله انخفاض معدل النتائج الصفرية من نحو 15% إلى أقل من 5% — لكنه استدلالي، واستعلام غامض فعليًا يسقط إلى understand_code ومزيج يميل دلاليًا. ذلك افتراضي آمن، لا ذكي. لم نستبدله بمصنِّف مُدرَّب لأن النسخة الرخيصة سريعة وكافية، ولأن مصنِّفًا واثقًا لكن مخطئًا أسوأ من احتياطي صادق. الأوزان نفسها هي أولويات مُختارة يدويًا، لا مُتعلَّمة من بيانات نقر لا نجمعها.
اطرح السؤال، لا الأداة
لا ينبغي أن يحتاج الوكيل إلى معرفة ما إذا كان يلجأ إلى grep أو التضمينات أو رسم الاستدعاء البياني — بل ينبغي أن يطرح سؤاله بعبارات بسيطة ويحصل على الإجابة الصحيحة. الدمج متعدد الأنماط هو ما يتيح لـfind_symbol، والبحث الدلالي، وتحليل التبعيات أن تجلس خلف سطح استعلام واحد: يقرأ النظام شكل السؤال ويُجمِّع بصمت المسترجِع الصحيح له. النموذج الذي يُقيِّم التضمينات مهم، لكن معرفة متى لا تُستخدَم مهمة أيضًا. اختيار الأداة الصحيحة لكل استعلام نوع خاص به من الجودة، وهو نوع نُفضِّل تولّيه بأنفسنا بدلًا من تحميله على المُستدعِي.
// you bring the question. it brings the tools.
قراءات ذات صلة
المزيد من سجل بناء Maguyva
لماذا رقّينا بحث الكود إلى voyage-4-large_
انتقلنا بتضميناتنا للكود إلى voyage-4-large — الذي يتصدر حاليًا لوحة صدارة RTEB العامة لاسترجاع الكود. النسخة الصادقة: المقايضة التي نقبلها، وما نُفهرِسه فعليًا، ولماذا ندفع مقابل تضمينات متميزة.
التحسين الذاتي المتكرر للغات: صقل ذكاء الكود عبر نحو 280 لغة_
ندعم ذكاء الكود لنحو 280 لغة. لا يستطيع أي إنسان تدقيق ذلك يدويًا. لذا بنينا حلقة تحسين ذاتي متكرر للغات — فحص عيّني، وحكَم LLM، وإصلاح شيء واحد، وإعادة تحقق — ونُشغِّلها بأسطول من الوكلاء المعزولين حتى يصبح الاستخلاص صحيحًا فعلًا، لا مجرد أخضر (green).
مراقبة الوكلاء (Observability): الخطافات (Hooks) وAlloy وGrafana_
ربطنا Claude Code وCodex بمكدس Grafana واحد باستخدام OpenTelemetry وAlloy، ثم استخدمنا التتبعات (traces) والسجلات لإيجاد مشكلات سلوك الوكلاء وإصلاحها من مصدرها.