هندسة
تشغيل Tesseract.js في الإنتاج
جعل Tesseract.js يتعرف على كلمة يستغرق عشر دقائق. أما تشغيله في الإنتاج لسنوات فيتطلب فهم ما ينزّله ومتى ومن أين — فهناك تحديدًا يقع العطب.
EasyInvoiceOCR · نُشر في · 10 دقائق قراءة
Tesseract.js غلاف بلغة JavaScript حول محرك التعرف الضوئي مفتوح المصدر Tesseract، مصرَّف إلى WebAssembly ليعمل داخل المتصفح. وهو مشروع مجتمعي مستقل: نستخدمه ونساهم في متتبّع مشكلاته، ولا تربطنا به صلة أخرى.
وما يجعله مثيرًا للاهتمام في الإنتاج ليس واجهة التعرف، فهي صغيرة، بل قصة الموارد التي تقوم تحتها.
ما الذي يُنزَّل فعلًا
تجلب عملية التعرف ثلاثة أشياء منفصلة، والخلط بينها سبب معظم مشكلات النشر.
- سكربت العامل — شيفرة JavaScript التي تنفّذ التعرف خارج الخيط الرئيسي.
- نواة WASM — المحرك المصرَّف. توجد منه صيغ متعددة (عادية، SIMD، SIMD مرن، ونسخ LSTM من كل منها) ويحصل المتصفح على أسرع ما يستطيع تشغيله.
- بيانات اللغة — ملف .traineddata لكل لغة، وهي المكوّن الأكبر حجمًا بفارق واسع.
ملفات اللغة هي الجزء المكلف
كل لغة ملف مستقل بحجم عدة ميغابايت. مجموع نماذجنا الخمسة الأساسية 32.75 ميغابايت: الإنجليزية 10.42، والإسبانية 7.98، والألمانية 6.77، والفرنسية 5.99، والعربية 1.60. وبإضافة نوى WASM يصل المجموع المستضاف إلى 76.00 ميغابايت.
يثير هذا الرقم القلق حتى تُذكر ما ليس هو: لا شيء منه ضمن حزمة الصفحة الأولية. يُجلب النموذج مرة واحدة عند الطلب، حين يشغّل أحدهم التعرف بتلك اللغة فعلًا، ثم يُخزَّن مؤقتًا. ومن يدمج ملفَّي PDF فقط لا ينزّل منه شيئًا.
الأوضاع المركّبة ليست نماذج إضافية
يقبل Tesseract وسيط لغة مثل eng+ara، فيحمّل نموذجين في تمريرة واحدة لقراءة صفحة ثنائية اللغة دون اختيار طرف. ويجدر ضبط الحساب: سبعة خيارات في قائمة قد تعني خمسة ملفات. نحن نوفّر خمس لغات أساسية ووضعين مركّبين، لا سبعة نماذج مستقلة.
الافتراضي شبكة توصيل خارجية، ولذلك تبعات
يجلب Tesseract.js افتراضيًا بيانات اللغة من شبكة توصيل عامة أثناء التشغيل. هذا مريح ويعمل — إلى أن يتوقف. فطلب متعثّر يترك عملية التحويل معلّقة في منتصفها، ولأن الطلب المتعثّر لا يمرّ بخوادمك أصلًا، يبقى العطل غير مرئي لمراقبتك ومرئيًا تمامًا لمستخدميك.
وثمة مشكلة ثانية أهدأ: المسار الافتراضي غير مثبّت على إصدار منشور، ولا يُتحقق من سلامة الملف المنزَّل، فقد تتغير البايتات التي تُغذّى للمحرك دون أن يلاحظ أحد. وهذه مناقشة مفتوحة داخل المشروع لا انتقادًا له، فالتوتر نفسه قائم في أي جلب مورد أثناء التشغيل.
استضافة الموارد ذاتيًا
صرنا نقدّم العامل والنوى وملفات اللغة من نطاقنا نفسه. يَنسخ سكربت إعداد العاملَ والنوى من node_modules — فتطابق دائمًا النسخة المثبتة بدل أن تنحرف عمّا تقدّمه شبكة خارجية في لحظة ما — ويُنزّل بيانات اللغة مرة واحدة وقت البناء، لا وقت التشغيل.
هذا يزيل الاعتماد على طرف ثالث من مسار التعرف تمامًا. ويزيل أيضًا تسربًا أدق: جلب المحرك من شبكة خارجية يعني أن ذلك الطرف يرى طلبًا، مع مرجع الصفحة، كلما بدأ أحدهم تحويلًا. لا محتوى مستند، لكن نمط طلبات يدل على النية.
الخلل الذي تُدخله الاستضافة الذاتية
هذا هو الجزء الجدير بالنقل، لأننا تعلمناه بثمن. حين تستضيف النماذج بنفسك، تصبح قائمة اللغات التي تعرضها واجهتك وقائمة ما ينزّله سكربت البناء قائمتين منفصلتين، في ملفين مختلفين، دون ما يضمن تطابقهما.
وقد انحرفت قائمتانا. كانت لغتان قابلتين للاختيار في القائمة بينما لم تُضف ملفاتهما إلى خطوة التنزيل قط. ولأن المسار يشير إلى نطاقنا نفسه لم يكن هناك أي شبكة خارجية للرجوع إليها: عاد الطلب بخطأ 404 وأخفق التعرف لهاتين اللغتين وحدهما. ولم تلتقط ذلك أي اختبار، لأن أي اختبار لم يكن يربط القائمتين.
ولم يكن الإصلاح الحقيقي إضافة الملفين، بل اختبار ثابت يفشل في الاتجاهين — لغة معروضة غير مستضافة، ونموذج مستضاف لم يعد معروضًا — ويقرأ سكربت البناء كنص مصدري، فلا يحتاج شبكة ولا موارد منزَّلة.
نصائح عملية
أربعة أمور كنا نتمنى لو قالها لنا أحد قبل أن نبدأ.
- ثبّت إصدارًا صريحًا لبيانات اللغة بدل الاعتماد على مسار يشير إلى الأحدث.
- إن استضفت ذاتيًا، أضف اختبارًا يتحقق أن كل لغة معروضة تقابل ملفًا موجودًا.
- راقب تبويب الشبكة عند بدء العامل. النموذج المفقود يبدو كأن «هذه اللغة لا تعمل»، لا كملف ناقص.
- لا تستخدم قائمة حروف مسموحة مع محرك LSTM، ولا تطلب وضع المحرك القديم مع بيانات لا تتضمنه. كلاهما يُضعف النتيجة بينما يبدو ضبطًا دقيقًا.
ما لا يغيّره ذلك
تغيّر الاستضافة الذاتية مصدر البايتات لا ما يفعله التعرف: نستخدم مجموعة النماذج القياسية، مطابقة بايتًا ببايت لما يجلبه Tesseract.js افتراضيًا، عن قصد، لأن تغيير الاستضافة مع تغيير صامت في المخرجات كابوس في التنقيح لاحقًا.
كما أنها لا تجعل التطبيق خاليًا من الشبكة. تُعالَج بيانات المستند محليًا داخل المتصفح ولا تُرفع أبدًا، ويبقى سجل موجز — اسم الملف ونوعه وحجمه وعدد الصفحات ومفتاح يعرّف المحاولة — يُرسل ليتمكن الخادم من تطبيق الرصيد.
المصادر الأساسية
- Tesseract.js — المشروع نفسه، بما فيه خيارات العامل التي تتحكم في مسارات الموارد.
- tessdata — بيانات لغة Tesseract — ملفات .traineddata المنشورة، ومنها مجموعة 4.0.0 القياسية المستخدمة هنا.
- توثيق Tesseract حول تقسيم الصفحة وأوضاع المحرك — مرجع وسيطي PSM وOEM المذكورين أعلاه.
- WebAssembly — MDN — خلفية عن هدف التصريف ودعم SIMD في المتصفحات.
خمس عمليات تحويل مجانية. تُعالَج بيانات المستند داخل متصفحك.
مقالات ذات صلة
- التعرف داخل المتصفح
ما هو التعرف الضوئي داخل المتصفح؟
يقرأ التعرف الضوئي داخل المتصفح النص من الصور وملفات PDF داخل التبويب بدل خادم بعيد. كيف يعمل، ومقارنته بالتعرف السحابي، ومتى يناسب كل منهما.
- المنتج
استخراج بيانات الفواتير بالعربية والفرنسية والنصوص المختلطة
التخطيط من اليمين إلى اليسار، والأرقام العربية المشرقية، والفواتير ثنائية اللغة: عقبات أمام أي محلّل صُمِّم لكتابة واحدة. ما الذي يختلّ فعلًا، وكيف يُكتشف.