تنسيق السرب
نظام السرب الكامل: المحرّك، وأنماط الإرسال الستة، واستراتيجيات التجميع، وطبقة الموثوقية (قواطع الدائرة، وإعادة المحاولة، والمهل، والمُدقّقات، والتزامن)، وسجلّ العمّال، ونقاط حفظ خط المعالجة، وناقل HITL (قرار مشترك واحد لكل طلب)، والمقاييس. الثوابت:
AGENTS.md§4–§7، §9، §14، §30.
1. SwarmEngine
Section titled “1. SwarmEngine”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.0e الحدود
Section titled “1.0e الحدود”| الحدّ | القيمة | أين |
|---|---|---|
| مهلة المهمة (لكل خطوة في الأنماط) | 1–3600 ثانية | swarm/limits.py |
| المُحفِّز / السياق / البيانات الوصفية | 64 KiB / 256 KiB / 64 KiB | نفسه |
| العمّال / نقاط الحفظ / السلسلة الاحتياطية | 32 / 32 / 16 | نفسه |
| خطوط المعالجة المتوقفة في آن واحد | 100 | نفسه |
| إرسالات العمّال المتوازية، لكل المهام | 64 | نفسه |
| تدفقات المهام لكل عميل / إجمالًا | 16 / 128 | kazma_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 في منتصف التشغيل، وعملية عامل حقيقية تُقتل في منتصف خطوة. ولا
يتخطّى خادم الاختبار الوقت ما دام نشاط مُجدوَلًا أو قيد العمل، لذا
تعمل تلك الحدود قصيرة وحقيقية في الاختبارات.
1.1 المُنشئ
Section titled “1.1 المُنشئ”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: ...1.2 السمات الأساسية
Section titled “1.2 السمات الأساسية”| السمة | النوع | الغرض |
|---|---|---|
_workers | dict[str, SwarmWorker] | العمّال المُسجَّلون. |
_active_tasks | dict[str, SwarmTask] | المهام قيد التنفيذ. |
_task_handles | dict[str, asyncio.Task] | مقابض الإلغاء. |
_task_history | dict[str, SwarmTask] | مقيّدة بـ LRU (_max_history=500). |
_routing_engine | UnifiedRouter | التوجيه التلقائي لحالة workers=["auto"]. |
_reliability | ReliabilityRegistry | قواطع/إعادة محاولة/مهل/مُدقّقات/تزامن. |
_checkpoint_handler | HITLCheckpointHandler | حالة خط المعالجة المتوقّف. |
_checkpoint_mgr | CheckpointManager | استمرارية نقاط الحفظ + المهل. |
_task_store | TaskStore | استمرارية SQLite. |
_metrics_collector | MetricsCollector | مقاييس في الذاكرة + SQLite. |
_tracing_emitter | TracingEmitter | باعث spans داخلي. |
_phonebook | WorkerPhonebook | البحث عن العمّال عبر الطوبولوجيا/DAG. |
_sse | SseBridge | جسر أحداث SSE. |
توقيع المُنشئ لم يتغيّر بعد تقسيم P2-1 (
reliability_registry.py، وphonebook.py، وcheckpoint_manager.py). تعمل تجهيزات الاختبار دون تعديل. لا تثبّت أعداد أسطرengine.py— فهي تنحرف.
2. أنماط الإرسال الستة
Section titled “2. أنماط الإرسال الستة”تعداد TaskType (swarm/task.py): DISPATCH، وBROADCAST، وPIPELINE، وFAN_OUT، وCONSULT، وCONDITIONAL. يعيش التوجيه في dispatch_inner.py (يُستدعى من engine.dispatch ← engine._dispatch_inner).
| النمط | نقطة الدخول في المحرّك | الدالة المنفِّذة | الوصف |
|---|---|---|---|
| dispatch | engine.dispatch ← dispatch_inner | عامل واحد مضمّن + سلسلة احتياطية اختيارية | يعالج عامل واحد المهمة. |
| broadcast | engine.dispatch (وengine.broadcast غلاف فوقه) | broadcast.broadcast_task | يستقبل جميع العمّال المُحفِّز نفسه؛ وتُقبل المهمة وتُسجَّل وتُنهى مثل أي مهمة. |
| pipeline | dispatch_inner | patterns.execute_pipeline | مراحل مرتّبة تتشارك سبورة؛ تدعم نقاط حفظ HITL. |
| fan_out | dispatch_inner | patterns.execute_fan_out | تنفيذ متوازٍ بتزامن محدود + تجميع. |
| consult | dispatch_inner | consultation.execute_consult | آراء متوازية + تركيب LLM. |
| conditional | dispatch_inner | patterns.execute_conditional | يقرّر المُوجّه أي عنصر من خريطة المسار يُرسَل إليه. |
2.1 التوجيه التلقائي
Section titled “2.1 التوجيه التلقائي”تُحلّ 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: القالب، والعامل، وهل أُعيد استخدامه).
2.2 تفاصيل خط المعالجة
Section titled “2.2 تفاصيل خط المعالجة”- السبورة المشتركة — ترى كل مرحلة مخرجات المراحل السابقة.
- نقاط حفظ 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 |
3. استراتيجيات التجميع
Section titled “3. استراتيجيات التجميع”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_DEPTH | 5 | يُرفض التسليم الذي عمقه 5: أربعة تسليمات على الأكثر، وخمسة تشغيلات. |
MAX_VISITS | 2 | يعمل العامل مرتين على الأكثر في سلسلة واحدة: يعمل A→B→A (العودة) وA→B→A→B؛ ويُرفض تسليم B عائدًا إلى A (تشغيل A الثالث). |
الآلية:
_visitedقاموسdict[str, int]لأعداد زيارات كل عامل (وليست مجموعة منطقية)، مفتاحه هوية العامل (SwarmEngine._handoff_identity): تُحسب نسخة المجمّع بوصفها قالبها، لأن التسليم إلى قالب يولّد نسخة جديدة في كل مرة.- يزيد
_register_handoff_visitعدد العامل المصدر. - يرفض
_handoff_guard_errorالتسليم حين يكون عمقه 5 أو أكثر، أو حين يكون هدفه قد عُدّ مرتين من قبل. - يُزاد
_depthداخلworker_dispatch.dispatch_workerعندما يستدعيengine._handle_handoff(..., _depth=_depth + 1)؛ ويسأل العرضُ الحارسَ نفسه عند العمق نفسه قبل إطلاق الطلب. - عند النجاح/الفشل، يُستدعى
record_success/record_failureالخاص بقاطع المصدر مع قبول الإرسال (§5.1)، وكل نهاية للتسليم، بما فيها الرفض، تُنهي عدّ المصدر قيد التنفيذ (_release_handoff_source؛ كان الرفض يتركه مشغولًا إلى الأبد). - تصل النتائج الوسيطة للعامل المُسلِّم إلى الهدف مُسيَّجة (§1.0d).
- كل تسليم يُعدّ في
kazma_swarm_handoffs_total.
البوابات: tests/test_swarm_handoff_tool.py (نموذج مُبرمَج بسيناريو، من طرف إلى طرف؛ مع
إزالة الحارس والعدّ بأسماء النسخ ضابطَين سلبيَّين) وtests/test_swarm_handoff.py.
ملاحظة تسمية الرموز: تشير docstring المحرّك نصًّا إلى
_MAX_VISITS، لكن الرمز المُصدَّر هوMAX_VISITS(handoff_guards.py). القيمة نفسها (2).
5. طبقة الموثوقية
Section titled “5. طبقة الموثوقية”ReliabilityRegistry (swarm/reliability_registry.py) هي حامل إعدادات. تعيش آلات الحالة الفعلية في swarm/reliability.py. ستة مكوّنات:
5.1 CircuitBreaker (swarm/reliability.py)
Section titled “5.1 CircuitBreaker (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 حول كل تغيير).
5.2 RetryPolicy (swarm/reliability.py)
Section titled “5.2 RetryPolicy (swarm/reliability.py)”| الحقل | الافتراضي |
|---|---|
max_retries | 3 |
base_delay | 1.0 ثانية |
max_delay | 60.0 ثانية |
jitter | True |
تراجع أُسّي: 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 (كانت قد عملت أداة تُغيّر شيئًا). والإرسال يُحسب عدًّا واحدًا قيد التنفيذ مهما كان عدد محاولاته.
5.3 TimeoutGuard (swarm/reliability.py)
Section titled “5.3 TimeoutGuard (swarm/reliability.py)”| الحقل | الافتراضي |
|---|---|
default_timeout | 300.0 ثانية |
on_timeout | fail | 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 عندما يتوقّع المخطط كائنًا/مصفوفة.
5.5 FallbackChain (swarm/reliability.py)
Section titled “5.5 FallbackChain (swarm/reliability.py)”احتياطيات متتابعة بعد فشل الأساسي. يحصل كل احتياطي على 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) | التزامن (تجاوز المهمة > افتراضي المحرّك). |
6. TaskStore
Section titled “6. TaskStore”swarm/task_store.py (TaskStore)، قاعدة البيانات الافتراضية <data dir>/swarm_tasks.db؛ وجدول kazma_swarm_tasks في Postgres حين يكون Postgres مُعدًّا.
6.1 التزامن
Section titled “6.1 التزامن”تُطبَّق 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، فتحفظ الواجهتان الخلفيتان النص نفسه.
6.2 المخطط
Section titled “6.2 المخطط”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).
6.3 التهجير التلقائي
Section titled “6.3 التهجير التلقائي”بعد 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 / HITLCheckpointHandler | swarm/checkpoint.py — dataclass + تنسيق التوقف/الموافقة/الرفض عبر أحداث asyncio.Event. |
CheckpointManager | swarm/checkpoint_manager.py — يمتلك حالة خط المعالجة المتوقّف، ومهلة الرفض التلقائي، والاستمرارية عبر كتابات المحرّك المرتّبة. |
8.1 التدفّق
Section titled “8.1 التدفّق”- يفوّض
SwarmEngine._handle_pipeline_checkpointإلىCheckpointManager.handle_pipeline_checkpoint، الذي يُسجّل أيضًا عبر_gate_register_pipeline(سجلّ بوابات HITL — صف واحد لكل توقف). - يخزّن المدير الحالة، ويسلّح مهلة رفض تلقائي إذا كان
task.metadata["checkpoint_timeout"] > 0، ويضبط حالة المهمة إلىPAUSED، ويُثبِتها في SQLite. - الموافقة: يلغي
SwarmEngine.approve_checkpointالمهلة، ويسوّي صف السجلّ، ويضبطcheckpoint.status="approved"، ويُخرج الإدخال المتوقّف، ويستأنف منnext_step = checkpoint.step + 1عبرresume_pipeline(...). - الرفض: يفوّض
SwarmEngine.reject_checkpointإلى المعالِج ويسوّي البوابة. - T-2: مهلة خط المعالجة يجب أن تُنهي المهمة وتُنفِّذ
settle_gate. واحدة دون الأخرى تترك بطاقة حية أو صفًا يتيمًا. - التعافي من الانهيار: يعيد
restore_paused_tasks()تحميل المهام المتوقفة بوصفها مهامَّ متوقفة قيد التنفيذ ويعيد تسليح المهل. - صف البوابة الذي لا يمكن تسجيله أو تسويته يُسجَّل تحذيرًا (WARNING) ويُعدّ في
kazma_swarm_failures_total{site="gate_register"|"gate_settle"}.
8.2 نقاط نهاية HTTP
Section titled “8.2 نقاط نهاية HTTP”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).
9. المقاييس
Section titled “9. المقاييس”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، والالتزام، وتقليلات السياق). لا تخلط بينهما. راجع البنية ← قابلية الملاحظة.
10. التتبّع
Section titled “10. التتبّع”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]
11.2 كيف يعمل (السرب)
Section titled “11.2 كيف يعمل (السرب)”- ينطلق الخطاف بعد كل اكتمال لنمط pipeline وfan-out وconditional (
patterns.py:_run_self_improvement). - يُحلَّل كل عامل مقابل نتيجته وحدها (لا كل المراحل).
- يولّد LLM المُحسِّن الفوقي (Meta-Refiner) دلتا من 2-3 جمل (تعزيز عند النجاح، تصحيح عند الفشل). تحسب
status=errorفي WorkerResult فشلًا. - تُطبَّق الدلتا تلقائيًا على مُحفِّز نظام العامل عبر
_cap_evolution_prompt(حد أقصى 12 كتلة، و8000 حرف) إلا إذا أمسكتها بوابة تأكيد الروح. - في الإرسالات المستقبلية، تُستخدم روح العامل المحدَّثة تلقائيًا.
11.2b كيف يعمل (المحادثة)
Section titled “11.2b كيف يعمل (المحادثة)”- بعد
doneفي Web SSE (غير مُقاطَع) أو اكتمال رسم البوابة، يعملschedule_chat_self_improvementفي الخلفية. - النتيجة: نجاح ما لم يكن الرد فارغًا/⚠️/يبدو خطأً.
- تُحدّ الدلتا وتُخزَّن تحت مُعرِّف الوكيل
supervisorفي ConfigStore. - الدور التالي: يحقن
agent_runner، وsse_chat/، وgraph.pyالخاص بالبوابة كتلةَ الروح كرسالة نظام مُسيَّجة (لا حاجة إلى إعادة بناء الرسم).
11.3 تتبّع الحالة
Section titled “11.3 تتبّع الحالة”- يسجّل
_mutation_logكل دلتا مُطبَّقة (عامل، دلتا، طابع زمني، حالة). - يُعيد
stats()قيمة\{"enabled": ..., "mutations_applied": N\}. - يُعيد
mutation_historyآخر 50 طفرة.
12. خطوط المعالجة المسبقة (kazma.yaml)
Section titled “12. خطوط المعالجة المسبقة (kazma.yaml)”| خط المعالجة | المراحل |
|---|---|
standard | researcher (العامل core) ← refiner (العامل bridge) ← builder (العامل core) ← validator (العامل bridge). |
quick | researcher (العامل 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) ينطبقون أيضًا على النسخ المولَّدة.
ملاحظات تدقيق التوثيق
Section titled “ملاحظات تدقيق التوثيق”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.

