تخطَّ إلى المحتوى
kazma.
البحث في كاظمه والتوثيق
EN

ذكاء المستندات

تستوعب منصة المستندات في كاظمه المستندات وتحلّلها وتتعرّف عليها ضوئيًا (OCR) وتفهرسها وتولّدها وتحوّلها وتحجبها عبر طبقة تنسيق واحدة، مع عمّال معزولين، وسجل تدقيق للإلحاق فقط، وإعداد حي. وما قيس وما لم يُقس مذكور تحت ما ليس مؤهَّلًا.

روابط سريعة:


User Input (file upload / workspace import / tool call / slash / TUI)
│
▼
DocumentIngestionService ◄── tenant + actor ACL, capacity gate, audit
│ (durable public boundary for Web/API/tools/gateway/TUI)
▼
Durable Job Queue ◄── CAS transitions, leases, retries, dead-letter
│
▼
DocumentService ◄── single *execution* boundary (sniff / parse / OCR / render)
│ every step below runs in an isolated worker
│ (process or container profile)
├─ Sniff (MIME, OOXML defenses, PDF active-content policy)
├─ Parse (one parser per format)
├─ OCR (per-page quality routing, the neural engine or Tesseract, ruled tables cell by cell)
├─ Index (structural chunking → Knowledge library — explicit action)
├─ Generate / Convert (render workers)
└─ Mutate / Redact (verified redaction pipeline)

طبقتان، ومسار واحد للعمل الدائم.

