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

تنسيق السرب

نظام السرب الكامل: المحرّك، وأنماط الإرسال الستة، واستراتيجيات التجميع، وطبقة الموثوقية (قواطع الدائرة، وإعادة المحاولة، والمهل، والمُدقّقات، والتزامن)، وسجلّ العمّال، ونقاط حفظ خط المعالجة، وناقل HITL (قرار مشترك واحد لكل طلب)، والمقاييس. الثوابت: AGENTS.md §4–§7، §9، §14، §30.


SwarmEngine (kazma-core/kazma_core/swarm/engine.py) هو المنسّق اللامتزامن المركزي. تغلّفه واجهة SwarmManager المتوافقة مع الإصدارات السابقة (manager.py، تشارك self._workers = self.engine._workers).

1.0 ناقل المنصّة (HITL / التقدّم)

Section titled “1.0 ناقل المنصّة (HITL / التقدّم)”

تمرّ تقدّمات السرب الصادرة + موافقات الأدوات الخطيرة عبر SwarmMessageBus (swarm/bus.py). عند إقلاع الويب (app.py):

  • يُجمع كل مُحوّل منصّة مُعدّ (Telegram / Discord / Slack).
  • محوّل واحد ← يوصَّل مباشرة.
  • اثنان أو أكثر ← يُغلَّفان في FanOutBusAdapter بحيث تتوزّع stream/report/alert على كل المنصات، وتُنشر بطاقة موافقة على كلٍّ منها.
  • لا شيء مُعدّ ← NullBusAdapter (فشل-مغلق للأدوات الخطيرة).
  • تمرّ الأدوات الخطيرة على هذا المسار عبر SafetyMiddleware.check(). والأدوات التي تطلب الموافقة تحدّدها أرضية الرسم نفسها: is_danger_tool() هي safety.hitl.approval_floor()، قاعدة المستوى التي تستخدمها requires_approval()، فالأداة غير المصنَّفة تطلب الموافقة هنا أيضًا. لا تُنشئ بوابة ويب ثانية من LocalToolRegistry.execute (H-8).

طلب واحد، قرار واحد (swarm/shared_approvals.py):

  • يفتح check() الطلبَ تحت مُعرِّف مُولَّد حديثًا، مربوط بالأداة وبتجزئة (hash) وسائطها. لا يأتي المُعرِّف أبدًا من وسائط الأداة. ويسجّله صف بوابة في hitl_gates.db (الآلية swarm_bus).
  • تعرض بطاقة كل منصّة الاستدعاء كاملًا: اسم كل وسيط وقيمة محدودة الطول، مُعلَّمة حين تُقطع، إضافة إلى تجزئة الوسائط (swarm/approval_card.py). وتُرفض الوسائط التي لا يمكن عرضها.
  • القرار صف مشترك واحد في مخزن الإعدادات (swarm.approval.<id>)، يُحسم مرة واحدة بالمقارنة والتعيين (compare-and-set). أول قرار بشري على أي منصّة يحسمه: موافقة، أو رفض، وهو نهائي (قرار المالك D7). ثم تُحرَّر كل بطاقة لتعرض تلك النتيجة الواحدة.
  • الموعد النهائي الخاص بالطلب هو الموعد الوحيد: بعده تكون النتيجة timeout، والمنتظِر الذي يُلغى ينهيه بـ cancelled. وليس أيٌّ منهما رفضًا، لا في سجلّ البوابات ولا في الملخّص اليومي.
  • يمرّ ضغط الزر عبر shared_approvals.decide_from_button(data, actor=…, platform=…). لا حاجة إلى نسخة من المحوّل، فالمُرسِل الذي أُعيد بناؤه منذ نُشرت البطاقة لا يُضيّع النقرة. ويسجّل صف البوابة الإنسانَ الذي ضغط (telegram:<id>، …)، لا swarm_bus أبدًا.
  • القرار الذي لا يمكن تسجيله (مخزن الإعدادات معطّل) يُسجَّل تحذيرًا (WARNING) يسمّي الفاعل، ويُعدّ في kazma_swarm_failures_total{site="approval_resolve"}؛ ويبقى الطلب عندئذٍ معلّقًا حتى موعده النهائي.
  • كل بطاقة ترتبط بالطلب على لوحة تحكم الويب (Full call:، قبل كتلة الوسائط، فلا يبلغه قطعُ أيّ منصّة): لوحة الموافقة في صفحة السرب، /swarm?approval=<id>، على KAZMA_PUBLIC_URL الخاص بالتثبيت (مسار الصفحة حين لا يُضبط). تعرض اللوحة الأداة وحالتها والاستدعاء الكامل الذي يحفظه سجلّ البوابة، مع تجزئة البطاقة، وما دام الطلب ينتظر ففيها موافقة ورفض: مكان آخر لإعطاء القرار نفسه، يُسجَّل بوصفه web مع المستخدم المسجَّل دخوله (إجراء للمشغّل؛ والقراءة متاحة لأي مستخدم مسجَّل الدخول). ولا يظهر طلب السرب أبدًا في قائمة المعلّقات في لوحة التحكم، التي تقبل البوابة بحسب جلسة الدردشة التي تنتمي إليها. واستدعاءان متطابقان يطلبان الموافقة في الوقت نفسه هما طلبان، لكلٍّ منهما سجلّ بوابته الخاص.

ما العمل الذي يطلب الموافقة أصلًا (safety/call_authority.py). تحسم interrupt() الخاصة بالرسم الاستدعاءاتِ الخطيرة في دور الدردشة؛ ولتلك الاستدعاءات يجب ألا يطلب الناقل الموافقة مرة أخرى. تلك الصلاحية تغطي استدعاء أداة واحدًا وتُستهلك عند أول تنفيذ له: كل ما يبدؤه الاستدعاء (مهمة سرب من dispatch_swarm، أو خيط، أو أداة أخرى) يجدها مُستنفَدة فيطلب الموافقة من الناقل. والجهات التي تبدأ عملًا منفصلًا تعمل أيضًا في سياق لا صلاحية فيه أصلًا (detached_context).

1.0b من يحقّ له استخدام السرب

