المنتج العربي الذي يبنيه فريق كفء يفشل مع ذلك بطرق لا يعرفها نظيره الإنجليزي. مستخدم يبحث عن الإسلام فلا يجد شيئاً، والوثيقة أمامه مكتوب فيها الاسلام. خط أنابيب RAG لا يسترجع أي مقطع لسؤال إجابته في الفقرة الثانية. فاتورة النموذج للمستأجر العربي أعلى بـ40% من الإنجليزي على الحركة نفسها. واستعلام WHERE name = ? يخفق على اسم كتبه صاحبه كتابة صحيحة.
تبدو أربع علل منفصلة. وهي علة واحدة، تسكن طبقة لا يكتبها أغلب الفرق: تطبيع النص.
المشكلة: كلمة واحدة، تسلسلات بايتات كثيرة
الإملاء العربي يجعل عدة نقاط يونيكود مختلفة تُعرَض متطابقة بصرياً أو شبه متطابقة، والكاتب العربي يتبادلها بحرية. كلمة «الإسلام» تُكتب بالهمزة تحت الألف (U+0625) وتُكتب بألف مجردة (U+0627). الصورتان صحيحتان، والاثنتان شائعتان. وفي قاعدة البيانات هما سلسلتان مختلفتان.
أبرز المصادر:
| النوع | نقاط يونيكود | المشكلة |
|---|---|---|
| صور الألف | ا أ إ آ ٱ (U+0627, 0623, 0625, 0622, 0671) | يتبادلها الكتّاب عملياً |
| التاء المربوطة والهاء | ة وه (U+0629, 0647) | تُكتبان متطابقتين كثيراً، خاصة في الخليج |
| الألف المقصورة والياء | ى وي (U+0649, 064A) | لوحات المفاتيح المصرية تبدّلهما باستمرار |
| الحركات (التشكيل) | U+064B–U+0652، U+0670 | وزن غير مرئي؛ موجود في مصادر وغائب في أخرى |
| التطويل (الكشيدة) | ـ (U+0640) | زخرفة محضة؛ مـــرحبا تساوي مرحبا |
| الأرقام العربية-الهندية | ٠١٢٣ (U+0660–0669)، ۰۱۲۳ (U+06F0–06F9) | مجموعتا أرقام كاملتان، إضافة إلى الغربية |
| أشكال العرض | U+FB50–FDFF، U+FE70–FEFF | مخرجات PDF وOCR القديمة؛ تُعرَض صحيحة وتُقارَن خاطئة |
| محارف تحكم خفية | ZWJ، ZWNJ، RLM، LRM، عوازل الاتجاه | مخلّفات النسخ واللصق لا يراها أحد |
الصفّان الأخيران هما سبب جلسة التنقيح في الثالثة فجراً. النص المستخرَج من PDF يصل غالباً بصيغة أشكال العرض العربية-ب، حيث يُرمَّز كل محرف بشكله الموضعي لا بحرفه الأصلي. يُعرَض على الشاشة سليماً، ويقارَن بكل شيء فيخرج مختلفاً.
خط أنابيب التطبيع
الحل دالة خالصة تُطبَّق في موضعين بالضبط: عند الفهرسة، وعند الاستعلام. وإن اختلف الموضعان يوماً عدت إلى نقطة الصفر.
والترتيب مهم. هذا هو التسلسل الذي استقرت عليه أبحاث المجال، والذي يستعمله AraToken، وهو بحث 2026 في تجزئة العربية لنموذج Qwen3:
const HARAKAT = /[\u064B-\u065F\u0670]/g; // من التنوين إلى الألف الخنجرية
const TATWEEL = /\u0640/g; // الكشيدة
const INVISIBLE = /[\u200B-\u200F\u202A-\u202E\u2066-\u2069]/g;
const LETTER_MAP: Record<string, string> = {
'أ': 'ا', 'إ': 'ا', 'آ': 'ا', 'ٱ': 'ا', // صور الألف
'ى': 'ي', // الألف المقصورة
'ة': 'ه', // التاء المربوطة
'ؤ': 'و', 'ئ': 'ي', // كراسي الهمزة
};
export function normalizeArabic(input: string): string {
return input
.normalize('NFKC') // 1. ردّ أشكال العرض إلى الحروف الأصلية
.replace(INVISIBLE, '') // 2. حذف محارف التحكم والاتجاه
.replace(TATWEEL, '') // 3. حذف الكشيدة
.replace(HARAKAT, '') // 4. حذف التشكيل
.replace(/[\u0623\u0625\u0622\u0671\u0649\u0629\u0624\u0626]/g, c => LETTER_MAP[c]) // 5. توحيد الحروف
.replace(/[\u0660-\u0669]/g, d => // 6. الأرقام العربية-الهندية
String.fromCharCode(d.charCodeAt(0) - 0x0660 + 48))
.replace(/[\u06F0-\u06F9]/g, d => // 7. الأرقام العربية-الهندية الممتدة
String.fromCharCode(d.charCodeAt(0) - 0x06F0 + 48))
.replace(/\s+/g, ' ')
.trim();
}الخطوة الأولى هي التي يتجاوزها الناس، وهي الأهم. استدعاء NFKC واحد يردّ كتلة أشكال العرض كاملة إلى الحروف الأصلية — وهذا وحده يصلح أكثر أعطاب PDF وOCR قبل أن تعمل قواعدك الخاصة أصلاً. وانتبه: NFKC مُفقِد للمعلومة بقصد، فهو تطبيع توافقي، وهو تماماً ما تريده لمفتاح بحث وما يجب ألا تطبّقه على نص تنوي عرضه.
عمودان، لا عمود واحد
القاعدة الذهبية: طبّع للمطابقة، ولا تطبّع أبداً على حساب الأصل المخزَّن.
ALTER TABLE documents
ADD COLUMN title_norm text
GENERATED ALWAYS AS (arabic_normalize(title)) STORED;
CREATE INDEX documents_title_norm_trgm
ON documents USING gin (title_norm gin_trgm_ops);المستخدم يرى title، والاستعلام يضرب title_norm. تشكيل الآية القرآنية، والهمزة التي اختارها الشاعر، وإملاء الوثيقة القانونية — كلها محفوظة، وكلها قابلة للبحث. أما إتلاف الأصل لكي يعمل البحث فمقايضة لم يطلبها منك أحد.
في PostgreSQL يمكن كتابة arabic_normalize مباشرة بـtranslate() وregexp_replace()، أو بامتداد Rust أو PL/Python. وفي Elasticsearch المقابل سلسلة محلّلات: icu_normalizer بنمط nfkc_cf، ثم arabic_normalization، ثم decimal_digit، ثم arabic_stem اختيارياً. المحلّل العربي المدمج في Elasticsearch يغطي جزءاً من هذا، لكنه لا يغطي ردّ أشكال العرض — فأضف icu_normalizer قبله.
لماذا يتوقف نجاح RAG على هذا
نماذج التضمين أرحب من المطابقة الحرفية، لكن ليس بما يكفي. فشل البحث الشعاعي في العربية ليس في الغالب مشكلة جودة نموذج، بل مشكلة تقطيع وتطبيع تظهر في هيئة مشكلة جودة نموذج.
ثلاثة أمور تنكسر تحديداً:
حدود المقاطع. التقطيع على . و? يفوّت ؟ (U+061F) و، (U+060C). كما أن النثر العربي يستعمل واو العطف بحرية تفوق كثيراً استعمال الإنجليزية لـ"and"، فتنتج القواعد التقريبية المضبوطة على الإنجليزية مقاطع شديدة التفاوت. قطّع على علامات الترقيم العربية صراحةً.
انزياح التضمين. الجملة نفسها بتشكيل وبدونه تنتج شعاعين مختلفين قياساً في أغلب النماذج متعددة اللغات. وإذا خلط متنك مصادر مشكولة وغير مشكولة — وهو شائع جداً حين تجمع النصوص التراثية مع المحتوى الشبكي الحديث — فأنت تضمّن لهجتين من اللغة نفسها في منطقتين مختلفتين من الفضاء. طبّع قبل التضمين، واستعمل التطبيع نفسه وقت الاستعلام.
عدم تناظر الاسترجاع الهجين. ساق BM25 في المسترجع الهجين مطابقة حرفية، ومن ثم مكشوفة بالكامل لكل ما سبق. تقضي الفرق أسابيع في ضبط الساق الكثيفة بينما تسهم الساق المتفرقة في العربية باسترجاع يقارب الصفر بصمت.
تشخيص نافع: خذ 50 استعلاماً تعرف أنها يجب أن تصيب، شغّلها خاماً ومطبَّعة، وقارن recall@10. إن حرّك التطبيع وحده هذا الرقم أكثر من بضع نقاط، فجودة الاسترجاع لم تكن يوماً عنق الزجاجة عندك.
زاوية كلفة التوكن
العربية مكلفة على واجهات نماذج اللغة، والسبب بنيوي. عبر المجزِّئات الأصلية للنماذج، تسير العربية بنحو 2.4 توكن للكلمة مقابل 1.5 إلى 1.6 للإنجليزية. ومُجزِّئ Qwen2.5 من نوع byte-level BPE يرمّز العربية بـ2.18 توكن للكلمة، أي نحو 1.4 ضعف معدله الإنجليزي. وتنتقل هذه النسبة إلى كل شيء: كلفة الموجّه، وزمن أول توكن، وحجم ذاكرة KV، وعدد الجلسات العربية المتزامنة التي تحتملها ميزانية معالجات ثابتة.
التطبيع لا يحلّ هذا، لكنه يستردّ شريحة حقيقية منه. يذكر عمل AraToken هبوط الخصوبة من 1.311 إلى 1.199 توكن للكلمة بوضع خط تطبيع أمام مُجزِّئ SentencePiece — انخفاض 8.5%، يتسع إلى نحو 18% بين أفضل التهيئات المختبَرة وأسوئها. والآلية بسيطة: كل تطويل شارد وحركة يتيمة ومحرف أشكال عرض هو توكن دفعت ثمنه ولم يكسب النموذج منه شيئاً.
عملياً، في نظام RAG يحشو 8 آلاف توكن من السياق العربي في كل طلب وعلى نطاق واسع، انخفاض 8% يعني 8% من أكبر بند في الفاتورة، دون تغيير النموذج ودون خسارة في الجودة.
ملاحظتان عمليتان. أولاً، طبّع السياق واحترس مع موجّه المستخدم نفسه — فإن كتب التشكيل عن قصد، فحذفه يغيّر ما سأل عنه. ثانياً، إن كنت تُدرّب أو تواصل التدريب المسبق على العربية، فطبّع متن التدريب ومسار الاستدلال بالدالة نفسها، وإلا درّبت على توزيع وخدمت آخر.
ما تفعله هذا الأسبوع فعلاً
- اكتب دالة
normalizeArabicواحدة. ملف واحد، بلا اعتماديات، مُصدَّر من حزمة مشتركة. كل خدمة تستورد الدالة نفسها. هذه هي البنية كلها. - أضف عموداً أو حقلاً مطبَّعاً بجانب كل حقل نصي عربي تبحث فيه: عمود مولَّد في Postgres، أو حقل فرعي في Elasticsearch. وافهرس ذاك.
- طبّع الاستعلام بالدالة ذاتها. أشيع خلل إنتاجي في البحث العربي هو تطبيع الفهرس ونسيان الاستعلام.
- اكتب جدول الاختبار.
الإسلامتطابقالاسلام.مـــرحباتطابقمرحبا.مُحَمَّدتطابقمحمد.فاطمهتطابقفاطمة.٢٠٢٦تطابق2026. ومدخل بأشكال العرض يطابق مدخلاً بالحروف الأصلية. ستة تأكيدات تلتقط كل شيء تقريباً. - قِس الاسترجاع قبل وبعد على استعلامات حقيقية. تحتاج الرقم لتبرير العمل، وهو عادة أكبر مما يتوقع أي أحد.
لا شيء من هذا بحثٌ علمي. إنه مئة سطر من الشيفرة غير اللامعة لم تكتبها ببساطة أغلب المنتجات الموجَّهة للعربية — ولهذا تُقرأ عبارة «البحث العربي سيّئ» كأنها حقيقة في اللغة، لا ما هي عليه فعلاً: طبقة لم تُنفَّذ.
وإن كنت تبني منتجات عربية أولاً، فالمواد المجاورة تستحق القراءة: لماذا يتغيّر شكل الحرف العربي يشرح قاعدة الاتصال التي تقف خلف أشكال العرض، والحركات والتشكيل يغطي ما تحذفه ومتى يجب ألا تحذفه، وأخطاء المواقع العربية RTL يعالج الجانب العرضي من المشكلة نفسها.