الطبقةالصنفمن يستدعيها
المنسّق الدائمDocumentIngestionService‏/api/documents/* في الويب، وأدوات document-platform الأصلية، و/documents في البوابة، ولوحة المستندات في واجهة الطرفية (عبر API الويب)
محرّك التنفيذDocumentServiceعمّال الاستيعاب فقط للمهام الدائمة؛ وكذلك التحليل العابر لمرفقات الدردشة عبر attachments.py في البوابة

لا تستورد البوابة ولا الواجهة داخليات المحلّل/OCR/المُعرِض (documents.parsers، وdocuments.ocr، والعمّال). وقد تستدعي الدردشة DocumentService لمقتطف مسيَّج على أفضل جهد من ملف مرفق — وهذا مقصود ولا ينشئ مخزنًا دائمًا ثانيًا.

الفهرسة التلقائية معطّلة افتراضيًا. التحليل الناجح يضع المستند في حالة ready. أما النشر في مكتبة معرفة فهو إجراء فهرسة صريح (API / أداة / واجهة).


المجلدات الإضافية (منح المسارات)

Section titled “المجلدات الإضافية (منح المسارات)”

المسارات خارج مساحة العمل مرفوضة افتراضيًا. لفتح مجلد بإذن:

  1. الدردشة (بسلاسة): حين تفشل أداة ملفات، يستدعي الوكيل request_path_access (بطاقة موافقة HITL). وعند الموافقة تُنشأ منحة جلسة لذلك المجلد ويعيد الوكيل محاولة الأداة.
  2. صفحة مساحة العمل / API: مساحة العمل ← مجلدات خارج مساحة العمل تعرض القائمة الدائمة workspace.extra_roots وتضيف إليها (قراءة فقط، أو قراءة وكتابة) وتحذف منها (GET/PUT /api/workspace/extra-roots: path، mode: read|write، label). يجب أن يكون المجلد الجديد مسارًا كاملًا لمجلد موجود، لا قرصًا كاملًا، ولا مجلدًا يحوي تثبيت كاظمه أو بياناتها (مفاتيحها، ومخازنها)؛ وتقول الصفحة أي قاعدة خالفها المسار المرفوض.

مدة منح الجلسة نحو ساعة؛ وتبقى الجذور الدائمة حتى تُزال. ومنح القراءة لا تسمح بالكتابة. انظر kazma_core.workspace.path_policy.

  • حدود منصة المستندات (الافتراضية): 50 MiB للملف، و10 ملفات للطلب (documents.intake.max_bytes، documents.intake.max_files). وتُقبل الأسماء البديلة documents.intake_max_bytes / documents.intake_max_files.
  • مرفقات دردشة البوابة (واجهة منفصلة): 20 MiB للملف، و10 ملفات، و50 MiB إجمالًا — فتبقى حمولات الموجّه محدودة.
  • القبول قبل الجسم: يُجاب عن رفع الويب قبل قراءة أي بايت من جسمه (الطول المُعلَن، والكتابات المعطّلة، وأرضية المساحة الحرة محسوبًا فيها الملف القادم، والطابور، وحدود المستأجر، ونافذة المعدل)؛ انظر الضغط العكسي. وتقرأ مسارات JSON العشرة (التوليد، والتحويل، والفهرسة، والبحث، …) جسمها عبر قارئ محدود بـ 2 MiB: فالجسم الأكبر يُرَد بـ 413، وJSON المشوّه بـ 422.
  • تخزين ثابت معنون بالمحتوى: إزالة التكرار بـ SHA-256؛ يصل الرفع إلى الحجر ويُرقّى إلى مخزن الأصول الثابت حين يجتاز التحقق (الفحص، الذي يجري في العامل).
  • فحص MIME قبل التحليل (داخل العامل): رفض عدم تطابق الامتداد/المحتوى، ودفاع بنيوي عن OOXML (XXE، والماكرو، والعلاقات الخارجية، والأرشيفات المتداخلة، وقنابل الضغط)، وسياسة واحدة للمحتوى النشط في PDF ‏(documents/pdf_policy.py). ملفات PDF المرفوضة: المشفّرة، والسكربت (JavaScript، ونماذج XFA)، والأفعال التي تشغّل أو تفتح أو ترسل أو تجلب خارج المستند (Launch، وGoTo البعيد — ومنه GoTo أو Thread يسمّي ملفًا آخر — وSubmitForm، وImportData، والعروض البديلة (renditions)، والوسائط الغنية، والصوت، والفيلم)، والملفات المضمّنة، والروابط التي ليس هدفها URI مطلقًا من نوع http أو https أو mailto. ويبقى التنقل داخل المستند وروابط الويب. ولا يوجد إعداد يعطّل هذا الرفض. وتجري السياسة نفسها على كل مُدخَل ومُخرَج للتعديل وعلى كل PDF مُعرَض.
  • كل محتوى يراه نموذج اللغة مسيَّج: تستخدم قراءات المنصة <kazma:data source="document" untrusted="true">…</kazma:data>. وقد تستخدم مقتطفات مرفقات الدردشة source="document_attachment".

يجري كل تحليل وOCR وتعديل وعرض في عامل خارج عملية كاظمه. ويختار KAZMA_DOCUMENT_WORKER_ISOLATION النمط (documents/worker_container.py):

  • process (الافتراضي): عملية فرعية على المضيف (python -I) ببيئة منقّاة (تُنزع أسرار الأب)، ومهلة العامل (documents.workers.timeout_seconds، وتُقتل شجرة العمليات)، وحدود للمخرجات. وعلى Windows يحمل Job Object حدّ الذاكرة (documents.workers.memory_mb)، وحدًّا لمعدل المعالج، وسقفًا للعمليات النشطة (32)؛ وينتظر العامل خلف بوابة إطلاق حتى تُطبَّق، والإعداد الفاشل يوقفه قبل أن يبدأ المحلّل. وعلى Unix الحدود هي الذاكرة ووقت المعالج (setrlimit)؛ ولا سقف لعدد العمليات. وهذا النمط لا عزل فيه لنظام الملفات ولا للشبكة: تُبلغ الجاهزية hostile_document_profile: unqualified.
  • container: يذهب كل تشغيل إلى وسيط عمّال المستندات (documents/worker_broker.py)، الحائز الوحيد لمقبس Docker، الذي يشغّله في حاوية جديدة من صورة واحدة مثبّتة: بلا شبكة، وبلا تركيبات، وجذر للقراءة فقط، وtmpfs محدود الحجم، وحدود للذاكرة/المعالج/PID، وكل الصلاحيات (capabilities) منزوعة، وno-new-privileges، ومستخدم غير جذر. ويتحقق الوسيط من هذه السياسة من داخل حاوية مهمة قبل أن يخدم. وتنتقل المدخلات والمخرجات حزمًا يُتحقَّق من بصمتها (digest). والوسيط غير المضبوط أو المتوقف أو غير المُصدَّق أو المشغول يرفض التشغيل (worker_isolation_unavailable / worker_isolation_busy، ويعيد الطابور المحاولة)؛ ولا رجوع إلى process. والمحرّكات الموجودة هي ما تبلغ عنه الصورة نفسها، وتنبّه الجاهزية إلى صورة بُنيت من شيفرة مستندات أخرى (document_worker_image_outdated). ولا تتضمن الصورة Docling، فالإنقاذ المحلي ليس جزءًا من هذا النمط.
  • أي قيمة أخرى ترفض كل تشغيل (worker_isolation_invalid). والإعداد security.execution.container_required يرفض نمط process (worker_isolation_required).

الانهيارات والمهلات والمخرجات المفقودة تفشل مغلقة: لا تغادر العامل بيانات جزئية. وتُعاد محاولة إخفاقات الموارد (المهلة، ونفاد الذاكرة، والعامل المقتول، وغياب النتيجة) ضمن ميزانية الطابور المحدودة ثم تُحال إلى الرسائل الميتة. وتتشارك الحاويات نواة المضيف؛ وما أُهِّل فيه نمط الحاوية وما لم يُؤهَّل فيه مذكور في أمان المستندات.

استخراج نص PDF (العربية + تعدد المحرّكات)

Section titled “استخراج نص PDF (العربية + تعدد المحرّكات)”

ملفات PDF الإلكترونية (ذات طبقة النص) لا تمر بـ OCR افتراضيًا. يشغّل المحلّل مفاضلة مسجَّلة الدرجات بين محرّكات متعددة ويحتفظ بأفضل تمثيل وسيط:

الترتيبالمحرّكالدور
1PyMuPDFالأساسي — عربية بالترتيب المنطقي، وجداول، وتخطيط متعدد الأعمدة
2pypdfium2 (اختياري)نظير PDFium في مفاضلة الدرجات (نص فقط؛ القدرة DEGRADED دون PyMuPDF/pdfplumber فلا تُعلَن الجداول)
3pdfplumberجداول قوية؛ وقد تنعكس العربية إن استُخدم وحده
4pypdfنص فقط، الملجأ الأخير
  • التسجيل: يفضّل score_document_extraction يونيكود النظيف، ويعاقب أشكال العرض العربية ومخرجات (cid:N)، ويمنح علاوة ترتيب صغيرة (PyMuPDF > pypdfium2 > pdfplumber). والدرجات القوية تختصر المفاضلة.
  • التخطيط: تستخدم الصفحات متعددة الأعمدة / القانونية هندسة dict في PyMuPDF (parsers/pdf_layout.py) — الأعمدة من الأعلى إلى الأسفل، LTR أو RTL حين تغلب العربية. ويحتفظ العمود الواحد بالنص العادي (عربية متصلة أفضل).
  • تُعاد قراءة الأسطر المكتوبة من اليمين إلى اليسار حرفًا رسوميًا تلو الآخر (parsers/pdf_rtl.py). يعكس PyMuPDF مثل هذا السطر حرفًا حرفًا، فكان يقلب الأرقام والحروف المركّبة («١٢٣٤٥٦٧» تُقرأ «٧٦٥٤٣٢١»، و«في» تُقرأ «يف») ويعكس خلايا الجداول العربية. تُقرأ الحروف الرسومية (glyphs) بترتيب رسمها، ولا تُعكس إلا المقاطع المكتوبة من اليمين إلى اليسار، حروفًا رسومية كاملة. ويحصل كل عنقود أرقام على القراءة التي ترسمها خوارزمية ثنائية الاتجاه (arabic.visual_order) كما تُظهرها الصفحة، فالتاريخ المكتوب «في 2026-09-30» يعود كما كُتب مع أن الصفحة ترسمه «30-09-2026». وتُقرأ أعمدة جدول RTL من اليمين إلى اليسار. والفجوة الأعرض من نصف حجم الخط فاصلٌ بين كلمتين.
  • الجداول والصفحات ثنائية اللغة: يُقرأ كل جدول مرة واحدة، من شبكته، في موضعه من الصفحة. ويأتي نص الخلايا من الحروف الرسومية في كل صفحة، فيُقرأ حرف مركّب مثل “fi” كما كُتب؛ والأسطر الملتفّة في الخلية قيمة واحدة، والخلية الرقمية وحدها في جدول من اليمين إلى اليسار تُقرأ من اليمين إلى اليسار. ولا يُعدّ جانبا الصفحة عمودين إلا إذا وقفا جنبًا إلى جنب، فتبقى الأسطر العربية والإنجليزية القصيرة المكدّسة بعضها فوق بعض في عمود واحد. والفقرة التي تحمل علامة اتجاه (RLM، أو عازلًا) لم تعد تُنهي نص الصفحة.
  • البيانات الوصفية: extractor، وextraction_score، وextractors_tried، ولكل صفحة layout.method / column_count.
  • ذهاب وإياب التوليد←التحليل: تستخدم عناوين RTL تغطية رموز تقريبية (تتحمل ضجيج الحروف)، لا «أي 20 حرفًا».

التثبيت: pip install -e ".[document-platform]" (يشمل pymupdf + pypdfium2). ودون PyMuPDF، قد تنعكس ملفات PDF العربية الإلكترونية مع pdfplumber وحده.

إنقاذ ملفات PDF الصعبة: إن كانت الدرجة الأصلية ضعيفة، يعمل Docling (pip install -e ".[docling]") داخل عامل المحلّل المحدود حين يسمح به documents.security.local_salvage (مفعّل افتراضيًا). أما LlamaParse / Reducto فيعملان من عملية كاظمه، لا من العامل أبدًا، وفقط بموافقة صريحة عبر documents.security.remote_parse (معطّلة افتراضيًا) ومفتاح مزوّد. والمفاتيح وحدها لا تأذن بالرفع أبدًا. وكل محاولة تتطلب تدقيقًا دائمًا يحمل مستأجر المهمة وفاعلها ومساحة عملها ومستندها ونسختها؛ وتُفحص الموافقة مجددًا قبل كل محاولة لدى مزوّد. ومقتطفات المرفقات العابرة ليس لها نطاق خروج دائم ولا يمكنها الرفع عن بُعد. ولا تدخل مفاتيح API العامل أبدًا. ويُجرَّب المزوّدون بالترتيب: يُدقَّق المزوّد الفاشل بوصفه إخفاقًا ويُجرَّب المزوّد المضبوط التالي، بعد قراءة السياسة الحية مجددًا. ويتجاهل عميل المزوّد متغيرات الوكيل (proxy) المحيطة، ولا يتبع أي إعادة توجيه، ويرفض جوابًا يتجاوز 32 MiB ‏(remote_parse_response_limit). اعتراضات إضافية: KAZMA_DOCLING=0، وKAZMA_REMOTE_PARSE=0 (يمكنها إيقاف طبقة، لا تشغيلها أبدًا). ولـ Markdown الإنقاذ مقاطع اصطناعية، موسومة صراحةً بأنها بلا استشهادات بصفحات المصدر الفعلية. وفي نمط container لا توجد طبقة Docling.

التعرّف الضوئي متعدد اللغات

Section titled “التعرّف الضوئي متعدد اللغات”
  • يُقرأ المستند الممسوح الطويل بضع صفحات في كل مرة (2026-10-22). يستغرق التعرّف الضوئي نحو 15 إلى 45 ثانية للصفحة على نواة واحدة (المحرّك العصبي في الطرف البطيء)، فالمستند الممسوح من 54 صفحة يستغرق نحو نصف ساعة. يقرأ تشغيل العامل الواحد من الصفحات ما يتسع له حده الزمني على الأكثر ويسمّي الباقي؛ وتقرأها تشغيلات لاحقة، يُحدَّد حجم كل منها من الثواني التي استغرقتها الصفحة حتى الآن (DocumentService.ocr_in_batches). تقرأ المهمة الدائمة كل الصفحات بهذه الطريقة، وتعرض حالتها الصفحات المقروءة حتى الآن (progress؛ ويقول document_status ‏“OCR: 12 of 54 pages read”). أما استدعاء الأداة (read_document وocr_document وfile_read) فيقرأ طبقة النص أولًا، ثم يُجري التعرّف الضوئي على الصفحات الممسوحة ضمن النطاق بقدر ما يسمح وقت الاستدعاء (agent.tool_timeout_seconds ناقصًا هامشًا)، ويسمّي الصفحات التي لم يصل إليها، مع الاستدعاء الذي يُكمل وdocument_import للملف كله.
  • ملف Word للمستند الممسوح هو نص التعرّف الضوئي. يبني document_convert إلى docx أو md لمستند جاءت قراءته من التعرّف الضوئي الملفَّ من ذلك النص، قسمًا لكل صفحة؛ فتحويل الصورة الممسوحة نفسها ينتج صور صفحات بلا نص. والمستند الذي له طبقة نص يُحوَّل من ملفه كما كان.
  • الملف المحوَّل أو المحجوب جاهز للإرسال. يحفظ document_convert وdocument_redact الملف الجديد في مجلد documents ضمن مجلد البيانات (حيث تكتب أدوات الإنشاء) ويذكران مساره، فيمرّره الوكيل إلى send_file. وكانا يجيبان بـ artifact_id وحده، ولا تقبله أي أداة لدى الوكيل. وتبقى القطعة في مخزن المستندات، قابلة للتنزيل من صفحة المستندات.
  • الاستدعاء الذي يُوقَف يوقف عامله. حين يُنهى استدعاء أداة (بحده الزمني أو بالإيقاف)، يُوقَف تشغيل عامله أيضًا: تُزال الحاوية، أو تُقتل شجرة العمليات المحلية. كان يستمر حتى حده الخاص.
  • توجيه بالجودة لكل صفحة: قد تتخطى الصفحات عالية كثافة النص OCR (يُحفظ النص الأصلي)؛ أما الصفحات كثيفة الصور / فقيرة النص، والطبقات الكثيرة بأشكال العرض / المشوّهة بـ CID، وطبقات النص الفارغة فتُوجَّه إلى OCR.
  • محرّكان (documents.ocr.engine، الإعدادات ← المستندات):
    • auto (الافتراضي) يقرأ بـ المحرّك العصبي حين تكون حزمه ونماذجه مثبّتة، وإلا بـ Tesseract؛ وneural يفعل الشيء نفسه ويذكر على المستند متى اضطر Tesseract إلى قراءته؛ وtesseract يستخدم Tesseract وحده.
    • المحرّك العصبي (documents/ocr/neural.py) هو الذي سجّل أفضل قياس على العربية والأرقام العربية الهندية (2026-10-15): كشف النص بـ PaddleOCR PP-OCRv5، ومُتعرِّف EasyOCR العربي (arabic_g1: الحروف العربية واللاتينية، ومجموعتا الأرقام) مع english_g2 للأسطر الخالية من العربية، وتصويت على الحروف مع مُتعرِّف PaddleOCR PP-OCRv5 العربي وكلمات Tesseract (اتفاق قارئَين، أو كلمة PaddleOCR واثقة، يحلّ محل كلمة EasyOCR؛ وقراءة EasyOCR الثانية لحرف شكّ فيه تُعدّ قارئًا؛ والرقم وعلامات ترقيم الكلمة دائمًا من EasyOCR). ولا يكتب EasyOCR الحركات: فحيث كتب همزة ورأى قارئ آخر للحروف نفسها تنوينًا هناك (“ريالاً”)، يُحتفظ بالتنوين. والعلامة الأخيرة في السطر يقرّرها قارئان: يقول EasyOCR إن هناك علامة، وتقول قراءة Tesseract للكلمة نفسها أيّ علامة هي (فنموذج EasyOCR الإنجليزي كثيرًا ما يقرأ ”.” الأخيرة ”:”)؛ والعلامة التي لا يقرؤها إلا Tesseract لا تُضاف أبدًا. وتُزال النقاط الأصغر من قلم السطر نفسه قبل قراءة السطر، وتُعاد ”,” أو ”.” بين الأرقام العربية الهندية إلى الفاصل العربي ٫ أو ٬ حين تحسم قواعدُ الرقم ذلك (وقبل ثلاثة أرقام بالضبط تبقى كما قُرئت: آلاف، أو ثلاث خانات عشرية). وتعمل النماذج الأربعة كلها على ONNX Runtime داخل عامل المستندات؛ ويُوضع السطر المرسوم في الترتيب المنطقي بقاعدة قارئ PDF، لا بقاعدة المُتعرِّف نفسه أبدًا. والنماذج كلها برخصة Apache-2.0.
    • الكلفة: نحو 15 ث لصفحة نص عربي و30–45 ث لصفحة فيها جداول مسطّرة على نواة واحدة (حدّ عامل المستندات)، مقابل 2–8 ث مع Tesseract؛ ونحو 800 MB من الذاكرة في العامل (ضمن الـ 1 GiB الخاصة به). وقرب حدّ وقت العامل (documents.workers.timeout_seconds، ‏300 ث) يقرأ Tesseract الصفحات المتبقية ويذكر المستند من أي صفحة؛ ارفع الحدّ لإبقاء المسوحات الطويلة على المحرّك العصبي.
  • اختيار اللغة: قائمة حزم لغات قابلة للضبط (يشمل الافتراضي الإنجليزية + العربية حين تكون مثبّتة). والترتيب التلقائي ara+eng حين تُضبط الاثنتان — فـ eng+ara كثيرًا ما يقرأ المسوحات العربية الخالصة هراءً لاتينيًا.
  • دمج حتمي: يُدمج النص الأصلي ونص OCR بقواعد قائمة على التشابه (عتبات SequenceMatcher + الثقة)، لا بدمج إحداثيات هندسي بحت. وتُستبدل الطبقات الأصلية المعروفة بسوئها (أشكال العرض / CID) حين تكون ثقة OCR مقبولة. وقد تحمل كتل OCR إحداثيات البكسل من TSV للاستخدام اللاحق.
  • الاحتياطي: تُنقَّط الصفحات الكبيرة بدقة DPI قابلة للضبط (200 افتراضيًا، 72–600). ويبقى OCR في العامل المعزول (apply_ocr) — ولا يستدعي محلّل PDF Tesseract مباشرة أبدًا.
  • تُقرأ الجداول المسطّرة خليةً خلية (documents/ocr/tables.py، وdocuments.ocr.tables، مفعّل افتراضيًا، ومفتاحه في الإعدادات ← المستندات). فقراءة Tesseract للصفحة كاملةً لا تحفظ صفوف الجدول المسطّر ولا معظم أرقامه. يجد هذا المرور الشبكة من خطوط التسطير ويقرأ كل خلية وحدها؛ والخلية التي لا حرف فيها أو ذات القراءة المشكوك فيها تُقرأ مجددًا أرقامًا فقط (حين تكون eng مثبّتة)، ولا تفوز تلك القراءة إلا حين تكون واثقة. ولا يُعثر إلا على الشبكات المسطّرة: فالجدول بلا خطوط تسطير، والجدول الضيق، والخلايا المدموجة التي تعبر الخطوط، كلها تحتفظ بقراءة الصفحة نفسها. والمرور محدود بـ 600 خلية و120 ث لكل صفحة؛ وبعد تجاوز أيٍّ منهما، أو حين تتعذر قراءة خلية، تحتفظ الصفحة بقراءتها ويسمّي تحذيرٌ الصفحة. وله كلفة زمنية: على المسح بدقة 300 dpi الذي قيس في 2026-10-13 استغرقت الصفحة نحو 8 ث بدل 2.
  • ترتيب الكلمات العربية: الرقم الذي يقرؤه OCR من سطر عربي يُعاد إلى الترتيب المنطقي بالقاعدة نفسها المطبَّقة على نص PDF (arabic.number_in_logical_order لكلمات Tesseract، وparsers.pdf_rtl.logical_line لأسطر المحرّك العصبي).
  • مؤهَّل مع المحرّك العصبي: التعرّف الضوئي العربي على صفحة نظيفة (ar-ocr: كل كلمة ورقم، CER 0). ومع Tesseract لا يكون التعرّف الضوئي العربي مؤهَّلًا: فـ ara لا يقرأ الأرقام العربية الهندية في أي خط أو نموذج قيس، ويتخطى بعض الأسطر. وغير مؤهَّل مع أيٍّ منهما: الصفحة العربية ذات الجدول المسطّر (كلمة واحدة أخطأ المحرّك العصبي قراءتها)، والأرقام المقروءة من مسوحات متدهورة، والأعداد العشرية والتواريخ العربية المكتوبة بالفواصل العربية (ليس لدى المُتعرِّف ٫ ونادرًا ما يقرأ / بين الأرقام العربية الهندية). انظر ما ليس مؤهَّلًا.
  • تقطيع بنيوي: تُقسم المستندات عند حدودها الطبيعية (الصفحة، والقسم، والجدول) مع تداخل، محافظةً على الاستشهادات.
  • تبديل النسخة النشطة: يعكس الفهرس دائمًا النسخة المنشورة الحالية؛ ويراعي شواهد الحذف.
  • عزل المستأجر: المقاطع محصورة بالمستأجر + الفاعل.
  • استرجاع بسياج واحد: يُغلَّف السياق المسترجع بأسيجة البيانات غير الموثوقة للنموذج.
  • البحث العربي: يطوي فهرس الكلمات المفتاحية للمكتبة النص العربي (arabic.fold_for_search) وقت الفهرسة ووقت الاستعلام.
  • إجراء صريح: POST /api/documents/{id}/index أو أداة document_index مع library_id — انظر مكتبة المعرفة.
  • حذف مستند يُخرجه من كل مكتبة أولًا. والمكتبة التي لا يمكنه مغادرتها توقف الحذف (document_unindex_failed، مع تسمية المكتبة)، كي تتمكن إعادة المحاولة من إتمامه؛ ولا يبقى مستند محذوف قابلًا للبحث أبدًا. وما دامت مكتبة المعرفة غير متاحة، فلا يُحذف كذلك مستند يسجّله المخزن مفهرسًا (document_unindex_failed، “nothing was deleted”)؛ فأعد المحاولة حين تعود. ويمسك الحذف قفل النشر الخاص بالمستند طوال الوقت، فلا يمكن لفهرس يُنشر في الأثناء أن ينجو منه.
  • يُسوّى الفهرس عند كل بدء للعمّال (DocumentIngestionService.reconcile_index): كل نسخة يسجّلها المخزن مفهرسةً ولا تحملها أي مكتبة يُعاد نشرها من ملف بيانها الخاص، تحت قفل النشر الخاص بالمستند، فلا يُتراجَع أبدًا عن إلغاء فهرسة متزامن. والمكتبة المفقودة أو النسخة المستبدَلة أو ملف البيان المفقود يُبلَّغ عنه، ولا يُختلَق. وهذا ما يعيد المكتبات بعد استعادة مخزن المستندات وحده.
  • توليد المستندات: حمولة منظّمة ← PDF/DOCX/HTML/markdown عبر عمّال عرض معزولين. حمولة التوليد محدودة بـ 1 MiB. وتُعاد النتائج للاستيعاب كمستندات يملكها المستأجر.
  • تحويل الصيغ: تحويل مستند معالَج إلى صيغة هدف (document_id معتم فقط — لا مسارات خادم خامًا من العملاء).
  • أسلوب دار واحد: تستخدم المستندات المولَّدة، وكل ملف HTML يُحوَّل إلى PDF، خط العلامة التجارية لكاظمه وعناوينها وجداولها ورأس الصفحة وتذييلها، مع تخطيط كل فقرة وجدول باتجاه لغته هو. وتأخذ أداة convert_document القائمة على المسار keep_source_style=true لتحفظ تصميم ملف HTML نفسه؛ أما تحويلات المنصة فتستخدم أسلوب الدار دائمًا. انظر عرض المستندات.
  • عمليات PDF: استخراج المعلومات، والتقسيم، والدمج، وملء حقول النماذج.
  • الحجب: خط حجب فعلي/نقطي مُتحقَّق منه مع فحوصات لاحقة. واجهة الويب تؤكد عبر kazmaConfirm قبل استدعاء API. أما API وأدوات الوكيل فيمكنها استدعاء الحجب دون ذلك الحوار (مع بقاء المستأجر/ACL).
  • العرض لا يجلب شيئًا أبدًا. لا يُقرأ إلا عناوين data: في المصدر والأصول المنسوخة إلى المهمة؛ ومصدر HTML أو Markdown يسمّي URL خارجيًا أو ملفًا محليًا آخر يُرفض (external_resource_denied). ويجتاز كل PDF مُعرَض سياسة المحتوى النشط في PDF قبل أن يغادر العامل.
  • المحرّكات الاختيارية: قد تغيب pymupdf / WeasyPrint / LibreOffice. وتظهر المحرّكات الغائبة جاهزيةً متدهورة واستجابات HTTP صادقة 422/503 — وقد تبقى المحلّلات النصية الأساسية جاهزة. وفي نمط container تكون المحرّكات محرّكات صورة العامل، كما تبلغ عنها الصورة.

المسار القديم: مهارة document-generator (generate_pdf / generate_docx / …) تعرض عبر العمّال المعزولين أنفسهم (DocumentService.generate) لكنها تكتب ملفات عادية تحت kazma-data/documents/ ولا تمر عبر المنصة الدائمة. فضّل أدوات document-platform للعمل بمعرّفات معتمة، محصورًا بالمستأجر، آمنًا عند إعادة التشغيل. ويقرأ generate_pdf ملف PDF المكتمل من جديد ويتحقق من أن الكلمات المميِّزة لكل قسم، وكل ترويسة جدول وخلية من صفّه الأول، قد وصلت إليه، مقارنًا علامات الترقيم والحروف المركّبة بالطريقة نفسها في الجانبين. وإن لم تصل، يجيب الاستدعاء بخطأ يسمّي الملف الناقص ويطلب استدعاءً جديدًا: خطأ عادي يستطيع الوكيل تصحيحه، لا أثر مجهول.


كل الحدود، والاستبقاء، وGC، والسعة، ورايات الإطلاق مخزّنة في ConfigStore (المفاتيح الأساسية متداخلة). وتقرأ كل عملية لقطة واحدة ثابتة من إعدادات documents حين تبدأ وتحتفظ بها حتى نهايتها، فيسري التغيير من العملية التالية دون إعادة تشغيل — باستثناء الإعدادات المرتبطة بالمخازن والعمّال المُنشأة: documents.storage_root، وdocuments.quotas.tenant_bytes، وdocuments.workers.concurrency، وdocuments.workers.lease_seconds، وdocuments.workers.heartbeat_seconds، وdocuments.workers.retry_base_seconds، وdocuments.workers.retry_max_seconds، فهذه تسري عند إعادة التشغيل التالية؛ وحتى ذلك الحين تسردها الجاهزية تحت policy.pending_restart. والإعدادات التي تتعذر قراءتها، أو القيمة المحفوظة غير الصالحة، ترفض العمل الجديد (HTTP 503 ‏document_policy_unavailable) بدل الرجوع إلى القيم الافتراضية.

كيف تضبطالمسار
واجهة الإعدادات/settings?tab=documents (الإطلاق، والاستلام، والعمّال، وOCR وقراءة الجداول، والبرمجيات الخبيثة، وGC، والفهرسة)
RESTGET/PUT /api/settings/documents أو PUT /api/settings/single
YAMLيبذر kazma.yaml القيم عند الإقلاع الأول؛ وتفوز تجاوزات قاعدة البيانات بعد ذلك
البيئة (للواجهات الخلفية والعزل)KAZMA_DOCUMENTS_JOBS_BACKEND، KAZMA_DOCUMENTS_METADATA_BACKEND، KAZMA_DOCUMENT_WORKER_ISOLATION (+ KAZMA_DOCUMENT_BROKER_URL، KAZMA_DOCUMENT_BROKER_TOKEN_FILE)

حدود المستندات ليست مدفوعة بالبيئة أساسًا — استخدم مفاتيح ConfigStore.

المجالبادئة المفتاح الأساسيملاحظات
الاستلامdocuments.intake.*أسماء بديلة: documents.intake_max_*؛ وdocuments.intake.lease_seconds
المحلّل / الحدودdocuments.limits.*مثل documents.limits.max_pages
الأمانdocuments.security.*البرمجيات الخبيثة: malware_scan + malware_fail_closed
OCRdocuments.ocr.*documents.ocr.tables (قراءة الجداول خليةً خلية)
العرضdocuments.render.*documents.render.embed_html_fonts
العمّالdocuments.workers.*المهلة، والذاكرة، والتزامن، وعقود الإيجار
الفهرسةdocuments.indexing.*
الاستبقاء / GCdocuments.retention.*، documents.gc.*
السعةdocuments.capacity.*
الحصصdocuments.quotas.*
الإطلاقdocuments.enabled، documents.shadow، documents.default_authoritative

صحة الطابور (آلة حالات حقيقية)

Section titled “صحة الطابور (آلة حالات حقيقية)”

تستخدم المهام الحالات الدائمة في DocumentJobState (لا خط PENDING/ACCEPTED عامًا):

received
→ quarantined
→ validating
→ ready_to_parse | ocr_required | rejected
→ parsing | ocr_running
→ normalizing → indexing → verifying → ready

المسارات الجانبية: retry_wait (عابرة / استعادة عقد الإيجار)، وcancelled، وdead_letter.

  • عقود الإيجار مع نبض تمنع العمل النشط العالق.
  • إعادة المحاولة مع تراجع أسّي (قابل للضبط).
  • الرسائل الميتة بعد أقصى المحاولات (documents.workers.max_attempts، افتراضيًا 3) لمراجعة المشغّل.
  • الانقطاعات تُعاد محاولتها؛ ولا يُرفض إلا المحتوى. الماسح المطلوب للبرمجيات الخبيثة الذي يتعذر تشغيله، والقرص الممتلئ (storage_full)، ومخزن البيانات الوصفية المقفل أو غير القابل للوصول (metadata_unavailable، SQLite أو Postgres)، ووسيط العمّال المتوقف، كلها تمر عبر ميزانية إعادة المحاولة ثم تُحال إلى الرسائل الميتة — ولا تصير rejected أبدًا. وخطأ المخزن أثناء تسجيل نتيجة مرحلة يترك المهمة مؤجَّرة؛ وتستأنفها استعادة عقود الإيجار.
  • الاستعادة عند إعادة التشغيل: تُستعاد عقود الإيجار المنتهية على حالات المعالجة النشطة، ويُتراجَع عن عمليات الاستلام التي قطعتها عملية ميتة (انظر الضغط العكسي).
  • مطالبات عادلة: يأخذ العمّال المهام أولًا من المستأجر صاحب أقل عدد من المهام الجارية، ثم بحسب وقت جاهزية المهمة (في الواجهتين الخلفيتين).
  • كل قراءة لمهمة تسمّي مستدعيها. حالة المهمة، وأحداثها، ومهام مستند ما، كلها تتحقق من أن الفاعل يستطيع قراءة مستند المهمة؛ أما الإلغاء وإعادة المحاولة فيحتاجان صلاحية الكتابة. والاستدعاء الذي لا يسمّي مستدعيًا يُرفض قبل البحث عن المهمة. والمهمة التي لا يستطيع المستدعي قراءتها تجيب تمامًا كمهمة مجهولة (404 على مسارات الويب) على كل واجهة؛ وإعادة إرسال مفتاح رفع يخص فاعلًا آخر هي idempotency_conflict. وفي تثبيت بمشغّل واحد تعمل صفحة الويب والدردشة وأدوات الوكيل بهوية واحدة، هي مشغّل التثبيت (documents.principal.OPERATOR): فالمستند المستوعَب على واجهة يُقرأ على الأخريات. والمستندات المستوعَبة تحت اسم أقدم (agent، أو اسم مستخدم مسجّل الدخول) تحصل على صف ACL من نوع owner للمشغّل، يمنحه مالكها ويُدقَّق؛ ولا تتغير الملكية أبدًا. ومع مستخدمي المنصة تعمل الصفحة باسم كل مستخدم وترفض الدردشة والوكيل (مشغّل واحد لكل تثبيت؛ وهوية الدردشة لكل مستخدم غير مبنية).

يُرفض الاستلام برموز حالة HTTP صادقة حين تُبلغ الحدود:

الحالةالرمزRetry-Afterمفتاح الإعداد
أرضية المساحة الحرة للتخزين507نعمdocuments.capacity.storage_free_floor_bytes (افتراضيًا 512 MiB)
سقف الطابور العام503نعمdocuments.capacity.max_queued_jobs
حدّ المهام المنتظرة/النشطة لكل مستأجر429نعمdocuments.capacity.max_tenant_queued_jobs / .max_tenant_active_jobs
نافذة معدل/بايتات الاستلام429نعمdocuments.capacity.intake_rate_per_minute / .intake_bytes_per_minute
رفوع كثيرة جدًا قيد الاستلام (لكل عملية)503 uploads_busyنعمdocuments.capacity.max_concurrent_uploads (افتراضيًا 4)
تجاوز الرفع عقد إيجاره فاستُولي عليه409 intake_taken_overلا (أعد الرفع)documents.intake.lease_seconds (افتراضيًا 300)

يُجاب عن رفع الويب قبل قراءة جسمه: فلا ينتظر الرفض أبدًا وصول الملف. والميزانيات حجوزات دائمة في قاعدة بيانات المهام، فتصمد عبر العمليات؛ والجسم الذي بلا طول مُعلَن يُحسب بقدر حدّ الاستلام حتى يُعرف حجمه الحقيقي. والقياس الذي تتعذر قراءته (التخزين، والطابور، وعدّادات المستأجر، ومخزن القبول) يرفض بـ 503 بدل القبول.

الاستلام الذي ماتت عمليته على هذا المضيف يُتراجَع عنه عند البدء التالي وبكنسٍ يجري كل دقيقة. والاستلام الذي لا يمكن إثبات موت مالكه (مضيف آخر) يجدّد عقد إيجار ما دام يعمل؛ وحين ينقضي العقد يُستولى عليه ويُتراجَع عنه، وإن استأنف المالك القديم فلا يمكنه النشر: لا تُنشأ مهمته إلا ما دام الحجز ما زال له، وتُزال الصفوف التي كتبها. والحجز الذي بلا مالك أو بلا عقد إيجار (كتبه إصدار أقدم) لا يُستولى عليه أبدًا: تعدّه الجاهزية ضمن stranded_intake_reservations (document_intake_reservations_stranded) للمشغّل. وتُحسب خانات تخزين الرفع المؤقت (spool) لكل عملية، وعدالة المطالبة بحسب أقل عدد من المهام الجارية، لا موزونة.

يعمل جامع وسم/كنس آمن من الانهيار وفق جدول قابل للضبط:

  • قاعدة البيانات هي المرجع الوحيد (لا يتامى على نظام الملفات وحده)؛ والوسم/الكنس هو نفسه على بيانات SQLite وPostgres الوصفية.
  • نسخ الحجر بعد الترقية، ومحتوى شواهد الحذف المنتهية، ومحتوى النسخ الفاشلة نهائيًا، والكتل اليتيمة، وصفوف التدقيق المتقادمة.
  • ما تركه انهيار من ملفات مؤقتة تحت جذر التخزين: ملفات الرفع المؤقتة (spools)، والنسخ المرحلية، وأدلة تشغيل المحلّل والعمليات.
  • الكتل التي ثبّتتها نسخة احتياطية جارية لـ Postgres تُحفظ حتى تنسخها.
  • فترات سماح، وحذوف محدودة في كل تشغيل، ورفض الروابط الرمزية/نقاط الوصل.
  • لا يعمل المرور المجدول (documents.gc.auto_maintain، كل documents.gc.interval_hours) إلا ما دامت الكتابات الدائمة مفعّلة.

يُلحق كل إجراء موجّه للمشغّل/المستأجر بجدول document_audit_events. وعلى SQLite ترفض مُشغِّلات قاعدة البيانات التحديثات وأي حذف سوى تقليم الاستبقاء؛ أما جدول Postgres فليس فيه مثل هذه المُشغِّلات، ولا تفعل كاظمه به سوى الإلحاق والتقليم. والتفاصيل مقصورة على قيم مفردة آمنة — لا يدخل المحتوى سجل التدقيق أبدًا. ولكل رفع لتحليل بعيد صف egress يُكتب قبل أن تتحرك البايتات. وقراءة السجل (GET /api/documents/ops/audit) للمسؤول فقط.

النسخ الاحتياطي والترحيل

Section titled “النسخ الاحتياطي والترحيل”

يُنسخ مخزن المستندات احتياطيًا كل ست ساعات، مع النسخة الاحتياطية الأصلية للذاكرة (مهمة native_backup).

  • مخزن SQLite: تُلتقط لقطة documents.db أولًا، ثم تُنسخ الشجرة المعنونة بالمحتوى المشار إليها وملفات البيان المطبَّعة. ويفحص التحقق قاعدة البيانات والكتل وملفات البيان المنسوخة، ويرفض جرد مجاميع اختبارية غائبًا أو غير صالح. ويعدّ الاستبقاء نقاط الاستعادة المُتحقَّق منها.
  • مخزن Postgres (documents/backup_pg.py، يُؤخذ تلقائيًا حين تكون البيانات الوصفية أو المهام المثبّتة على Postgres): لقطة REPEATABLE READ واحدة مُصدَّرة تخدم كلًّا من pg_dump --snapshot لجداول المستندات وقائمة الملفات التي تشير إليها؛ وتُثبَّت تلك الملفات في وجه جمع المهملات وتُنسخ مع مجاميع اختبارية. ولا تجري الاستعادة إلا إلى قاعدة بيانات وجذر تخزين فارغَين، وتعيد فحص عدد صفوف كل جدول مقابل اللقطة، وتُنهي عقود إيجار المصدر الميت، وتتراجع عن عمليات الاستلام التي لم يُتمّها. أوامر المشغّل: scripts/document_pg_backup.py (backup، verify، restore، index).
  • فهرس المعرفة: مقاطع المكتبات ومتجهاتها مخزن منفصل وليست في أيٍّ من النسختين الاحتياطيتين. وبعد الاستعادة تعيد الخدمة نشر كل مستند يسجّله المخزن مفهرسًا، عند البدء التالي للعمّال (انظر فهرسة المعرفة).

تحمل حزم الترحيل عبر kazma migrate ملف documents.db + شجرة المحتوى (انظر الترحيل و تشغيل معالجة المستندات). وأرقام نقطة الاستعادة وزمن الاستعادة لنشر معيّن تأتي من تشغيل تدريبات الاستعادة على عتاده الخاص؛ ولا يُؤكَّد هنا أي رقم منها.


الوضعenabledshadowdefault_authoritativeالأثر
معطّلfalse——لا كتابات دائمة؛ وتُحفظ البيانات الموجودة
ظلّيtruetruefalseتُقبل الكتابات؛ ويُبلَّغ عنه بوصفه shadow
توافقtruefalsefalseتُقبل الكتابات (وضع الشحن الافتراضي)
مرجعيtruefalsetrueتُقبل الكتابات؛ ويُبلَّغ عنه بوصفه authoritative

لا يغيّر السلوك اليوم إلا enabled: فهو يتحكم بالكتابات الدائمة وبجمع المهملات المجدول. أما shadow وdefault_authoritative فيُسجَّلان ويُبلَّغ عنهما (الجاهزية rollout.mode، وفحص safe_rollout لدى المُعتمِد)؛ ولا يوجّه أي مسار شيفرة بحسبهما. وحين يُضبطان معًا يفوز المرجعي. والإعدادات التي تتعذر قراءتها تُعدّ معطّلة للكتابات.

تراجع آمن: ضبط documents.enabled=false يوقف الكتابات الدائمة الجديدة لكنه يحفظ كل الكتل والمهام والبيانات الوصفية وملفات البيان. ولا تُحذف البيانات أبدًا عند تعطيل الإطلاق.


الواجهةالمدخل
واجهة الويب/documents — الرفع، والمكتبة، والتفاصيل، ولوحة التشغيل (تجربة GC جافة + تأكيد)
الإعدادات/settings?tab=documents — الإطلاق الحي، والاستلام، وOCR، والبرمجيات الخبيثة، وGC، والعمّال
REST/api/documents/* — انظر مسارات API
أدوات الوكيلdocument_import، وdocument_read، وdocument_index، … (مهارة document-platform)
دردشة البوابة/documents أو /docs — السرد، والحالة، والقراءة، والتحويل، وpdf-info، والحجب، والبحث، والصحة
واجهة الطرفيةتبويب المستندات (السرد والمعاينة عبر API الويب)
الاعتمادpython scripts/certify_documents.py

يبلّغ المُعتمِد عمّا شغّله فعلًا على هذا الجهاز، مع النمط المضبوط:

نافذة طرفيّة
python scripts/certify_documents.py # CI smoke
python scripts/certify_documents.py --soak # Extended smoke (100 iterations), not a multi-day soak
python scripts/certify_documents.py --output report.json
الفحصما هوالحالة التي يمكن أن يبلغها
hostile_corpusالحالات العدائية الـ 26 الملتزم بها، كلٌّ منها مرفوض أو مسيَّج كما هو مُعلَنPASS / FAIL
bounded_performance_event_loop_resourcesتحليلات متكررة بحدود للمدة ولتوقف حلقة الأحداث وللذاكرةPASS / FAIL
runtime_capabilitiesجاهزية المحلّل وOCR والمُعرِض والمُعدِّلCONDITIONAL حين يغيب محرّك اختياري، وFAIL حين لا يكون أي محلّل جاهزًا
safe_rolloutوضع الإطلاق الحيCONDITIONAL في الوضع المرجعي أو المعطّل
worker_isolationنمط العزل المضبوطprocess دائمًا CONDITIONAL؛ وcontainer يكون PASS حين يكون الوسيط جاهزًا ولم تُبنَ صورته من شيفرة مستندات أخرى، وإلا FAIL؛ والنمط غير الصالح FAIL
extraction_fidelityمجموعة الدقة مقابل أهداف الإصدارCONDITIONAL ما دامت أي حالة دون الهدف أو تتعذر قياسها (اليوم: ثلاث حالات دونه)
backup_restore_sqliteمستند جاهز واحد يُنسخ احتياطيًا ويُتحقَّق منه ويُستعاد ويُقرأ ثانيةًPASS / FAIL
postgres_multi_replica، metadata_multi_replica، multi_day_soak، external_security_reviewلا يجريها تشغيل محليدائمًا NOT RUN

ويسمّي التقرير أيضًا نمط العزل، وصورة العامل، والواجهات الخلفية المضبوطة للبيانات الوصفية/المهام، ولا تكون حالته الإجمالية أفضل من CONDITIONAL ما دام أي فحص CONDITIONAL أو NOT RUN.

تُولَّد المجموعة العدائية برمجيًا — لا ملفات ثنائية معتمة في المستودع. لكل حالة وصف مراجَع، وتصرّف متوقع (رفض/تسييج)، ورموز أخطاء متوقعة. ويُتحقَّق من ملف البيان الملتزم به تحت tests/fixtures/documents/hostile_manifest.json وقت الإصدار.

دقة الاستخراج (documents/fidelity.py، والعتبات في tests/fixtures/documents/fidelity_thresholds.json): مجموعة ذات إصدارات مبنية بمحرّكات كاظمه نفسها — DOCX وPDF وHTML بالإنجليزية والعربية وثنائية اللغة، ومستندات طويلة، وعمودان من مُنتِج آخر، وجداول عريضة، وصفحات OCR (عادية، وبجدول مسطّر، ومسح متدهور) — تُقاس على المحلّل الحقيقي من حيث استرجاع الكلمات والمقاطع والأرقام وخلايا الجداول، وترتيب القراءة، وصدق الصفحات، والمحاذاة، وCER/WER للتعرّف الضوئي. وللإصدار 5 من المجموعة 19 حالة (أدخل الإصدار 4 عناصر الصفحة الثابتة في مرجع OCR؛ ويعدّ الإصدار 5 علامات الترقيم). وتُقاس حالات OCR الخمس بكل محرّك مقابل أرضياته الخاصة: مع المحرّك العصبي تبلغ الحالات 19 كلها أهداف الإصدار؛ ومع Tesseract ‏16. غير مؤهَّلة مع Tesseract: ar-ocr وar-ocr-table (لا تُقرأ الأرقام العربية الهندية) وen-ocr-degraded (قُرئ 3.75 على أنه 3:75). ويعيد python scripts/document_fidelity.py قياسها بكل محرّك مثبّت هنا.

ملاحظة: بوابات البنية وإمكانية الوصول ومصفوفة الانهيار الكاملة تعيش في tests/test_document_certification_phase10.py. ولاستعادة موت العمليات، والاستيلاء على الاستلام، وتدريبات الاستعادة، ونمط الحاوية مجموعات اختبار خاصة بها (انظر خريطة المراحل).

انظر خريطة المراحل وتقرير التدقيق في المستودع docs/audits/AUDIT_DOCUMENT_CERTIFICATION.md (ليس جزءًا من شجرة Docusaurus).


الطبقةالواجهة الخلفيةمتعدد النسخ؟
المطالبة بالمهامPostgres حين يكون المجمّع قائمًا (jobs_pg.py)نعم — SELECT … FOR UPDATE SKIP LOCKED
البيانات الوصفية (المستندات/النسخ/الكتل/المخرجات/المقاطع)تتبع واجهة المهام الخلفية افتراضيًا (auto)؛ ويفرضها KAZMA_DOCUMENTS_METADATA_BACKEND=sqlite أو postgres ‏(repository_pg.py)نعم حين تكون بيانات Postgres الوصفية نشطة
قبول الاستلامحجوزات في قاعدة بيانات المهام؛ ويُستولى على عقد الإيجار المنقضي ويُسيَّج مالكهنعم على مهام Postgres
كتل المحتوىشجرة معنونة بالمحتوى تحت documents.storage_rootيلزم نظام ملفات / وحدة تخزين مشتركة بين النسخ
وسم/كنس GCمستقل عن الواجهة الخلفية — يوجّه retention._mark إلى repository.gc_mark الذي تنفذه الواجهتاننعم

هذا ما بُنيت كل واجهة خلفية من أجله. أما تشغيل عدة نسخ فليس مؤهَّلًا: يبلّغ المُعتمِد عن postgres_multi_replica وmetadata_multi_replica بوصفهما NOT RUN، وتُحسب خانات تخزين الرفع المؤقت لكل عملية. وتبلّغ الجاهزية عن بيانات SQLite الوصفية بأنها أحادية النسخة كلما كانت هي الواجهة الخلفية العاملة.

البيئة:

المتغيرالقيمالأثر
KAZMA_DOCUMENTS_JOBS_BACKENDauto (افتراضي) / sqliteفرض مهام SQLite، أو اتباع Postgres المنصة
KAZMA_DOCUMENTS_METADATA_BACKENDauto / sqlite / postgres‏auto يتبع واجهة المهام الخلفية

الفحص: GET /api/documents/ops/readiness ← jobs_multi_replica، وmetadata_multi_replica، وdegraded_reasons (وأيضًا worker_isolation، وstranded_intake_reservations، وpolicy، وmalware، وaccepting_durable_writes).


نافذة طرفيّة
# Simple PDF/DOCX/XLSX generators only (legacy skill)
pip install -e ".[document]"
# Full platform engines (OCR helpers, WeasyPrint, PyMuPDF, pypdfium2 bake-off)
pip install -e ".[document-platform]"
# The neural OCR engine: its packages (part of the ocr extra, which
# document-platform includes), then its models, once
pip install -e ".[ocr]"
python scripts/ocr_models.py install
# Everything
pip install -e ".[all]"
الحزمةالغرض
Tesseract + حزم اللغات (eng، ara، …)OCR (المسوحات / طبقة النص السيئة؛ وara لازمة للمسوحات العربية؛ وeng تخدم أيضًا مرور الأرقام فقط لخلايا الجداول)؛ وهو أيضًا القارئ الثالث في تصويت الحروف لدى المحرّك العصبي
ClamAV (clamscan / clamdscan على PATH)فحص البرمجيات الخبيثة عند الاستلام
LibreOfficeتحليل ملفات .doc / .xls / .ppt القديمة، وبعض تحويلات الصيغ، ومسار توليد PDF للمستندات التي تحوي العربية
خطوط النظام‏WeasyPrint من HTML إلى PDF ‏(يُشحن IBM Plex Sans Arabic وAmiri مع كاظمه للمستندات المولَّدة)

في نمط container تأتي هذه من صورة العامل (deploy/document-worker/Dockerfile)، لا من المضيف — بما فيها محرّك OCR العصبي ونماذجه (مرحلة بناء ocr-models تنزّلها وتحوّلها؛ والعامل بلا شبكة).

ينزّل python scripts/ocr_models.py install أربعة ملفات مثبّتة بعنوان URL والحجم وSHA-256 في documents/ocr/neural_models.py (كشف PP-OCRv5 والتعرّف العربي من PaddleOCR بصيغة تحويلات RapidAI إلى ONNX، وarabic_g1 وenglish_g2 من EasyOCR)، ويحوّل نموذجَي EasyOCR من PyTorch إلى ONNX ويتحقق من التحويل مقابل PyTorch على مدخلات ثابتة، ويكتبها مع manifest.json في KAZMA_OCR_MODEL_DIR (افتراضيًا <data dir>/models/ocr، نحو 240 MB، لا يُنسخ احتياطيًا ولا يُحمل في حزمة: بل يُعاد تثبيته). ويحتاج التحويل إلى PyTorch مرة واحدة: فحين يخلو منه المفسّر يشغّله البرنامج في بيئة مؤقتة تُرمى بعده عبر uv، ولا يُضاف شيء إلى بيئة كاظمه نفسها. ويتحقق python scripts/ocr_models.py verify من مجموعة مثبّتة. ولا يستخدم عامل المستندات النماذج إلا حين يطابق كل ملف ملف البيان، ولا ينزّل أي شيء أبدًا.

  1. شغّل كاظمه وافتح /documents.
  2. (اختياري) الإعدادات ← المستندات — اترك enabled، واضبط البرمجيات الخبيثة على auto أو on إن كان ClamAV مثبّتًا.
  3. ارفع ملف PDF صغيرًا أو .txt ← انتظر الحالة ready.
  4. عاين المحتوى (آمن ثنائي الاتجاه مع dir="auto").
  5. اضبط library_id ← فهرسة ← ابحث عبر الواجهة/API/document_search.
  6. لوحة التشغيل: السعة، والجاهزية، والتدقيق؛ وGC = تجربة جافة ثم تأكيد.
  7. اختبار دخان الاعتماد: python scripts/certify_documents.py
  • أرفق ملف PDF في الدردشة ← مقتطف من طبقة نصه (مسيَّج) حين يتوفر محلّل. ولا يمر الملف الممسوح بالتعرّف الضوئي قبل أن يرى النموذج الرسالة: يُخبَر النموذج بأنه ممسوح، وبعدد صفحاته، وبالأدوات التي تقرؤه.
  • أدوات الوكيل: document_import (مسار في مساحة العمل)، وdocument_read، وdocument_index، …
  • البوابة: /documents list، /documents read <id>، …

الصيغ المدعومة (المعتادة)

Section titled “الصيغ المدعومة (المعتادة)”
النوعأمثلةملاحظات
نص / بيانات.txt، .md، .markdown، .log، .csv، .tsv، .json، .html، .htm، .rtfالفحص + محلّلات النص (UTF-8 صارم)
Office OOXML.docx، .xlsx، .pptxدفاعات بنيوية قبل التحليل؛ وتُرفض الامتدادات المفعَّل فيها الماكرو
Office القديم.doc، .xls، .pptيحوّلها LibreOffice داخل العامل، ثم تُحلَّل
PDF.pdfترفض سياسة المحتوى النشط في PDF الملفات المشفّرة، والسكربت، والأفعال الخارجية، والملفات المضمّنة
الصور.png، .jpg، .jpeg، .tif، .tiff، .bmp، .webpمسار OCR حين يكون مفعّلًا

تُبلَّغ الجاهزية الدقيقة لكل محلّل/مُعرِض في GET /api/documents/health.


يفحص الاستلام البايتات المحجورة عبر documents.security.malware_scan:

الوضعالسلوك
auto (افتراضي)يفحص إن كان ClamAV على PATH؛ وإلا يتخطى (ما لم يكن الفشل مغلقًا). ويُعامَل خطأ الماسح بوصفه تخطيًا ما لم يكن الفشل مغلقًا
onيشترط ماسحًا يعمل
offلا يفحص أبدًا

يجعل documents.security.malware_fail_closed الوضع auto يشترط ماسحًا هو الآخر؛ وهو مفعّل افتراضيًا حين KAZMA_PRODUCTION=1. وحين يتعذر تشغيل ماسح مطلوب، يُرفض الرفع الجديد بـ malware_scanner_unavailable وتبلّغ الجاهزية accepting_durable_writes: false؛ أما المهمة المنتظرة أصلًا في الطابور (فهي تُفحص مجددًا قبل التحليل، وفق سياسة اليوم) فتُحتجز وتُعاد محاولتها، ثم تُحال إلى الرسائل الميتة — ولا تُرفض أبدًا كأن محتواها غير آمن. الملفات المصابة ← رمز الخطأ malware_detected. انظر أمان المستندات.


العَرَضافحص
الرفع 413 / 429 / 503 / 507حدود السعة؛ الإعدادات أو لقطة سعة لوحة التشغيل
الرفع 409 intake_taken_overتجاوز الرفع عقد إيجاره فجرى التراجع عنه؛ أرسله مجددًا
كل عملية 503 document_policy_unavailableتتعذر قراءة إعدادات المستندات المحفوظة أو أحدها غير صالح؛ أصلح الإعداد
مهام تعيد المحاولة أو تفشل بـ worker_isolation_*‏_unavailable / _busy (تُعاد محاولتها): وسيط نمط container متوقف أو غير مُصدَّق أو ممتلئ. ‏_required: التثبيت يشترط نمط الحاوية. ‏_invalid: نمط مكتوب خطأً. وتقول جاهزية worker_isolation أيّها
الحالة عالقة / dead_letterأحداث المهمة؛ هل العامل يعمل؛ GET /api/documents/health؛ وجاهزية stranded_intake_reservations
معاينة فارغةلم يصل إلى ready بعد؛ رمز فشل المحلّل على المهمة
التحويل/الحجب 422/503محرّك اختياري غائب — ثبّت document-platform + تبعيات النظام (أو صورة العامل، في نمط container)
رفوض برمجيات خبيثة غير متوقعةإيجابية كاذبة من ClamAV، أو ماسح مطلوب (on، أو الفشل المغلق في الإنتاج) يتعذر تشغيله
غرابة تعدد النسخops/readiness؛ ووحدة الكتل المشتركة؛ وتشغيل عدة نسخ غير مؤهَّل
404 بين المستأجرينمتوقع — ACL المستأجر/الفاعل؛ والمهمة التي لا تُقرأ تجيب كمهمة مجهولة

مزيد من تفاصيل التشغيل: معالجة المستندات.


الموردالافتراضيمفتاح ConfigStore الأساسي
أقصى حجم للملف50 MiBdocuments.intake.max_bytes
أقصى ملفات للطلب10documents.intake.max_files
أقصى صفحات500documents.limits.max_pages
أقصى صفوف للورقة100,000documents.limits.max_rows_per_sheet
أقصى خلايا2,000,000documents.limits.max_cells
أقصى بايتات بعد الفك256 MiBdocuments.limits.max_expanded_bytes
أقصى أعضاء الأرشيف10,000documents.limits.max_archive_members
دقة OCR ‏(DPI)200documents.ocr.dpi
مهلة العامل300 ثdocuments.workers.timeout_seconds
ذاكرة العامل1 GiBdocuments.workers.memory_mb
حصة المستأجر10 GiBdocuments.quotas.tenant_bytes
استبقاء التدقيق365 يومًاdocuments.retention.audit_days
أرضية المساحة الحرة للتخزين512 MiBdocuments.capacity.storage_free_floor_bytes
رفوع الويب المتزامنة4 لكل عمليةdocuments.capacity.max_concurrent_uploads
عقد إيجار الاستلام300 ثdocuments.intake.lease_seconds
خلايا الجداول المقروءة لكل صفحة ممسوحة600 خلية / 120 ثثابت (documents/ocr/pipeline.py)
صفحات التعرّف الضوئي في كل تشغيل للعاملبقدر ما يتسع له حد التشغيل (12 على الأكثر)ثابت (documents/service.py)
محرّك OCRautodocuments.ocr.engine (auto، neural، tesseract)
جسم طلب JSON2 MiBثابت (documents_api.py)
حمولة التوليد1 MiBثابت (ingestion.py)
جواب التحليل البعيد32 MiBثابت (extract_salvage.py)

قائمة الحقول الكاملة: kazma_core.documents.config.DocumentConfig (75 حقلًا).

هذه حدود مُعلَنة للإصدار الحالي، مأخوذة من الشيفرة والمُعتمِد ومجموعة الدقة — لا خطط:

  • المستندات العدائية على نمط process الافتراضي. فهو يحدّ الذاكرة والمعالج والوقت لكنه لا يحصر نظام الملفات ولا الشبكة. ولا يفعل ذلك إلا نمط container، وحاوياته تتشارك نواة المضيف.
  • التعرّف الضوئي مع Tesseract: العربية (ar-ocr، ar-ocr-table: لا تُقرأ الأرقام العربية الهندية) والأرقام من المسوحات المتدهورة (en-ocr-degraded: قُرئ 3.75 على أنه 3:75). ومع المحرّك العصبي تبلغ كل حالة OCR في المجموعة أهداف الإصدار، لكن المسوحات الحقيقية قيست على مسح واحد فقط (تفوّق فيه على Tesseract في معظم الصفحات وخسر في بعض الكلمات). والجداول بلا خطوط تسطير والخلايا المدموجة لا تُقرأ خليةً خلية.
  • تشغيل عدة نسخ (فحصا postgres_multi_replica وmetadata_multi_replica لدى المُعتمِد هما NOT RUN).
  • التشغيل المستدام: نجح اختبار تحمّل واحد بحمل مختلط لمدة 48 ساعة (scripts/document_soak.py، من 2026-10-17 إلى 2026-10-19، مضيف Windows واحد، ومخازن SQLite خاصة، ونمط process): 12,865 مستندًا أُرسلت واكتملت، و190 إعادة تشغيل، و143 انهيارًا مُحقنًا، ولا أخطاء، ولا ملفات مؤقتة متبقية؛ و4.5 مستندات في الدقيقة (يفحص ClamAV كل رفع)، وp95 ‏14.1 s. ولم يُختبر تحمّل Postgres ولا نمط container. ولا يزال --soak لدى المُعتمِد اختبار دخان ممتدًا.
  • مراجعة أمنية مستقلة: مراجعة واحدة لملف أمان المستندات (2026-10-15، docs/audits/Q04_REVIEW_RESULTS_2026-10-15.md): صمد 13 ادعاءً من 14، والملاحظة الوحيدة (FIND-01) أُصلحت وأكّدها اختبار المراجع المُعاد. وقد غطّت مضيف Windows Docker Desktop واحدًا؛ وكان خارج نطاقها نمط process بوصفه دفاعًا ضد المستندات العدائية، والإشراف على Linux، وتعدد النسخ، والحمل المستدام. ولا يزال external_security_review لدى المُعتمِد يقرأ NOT RUN: فالتشغيل المحلي لا يستطيع إجراء مراجعة كهذه.
  • أهداف نقطة الاستعادة وزمن الاستعادة لنشر معيّن: تقيسها تدريبات الاستعادة؛ ولا يُوعَد بأي رقم.
  • إنقاذ Docling في نمط container: ليس في صورة العامل.