Section titled “1.0b من يحقّ له استخدام السرب”
  • الأدوار (kazma_ui/swarm_panel/route_roles.py). كل مسار /api/swarm/* موجود في SWARM_ROUTE_ROLES، ويفرضه RoleCheckedRoute الخاص بالموجِّهات: القراءة (GET) تحتاج مستخدمًا مسجَّل الدخول؛ وكل ما يشغّل عمل السرب أو يغيّره أو يوجّهه (الإرسال، والموافقة، والرفض، والإلغاء، وإعادة المحاولة، والعمّال ومُحفِّزات أنظمتهم، والقوالب، وهدف المخرجات، وتصفير القواطع، والبدء/الإيقاف) يحتاج قرار المسؤول. والمسار الغائب عن الجدول يُرفض بوصفه للمسؤول فقط، ويفشل tests/test_swarm_route_roles.py حتى يُدرَج.
  • سطح لمشغّل واحد (القرار D1، swarm/single_operator.py). مهام السرب غير مفصولة لكل مستخدم، لذا في تثبيت فيه مستخدمو منصّات (أو KAZMA_MULTI_USER=1) يرفض كل سطح من أسطح السرب: تجيب واجهة الويب البرمجية بـ 409 swarm_single_operator، وتقول الصفحة ذلك بلغتها، ويردّ أمر الدردشة /swarm والأداتان dispatch_swarm / check_swarm_task الخاصتان بالوكيل بالسبب. ويُرفض الطلب أيضًا حين لا يستطيع مخزن الإعدادات الإجابة.
  • توجيه نتيجة إلى دردشة أخرى باللاحقة -> platform:chat في أمر الدردشة للمسؤول فقط، مثل /swarm config.

1.0c تشغيل المهمة: مالك واحد، وسجلّ واحد

Section titled “1.0c تشغيل المهمة: مالك واحد، وسجلّ واحد”
  • المحرّك يملك التشغيل (SwarmEngine._run_owned). يسجّل التشغيلَ قبل أول await له، فيستطيع الإلغاء والمراقب (watchdog) إيقافه أيًّا كان من بدأه. والإرسال الثاني لمُعرِّف قيد التشغيل ينتظر ذلك التشغيل؛ والمُعرِّف الذي انتهى من قبل (أو توقّف مؤقتًا) يُجاب من سجلّه ولا يُشغَّل مرة أخرى أبدًا. والمستدعي الذي يكفّ عن الانتظار يوقف التشغيل، وينتظر (بحدّ) حتى ينتهي. ويوقف الإغلاقُ كل تشغيل (stop_all_runs).
  • القبول مقارنة وتعيين على صف RUNNING (TaskStore.admit). لكل تشغيل generation وrun_id؛ ولا يتحرك الصف إلا على جدول الانتقالات (persist_task): الحالة المنتهية لا تُغادَر أبدًا، والصف لا يُعاد أبدًا إلى pending، والكتابة من جيل أقدم تُرفض. والنتيجة المتأخرة المرفوضة تُحفظ دليلًا (metadata.orphan_results)، لا نتيجةً للمهمة أبدًا.
  • التشغيل المُقاطَع يقول ما قد يكون غيّره. لكل إرسال وخطوة ومهمة متتبِّع آثار (agent/dispatch_effects.py). والتشغيل الذي يوقفه المستخدم أو موعد نهائي أو الإغلاق ينتهي بـ interrupted_by (سببه) وexecution_outcome: إما none_invoked، أو unknown مع invoked_tools (الأدوات التي تُغيّر شيئًا وكان قد شغّلها). ومهلة الخطوة في النمط مبنية من المتتبِّع نفسه. والنتيجة التي آثارها غير مؤكَّدة تُخزَّن failed، لا مكتملة أبدًا.
  • إعادة المحاولة تسأل أولًا حين تكون النتيجة مجهولة. يجيب POST /api/swarm/tasks/{id}/retry بـ 409 retry_outcome_unknown (مع الأدوات) لمهمة آثارها مجهولة؛ فتسأل الصفحة المشغّل وترسل acknowledge_unknown=1. ونتيجة العامل المُعلَّمة retryable: false (كانت قد عملت أداة تُغيّر شيئًا) توقف إعادة المحاولة والسلسلة الاحتياطية.
  • خط المعالجة المُستأنَف يتخطّى الخطوات التي شغّلها من قبل. كل خطوة منتهية تُدوَّن في سجلّ المهمة؛ والاستئناف بعد انهيار يبدأ بعد آخرها. وتُستعاد خطوط المعالجة المتوقفة عند الإقلاع مهامَّ متوقفة قيد التنفيذ (لا تُحسب ضمن حدّ القبول)، فيصل إليها الرفض والإلغاء.

1.0d ما يصل إلى النموذج مُسيَّج

Section titled “1.0d ما يصل إلى النموذج مُسيَّج”

نص العامل هو ما قرأه وكتبه (صفحات الويب، والملفات، ومخرجات الأدوات الأخرى)، لذا يصل إلى كل نموذج بوصفه بيانات داخل كتلة fence_untrusted(...)، لا تعليمات أبدًا: نتيجة check_swarm_task الخاصة بالوكيل وخطؤها، ومُخرَج العامل السابق والسبورة المُسلَّمان إلى خطوة خط المعالجة التالية، والنتائج الوسيطة للتسليم، ومُحفِّز التركيب (aggregator._synthesize_by_model) و”Refiner” خط المعالجة (patterns._refiner_input: كل خطوة تحت اسم عاملها). يفشل tests/test_swarm_boundaries_and_signals.py على دالة سرب تبني نصًا من مُخرَج عامل خارج سياج، إلا إذا صُرّح بها بوصفها النتيجة المخزَّنة للمهمة (التي يسيّجها check_swarm_task حيث تصل إلى نموذج).

الحدّالقيمةأين
مهلة المهمة (لكل خطوة في الأنماط)1–3600 ثانيةswarm/limits.py
المُحفِّز / السياق / البيانات الوصفية64 KiB / 256 KiB / 64 KiBنفسه
العمّال / نقاط الحفظ / السلسلة الاحتياطية32 / 32 / 16نفسه
خطوط المعالجة المتوقفة في آن واحد100نفسه
إرسالات العمّال المتوازية، لكل المهام64نفسه
تدفقات المهام لكل عميل / إجمالًا16 / 128kazma_ui/swarm_sse.py
نبضة التدفق / عمره15 ثانية / 30 دقيقة (يستأنف العميل)نفسه
انتظار الوكيل لنتيجة (check_swarm_task)60 ثانية افتراضيًا، و90 ثانية على الأكثر (wait_seconds؛ و0 يفحص مرة واحدة)agent/tool_builtins/system.py

يجيب check_swarm_task لحظة انتهاء المهمة؛ والمهمة التي لا تزال تعمل بعد الانتظار يُبلَّغ عنها أنها تعمل، ويستدعي النموذج الأداة مجددًا بالمعرّف نفسه. ويبقى الحد الأقصى دون مهلة الأداة البالغة 120 ثانية.

ترفض واجهة الويب البرمجية الطلبَ الخارج عن النطاق بـ 422؛ ويرفض المحرّك مثله القادمَ من أي مدخل آخر قبل القبول. والسرب الممتلئ يجيب بـ 429 swarm_at_capacity مع Retry-After. وتحفظ النتيجة المخزَّنة كل مُخرَج عامل كبير مرة واحدة، مُشارًا إليه من مواضعه الأخرى.

1.0f ما يتركه العطل (تشغيلات الفوضى)

Section titled “1.0f ما يتركه العطل (تشغيلات الفوضى)”

يحقن scripts/swarm_chaos.py عطلًا واحدًا في كل مرة في محرّك حقيقي بعمّال مُبرمَجين بسيناريو، ويسجّل النهاية (يُثبّت tests/test_swarm_chaos.py كل واحدة منها؛ متعقِّب السرب SQ04). القواعد التي تتبعها تلك النهايات:

  • كتابة المهمة التي يرفضها المخزن لسبب عابر (قفل، أو ملف مشغول، أو قاعدة بيانات غائبة) تُجرَّب مرة أخرى: ثلاث محاولات إضافية، تفصل بينها 0.5 ثانية و1 ثانية و2 ثانية، وكلٌّ منها تنتظر أيضًا مهلة انشغال المخزن (5 ثوانٍ في SQLite) أو مهلة اتصاله (Postgres).
  • التشغيل الذي لم يأخذ المخزن نهايته قط لا يُبلَّغ عنه بوصفه عملًا مخزَّنًا. يتلقّى المستدعي، وسجلّ المحرّك الخاص، وتدفق المهمة failed مع السبب (metadata.unsaved، وstore_error، وresult_status الذي يحمل ما انتهى إليه التشغيل؛ ويُحتفَظ بالمُخرَج). ويبقى الصف على آخر حالة مخزَّنة له، RUNNING بعد القبول، ويعدّه الإقلاع التالي مُقاطَعًا بنتيجة مجهولة.
  • المهمة الجديدة التي لا يمكن كتابة صف RUNNING الخاص بها لا تُشغَّل. تُجرَّب كتابة الصف مرة أخرى كما تُجرَّب أي كتابة مهمة؛ والكتابة التي تفشل رغم ذلك ترفض المهمة بـ task_store_unavailable (تحذير WARNING، ويُعدّ running_row، وتُحرَّر خانة قبولها، ولم يعمل شيء). يجيب مسار الإرسال بـ 503 مع Retry-After، وينتهي تدفق الإرسال في الخلفية بالرفض نفسه، وتقوله صفحة السرب بلغة الصفحة؛ وإرسالها مرة أخرى يشغّلها. حتى 2026-10-15 كانت المهمة تعمل دون صف RUNNING الخاص بها، فكان الانهيار فيها يترك آثارًا لا يراها أي تعافٍ. الموافقة التي لا يمكن كتابة صفها لا تستأنف شيئًا: تعود نقطة الحفظ معلّقة، ويُعاد تسليح رفضها التلقائي، وتجيب الصفحة بـ 503 approval_not_recorded — فلو استُؤنفت، لعرض الانهيار نقطة الحفظ مرة أخرى وشغّل خطواتها مرتين.
  • الإرسال الدائم الذي لم يعمل قط (Temporal مطلوب وغير قابل للوصول) يُنهى فاشلًا مع السبب، فيُحرِّر صفه، وخانة قبوله، وتدفّقه.
  • كل كتابة ضائعة تُعدّ في kazma_swarm_failures_total: running_row، وtask_write، وstep_journal، وworker_metric، وللقواطع المشتركة breaker_read، وbreaker_persist، وbreaker_lease.
  • القاطع المشترك لا يأخذ أبدًا حالة أقدم من مخزن الإعدادات: يطبّق التحديث حالةَ نسخة أخرى فقط حين يكون جيلها أحدث (التقط اختبار التحمّل (soak) انطلاقًا للقاطع ألغته النسخة المكتوبة قبله).
العطلما يُبلَّغ به المستدعيما يحفظه المخزن (بعد الإقلاع التالي)ما يُعدّ
مخزن SQLite مقفل طوال كل محاولات كتابة صف RUNNINGفشل، task_store_unavailable (لم يعمل شيء؛ أعد المحاولة)لا شيء (لا صف)running_row
مقفل عند المحاولة الأولى لصف RUNNING، وحرّ قبل نفاد المحاولاتنجاح (أُعيدت تجربة الكتابة، وعملت المهمة مرة واحدة)مكتملةلا شيء
مقفل طوال سجلّ خطوات خط معالجة مُستأنَفنجاح (كل خطوة مرة واحدة)مكتملةstep_journal x2
مقفل بعد محاولة الكتابة الأولى للنهايةنجاح (أُعيدت تجربة الكتابة)مكتملةworker_metric
مقفل بعد كل محاولات كتابة النهايةفشل، unsaved (ما انتهى إليه التشغيل: نجاح)قيد التشغيل (فاشلة، النتيجة مجهولة)task_write، worker_metric
مقفل عند الموافقة على خط معالجة متوقف، ثم حرّالموافقة الأولى: لم يُستأنف شيء، ونقطة الحفظ مفتوحة؛ الثانية: نجاحمكتملةrunning_row
القواطع المشتركة مفعّلة، ومخزن الإعدادات معطّل؛ حدّ القبول 1رُفض عند السعة؛ الانطلاق والرفض والتعافي بالفحص على القاطع المحليمكتملة / فاشلة كما انتهت كلٌّ منهاbreaker_read، breaker_persist، breaker_lease
Temporal مُعدّ، ولا شيء يستمعنجاح (احتياطي داخل العملية، تحذير WARNING)مكتملةلا شيء
Temporal مطلوب، ولا شيء يستمعفشل، يسمّي Temporal (لم يعمل العامل قط)فاشلةلا شيء
Temporal (مطلوب) يقبل سير العمل، ثم يغيب أثناء عمل المهمةنجاح (نهاية العمل نفسه؛ لا انتظار لـ Temporal بعد 10 ثوانٍ)مكتملةdurable_workflow
إغلاق بينما يعمل خط معالجة وتوزيع متشعّب واستشارة وإرسالأُلغيتملغاة، interrupted_by: shutdown، وunknown + file_write حيث يُذكر، وnone_invoked عدا ذلك، مكتوبة قبل أن يعود stop_all_runs؛ لا شيء يحتاج إلى تعافٍلا شيء
مخزن Postgres يرفض الاتصالات عند القبول، ويعود أثناء إعادة تجربة كتابة صف RUNNINGنجاح (عملت المهمة مرة واحدة)مكتملةلا شيء
مخزن Postgres يرفض الاتصالات عند النهايةفشل، unsaved (ما انتهى إليه التشغيل: نجاح)قيد التشغيل (فاشلة، النتيجة مجهولة)task_write، worker_metric

سُجّل في 2026-10-15 بمهل المنتج (Windows، وSQLite، وpostgres:16-alpine مؤقت، وخادم اختبار Temporal ذي تخطّي الوقت). حالات المسار الدائم الخاصة (المواعيد النهائية، والإلغاءات، وعملية عامل مقتولة) موجودة في §1.0a.

1.0a التنفيذ الدائم (Temporal، اختياري)

Section titled “1.0a التنفيذ الدائم (Temporal، اختياري)”

يبقى تخطيط السرب داخل العملية. عند ضبط KAZMA_TEMPORAL_HOST، تعمل المهمة المقبولة عبر سير عمل Temporal واحد لكل تشغيل (kazma_core/swarm/durable.py): kazma-swarm-<task id>، وkazma-swarm-<task id>-g<generation> لاستئناف خط معالجة تمّت الموافقة عليه، وهو يسلك المسار الدائم أيضًا. يعمل عمّال Temporal داخل عملية كاظمه (دورة حياة التطبيق، فشل-مفتوح؛ ويُوقَفون عند الإغلاق بعد أن يوقف المحرّك تشغيلاته). ويبقى الافتراضي asyncio. مفتاح الإيقاف KAZMA_TEMPORAL=0. الوضع الصارم: KAZMA_TEMPORAL_REQUIRED=1 (لا احتياطي داخل العملية؛ فتُؤجَّل الموافقة عندئذٍ، وتُترك نقطة حفظها مفتوحة، ما دام Temporal غير متاح: 503 temporal_unavailable). إضافي: pip install -e ".[durable]".

ما يضيفه Temporal هو هوية دائمة وتاريخ لكل تشغيل، ونبضة تسمّي العملية التي ماتت. أما ما يعمل، وكيف ينتهي، فهو تشغيل المحرّك نفسه:

  • لا يعمل العمل إلا حيث قُبل. يُجدوَل النشاط الوحيد لسير العمل على طابور مهام لا تستطلعه إلا عملية كاظمه تلك، ولا يعمل إلا لتشغيل مسجَّل ينتظر هناك، في سياق ذلك التشغيل نفسه. ولا تشغّله أبدًا عمليةُ كاظمه أخرى، ولا العملية نفسها بعد إعادة التشغيل.
  • مالك واحد للمواعيد النهائية: المحرّك. يشغّل النشاط الجسمَ نفسه الذي يشغّله التشغيل داخل العملية (SwarmEngine._run_admitted، أو _resume): الموعد النهائي نفسه، والمراقب نفسه، والنهايات نفسها، بما فيها interrupted_by وexecution_outcome. وتُؤخذ حدود Temporal من الميزانية نفسها وتقع بعد نهاية المحرّك للتشغيل (هامش المراقب، ومهلة إيقافه، و60 ثانية إضافية). فإن انطلق أحدها مع ذلك، أو أُجري إلغاء أو إنهاء عبر Temporal، يوقف التشغيلُ العملَ ويسجّله كما يسجّله المحرّك: timeout / deadline، وfailed / worker_lost (توقفت النبضة)، وcancelled / temporal_cancel.
  • مالك واحد لإعادة المحاولة: المحرّك. لا يشغّل Temporal النشاط مرة أخرى أبدًا (maximum_attempts=1). وتعمل محاولات العامل الخاصة داخله، فقط ما دامت لم تعمل أداة تُغيّر شيئًا.
  • الإيقاف يوقف العمل. الإلغاء، أو المراقب، أو الإغلاق، أو المستدعي الذي يكفّ عن الانتظار، يوقف العمل في العملية أولًا (دون حاجة إلى أن يكون Temporal قابلًا للوصول)، ثم يطلب من Temporal إلغاء سير العمل.
  • عملية تموت في منتصف التشغيل. يعدّ Temporal عاملها مفقودًا بعد 20 ثانية بلا نبضة (ينبض النشاط كل ثانيتين)، ويُغلق عاملُ سير العمل في كاظمه المُعاد تشغيلها ذلك السيرَ فورًا (لا يحتفظ العمّال بذاكرة تخزين مؤقت لسير العمل، فلا تنتظر أي مهمة على الطابور اللاصق (sticky queue) للعملية الميتة). ويعدّ الإقلاع التالي المهمةَ ذات نتيجة مجهولة تسمّي الخطوة التي توقفت فيها (يدوّن خط المعالجة كل خطوة، في التشغيل الأول والاستئناف على السواء)؛ ولا يعمل شيء مرة أخرى.
  • لا يغادر العمليةَ شيءٌ كبير. يحمل سير العمل مُعرِّف المهمة، ونوعها وحدودها، لا مُحفِّزها ولا مُخرَجها أبدًا؛ ونتيجته ملخّص (يحفظ مخزن المهام النتيجة). والتشغيل الذي انتهى عمله ينتظر 10 ثوانٍ على الأكثر أن يُغلق Temporal سير العمل (ويُعدّ durable_workflow حين لا يفعل): فـ Temporal الذي غاب لا يحتجز أي نهاية رهينةً.

مؤهَّل على خادم اختبار Temporal ذي تخطّي الوقت (tests/test_swarm_temporal_durable.py): مهمة وخط معالجة مرة واحدة، وعامل تجاوز موعده النهائي (السجلّ نفسه كما داخل العملية)، وحدّ Temporal الخاص، وإلغاء من كاظمه وآخر عبر Temporal، وإغلاق في منتصف التشغيل، واستئناف تمّت الموافقة عليه، وإقرارات بدء ونتيجة ضائعة، ونشاط لا ينتظره أي تشغيل، وغياب Temporal في منتصف التشغيل، وعملية عامل حقيقية تُقتل في منتصف خطوة. ولا يتخطّى خادم الاختبار الوقت ما دام نشاط مُجدوَلًا أو قيد العمل، لذا تعمل تلك الحدود قصيرة وحقيقية في الاختبارات.

def __init__(
self,
config: SwarmConfig | None = None,
*,
result_aggregator: ResultAggregator | None = None,
task_store: TaskStore | None = None,
metrics_collector: MetricsCollector | None = None,
tracing_emitter: TracingEmitter | None = None,
) -> None: ...
السمةالنوعالغرض
_workersdict[str, SwarmWorker]العمّال المُسجَّلون.
_active_tasksdict[str, SwarmTask]المهام قيد التنفيذ.
_task_handlesdict[str, asyncio.Task]مقابض الإلغاء.
_task_historydict[str, SwarmTask]مقيّدة بـ LRU (_max_history=500).
_routing_engineUnifiedRouterالتوجيه التلقائي لحالة workers=["auto"].
_reliabilityReliabilityRegistryقواطع/إعادة محاولة/مهل/مُدقّقات/تزامن.
_checkpoint_handlerHITLCheckpointHandlerحالة خط المعالجة المتوقّف.
_checkpoint_mgrCheckpointManagerاستمرارية نقاط الحفظ + المهل.
_task_storeTaskStoreاستمرارية SQLite.
_metrics_collectorMetricsCollectorمقاييس في الذاكرة + SQLite.
_tracing_emitterTracingEmitterباعث spans داخلي.
_phonebookWorkerPhonebookالبحث عن العمّال عبر الطوبولوجيا/DAG.
_sseSseBridgeجسر أحداث SSE.

توقيع المُنشئ لم يتغيّر بعد تقسيم P2-1 (reliability_registry.py، وphonebook.py، وcheckpoint_manager.py). تعمل تجهيزات الاختبار دون تعديل. لا تثبّت أعداد أسطر engine.py — فهي تنحرف.


تعداد TaskType (swarm/task.py): DISPATCH، وBROADCAST، وPIPELINE، وFAN_OUT، وCONSULT، وCONDITIONAL. يعيش التوجيه في dispatch_inner.py (يُستدعى من engine.dispatch ← engine._dispatch_inner).

النمطنقطة الدخول في المحرّكالدالة المنفِّذةالوصف
dispatchengine.dispatch ← dispatch_innerعامل واحد مضمّن + سلسلة احتياطية اختياريةيعالج عامل واحد المهمة.
broadcastengine.dispatch (وengine.broadcast غلاف فوقه)broadcast.broadcast_taskيستقبل جميع العمّال المُحفِّز نفسه؛ وتُقبل المهمة وتُسجَّل وتُنهى مثل أي مهمة.
pipelinedispatch_innerpatterns.execute_pipelineمراحل مرتّبة تتشارك سبورة؛ تدعم نقاط حفظ HITL.
fan_outdispatch_innerpatterns.execute_fan_outتنفيذ متوازٍ بتزامن محدود + تجميع.
consultdispatch_innerconsultation.execute_consultآراء متوازية + تركيب LLM.
conditionaldispatch_innerpatterns.execute_conditionalيقرّر المُوجّه أي عنصر من خريطة المسار يُرسَل إليه.

تُحلّ workers=["auto"] في dispatch_inner.py عبر engine._routing_engine.route(...) (‏UnifiedRouter). لا ينطلق المُغيّر التلقائي maybe_scale(...) إلا عند NoCapableWorkersError — أبدًا لا عند طلب عامل مُسمّى أو نجاح التوجيه.

التوجيه الدلالي (swarm/semantic_router.py، تدقيق السرب SW-E10). تُضمَّن المهمة وملفّ تعريف كل عامل (اسمه — وهو قالب نسخة المُغيّر التلقائي —، وخبرته وبداية مُحفِّز نظامه، وإلا فدوره) بمُضمِّن كاظمه الخاص (memory.embedder.get_embedder، النموذج الذي تستخدمه الذاكرة) وتُقارن بجيب التمام (cosine)، بدقة، داخل العملية: بلا مخزن متجهات، فلا دالة تضمين افتراضية لأي مخزن. (كان المُوجِّه يستخدم مجموعة Chroma تُضمِّن ملفات التعريف بنموذج Chroma الافتراضي ذي 384 بُعدًا بينما تستخدم المهمة bge-m3 ذا 1,024 بُعدًا: فكان كل استعلام يفشل على الأبعاد.) يطابق العاملُ حين يبرز ملفّ تعريفه: تشابهه ناقصًا متوسط تشابه ملفات التعريف المتمايزة الأخرى (ممزوجًا بـ 0.40 حين تكون الأخرى أقل من ثلاثة) لا يقل عن MATCH_FLOOR (0.045)؛ ونسخ القالب الواحد تتشارك ملفّ تعريف واحدًا. قيس ذلك بـ bge-m3 على tests/fixtures/swarm_routing/ (35 مهمة بالإنجليزية والعربية، و7 عمّال؛ scripts/swarm_routing_bench.py): وُجِّهت 19 من 24 مهمة تخصصية توجيهًا صحيحًا (21 مع الاحتياطي بالكلمات المفتاحية خلفه؛ والكلمات المفتاحية وحدها 5، فهي لا تقرأ العربية)، وأُرسلت 1 من 11 مهمة غير ذات صلة إلى عامل متخصص، والعامل المنفرد لا يأخذ أي مهمة تخصّ غيره (0 من 185). أما أقرب عامل بلا أرضية فأرسل 7 من 11 مهمة غير ذات صلة إلى متخصص ولم يدع أي مهمة تصل إلى المُغيّر التلقائي.

يجري التضمين في خيط ضمن SEMANTIC_DEADLINE_S (5 ثوانٍ)، اثنان على الأكثر في آن واحد. ومع غياب المُضمِّن، أو فشله أو بطئه أو انشغاله، تُوجَّه المهمة بالكلمات المفتاحية؛ وتقول تشخيصات التوجيه السبب (semantic.reason)، ويُسجَّل التحوّل مرة واحدة عند مستوى WARNING (kazma_swarm_failures_total{site="semantic_routing"})، وتعافيه عند INFO.

تسمّي المهمة العمّالَ الذين عملوا. يشغّل DISPATCH أفضل مرشّح، فيحمل task.workers (وmetadata.routed_workers) ذلك العامل وحده؛ وكل مرشّح، مع تشابهه، ودليله، وتداخل كلماته المفتاحية، وتعزيز اللهجة، موجود في metadata.routing_diagnostics (candidates، وscores، وsemantic)، مكتوبًا قبل الرفض أيضًا. والمهمة التي أخذها المُغيّر التلقائي تسجّل ذلك هناك (autoscaled: القالب، والعامل، وهل أُعيد استخدامه).

  • السبورة المشتركة — ترى كل مرحلة مخرجات المراحل السابقة.
  • نقاط حفظ HITL — task.metadata["hitl_checkpoints"] مجموعة من فهارس خطوات ذات أساس 1. عند نقطة حفظ، يتوقف التنفيذ بحالة status="paused" مع بيانات وصفية لنقطة الحفظ.
  • التسليم إلى الخطوة التالية — تتلقّى الخطوة التالية مُخرَج العامل السابق والسبورة مُسيَّجَين (§1.0d).
  • الاستئناف — patterns.resume_pipeline، بعد آخر خطوة مُدوَّنة (§1.0c).
  • المعالجة اللاحقة (patterns._finalize_pipeline) تشغّل أربعة خطافات: تركيب “Refiner” عبر LLM، والتحسين الذاتي، ومُسجّل خط المعالجة، ثم تُعيد PatternExecution.
  • الـ Refiner (تدقيق السرب SW-E15، القرار D8: أُصلح). خط المعالجة الذي نجح — في تشغيله الأول أو في استئناف — يُقطَّر باستدعاء نموذج واحد إلى تقرير. التقرير هو synthesized_output في النتيجة (تركيب الصفحة)؛ ويبقى منتَج خط المعالجة، أي مُخرَج خطوته الأخيرة، aggregated_output، ويبقى مُخرَج كل خطوة في worker_results. وتُضاف رموزه وكلفته إلى total_tokens / total_cost، وتُحفظ في metadata.refiner، وتُسجَّل في دفتر LLM لكل استدعاء مع مُعرِّف المهمة بوصفه thread_id، كما تُسجَّل استدعاءات العامل. يقرأ مخرجات الخطوات مُسيَّجة، 4,000 حرف إجمالًا، ويكتب 2,048 رمزًا على الأكثر، ويُعطى موعد الخطوة النهائي (120 ثانية على الأكثر)؛ وتعدّه ميزانية المراقب لخط المعالجة خطوة إضافية. والـ Refiner الذي يفشل، أو لا نموذج له، أو ينفد وقته يترك النتيجة غير المُنقّحة: تحذير WARNING وkazma_swarm_failures_total{site="refiner"}، وmetadata.refiner.status = "failed". ولا يُنقَّح خط المعالجة الفاشل أو الجزئي (لا تقرير فوق تشغيل مكسور). قبل ذلك، لم يكن مُخرَجه يصل قط إلى النتيجة المخزَّنة، وعند الفشل كان “تقرير خام” يحلّ محلّه.

2.3 تخطيط حالة التوزيع المتشعّب (fan-out)

Section titled “2.3 تخطيط حالة التوزيع المتشعّب (fan-out)”
الشرطالحالة
نجاح جميع العمّالsuccess
نجاح بعضهمpartial
عدم نجاح أيٍّ منهمfailed

ResultAggregator.aggregate (swarm/aggregator.py)، مفتاح التوجيه task.aggregation (الافتراضي "collect"):

الاستراتيجيةالسلوك
collectبيانات وصفية فقط؛ لا مُخرَج مجمَّع.
first_validأول نتيجة ناجحة.
merge_allضمّ كتل [worker] output.
voteفرز الأغلبية؛ تُكسَر التعادلات بترتيب الظهور الأول.
synthesizeتركيب LLM عبر _SYNTHESIS_SYSTEM_PROMPT، مع تسييج مُخرَج كل عامل؛ احتياطي حتمي _fallback_synthesis.
(غير معروفة)تُطلِق ValueError.

التركيب (هنا وفي الاستشارة) تُحسب كلفته على المهمة، مثل الـ Refiner: تنضمّ رموزه وكلفته إلى إجماليات المهمة وإلى metadata.synthesis، ويحمل الدفتر الاستدعاء. كل استدعاء نموذج يجريه السرب لمهمة خارج عامل يمرّ عبر swarm/task_model.call_task_model (أي resilient_chat الخاص بالوكيل: إعادة المحاولات، والتحويل عند الفشل (failover)، وصف الدفتر)؛ والاحتياطي حين لا يستطيع النموذج الإجابة يُسجَّل تحذيرًا (WARNING) ويُعدّ في kazma_swarm_failures_total{site="synthesis"}.


4. التسليمات واكتشاف الدورات

Section titled “4. التسليمات واكتشاف الدورات”

يستطيع العامل تسليم ما تبقّى من مهمته إلى عامل آخر في منتصفها. ويفعل ذلك عامل LLM المُشحون (InProcessWorker) عبر أداة يستدعيها نموذجه، handoff_to_worker(worker, reason, task?, context?) (swarm/handoff.py، تدقيق السرب SW-E13، قرار المالك D6: التسليم ميزة في المنتج):

  • لا يُعرض إلا داخل المحرّك. كل إرسال يشغّله المحرّك يربط HandoffOffer (worker_dispatch.dispatch_worker): العمّال المسجَّلين وقوالب المُغيّر التلقائي التي يجوز للعامل التسليم إليها (لا نفسه أبدًا؛ وتُعرض نسخة المجمّع بوصفها قالبها)، وحارس الدورات أدناه. والأداة أداة العامل الخاصة، لا أداة سجلّ الأدوات: لا يراها الوكيل الرئيسي، ولا خادم MCP، ولا المهام المجدولة أبدًا. والعامل الذي تكون capabilities.tools لديه قائمة سماح لا يحصل عليها إلا حين تسمّيها القائمة.
  • ما يفعله الاستدعاء. يُطلق العامل HandoffRequest؛ فيرسل المحرّك (SwarmEngine._handle_handoff) الهدفَ مع المُحفِّز الأصلي، والسياق الأصلي، وcontext الذي مرّره النموذج (مُسيَّجًا، §1.0d)، ويحتفظ ببيانات الإرسال الوصفية، ومُعرِّف المهمة، ومساحة العمل (فرع conditional لا سبورة له، وكان يفقدها)، ويسجّل HandoffRecord (من، وإلى، والسياق، والسبب) على نتيجة المصدر. وإجابة الهدف هي إجابة المهمة؛ وتُحسب رموز المصدر وكلفته قبل أن يسلّم ضمن إجماليات المهمة. وترسم صفحة السرب السلسلة من حدث handoff الخاص بالمهمة ومن النتيجة المخزَّنة.
  • عند الرفض يُبلَّغ النموذج. الاستدعاء الذي يسمّي عاملًا لا يدرجه العرض، أو بلا سبب، أو بجانب استدعاءات أدوات أخرى، أو تحت نطاق عامل لا يسمح بالفعل delegate (طبقة الالتزام، كما في dispatch_swarm)، أو الذي يرفضه حارس الدورات، يُجاب بوصفه نتيجة الأداة، ويُنهي العامل المهمة بنفسه.
  • لا يوافق على شيء. تمرّ أدوات الهدف عبر سجلّ الأدوات وناقل السرب كما تمرّ أدوات أي عامل (§1.0)؛ وhandoff_to_worker من المستوى write، وأثره delegate، ولا بطاقة له أبدًا.

تُمنَع الحلقات اللانهائية بحارسين في swarm/handoff_guards.py:

الثابتالقيمةالمعنى
MAX_HANDOFF_DEPTH5يُرفض التسليم الذي عمقه 5: أربعة تسليمات على الأكثر، وخمسة تشغيلات.
MAX_VISITS2يعمل العامل مرتين على الأكثر في سلسلة واحدة: يعمل A→B→A (العودة) وA→B→A→B؛ ويُرفض تسليم B عائدًا إلى A (تشغيل A الثالث).

الآلية:

  1. _visited قاموس dict[str, int] لأعداد زيارات كل عامل (وليست مجموعة منطقية)، مفتاحه هوية العامل (SwarmEngine._handoff_identity): تُحسب نسخة المجمّع بوصفها قالبها، لأن التسليم إلى قالب يولّد نسخة جديدة في كل مرة.
  2. يزيد _register_handoff_visit عدد العامل المصدر.
  3. يرفض _handoff_guard_error التسليم حين يكون عمقه 5 أو أكثر، أو حين يكون هدفه قد عُدّ مرتين من قبل.
  4. يُزاد _depth داخل worker_dispatch.dispatch_worker عندما يستدعي engine._handle_handoff(..., _depth=_depth + 1)؛ ويسأل العرضُ الحارسَ نفسه عند العمق نفسه قبل إطلاق الطلب.
  5. عند النجاح/الفشل، يُستدعى record_success/record_failure الخاص بقاطع المصدر مع قبول الإرسال (§5.1)، وكل نهاية للتسليم، بما فيها الرفض، تُنهي عدّ المصدر قيد التنفيذ (_release_handoff_source؛ كان الرفض يتركه مشغولًا إلى الأبد).
  6. تصل النتائج الوسيطة للعامل المُسلِّم إلى الهدف مُسيَّجة (§1.0d).
  7. كل تسليم يُعدّ في kazma_swarm_handoffs_total.

البوابات: tests/test_swarm_handoff_tool.py (نموذج مُبرمَج بسيناريو، من طرف إلى طرف؛ مع إزالة الحارس والعدّ بأسماء النسخ ضابطَين سلبيَّين) وtests/test_swarm_handoff.py.

ملاحظة تسمية الرموز: تشير docstring المحرّك نصًّا إلى _MAX_VISITS، لكن الرمز المُصدَّر هو MAX_VISITS (handoff_guards.py). القيمة نفسها (2).


ReliabilityRegistry (swarm/reliability_registry.py) هي حامل إعدادات. تعيش آلات الحالة الفعلية في swarm/reliability.py. ستة مكوّنات:

الجانبالتفصيل
الحالاتCLOSED، وOPEN، وHALF_OPEN (CircuitState StrEnum).
failure_thresholdالافتراضي 5.
cooldown_secondsالافتراضي 60.0.
OPEN → HALF_OPENانتقال تلقائي عندما يصبح time.monotonic() - _opened_at >= cooldown (الخاصية state).
القبوليُعيد admit() (وcheck_or_raise() على مسار الإرسال) قيمة BreakerAdmission(generation, probe)، أو None عند الرفض. في HALF_OPEN يُسمح بمرور إرسال واحد بالضبط، محجوبًا بـ _probe_in_flight. لا تُزل هذه العلامة أبدًا. وallow_probe() هي admit() is not None.
الأجيالكل تغيير في الحالة (انطلاق، أو إغلاق، أو إعادة فتح، أو تصفير) يزيد generation. والنتيجة المُمرَّرة مع قبول من جيل أقدم، أو أي نتيجة غير نتيجة الفحص نفسه بينما فحص قيد التنفيذ، تكون قديمة: تُعدّ في stale_outcomes، ولا تُطبَّق أبدًا. والإرسال المقبول قبل الانطلاق لا يستطيع إغلاق قاطع نصف مفتوح بينما الفحص لا يزال يعمل.
record_success(admission)يُصفّر consecutive_failures، ويمحو الفحص، وHALF_OPEN ← CLOSED.
record_failure(admission)يمحو الفحص؛ HALF_OPEN ← إعادة OPEN بمؤقّت جديد؛ CLOSED ← زيادة، والانطلاق عند الحدّ.
الحالة المشتركةمع تفعيل KAZMA_SHARED_BREAKERS، يكتب persist_shared بالمقارنة والتعيين على مخزن الإعدادات: كتابة النسخة (replica) لا تحلّ أبدًا محلّ جيل أحدث. وتُحذف مفاتيح عامل المجمّع المحصود (forget_shared).
الاستمراريةto_dict() / from_dict() (بما فيها الجيل)؛ السجلّ save_breaker_state/load_breaker_state.

يُقرأ القاطع خارج حلقة الأحداث (ReliabilityRegistry.breaker_for_dispatch، مع RLock حول كل تغيير).

الحقلالافتراضي
max_retries3
base_delay1.0 ثانية
max_delay60.0 ثانية
jitterTrue

تراجع أُسّي: base_delay * 2**(attempt-1). تصنيف واحد مشترك مع طبقة LLM (_is_retryable_exception): يحدّد LLMError حاله بنفسه (transient)؛ وإلا فإن 401/403، وauth/unauthorized/forbidden، وapi key، وnot found، وquota، وbilling دائمة، و429 / rate limit عابرة. تُعاد المحاولة عند الاستثناءات وعند القواميس ذات status in ("error","timeout")، إلا النتيجة المُعلَّمة retryable: false (كانت قد عملت أداة تُغيّر شيئًا). والإرسال يُحسب عدًّا واحدًا قيد التنفيذ مهما كان عدد محاولاته.

الحقلالافتراضي
default_timeout300.0 ثانية
on_timeoutfail | retry | skip

يستخدم asyncio.wait_for. عند انتهاء المهلة يُعيد \{"status": "timeout", ...\} مع retry=True / skipped=True اختياريًا.

5.4 OutputValidator (swarm/reliability.py)

Section titled “5.4 OutputValidator (swarm/reliability.py)”

يدعم ثلاثة أنواع من المخططات:

  • BaseModel في Pydantic
  • JSON Schema (عبر jsonschema إذا كان مثبَّتًا، وإلا احتياطي مدمج)
  • قاموس أنواع بسيط (\{"name": "str"\})

يُحلّل تلقائيًا المُخرَج النصّي بوصفه JSON عندما يتوقّع المخطط كائنًا/مصفوفة.

احتياطيات متتابعة بعد فشل الأساسي. يحصل كل احتياطي على HandoffRecord. يُنهي النجاحُ الأول السلسلة؛ ويعيد النفاد خطأً مُلخَّصًا.

5.6 BoundedConcurrency (swarm/reliability.py)

Section titled “5.6 BoundedConcurrency (swarm/reliability.py)”

غلاف asyncio.Semaphore، الافتراضي max_concurrent=5؛ مدير سياق لامتزامن يُطلِق دائمًا عند الخروج. ويقع حدّ كل مهمة تحت سقف واحد على مستوى العملية قدره 64 إرسالًا متوازيًا للعمّال (limits.MAX_PARALLEL_WORKERS).

5.7 الإعداد لكل عامل (عبر مفوّضي المحرّك)

Section titled “5.7 الإعداد لكل عامل (عبر مفوّضي المحرّك)”
الدالةما تُهيّئه
set_circuit_breaker_config(worker, failure_threshold, cooldown_seconds)حدود قاطع الدائرة.
set_retry_policy(worker, policy)سياسة إعادة المحاولة.
set_timeout_guard(worker, guard)المهلة.
set_output_validator(worker, validator)مخطط المُخرَج.
get_bounded_concurrency(task_max_concurrent)التزامن (تجاوز المهمة > افتراضي المحرّك).

swarm/task_store.py (TaskStore)، قاعدة البيانات الافتراضية <data dir>/swarm_tasks.db؛ وجدول kazma_swarm_tasks في Postgres حين يكون Postgres مُعدًّا.

تُطبَّق PRAGMAs مركزيًا عبر apply_sqlite_pragmas() من config_store:

PRAGMA journal_mode=WAL;
PRAGMA busy_timeout=5000;
PRAGMA synchronous=FULL; -- the task store only: the RUNNING row is crash evidence

اتصال مشترك واحد + threading.Lock (يمسكه close() أيضًا). يكتب المحرّك من خيوط، وكتابات المهمة الواحدة بالترتيب (SwarmEngine._persist). وعلى Postgres يمرّ كل مُعامِل عبر _pg_execute / _pg_execute_one، اللتين تضعان U+FFFD مكان كل NUL، فتحفظ الواجهتان الخلفيتان النص نفسه.

swarm_tasks: id, type, prompt, status, workers (JSON), result, context, dependencies, fallback_chain, validation_schema, aggregation, timeout, created_at, started_at, completed_at, sort_at, cost, tokens, metadata, workspace_id, generation, run_id. فهارس على status، وtype، وcompleted_at، وcreated_at، وsort_at.

swarm_worker_metrics: worker, date, tasks_completed, tasks_failed, avg_latency, total_tokens, total_cost، المفتاح الأساسي (worker, date).

بعد executescript(_SCHEMA)، يضيف kazma_core.db.sqlite_columns.add_missing_columns ما ينقص من الأعمدة اللاحقة (AGENTS §6): يقرأ PRAGMA table_info، والفشل الحقيقي يُطلَق. وعلى Postgres تمرّ الأعمدة عبر db.pg_schema.ensure_schema.

6.4 مرشِّح العامل (تطابق تام)

Section titled “6.4 مرشِّح العامل (تطابق تام)”

يرشِّح list_tasks() حسب العامل باستخدام json_each() — وليس LIKE:

WHERE EXISTS (SELECT 1 FROM json_each(workers) WHERE value = ?)

هذا يتجنّب الإيجابيات الزائفة للسلاسل الفرعية (مثل مطابقة العامل "a" للسلسلة "ab"). وعلى Postgres يصبح المرشِّح نفسه احتواء JSONB ‏(workers @> '["a"]')؛ ويُلزم tests/test_task_store_backends.py كلا الواجهتين الخلفيتين بالإجابات نفسها.

6.5 الاحتفاظ (كم تُحفَظ المهام المنتهية)

Section titled “6.5 الاحتفاظ (كم تُحفَظ المهام المنتهية)”

الإعدادات ← النظام ← سجلّ مهام السرب، مخزَّن كـ swarm.task_retention_days (الافتراضي 30؛ و0 تحفظ كل مهمة؛ وبحد أقصى 3650). كل 15 دقيقة تحذف عمليةُ مسح الصيانة (memory.worker_bootstrap._MAINTENANCE_SWEEPS) المهامَ المكتملة والفاشلة والملغاة والمنتهية مهلها الأقدم من ذلك. أما المهام المتوقفة والمعلّقة وقيد التشغيل فلا تُحذف أبدًا. وتُحذف معها أيام مقاييس العمّال الأقدم من الحدّ نفسه. تُقرأ القيمة عند كل مسح، فيسري التغيير خلال 15 دقيقة، ويرفض PUT /api/settings/single أي قيمة ليست عددًا صحيحًا من الأيام بين 0 و3650. حتى 2026-09-25 كانت روتينة التقليم موجودة دون أن يستدعيها أحد، فلم يُقنَّم التاريخ قط.


7. سجلّ العمّال ودليلهم (phonebook)

Section titled “7. سجلّ العمّال ودليلهم (phonebook)”
  • WorkerRegistry (swarm/registry.py) — سجلّ مدعوم بـ JSON في مجلد البيانات (<data dir>/swarm_registry.json، فتنسخه النسخة الاحتياطية)، يُحمَّل عند إنشاء المُفرد؛ وملف بناءٍ أقدم في مجلد العمل يُنقل إليه مرة واحدة ويُعاد تسميته .migrated. الكتابة ذرّية، وكل تغيير وكل قراءة يمسكان قفل السجل. لكل WorkerEntry: name, expertise, roles, model, provider, worker_type, system_prompt, enabled, tools, metadata.
  • WorkerPhonebook (swarm/phonebook.py) — يبني summon(name) عامل InProcessWorker من إدخال في WorkerRegistry (أُزيل مسار العملية الفرعية القديم لـ TelegramWorker)؛ ولا يشغّل هو نفسه شيئًا. يسجّل SwarmEngine.dispatch_by_name العاملَ المُستدعى الذي لا يحمله المحرّك، ويضيف نتائج V2 لـ recall.search (استراتيجيات + ما تعلّمه هذا العامل، مُسيَّجةً، خارج حلقة الأحداث: phonebook.memory_context)، ويرسل عبر dispatch()، مع القبول، وصف RUNNING، والإلغاء مثل أي مهمة.
  • worker_factory._IN_PROCESS_TYPES = \{"in_process", "telegram_bot"\} — كلاهما يحلّ إلى InProcessWorker.

لا يوجد كتالوج أدوار/إعدادات مسبقة محدَّد. الأدوار سلاسل حرة. يبدأ السجل فارغًا (لا يُشحن فيه شيء): يحمل العمّال الذين يُضافون من صفحة السرب أو الواجهة البرمجية. والعامل ذو system_prompt الفارغ يُعدّ عامًّا (WorkerEntry.is_generalist) أيًّا كانت وسوم خبرته.


8. نقاط حفظ خط المعالجة (HITL)

Section titled “8. نقاط حفظ خط المعالجة (HITL)”

يمكن لخط المعالجة التوقف عند خطوات مُعدَّة للموافقة البشرية.

المكوّنالدور
HITLCheckpoint / HITLCheckpointHandlerswarm/checkpoint.py — dataclass + تنسيق التوقف/الموافقة/الرفض عبر أحداث asyncio.Event.
CheckpointManagerswarm/checkpoint_manager.py — يمتلك حالة خط المعالجة المتوقّف، ومهلة الرفض التلقائي، والاستمرارية عبر كتابات المحرّك المرتّبة.
  1. يفوّض SwarmEngine._handle_pipeline_checkpoint إلى CheckpointManager.handle_pipeline_checkpoint، الذي يُسجّل أيضًا عبر _gate_register_pipeline (سجلّ بوابات HITL — صف واحد لكل توقف).
  2. يخزّن المدير الحالة، ويسلّح مهلة رفض تلقائي إذا كان task.metadata["checkpoint_timeout"] > 0، ويضبط حالة المهمة إلى PAUSED، ويُثبِتها في SQLite.
  3. الموافقة: يلغي SwarmEngine.approve_checkpoint المهلة، ويسوّي صف السجلّ، ويضبط checkpoint.status="approved"، ويُخرج الإدخال المتوقّف، ويستأنف من next_step = checkpoint.step + 1 عبر resume_pipeline(...).
  4. الرفض: يفوّض SwarmEngine.reject_checkpoint إلى المعالِج ويسوّي البوابة.
  5. T-2: مهلة خط المعالجة يجب أن تُنهي المهمة وتُنفِّذ settle_gate. واحدة دون الأخرى تترك بطاقة حية أو صفًا يتيمًا.
  6. التعافي من الانهيار: يعيد restore_paused_tasks() تحميل المهام المتوقفة بوصفها مهامَّ متوقفة قيد التنفيذ ويعيد تسليح المهل.
  7. صف البوابة الذي لا يمكن تسجيله أو تسويته يُسجَّل تحذيرًا (WARNING) ويُعدّ في kazma_swarm_failures_total{site="gate_register"|"gate_settle"}.
  • POST /api/swarm/tasks/\{task_id\}/approve (swarm_panel/routes_tasks.py) يطالب بنقطة الحفظ، ويسجّل التشغيل المُستأنَف RUNNING، ويجيب 202 فورًا (task_status: running)؛ ويصل التقدّم والنهاية إلى الصفحة على تدفق المهمة. نقطة الحفظ التي لم تعد معلّقة تُجاب بـ 409 not_pending؛ والسرب الممتلئ بـ 429 swarm_at_capacity؛ والموافقة التي لم يستطع مخزن المهام تسجيلها بـ 503 approval_not_recorded (لم يُستأنف شيء، ونقطة الحفظ لا تزال معلّقة: §1.0f).
  • POST /api/swarm/tasks/\{task_id\}/reject يجيب بـ status: ok مع حالة المهمة نفسها بجانبها (task_status).

MetricsCollector (swarm/metrics.py) — مُجمِّع في الذاكرة آمن للخيوط مدعوم بـ TaskStore اختياري. لقطات لكل (worker, date)؛ وتُعدّ كل نتيجة عامل مرة واحدة (_finalize_task)، بما فيها رفض القاطع المفتوح:

المقياسالمعنى
tasks_completedالمهام الناجحة.
tasks_failedالمهام الفاشلة.
avg_latencyمتوسط جارٍ مُرجَّح.
total_tokensمجموع الرموز.
total_costالمجموع بالدولار الأمريكي.

يُفرَّغ إلى TaskStore.record_worker_metric عند كل تسجيل، في خيط. مكشوف عبر REST عند GET /api/swarm/workers/\{name\}/metrics.

عمليات تدوين السرب التي تفشل فتُضيّع دليلًا أو قرارًا تُسجَّل تحذيرًا (WARNING) وتُعدّ في kazma_swarm_failures_total{site} على GET /metrics: running_row (صف RUNNING)، وtask_write، وorphan_evidence، وrecovery_read، وreap، وidle_reap، وgate_register، وgate_settle، وcheckpoint_timeout_arm، وbreaker_persist، وapproval_resolve، وdurable_workflow (سير عمل تشغيل دائم لم يُغلَق بعد انتهاء عمله: يفتقد تاريخ Temporal النهاية التي يحفظها مخزن المهام)؛ وللنتيجة المتدهورة: refiner (أُبقيت النتيجة غير المُنقّحة)، وsynthesis (الاحتياطي الحتمي)، وsemantic_routing (تُوجِّه الكلمات المفتاحية حتى يعود المُضمِّن، ويُعدّ مرة واحدة). ومعالِج الالتقاط الشامل في السرب أو في محوّلات الناقل الذي يبقى دون مستوى WARNING يجب التصريح به مع سببه (tests/test_swarm_boundaries_and_signals.py).

مقاييس عمّال السرب هي هذا المُجمِّع (في الذاكرة + upsert إلى SQLite / Postgres). ويبقى التطبيق يكشف Prometheus عند GET /metrics (kazma_ui/metrics.py) لعدّادات أخرى (بوابات HITL، والالتزام، وتقليلات السياق). لا تخلط بينهما. راجع البنية ← قابلية الملاحظة.


TracingEmitter / Span / InMemorySpanExporter (داخلي، swarm/tracing.py). ليس OpenTelemetry. تُبعث الـ spans لأحداث الإرسال والتسليم ونقاط الحفظ.


11. محرّك التحسين الذاتي (حلقة التغذية الراجعة)

Section titled “11. محرّك التحسين الذاتي (حلقة التغذية الراجعة)”

تتعلّم مهارة التحسين الذاتي (skills/self_improvement.py) من النتائج على مستوى كاظمه كلها. لا يوجد ملف SOUL.md ولا agent_evolution.json حيّ (يُهاجَر ذلك الملف مرة واحدة إلى ConfigStore ويُعاد تسميته .migrated).

السطحالخطافأين تُخزَّن الروح (Soul)
المحادثة (Web SSE، بوابة Telegram/Discord/Slack)بعد كل دور مكتمل (تتخطى وقفات HITL)مفتاح ConfigStore ‏self_improvement.agent_evolution ({"agents": {supervisor: {soul, history}}})
السرب (pipeline / fan-out / conditional)_run_self_improvement بعد اكتمال النمطsystem_prompt في WorkerRegistry (كتل [SelfImprovement] مقيّدة)

مفتاح الإيقاف: KAZMA_SELF_IMPROVEMENT=0 (يُفحَص حيًّا). كل دلتا محقونة مُغلَّفة بـ format_untrusted_block(source="self_improvement") وتُرفَض إذا كانت is_override_delta. تأكيد الروح مُفعَّل تلقائيًا في الإنتاج / تعدد المستخدمين (POST /api/commitment/soul/{cid}/confirm).

11.1 حلقة التغذية الراجعة

Section titled “11.1 حلقة التغذية الراجعة”
flowchart LR
    T[Task or chat turn completes] --> SI[analyze / schedule_chat_self_improvement]
    SI --> A{analyze}
    A -->|success| S[Meta-Refiner: reinforce]
    A -->|failure/timeout/error| F[Meta-Refiner: correct]
    S --> AA[apply mutation]
    F --> AA
    AA --> CP[_cap_evolution_prompt]
    CP --> WR[WorkerRegistry OR ConfigStore]
  1. ينطلق الخطاف بعد كل اكتمال لنمط pipeline وfan-out وconditional (patterns.py:_run_self_improvement).
  2. يُحلَّل كل عامل مقابل نتيجته وحدها (لا كل المراحل).
  3. يولّد LLM المُحسِّن الفوقي (Meta-Refiner) دلتا من 2-3 جمل (تعزيز عند النجاح، تصحيح عند الفشل). تحسب status=error في WorkerResult فشلًا.
  4. تُطبَّق الدلتا تلقائيًا على مُحفِّز نظام العامل عبر _cap_evolution_prompt (حد أقصى 12 كتلة، و8000 حرف) إلا إذا أمسكتها بوابة تأكيد الروح.
  5. في الإرسالات المستقبلية، تُستخدم روح العامل المحدَّثة تلقائيًا.
  1. بعد done في Web SSE (غير مُقاطَع) أو اكتمال رسم البوابة، يعمل schedule_chat_self_improvement في الخلفية.
  2. النتيجة: نجاح ما لم يكن الرد فارغًا/⚠️/يبدو خطأً.
  3. تُحدّ الدلتا وتُخزَّن تحت مُعرِّف الوكيل supervisor في ConfigStore.
  4. الدور التالي: يحقن agent_runner، وsse_chat/، وgraph.py الخاص بالبوابة كتلةَ الروح كرسالة نظام مُسيَّجة (لا حاجة إلى إعادة بناء الرسم).
  • يسجّل _mutation_log كل دلتا مُطبَّقة (عامل، دلتا، طابع زمني، حالة).
  • يُعيد stats() قيمة \{"enabled": ..., "mutations_applied": N\}.
  • يُعيد mutation_history آخر 50 طفرة.

12. خطوط المعالجة المسبقة (kazma.yaml)

Section titled “12. خطوط المعالجة المسبقة (kazma.yaml)”
خط المعالجةالمراحل
standardresearcher (العامل core) ← refiner (العامل bridge) ← builder (العامل core) ← validator (العامل bridge).
quickresearcher (العامل core) ← builder (العامل core).

تحمل كل مرحلة system_prompt. راجع الإعداد ← خطوط المعالجة.


13. أوامر CLI الخاصة بالسرب

Section titled “13. أوامر CLI الخاصة بالسرب”

سطح kazma swarm الكامل (dispatch، وbroadcast، وconsult، وpipeline، وfanout، وhistory، وmetrics، وapprove، وreject، وcircuit-breaker، …) موثَّق في مرجع CLI ← swarm.


14. المُغيّر التلقائي الديناميكي وقوالب العمّال

Section titled “14. المُغيّر التلقائي الديناميكي وقوالب العمّال”

عندما تكون لمهمة مُرسَلة workers=["auto"] ولا يستطيع أي عامل مُسجَّل معالجتها (NoCapableWorkersError)، يرتد المحرّك إلى المُغيّر التلقائي (swarm/autoscaler.py) الذي يولّد عاملًا عند الطلب من قالب — فيعمل السرب بصفر عمّال مُسجَّلين مسبقًا.

  • القوالب تعيش في swarm_templates.json (جذر المستودع). الافتراضيات المُصدَرة هي coder (حد أقصى 3)، وresearcher (حد أقصى 2)، وgeneralist (حد أقصى 5). يطابق القالبُ مهمةً بمطابقة الرموز عند حدود الكلمات على وسوم capabilities.expertise الخاصة به (الوسم coding يطابق المُحفِّز “write some python code” لكنه لا يطابق “decode this barcode”). تُرتَّب القوالب من الأخصّ إلى العامّ (أول تطابق يفوز)؛ والعمومي هو الذي يلتقط ما تبقّى.
  • النسخ المولَّدة تُسمّى <template>-pool-<boot tag>-<n> (مثل coder-pool-3f9a-1: الوسم جديد عند كل إقلاع، فلا يُورَث أبدًا قاطعٌ مشترك لنسخة من تشغيل سابق)، مقيّدة لكل قالب بـ max_instances، وتُحصَد عند الخمول بعد 5 دقائق (AutoScaler.record_activity يُنعش المؤقّت عند كل إرسال). والنسخة التي لديها إرسال قيد التنفيذ (SwarmWorker.in_flight) لا تُحصَد أبدًا.
  • تأخذ النسخة الخاملة المهمة التالية قبل توليد نسخة جديدة (AutoScaler._take، لـ maybe_scale وspawn_by_name): نسخة ليس لديها إرسال قيد التنفيذ ولا مهمة سُلّمت إليها من قبل. والنسخة المُسلَّمة تُحجز حتى يبدأ إرسالها (يغطيها عدّها قيد التنفيذ منذ ذلك الحين) أو يرفضها قاطعها، وإلا فلمدة 60 ثانية؛ والنسخة المحجوزة لا تُحصَد. ولا يُتهرَّب من القاطع المفتوح: تُسلَّم نسخته وهي ترفض، بدلًا من إرسال نسخة جديدة إلى النموذج الفاشل. وهكذا فإن سلسلة من المهام التي يأخذها القالب العمومي تستخدم نسخة واحدة، والمهام المتزامنة تأخذ كلٌّ منها نسخة، حتى الحدّ. (اكتشفه اختبار التحمّل SQ01: المُوجِّه يطابق النسخ بكلمات قالبها، فالمهمة التي لم تطابقها كلمات أي قالب لم تصل قط إلى نسخة موجودة من القالب العمومي؛ فكانت كلٌّ منها تولّد نسخة جديدة حتى الحدّ، ثم تفشل لعدم السعة حتى تُحصَد النسخ الخاملة.)
  • اسم القالب يُرسَل إليه أيضًا: المهمة التي تسمّي coder دون عامل بهذا الاسم تحصل على نسخة خاملة من القالب، أو على نسخة جديدة.
  • أفضل نموذج لنوع المهمة. يُترَكان model/provider في القالب فارغين افتراضيًا؛ فيُصنّف العامل المولَّد مهمته بعد ذلك (models/router.py::classify_prompt ← CODING/VISION/REASONING/GENERAL) ويختار أفضل نموذج متاح عبر models/selection.py (find_best_model_for_task). الأولوية: الافتراضي للمهمة من المستخدم (models.defaults.<kind> من الإعدادات ← النماذج) ← أفضل تطبيق استدلالي بأنماط مُعرِّف النموذج ← احتياطي الملف الشخصي النشط. قفل البيئة (KAZMA_MODEL) يفوز دائمًا.
  • الواجهة. أدر القوالب في لوحة السرب ← تبويب القوالب (REST: GET/POST/DELETE /api/swarm/templates، وPOST /api/swarm/autoscaler/reap).
  • المُغيّر التلقائي قالبيّ، لا LLM-لكل-مهمة. رموز الكلمات المفتاحية + استدلالات المُعرِّف تحقّق نحو 80% من القيمة بكلفة تقارب 0؛ ولا يزال خطاف رسم ModelRouter الخامد (model_router=None في agent_runner.py) متاحًا لمرحلة توجيه LLM مستقبلية. حرّاس دورة التسليم (§4) ينطبقون أيضًا على النسخ المولَّدة.

  • reliability_registry.py للإعدادات فقط. منطق _probe_in_flight نصف المفتوح والأجيال يعيشان على dataclass ‏CircuitBreaker في reliability.py. من يُعدِّل دلالات قاطع الدائرة يجب أن يُحرّر reliability.py، لا السجلّ.
  • لا أرقام أسطر هنا. تسمّي هذه الصفحة الملفات والرموز؛ فقد انحرفت أرقام الأسطر (تحرّك مسارا الموافقة والرفض نحو 90 سطرًا) ولا يُحتفَظ بها.
  • الرمز _MAX_VISITS مقابل MAX_VISITS: تستخدم docstring المحرّك الصيغة ذات الشرطة السفلية، لكن الثابت المُصدَّر هو MAX_VISITS. القيمة نفسها (2).
  • السجل لا يحمل إلا العمّال الذين تضيفهم. يُشحن فارغًا؛ وتعريفات العمّال للإنتاج هي القوالب في swarm_templates.json، ومنها يُنشئ المُغيّر التلقائي العمّال عند الطلب.
  • Prometheus موجود على التطبيق (GET /metrics). ‏MetricsCollector الخاص بالسرب لقطة SQLite منفصلة — وليس سجلّ Prom ثانيًا.
  • الروح هي ConfigStore / WorkerRegistry، أبدًا ليست agent_evolution.json وأبدًا ليست SOUL.md من markdown.
  • قواعد HITL الرابطة: AGENTS.md §7 (قرار مشترك واحد لكل طلب على الناقل)، و§30 (السجلّ)، والتصادم H-8/H-9/H-12/T-2.
  • تدقيق السرب 2026-10-13 (docs/audits/AUDIT_SWARM_2026-10-13.md، المتعقِّب docs/plans/SWARM_INDUSTRIAL_HARDENING_GOAL.md): حدّ الموافقة، وملكية التشغيل، ودليل الآثار، والصفحة الحيّة والتدفق، والأدوار، والأسيجة والإشارات المذكورة أعلاه هي مراحله 1–5.