تخطَّ إلى المحتوى

البوابات والمنصات

كيف تتحدث “العقل الواحد” لكاظمة مع قنوات عديدة: طبقة المُحوّلات، عزل المنصات، مخزن الجلسات، أوامر الشرطة المائلة، ومصفوفة تكافؤ الميزات — كلها موثّقة مقابل kazma-gateway/.


أُعيد هيكلة حزمة البوابة لكاظمة (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.1 تيليجرام (Telegram) (adapters/telegram.py)

Section titled “2.1 تيليجرام (Telegram) (adapters/telegram.py)”
الجانبالتفصيل
الصنفTelegramAdapter (telegram.py:70name = "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:50name = "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 الخاص بديسكورد).

أوامر الشرطة المائلة: يحجز ديسكورد الأوامر ذات البادئة / لنظام تفاعلاته الخاص. تستقبلها كاظمة كنص عادي وتُحلِّلها داخليًا.

الجانبالتفصيل
الصنف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. كسر هذا الثابت يُسرّب مُعرّفات المنصة إلى المخطط ويفسد الجلسات.

_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",
})

_build_initial_state(msg, store) (store.py:95):

  1. يحلّ thread_id عبر _resolve_thread (store.py:34):
    • thread_id موجود في context_metadata، وإلّا
    • مُحدَّد بشكل حتمي من مُعرّف المُرسِل (مثلًا "telegram:12345""gw-telegram-12345")، وإلّا
    • UUID4 جديد.
  2. يحفظ سياق المنصة الكامل في SessionStore: await store.put(thread_id, dict(ctx)).
  3. يبني حالة مخطط مُصغَّرة تحتوي كتلة _gateway فقط:
state["_gateway"] = {
"thread_id": thread_id,
"display_name": ctx.get("username") or "unknown",
"platform": msg.platform,
}
  1. تمريرة دفاعية معمّقة تنتزع أي مفتاح مُسرّب من _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).


يحتفظ SessionStore بتعيينات مُعرّفات المنصات ↔ thread_id — لا المخطط.

التنفيذالموقعالاستخدام
SessionStore (مجرّد)gateway.py:185get، put، delete، evict_older_than اختياري.
_InMemoryStoreagent_handler/store.py:63للاختبار/الاحتياطي؛ يتتبع طوابع زمنية رتيبة للـ TTL.
SQLite SessionStorestores/sqlite.pyدائم.
LangGraph checkpointerstores/checkpoint.pyيُستخدَم من واجهة المستخدم لحالة المحادثة (app.py:724).

تذكر وثائق IncomingMessage (gateway.py:62-66) ذلك بوضوح: “لا يلمس العقل الحقول الخاصة بالمنصة. تعيش مُعرّفات المنصة الخام داخل context_metadata.”


تستخدم واجهة الويب أحداث الخادم المُرسَلة (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 v2swarm_approve_{task_id} / swarm_reject_{task_id}discord.py:312
سلاكاستدعاء تفاعليكتلة موافقة السربslack.py:401
واجهة الويبزر ← POST /api/approve/{thread_id} + حدث SSE approval_requiredroutes_direct.py:454

أولوية النسخة المفردة لمُحوّل الناقل هي تيليجرام > ديسكورد > سلاك (واحد نشط فقط في كل مرة، موصول في app.py:506-556). راجع الأمن والسلامة.


الميزةتيليجرامديسكوردسلاكواجهة الويبواجهة الطرفية (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:mainKazmaTUI().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).
RTLupdate_localization() (app.py:512) يُبدِّل صف CSS باسم rtl-mode ويُترجِم تسميات التبويبات.

عدم اتساق طفيف: تُسمّي واجهة الطرفية (TUI) لوحة التحكم بـ “لوحة القيادة” (app.py:539)؛ بينما يستخدم التدويل (i18n) للويب “لوحة التحكم” (i18n.py:77).


يستدعي _register_bot_commands (telegram.py:961) دالة setMyCommands في تيليجرام عبر ثلاثة نطاقات (default، all_private_chats، all_group_chats). القائمة المُسجَّلة تتضمّن أوامر الشرطة المائلة القياسية بالإضافة إلى /swarm. وهي لا تُسجّل /hitl (لأن سلاك يحتاج الصيغة المجردة hitl، والتسجيل منطق مشترك).


  • agent_handler.py لم يعد موجودًا كملف واحد — بل أصبح الحزمة agent_handler/. التوثيقات الأقدم التي تشير إلى الملف قديمة.
  • نقطة نهاية محادثة WebSocket القديمة ميتة (410 Gone). التوثيق الذي لا يزال يصف /ws/chat كحيٍّ غير صحيح — SSE هو الناقل.
  • أوامرا الشرطة المائلة /undo و/edit كبوبن (stubs) ويجب ألا تُعلَن كوظيفية.
  • /help تتجاهل /hitl و/swarm رغم أن كليهما يعمل. هذه فجوة في نص المساعدة داخل التطبيق، وليست ميزة مفقودة.