تخطَّ إلى المحتوى
kazma.
EN نجمة 7 ابدأ الآن

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

كيف يتحدث عقل كاظمة الواحد مع قنوات عديدة: طبقة المُكيّفات، عزل المنصات، مخزن الجلسات، أوامر الشرطة المائلة، ومصفوفة تكافؤ الميزات — كلها موثّقة مقابل 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 الخاصة بالسرب (Telegram/Discord/Slack). راجع الأمن والسلامة.

الجانبالتفصيل
الصنف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، التقاط الصور/المستندات/الفيديو/الصور المتحركة الواردة، الإرسال الصادر عبر sendPhoto/sendDocument/sendVideo/sendAudio.
وحدات مساعدةtelegram_callbacks.py، telegram_keyboards.py، telegram_parse.py، telegram_send.py، telegram_stt.py.

Telegram هو المُكيّف الأكثر اكتمالًا في الميزات.

الجانبالتفصيل
الصنف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 الخاص بـ Discord).

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

الجانبالتفصيل
الصنفSlackAdapter (slack.py:47).
الاستقبالوضع Socket (عند وجود xapp- app_token) أو اقتراع احتياطي لـ conversations.history.
الإرسالREST عبر واجهة Slack API POST.
مفاتيح السياقchannel_id، user_id، team_id، thread_ts، message_ts، username.

أوامر الشرطة المائلة: يحظر Slack أوامر الشرطة المائلة الصادرة عن البوت، لذا يُصدَر طلب موافقة 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):

  • Telegram → chat_id
  • Discord/Slack → 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 لكل منصة”
المنصةالآليةنمط مُعرّف الاستدعاءالمُعالِج
Telegramأزرار لوحة المفاتيح المضمّنةhitl:approve:\{id\} / hitl:deny:\{id\}telegram.py:733
Discordأزرار Components v2swarm_approve_\{task_id\} / swarm_reject_\{task_id\}discord.py:312
Slackاستدعاء تفاعليكتلة موافقة السربslack.py:401
واجهة الويبزر → POST /api/approve/\{thread_id\} + حدث SSE approval_requiredroutes_direct.py:454

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


الميزةTelegramDiscordSlackواجهة الويبTUI
آلية الاستقبالاقتراع طويل HTTPبوابة WSوضع Socket / اقتراعHTTP POST (SSE)محلي
أزرار موافقة HITLنافذة منبثقة
تصيير Markdown→ Telegram HTMLنص عادينص عاديمن جهة العميلعادي
أوامر الشرطة المائلةمجموعة كاملة (مُسجَّلة)تحليل نص عاديhitl بدون /أزرار واجهة
الصوت / STT (تحويل الكلام الوارد إلى نص)/api/voice/stt
الصوت / TTS (الرد الصوتي الصادر)/api/voice/tts
الوسائط دخول/خروج (صورة/مستند/فيديو)/api/chat/upload
مؤشر الكتابةلا ينطبقلا ينطبق
التفاعلات✅ ✅/🎯/❌لا ينطبقلا ينطبق
واجهة مُحدِّد النموذج✅ لوحة مفاتيح مضمّنة✅ إعداداتللقراءة فقط

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 في Telegram عبر ثلاثة نطاقات (default، all_private_chats، all_group_chats). القائمة المُسجَّلة تتضمّن أوامر الشرطة المائلة القياسية بالإضافة إلى /swarm. وهي لا تُسجّل /hitl (لأن Slack يحتاج الصيغة المجردة hitl، والتسجيل منطق مشترك).


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