البوابات والمنصات
كيف تتحدث “العقل الواحد” لكاظمة مع قنوات عديدة: طبقة المُحوّلات، عزل المنصات، مخزن الجلسات، أوامر الشرطة المائلة، ومصفوفة تكافؤ الميزات — كلها موثّقة مقابل
kazma-gateway/.
1. البنية
Section titled “1. البنية”أُعيد هيكلة حزمة البوابة لكاظمة (kazma-gateway/kazma_gateway/) من ملف agent_handler.py واحد إلى حزمة agent_handler/ (التفكيك مُوثَّق في agent_handler/__init__.py:4). الشكل كالتالي:
flowchart LR subgraph Platform Adapters TG[TelegramAdapter] DC[DiscordAdapter] SL[SlackAdapter] end subgraph agent_handler package ST[store.py<br/>SessionStore + isolation] GH[graph.py<br/>graph bridge] CM[commands.py<br/>interactive cmds] end SS[(SessionStore<br/>SQLite / in-memory)] GB[Supervisor Graph]
TG & DC & SL -->|IncomingMessage| GH GH --> ST ST <--> SS GH -->|graph.ainvoke| GB GB -->|final state| GH GH -->|OutboundMessage| TG & DC & SL- المُحوّلات (
adapters/) — واحد لكل منصة؛ جميعها تمدّBaseAdapter(gateway.py:239). يُترجِم كلٌّ منها الأحداث الأصلية للمنصة إلىIncomingMessageالخاصة بكاظمة ويُعيد تصييرOutboundMessage. - حزمة agent_handler — الجسر بين المُحوّلات ومخطط المُشرف.
- مُحوّلات الناقل (
adapters/*_bus.py) — مجموعة منفصلة من المُحوّلات لموافقات الموافقة البشرية في الحلقة (HITL) للسرب (تيليجرام/ديسكورد/سلاك). راجع الأمن والسلامة.
2. مُحوّلات المنصات
Section titled “2. مُحوّلات المنصات”2.1 تيليجرام (Telegram) (adapters/telegram.py)
Section titled “2.1 تيليجرام (Telegram) (adapters/telegram.py)”| الجانب | التفصيل |
|---|---|
| الصنف | TelegramAdapter (telegram.py:70)، name = "telegram". |
| الاستقبال | getUpdates يدوي بالاقتراع الطويل (HTTP)، مع تفاوت 1-3 ثوانٍ؛ موجّه webhook اختياري عبر create_webhook_router() مُركَّب عند /api/webhooks/telegram. |
| الإرسال | POST /sendMessage مع parse_mode بصيغة HTML، إعادة محاولة عند 429، وتقسيم الرسائل. |
| مفاتيح السياق | chat_id (int)، user_id (int)، username (str)، message_id (int)، chat_type (str). |
| إضافات | الصوت/تحويل الكلام إلى نص (STT) (telegram_stt.py)، لوحات المفاتيح المضمّنة (telegram_keyboards.py)، التفاعلات (✅/🎯/❌)، مؤشر الكتابة، قائمة أوامر مُسجَّلة عبر setMyCommands. |
| وحدات مساعدة | telegram_callbacks.py، telegram_keyboards.py، telegram_parse.py، telegram_send.py، telegram_stt.py. |
تيليجرام هو المُحوّل الأكثر اكتمالًا في الميزات.
2.2 ديسكورد (Discord) (adapters/discord.py)
Section titled “2.2 ديسكورد (Discord) (adapters/discord.py)”| الجانب | التفصيل |
|---|---|
| الصنف | DiscordAdapter (discord.py:50)، name = "discord". |
| الاستقبال | بوابة WebSocket لديسكورد (wss://gateway.discord.gg/?v=10&encoding=json). |
| الإرسال | REST عبر POST /channels/{channel_id}/messages، إعادة محاولة عند 429، تقسيم عند 2000 محرف. |
| مفاتيح السياق | channel_id، guild_id، user_id، message_id، username، guild_name. |
| Markdown | نص عادي (لا يُولَّد Markdown الخاص بديسكورد). |
أوامر الشرطة المائلة: يحجز ديسكورد الأوامر ذات البادئة
/لنظام تفاعلاته الخاص. تستقبلها كاظمة كنص عادي وتُحلِّلها داخليًا.
2.3 سلاك (Slack) (adapters/slack.py)
Section titled “2.3 سلاك (Slack) (adapters/slack.py)”| الجانب | التفصيل |
|---|---|
| الصنف | SlackAdapter (slack.py:47). |
| الاستقبال | وضع Socket (عند وجود xapp- app_token) أو اقتراع احتياطي لـ conversations.history. |
| الإرسال | REST عبر واجهة سلاك API POST. |
| مفاتيح السياق | channel_id، user_id، team_id، thread_ts، message_ts، username. |
أوامر الشرطة المائلة: يحظر سلاك أوامر الشرطة المائلة الصادرة عن البوت، لذا يُصدَر طلب الموافقة البشرية في الحلقة (HITL) بصيغة
hitl approve|deny <id>بدون بادئة/(graph.py:184).
3. عزل المنصات (الثابت الجوهري)
Section titled “3. عزل المنصات (الثابت الجوهري)”هذا هو العقد الأهم في البوابة على الإطلاق. يجب ألا يحتوي حالة LangGraph على chat_id أو user_id أو message_id. كسر هذا الثابت يُسرّب مُعرّفات المنصة إلى المخطط ويفسد الجلسات.
3.1 المفاتيح المُحرَّمة
Section titled “3.1 المفاتيح المُحرَّمة”_PLATFORM_KEYS (agent_handler/store.py:16-31) هي المجموعة المجمّدة (frozen) الموثوقة:
_PLATFORM_KEYS = frozenset({ # Telegram "chat_id", "user_id", "message_id", "update_id", "chat_type", # Discord / Slack "channel_id", "guild_id", "team_id", "thread_ts", "message_ts",})3.2 كيف يُفرَض العزل
Section titled “3.2 كيف يُفرَض العزل”_build_initial_state(msg, store) (store.py:95):
- يحلّ
thread_idعبر_resolve_thread(store.py:34):thread_idموجود في context_metadata، وإلّا- مُحدَّد بشكل حتمي من مُعرّف المُرسِل (مثلًا
"telegram:12345"←"gw-telegram-12345")، وإلّا UUID4جديد.
- يحفظ سياق المنصة الكامل في SessionStore:
await store.put(thread_id, dict(ctx)). - يبني حالة مخطط مُصغَّرة تحتوي كتلة
_gatewayفقط:
state["_gateway"] = { "thread_id": thread_id, "display_name": ctx.get("username") or "unknown", "platform": msg.platform,}- تمريرة دفاعية معمّقة تنتزع أي مفتاح مُسرّب من
_PLATFORM_KEYS(store.py:139-141).
3.3 كيف تعود الردود إلى مسارها
Section titled “3.3 كيف تعود الردود إلى مسارها”بعد اكتمال graph.ainvoke() (agent_handler/graph.py:316)، يُنفِّذ المعالِج ctx = await _store.get(thread_id) لإعادة تملء مُعرّفات المنصة، ثم يوجّهها عبر _build_target_id(platform, ctx) (store.py:146):
- تيليجرام ←
chat_id - ديسكورد/سلاك ←
channel_id - يُنتج مُعرّفات مسبوقة بالمنصة مثل
"telegram:12345".
الاستمرارية بالتصميم: مدخلات الجلسة لا تُحذَف بعد الرد (
graph.py:312-315) كي يتمكن توجيه الاستعادة بعد الانهيار من إعادة تملء السياق. تُطرَد المدخلات المعلّقة بتكاسل عبر TTL مدته 300 ثانية (graph.py:98،_session_ttl_seconds).
4. SessionStore
Section titled “4. SessionStore”يحتفظ SessionStore بتعيينات مُعرّفات المنصات ↔ thread_id — لا المخطط.
| التنفيذ | الموقع | الاستخدام |
|---|---|---|
SessionStore (مجرّد) | gateway.py:185 | get، put، delete، evict_older_than اختياري. |
_InMemoryStore | agent_handler/store.py:63 | للاختبار/الاحتياطي؛ يتتبع طوابع زمنية رتيبة للـ TTL. |
SQLite SessionStore | stores/sqlite.py | دائم. |
| LangGraph checkpointer | stores/checkpoint.py | يُستخدَم من واجهة المستخدم لحالة المحادثة (app.py:724). |
تذكر وثائق IncomingMessage (gateway.py:62-66) ذلك بوضوح: “لا يلمس العقل الحقول الخاصة بالمنصة. تعيش مُعرّفات المنصة الخام داخل context_metadata.”
5. ناقل واجهة الويب (SSE)
Section titled “5. ناقل واجهة الويب (SSE)”تستخدم واجهة الويب أحداث الخادم المُرسَلة (SSE) وليس WebSocket كناقل رئيسي للمحادثة.
- نقطة النهاية:
POST /api/chat/stream(sse_chat.py:353) — تقبل{message, session_id, model}وتُعيدStreamingResponse(text/event-stream). - WebSocket القديم:
GET /ws/chatيُعيد 410 Gone (chat.py:4) ويجب ألا يُنفّذ الأدوات. لا تستخدمه. - توصيل العميل:
chat.js:332يستدعيKS.sse('/api/chat/stream', {...}).
عقد أحداث SSE الكامل (token، tool_call، tool_result، approval_required، done، error) موثَّق في واجهة API ونقاط الامتداد.
6. سطح الموافقة البشرية في الحلقة (HITL) لكل منصة
Section titled “6. سطح الموافقة البشرية في الحلقة (HITL) لكل منصة”| المنصة | الآلية | نمط مُعرّف الاستدعاء | المُعالِج |
|---|---|---|---|
| تيليجرام | أزرار لوحة المفاتيح المضمّنة | hitl:approve:{id} / hitl:deny:{id} | telegram.py:733 |
| ديسكورد | أزرار Components v2 | swarm_approve_{task_id} / swarm_reject_{task_id} | discord.py:312 |
| سلاك | استدعاء تفاعلي | كتلة موافقة السرب | slack.py:401 |
| واجهة الويب | زر ← POST /api/approve/{thread_id} + حدث SSE approval_required | — | routes_direct.py:454 |
أولوية النسخة المفردة لمُحوّل الناقل هي تيليجرام > ديسكورد > سلاك (واحد نشط فقط في كل مرة، موصول في
app.py:506-556). راجع الأمن والسلامة.
7. مصفوفة تكافؤ الميزات
Section titled “7. مصفوفة تكافؤ الميزات”| الميزة | تيليجرام | ديسكورد | سلاك | واجهة الويب | واجهة الطرفية (TUI) |
|---|---|---|---|---|---|
| آلية الاستقبال | اقتراع طويل HTTP | بوابة WS | وضع Socket / اقتراع | HTTP POST (SSE) | محلي |
| أزرار موافقة HITL | ✅ | ✅ | ✅ | ✅ | نافذة منبثقة |
| تصيير Markdown | ← تيليجرام HTML | نص عادي | نص عادي | من جهة العميل | عادي |
| أوامر الشرطة المائلة | مجموعة كاملة (مُسجَّلة) | تحليل نص عادي | hitl بدون / | أزرار واجهة | — |
| الصوت / STT | ✅ | ❌ | ❌ | ❌ | ❌ |
| مؤشر الكتابة | ✅ | ✅ | ✅ | لا ينطبق | لا ينطبق |
| التفاعلات | ✅ ✅/🎯/❌ | ❌ | ❌ | لا ينطبق | لا ينطبق |
| واجهة مُحدِّد النموذج | ✅ لوحة مفاتيح مضمّنة | ❌ | ❌ | ✅ إعدادات | للقراءة فقط |
8. واجهة Textual الطرفية (TUI) (kazma-tui)
Section titled “8. واجهة Textual الطرفية (TUI) (kazma-tui)”لوحة تحكم للمراقبة في الغالب للقراءة، فوق نفس النسخ المفردة (singletons) الأساسية.
| الجانب | التفصيل |
|---|---|
| نقطة الدخول | kazma_tui.app:main ← KazmaTUI().run() (app.py:576). |
| التبويبات | لوحة التحكم (MetricsDashboard)، الدردشة (ChatPanel)، الملفات (FilesPanel)، التتبعات (TracesPanel)، السرب (SwarmPanel)، الإعدادات (SettingsPanel). |
| تهيئة النسخة المفردة | _initialize_core() (app.py:158) يُهيّئ ModelRegistry وSwarmEngine إذا أُطلق مستقلًا. |
| HITL | نافذة الموافقة المنبثقة (widgets/hitl_modal.py)؛ _check_pending_approvals (app.py:443)، _submit_hitl_decision (app.py:483). |
| RTL | update_localization() (app.py:512) يُبدِّل صف CSS باسم rtl-mode ويُترجِم تسميات التبويبات. |
عدم اتساق طفيف: تُسمّي واجهة الطرفية (TUI) لوحة التحكم بـ “لوحة القيادة” (
app.py:539)؛ بينما يستخدم التدويل (i18n) للويب “لوحة التحكم” (i18n.py:77).
9. تسجيل أوامر تيليجرام
Section titled “9. تسجيل أوامر تيليجرام”يستدعي _register_bot_commands (telegram.py:961) دالة setMyCommands في تيليجرام عبر ثلاثة نطاقات (default، all_private_chats، all_group_chats). القائمة المُسجَّلة تتضمّن أوامر الشرطة المائلة القياسية بالإضافة إلى /swarm. وهي لا تُسجّل /hitl (لأن سلاك يحتاج الصيغة المجردة hitl، والتسجيل منطق مشترك).
ملاحظات تدقيق التوثيق
Section titled “ملاحظات تدقيق التوثيق”agent_handler.pyلم يعد موجودًا كملف واحد — بل أصبح الحزمةagent_handler/. التوثيقات الأقدم التي تشير إلى الملف قديمة.- نقطة نهاية محادثة WebSocket القديمة ميتة (410 Gone). التوثيق الذي لا يزال يصف
/ws/chatكحيٍّ غير صحيح — SSE هو الناقل. - أوامرا الشرطة المائلة
/undoو/editكبوبن (stubs) ويجب ألا تُعلَن كوظيفية. /helpتتجاهل/hitlو/swarmرغم أن كليهما يعمل. هذه فجوة في نص المساعدة داخل التطبيق، وليست ميزة مفقودة.