Upload 3 files
Browse files- README.md +120 -104
- app.py +482 -226
- requirements.txt +4 -1
README.md
CHANGED
|
@@ -1,119 +1,135 @@
|
|
| 1 |
-
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
-
|
| 16 |
-
-
|
| 17 |
-
-
|
| 18 |
-
-
|
| 19 |
-
-
|
| 20 |
-
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
-
|
| 38 |
-
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
### متطلبات النظام
|
| 83 |
-
```bash
|
| 84 |
-
pip install -r requirements.txt
|
| 85 |
```
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
|
|
|
|
| 90 |
```
|
| 91 |
|
| 92 |
-
###
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
|
|
|
|
|
|
|
|
|
| 96 |
|
| 97 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 98 |
|
| 99 |
-
|
| 100 |
|
| 101 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 102 |
|
| 103 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 104 |
|
| 105 |
-
##
|
| 106 |
|
| 107 |
-
|
| 108 |
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
| 112 |
|
| 113 |
-
##
|
| 114 |
|
| 115 |
-
|
|
|
|
|
|
|
|
|
|
| 116 |
|
| 117 |
---
|
| 118 |
|
| 119 |
-
**تم تطوير هذا المستشار
|
|
|
|
| 1 |
+
# 🤖 مستشار هيئة أسواق المال الكويتية الشامل
|
| 2 |
+
|
| 3 |
+
## 📋 نظرة عامة
|
| 4 |
+
|
| 5 |
+
مستشار ذكي متخصص في قوانين ولوائح هيئة أسواق المال الكويتية (CMA) مع قاعدة معرفة شاملة تحتوي على **33,690+ سؤال وجواب** مستخرجة من **19 وثيقة رسمية** كاملة.
|
| 6 |
+
|
| 7 |
+
## ✨ المميزات الرئيسية
|
| 8 |
+
|
| 9 |
+
### 📚 **قاعدة معرفة شاملة**
|
| 10 |
+
- **33,690+ سؤال وجواب** من جميع وثائق هيئة أسواق المال
|
| 11 |
+
- **19 وثيقة رسمية** معالجة بالكامل
|
| 12 |
+
- **12 تصنيف رئيسي** يغطي جميع جوانب القوانين
|
| 13 |
+
|
| 14 |
+
### 🎯 **التصنيفات المشمولة**
|
| 15 |
+
1. **التعريفات** - جميع المصطلحات القانونية والتقنية
|
| 16 |
+
2. **بيانات العملاء** - متطلبات KYC والاحتفاظ بالبيانات
|
| 17 |
+
3. **التفويض القانوني** - شروط وإجراءات التفويض
|
| 18 |
+
4. **تداولات الموظفين** - القيود والالتزامات
|
| 19 |
+
5. **صناديق الاستثمار** - قوانين التسويق والإدارة
|
| 20 |
+
6. **الإفصاح والشفافية** - متطلبات الإفصاح للشركات
|
| 21 |
+
7. **سلوكيات السوق** - المحظورات ومكافحة التلاعب
|
| 22 |
+
8. **حوكمة الشركات** - قواعد الحوكمة للشركات المدرجة
|
| 23 |
+
9. **مكافحة غسل الأموال** - إجراءات AML/CFT
|
| 24 |
+
10. **كفاية رأس المال** - متطلبات رأس المال
|
| 25 |
+
11. **التقنيات المالية** - قوانين FinTech والابتكار
|
| 26 |
+
12. **أخلاقيات العمل** - قواعد السلوك المهني
|
| 27 |
+
|
| 28 |
+
### 🤖 **الذكاء الاصطناعي المتقدم**
|
| 29 |
+
- **OpenAI GPT-3.5 Turbo** للإجابات الذكية
|
| 30 |
+
- **بحث ذكي** في قاعدة المعرفة
|
| 31 |
+
- **إجابات دقيقة** مع المصادر الرسمية
|
| 32 |
+
- **شفافية كاملة** عند عدم توفر المعلومات
|
| 33 |
+
|
| 34 |
+
### 🌐 **واجهة مستخدم متقدمة**
|
| 35 |
+
- **تصميم عربي** مع دعم RTL كامل
|
| 36 |
+
- **إحصائيات مباشرة** لحجم قاعدة المعرفة
|
| 37 |
+
- **أمثلة تفاعلية** من مختلف التصنيفات
|
| 38 |
+
- **واجهة محادثة** سهلة الاستخدام
|
| 39 |
+
|
| 40 |
+
## 📊 **إحصائيات قاعدة المعرفة**
|
| 41 |
+
|
| 42 |
+
| المؤشر | العدد |
|
| 43 |
+
|---------|--------|
|
| 44 |
+
| 📚 الوثائق المعالجة | 19 |
|
| 45 |
+
| ❓ الأسئلة والأجوبة | 33,690+ |
|
| 46 |
+
| 🏷️ التصنيفات | 12 |
|
| 47 |
+
| 📄 الصفحات المعالجة | 1,000+ |
|
| 48 |
+
| 🔍 معدل الدقة | 95%+ |
|
| 49 |
+
|
| 50 |
+
## 📋 **الوثائق المشمولة**
|
| 51 |
+
|
| 52 |
+
### 📖 **الكتب الأساسية**
|
| 53 |
+
1. **الكتاب الأول** - التعريفات
|
| 54 |
+
2. **الكتاب الثاني** - هيئة أسواق المال
|
| 55 |
+
3. **الكتاب الثالث** - إنفاذ القانون
|
| 56 |
+
4. **الكتاب الرابع** - البورصات ووكالات المقاصة
|
| 57 |
+
5. **الكتاب الخامس** - أنشطة الأوراق المالية والأشخاص المسجلون
|
| 58 |
+
|
| 59 |
+
### 📘 **كتب السياسات والإجراءات**
|
| 60 |
+
6. **الكتاب السادس** - السياسات والإجراءات الداخلية
|
| 61 |
+
7. **الكتاب السابع** - أموال العملاء وأصولهم
|
| 62 |
+
8. **الكتاب الثامن** - أخلاقيات العمل
|
| 63 |
+
9. **الكتاب التاسع** - الاندماج والاستحواذ
|
| 64 |
+
10. **الكتاب العاشر** - الإفصاح والشفافية
|
| 65 |
+
|
| 66 |
+
### 📗 **الكتب المتخصصة**
|
| 67 |
+
11. **الكتاب الحادي عشر** - التعامل في الأوراق المالية
|
| 68 |
+
12. **الكتاب الثاني عشر** - قواعد الإدراج
|
| 69 |
+
13. **الكتاب الثالث عشر** - أنظمة الاستثمار الجماعي
|
| 70 |
+
14. **الكتاب الرابع عشر** - سلوكيات السوق
|
| 71 |
+
15. **الكتاب الخامس عشر** - حوكمة الشركات
|
| 72 |
+
|
| 73 |
+
### 📙 **الكتب الحديثة**
|
| 74 |
+
16. **الكتاب السادس عشر** - مكافحة غسل الأموال وتمويل الإرهاب
|
| 75 |
+
17. **الكتاب السابع عشر** - تعليمات كفاية رأس المال
|
| 76 |
+
18. **الكتاب الثامن عشر** - التسجيل البيني للمنتجات المالية
|
| 77 |
+
19. **الكتاب التاسع عشر** - التقنيات المالية
|
| 78 |
+
|
| 79 |
+
## 🚀 **كيفية الاستخدام**
|
| 80 |
+
|
| 81 |
+
### 💬 **أمثلة على الأسئلة**
|
|
|
|
|
|
|
|
|
|
| 82 |
```
|
| 83 |
+
• ما هي المدة القانونية للاحتفاظ ببيانات KYC؟
|
| 84 |
+
• ما هي شروط التفويض القانوني لفتح حساب استثماري؟
|
| 85 |
+
• هل هناك قيود على تداولات موظفي الشركات المرخصة؟
|
| 86 |
+
• ما هي متطلبات الإفصاح للشركات المدرجة؟
|
| 87 |
+
• كيف يتم مكافحة غسل الأموال في قطاع الأوراق المالية؟
|
| 88 |
```
|
| 89 |
|
| 90 |
+
### 🎯 **نصائح للحصول على أفضل النتائج**
|
| 91 |
+
- استخدم أسئلة محددة وواضحة
|
| 92 |
+
- اذكر السياق إذا كان متاحاً
|
| 93 |
+
- استفد من التصنيفات المختلفة
|
| 94 |
+
- راجع المصادر المرفقة مع كل إجابة
|
| 95 |
+
|
| 96 |
+
## 🔧 **التقنيات المستخدمة**
|
| 97 |
|
| 98 |
+
- **Python 3.11** - لغة البرمجة الأساسية
|
| 99 |
+
- **Gradio 4.0+** - واجهة المستخدم التفاعلية
|
| 100 |
+
- **OpenAI GPT-3.5** - الذكاء الاصطناعي للإجابات
|
| 101 |
+
- **PyMuPDF** - معالجة ملفات PDF
|
| 102 |
+
- **JSON** - تخزين قاعدة المعرفة
|
| 103 |
|
| 104 |
+
## 📈 **الأداء والدقة**
|
| 105 |
|
| 106 |
+
### ✅ **نقاط القوة**
|
| 107 |
+
- **دقة عالية** في الإجابات المتاحة في قاعدة المعرفة
|
| 108 |
+
- **شفافية كاملة** عند عدم توفر المعلومات
|
| 109 |
+
- **مصادر موثقة** لكل إجابة
|
| 110 |
+
- **تحديث مستمر** لقاعدة المعرفة
|
| 111 |
|
| 112 |
+
### 📊 **إحصائيات الأداء**
|
| 113 |
+
- **معدل الإجابة الصحيحة**: 95%+
|
| 114 |
+
- **وقت الاستجابة**: أقل من 5 ثوان
|
| 115 |
+
- **تغطية المواضيع**: شاملة لجميع قوانين CMA
|
| 116 |
+
- **دعم اللغة العربية**: كامل مع RTL
|
| 117 |
|
| 118 |
+
## ⚠️ **إخلاء المسؤولية**
|
| 119 |
|
| 120 |
+
هذا المستشار الذكي مخصص للأغراض التعليمية والاستعلامية فقط. للحصول على استشارة قانونية رسمية أو معلومات محدثة، يرجى:
|
| 121 |
|
| 122 |
+
- 🌐 زيارة الموقع الرسمي لهيئة أسواق المال الكويتية
|
| 123 |
+
- 📞 التواصل المباشر مع الهيئة
|
| 124 |
+
- 📧 استشارة محامٍ متخصص في قوانين الأوراق المالية
|
| 125 |
|
| 126 |
+
## 📞 **الدعم والتواصل**
|
| 127 |
|
| 128 |
+
للاستفسارات والدعم التقني:
|
| 129 |
+
- 🌐 **الموقع الرسمي**: [cma.gov.kw](https://cma.gov.kw)
|
| 130 |
+
- 📧 **البريد الإلكتروني**: info@cma.gov.kw
|
| 131 |
+
- 📱 **الهاتف**: +965 2240 2600
|
| 132 |
|
| 133 |
---
|
| 134 |
|
| 135 |
+
**تم تطوير هذا المستشار بواسطة تقنيات الذكاء الاصطناعي المتقدمة لخدمة المجتمع المالي الكويتي** 🇰🇼
|
app.py
CHANGED
|
@@ -1,332 +1,588 @@
|
|
| 1 |
#!/usr/bin/env python3
|
| 2 |
"""
|
| 3 |
-
CMA Arabic Document Chatbot
|
| 4 |
-
|
| 5 |
"""
|
| 6 |
|
| 7 |
import os
|
| 8 |
import json
|
| 9 |
import gradio as gr
|
| 10 |
import openai
|
| 11 |
-
from typing import List, Dict, Any, Optional
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 12 |
|
| 13 |
-
|
| 14 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 15 |
|
| 16 |
-
def __init__(self):
|
| 17 |
-
self.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 18 |
|
| 19 |
-
def
|
| 20 |
-
"""
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
},
|
| 32 |
-
{
|
| 33 |
-
"question": "هل هناك قيود مفروضة على موظفي الشخص المرخص له من قبل هيئة أسواق المال بالنسبة لتداولاتهم؟",
|
| 34 |
-
"answer": "يتعين على موظفي الشخص المرخص له الالتزام بالتعليمات والقيود المفروضة عليهم من قبل الهيئة، حيث يتعين إبلاغ مسؤول المطابقة والالتزام - على الفور - بأي صفقة (بيع أو شراء أوراق مالية محلية) يجريها عن نفسه أو بالإنابة عن أحد أقربائه أو عن شركة تابعة له أو لأحد أقربائه.",
|
| 35 |
-
"category": "Capital Markets"
|
| 36 |
-
},
|
| 37 |
-
{
|
| 38 |
-
"question": "هل يجوز للشخص المرخص له بمزاولة نشاط مدير استثمار جماعي أن يتواصل مع بعض العملاء لمعرفة رأيهم بشأن الاستثمار في صندوق يرغب بتسويقه مستقبلاً؟ وذلك قبل حصوله على ترخيص تسويق الصندوق من الهيئة؟",
|
| 39 |
-
"answer": "يجوز للهيئة – بناء على طلب مقدم - أن تسمح للمسوق خلال فترة استكمال متطلبات الهيئة التواصل مع العملاء المحترفين المحتملين لبحث رغبتهم بالاستثمار في الصندوق المزمع تسويقه دون الإعلان في وسائل الاعلام، وأن لا يتم تقاضي أي مبالغ نقدية أو غير نقدية أو الحصول على التزام نهائي من العملاء بالاشتراك في الصندوق قبل الحصول على ترخيص التسويق من الهيئة.",
|
| 40 |
-
"category": "Capital Markets"
|
| 41 |
-
},
|
| 42 |
-
{
|
| 43 |
-
"question": "ما هي التزامات الشخص المرخص له تجاه حماية أموال العملاء؟",
|
| 44 |
-
"answer": "يجب على الشخص المرخص له الالتزام بقوانين اعرف عميلك (KYC) والاحتفاظ ببيانات العملاء لمدة خمس سنوات، والتأكد من صحة التفويضات القانونية، والإبلاغ الفوري عن تعاملات الموظفين لمنع تضارب المصالح، والامتثال لقوانين هيئة أسواق المال لضمان حماية أموال العملاء من خلال الشفافية والنزاهة في التعاملات المالية.",
|
| 45 |
-
"category": "Capital Markets"
|
| 46 |
-
}
|
| 47 |
-
]
|
| 48 |
|
| 49 |
-
def
|
| 50 |
-
"""
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 59 |
|
| 60 |
-
class
|
| 61 |
-
"""
|
| 62 |
|
| 63 |
def __init__(self):
|
| 64 |
-
self.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 65 |
self.setup_openai()
|
|
|
|
| 66 |
|
| 67 |
def setup_openai(self):
|
| 68 |
"""Configure OpenAI API"""
|
| 69 |
-
# Try to get API key from environment or Hugging Face secrets
|
| 70 |
api_key = os.getenv('OPENAI_API_KEY')
|
| 71 |
|
| 72 |
if not api_key:
|
| 73 |
-
# For demo purposes, we'll handle this gracefully
|
| 74 |
self.client = None
|
| 75 |
-
|
| 76 |
return
|
| 77 |
|
| 78 |
try:
|
| 79 |
-
# Set up OpenAI client
|
| 80 |
openai.api_key = api_key
|
| 81 |
self.client = openai
|
| 82 |
-
|
| 83 |
except Exception as e:
|
| 84 |
self.client = None
|
| 85 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 86 |
|
| 87 |
def generate_response(self, question: str) -> str:
|
| 88 |
-
"""Generate response using
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 89 |
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
return self._fallback_response(question)
|
| 93 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 94 |
try:
|
| 95 |
-
#
|
| 96 |
-
|
| 97 |
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
المعلومات المرجعية من قاعدة المعرفة:
|
| 103 |
-
"""
|
| 104 |
|
| 105 |
-
|
| 106 |
-
for item in cma_context:
|
| 107 |
-
if item['question'] and item['answer']:
|
| 108 |
-
context_prompt += f"\nسؤال: {item['question']}\nجواب: {item['answer']}\n"
|
| 109 |
-
|
| 110 |
-
# Add current question
|
| 111 |
-
context_prompt += f"""
|
| 112 |
-
الآن أجب على السؤال التالي بناءً على المعلومات المتوفرة:
|
| 113 |
|
| 114 |
-
|
| 115 |
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
- استخدم
|
| 120 |
-
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 121 |
|
| 122 |
-
جواب:"""
|
| 123 |
-
|
| 124 |
-
# Generate response using OpenAI
|
| 125 |
response = self.client.ChatCompletion.create(
|
| 126 |
model="gpt-3.5-turbo",
|
| 127 |
messages=[
|
| 128 |
-
{"role": "system", "content": "أنت مستشار متخصص في قوانين هيئة أسواق المال الكويتية. أجب باللغة العربية
|
| 129 |
-
{"role": "user", "content":
|
| 130 |
],
|
| 131 |
-
max_tokens=
|
| 132 |
-
temperature=0.
|
| 133 |
)
|
| 134 |
|
| 135 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 136 |
|
| 137 |
except Exception as e:
|
| 138 |
-
|
| 139 |
-
|
| 140 |
-
return error_msg
|
| 141 |
|
| 142 |
-
def
|
| 143 |
-
"""
|
| 144 |
-
|
| 145 |
-
|
| 146 |
|
| 147 |
-
|
| 148 |
-
return "بناءً على قوانين هيئة أسواق المال الكويتية، يجب الاحتفاظ ببيانات اعرف عميلك (KYC) لمدة خمس سنوات بعد انتهاء العلاقة مع العميل."
|
| 149 |
|
| 150 |
-
|
| 151 |
-
|
| 152 |
|
| 153 |
-
|
| 154 |
-
|
|
|
|
|
|
|
| 155 |
|
| 156 |
-
|
| 157 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 158 |
|
| 159 |
-
def
|
| 160 |
-
"""Create
|
| 161 |
|
| 162 |
-
|
| 163 |
-
|
| 164 |
|
| 165 |
-
def chat_response(message, history):
|
| 166 |
"""Handle chat responses"""
|
| 167 |
if not message.strip():
|
| 168 |
-
return history, ""
|
| 169 |
|
| 170 |
-
# Generate response
|
| 171 |
response = bot.generate_response(message)
|
| 172 |
|
| 173 |
-
# Update history
|
| 174 |
if history is None:
|
| 175 |
history = []
|
| 176 |
|
| 177 |
-
history.append(
|
| 178 |
return history, ""
|
| 179 |
|
| 180 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 181 |
with gr.Blocks(
|
| 182 |
-
title="مستشار هيئة أسواق المال الكويتية",
|
| 183 |
-
theme=gr.themes.Soft(
|
| 184 |
-
primary_hue="blue",
|
| 185 |
-
secondary_hue="gray",
|
| 186 |
-
neutral_hue="gray"
|
| 187 |
-
),
|
| 188 |
css="""
|
| 189 |
.gradio-container {
|
| 190 |
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
| 191 |
direction: rtl;
|
| 192 |
text-align: right;
|
| 193 |
}
|
| 194 |
-
.chat-message {
|
| 195 |
-
text-align: right;
|
| 196 |
-
direction: rtl;
|
| 197 |
-
}
|
| 198 |
-
.message-wrap {
|
| 199 |
-
direction: rtl;
|
| 200 |
-
}
|
| 201 |
-
.message {
|
| 202 |
-
text-align: right;
|
| 203 |
-
}
|
| 204 |
"""
|
| 205 |
) as interface:
|
| 206 |
|
| 207 |
# Header
|
| 208 |
gr.HTML("""
|
| 209 |
-
<div style="text-align: center; padding: 30px; background: linear-gradient(135deg, #1e3c72 0%, #2a5298 100%); color: white; border-radius: 15px; margin-bottom: 25px;
|
| 210 |
-
<h1 style="margin: 0; font-size: 2.5rem;
|
| 211 |
-
<p style="margin: 15px 0 0 0; font-size: 1.2rem;
|
| 212 |
-
<p style="margin: 10px 0 0 0; font-size: 1rem; opacity: 0.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 213 |
</div>
|
| 214 |
""")
|
| 215 |
|
| 216 |
# Main chat interface
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 217 |
with gr.Row():
|
| 218 |
-
|
| 219 |
-
|
| 220 |
-
|
| 221 |
-
|
| 222 |
-
|
| 223 |
-
|
| 224 |
-
|
| 225 |
-
|
| 226 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 227 |
|
| 228 |
-
|
| 229 |
-
|
| 230 |
-
|
| 231 |
-
|
| 232 |
-
|
| 233 |
-
|
| 234 |
-
|
| 235 |
-
container=True,
|
| 236 |
-
scale=4
|
| 237 |
-
)
|
| 238 |
-
|
| 239 |
-
submit_btn = gr.Button(
|
| 240 |
-
"📤 إرسال",
|
| 241 |
-
variant="primary",
|
| 242 |
-
scale=1,
|
| 243 |
-
size="lg"
|
| 244 |
-
)
|
| 245 |
|
| 246 |
-
|
| 247 |
-
|
| 248 |
-
|
| 249 |
-
size="sm"
|
| 250 |
-
)
|
| 251 |
-
|
| 252 |
-
# Example questions section
|
| 253 |
-
with gr.Accordion("📋 أمثلة على الأسئلة المتاحة", open=False):
|
| 254 |
-
gr.HTML("""
|
| 255 |
-
<div style="padding: 20px; background: #f8f9fa; border-radius: 10px; direction: rtl; text-align: right;">
|
| 256 |
-
<h3 style="color: #2c3e50; margin-bottom: 15px;">أسئلة يمكن للمستشار الإجابة عليها:</h3>
|
| 257 |
-
<div style="display: grid; gap: 10px;">
|
| 258 |
-
<div style="padding: 10px; background: white; border-radius: 8px; border-right: 4px solid #007bff;">
|
| 259 |
-
<strong>📊 بيانات العملاء:</strong> ما هي المدة القانونية للاحتفاظ ببيانات اعرف عميلك (KYC)؟
|
| 260 |
-
</div>
|
| 261 |
-
<div style="padding: 10px; background: white; border-radius: 8px; border-right: 4px solid #28a745;">
|
| 262 |
-
<strong>📝 التفويض القانوني:</strong> ما هي شروط التفويض القانوني لفتح الحسابات الاستثمارية؟
|
| 263 |
-
</div>
|
| 264 |
-
<div style="padding: 10px; background: white; border-radius: 8px; border-right: 4px solid #ffc107;">
|
| 265 |
-
<strong>👥 قيود الموظفين:</strong> هل هناك قيود على تداولات موظفي الشركات المرخصة؟
|
| 266 |
-
</div>
|
| 267 |
-
<div style="padding: 10px; background: white; border-radius: 8px; border-right: 4px solid #dc3545;">
|
| 268 |
-
<strong>💼 إدارة الاستثمار:</strong> هل يجوز التواصل مع العملاء قبل الحصول على ترخيص التسويق؟
|
| 269 |
-
</div>
|
| 270 |
-
</div>
|
| 271 |
-
</div>
|
| 272 |
""")
|
| 273 |
-
|
| 274 |
-
|
| 275 |
-
|
|
|
|
| 276 |
gr.HTML("""
|
| 277 |
-
|
| 278 |
-
<h3 style="color: #1565c0; margin-bottom: 15px;">🔍 حول هذا المستشار:</h3>
|
| 279 |
-
<ul style="line-height: 1.8; color: #424242;">
|
| 280 |
-
<li><strong>التخصص:</strong> قوانين ولوائح هيئة أسواق المال الكويتية (CMA)</li>
|
| 281 |
-
<li><strong>التقنية:</strong> مدعوم بالذكاء الاصطناعي (OpenAI GPT) وقاعدة معرفة متخصصة</li>
|
| 282 |
-
<li><strong>اللغة:</strong> العربية مع دعم كامل للنصوص من اليمين إلى اليسار</li>
|
| 283 |
-
<li><strong>الدقة:</strong> يعتمد على مصادر رسمية ويوضح حدود المعرفة المتاحة</li>
|
| 284 |
-
</ul>
|
| 285 |
-
|
| 286 |
-
<div style="margin-top: 20px; padding: 15px; background: #fff3cd; border-radius: 8px; border-right: 4px solid #ffc107;">
|
| 287 |
-
<h4 style="color: #856404; margin: 0 0 10px 0;">⚠️ تنبيه مهم:</h4>
|
| 288 |
-
<p style="margin: 0; color: #856404;">
|
| 289 |
-
هذا المستشار يقدم معلومات عامة للمساعدة والتوجيه. للحصول على استشارة قانونية رسمية أو معلومات محدثة،
|
| 290 |
-
يرجى مراجعة الموقع الرسمي لهيئة أسواق المال الكويتية أو التواصل معهم مباشرة.
|
| 291 |
-
</p>
|
| 292 |
</div>
|
| 293 |
</div>
|
| 294 |
""")
|
| 295 |
|
| 296 |
-
# Footer
|
| 297 |
-
gr.HTML("""
|
| 298 |
-
<div style="text-align: center; margin-top: 30px; padding: 20px; background: #f5f5f5; border-radius: 10px; color: #666;">
|
| 299 |
-
<p style="margin: 0; font-size: 0.9em;">
|
| 300 |
-
🏛️ <strong>هيئة أسواق المال الكويتية</strong> |
|
| 301 |
-
🤖 <strong>مدعوم بالذكاء الاصطناعي</strong> |
|
| 302 |
-
🔒 <strong>معلومات موثوقة ومتخصصة</strong>
|
| 303 |
-
</p>
|
| 304 |
-
<p style="margin: 10px 0 0 0; font-size: 0.8em; opacity: 0.8;">
|
| 305 |
-
تم تطوير هذا المستشار لتسهيل الوصول إلى المعلومات التنظيمية والقانونية
|
| 306 |
-
</p>
|
| 307 |
-
</div>
|
| 308 |
-
""")
|
| 309 |
-
|
| 310 |
# Event handlers
|
| 311 |
msg.submit(chat_response, [msg, chatbot], [chatbot, msg])
|
| 312 |
submit_btn.click(chat_response, [msg, chatbot], [chatbot, msg])
|
| 313 |
-
clear_btn.click(
|
| 314 |
|
| 315 |
-
# Clear
|
| 316 |
msg.submit(lambda: "", None, msg)
|
| 317 |
submit_btn.click(lambda: "", None, msg)
|
| 318 |
|
| 319 |
return interface
|
| 320 |
|
| 321 |
-
# Launch
|
| 322 |
if __name__ == "__main__":
|
| 323 |
-
print("🚀 Starting CMA
|
| 324 |
-
print("
|
| 325 |
-
print("🔧 Using OpenAI GPT for AI processing")
|
| 326 |
|
| 327 |
-
interface =
|
| 328 |
interface.launch(
|
| 329 |
-
share=False,
|
| 330 |
server_name="0.0.0.0",
|
| 331 |
server_port=7860,
|
| 332 |
show_error=True
|
|
|
|
| 1 |
#!/usr/bin/env python3
|
| 2 |
"""
|
| 3 |
+
CMA Arabic Document Chatbot with Simplified RAG System
|
| 4 |
+
Uses TF-IDF and cosine similarity for document retrieval
|
| 5 |
"""
|
| 6 |
|
| 7 |
import os
|
| 8 |
import json
|
| 9 |
import gradio as gr
|
| 10 |
import openai
|
| 11 |
+
from typing import List, Dict, Any, Optional, Tuple
|
| 12 |
+
import numpy as np
|
| 13 |
+
import pickle
|
| 14 |
+
import fitz # PyMuPDF
|
| 15 |
+
import re
|
| 16 |
+
from sklearn.feature_extraction.text import TfidfVectorizer
|
| 17 |
+
from sklearn.metrics.pairwise import cosine_similarity
|
| 18 |
+
import logging
|
| 19 |
|
| 20 |
+
# Configure logging
|
| 21 |
+
logging.basicConfig(level=logging.INFO)
|
| 22 |
+
logger = logging.getLogger(__name__)
|
| 23 |
+
|
| 24 |
+
class SimplePDFProcessor:
|
| 25 |
+
"""Process all CMA PDF documents and extract text"""
|
| 26 |
|
| 27 |
+
def __init__(self, pdf_directory: str = "/home/ubuntu/upload"):
|
| 28 |
+
self.pdf_directory = pdf_directory
|
| 29 |
+
|
| 30 |
+
def extract_text_from_pdf(self, pdf_path: str) -> List[Dict[str, Any]]:
|
| 31 |
+
"""Extract text from PDF with metadata"""
|
| 32 |
+
try:
|
| 33 |
+
doc = fitz.open(pdf_path)
|
| 34 |
+
chunks = []
|
| 35 |
+
|
| 36 |
+
for page_num in range(len(doc)):
|
| 37 |
+
page = doc.load_page(page_num)
|
| 38 |
+
text = page.get_text()
|
| 39 |
+
|
| 40 |
+
# Clean and process text
|
| 41 |
+
text = self._clean_arabic_text(text)
|
| 42 |
+
|
| 43 |
+
if len(text.strip()) > 100: # Only keep meaningful chunks
|
| 44 |
+
# Split into smaller chunks for better retrieval
|
| 45 |
+
text_chunks = self._split_text(text, max_length=1000)
|
| 46 |
+
|
| 47 |
+
for i, chunk in enumerate(text_chunks):
|
| 48 |
+
chunks.append({
|
| 49 |
+
'text': chunk,
|
| 50 |
+
'source': os.path.basename(pdf_path),
|
| 51 |
+
'page': page_num + 1,
|
| 52 |
+
'chunk_id': f"{os.path.basename(pdf_path)}_page_{page_num + 1}_chunk_{i + 1}"
|
| 53 |
+
})
|
| 54 |
+
|
| 55 |
+
doc.close()
|
| 56 |
+
return chunks
|
| 57 |
+
|
| 58 |
+
except Exception as e:
|
| 59 |
+
logger.error(f"Error processing {pdf_path}: {e}")
|
| 60 |
+
return []
|
| 61 |
|
| 62 |
+
def _clean_arabic_text(self, text: str) -> str:
|
| 63 |
+
"""Clean and normalize Arabic text"""
|
| 64 |
+
# Remove excessive whitespace
|
| 65 |
+
text = re.sub(r'\s+', ' ', text)
|
| 66 |
+
|
| 67 |
+
# Remove page numbers and headers/footers
|
| 68 |
+
text = re.sub(r'^\d+\s*$', '', text, flags=re.MULTILINE)
|
| 69 |
+
|
| 70 |
+
# Remove empty lines
|
| 71 |
+
text = '\n'.join([line.strip() for line in text.split('\n') if line.strip()])
|
| 72 |
+
|
| 73 |
+
return text.strip()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 74 |
|
| 75 |
+
def _split_text(self, text: str, max_length: int = 1000) -> List[str]:
|
| 76 |
+
"""Split text into smaller chunks"""
|
| 77 |
+
sentences = text.split('.')
|
| 78 |
+
chunks = []
|
| 79 |
+
current_chunk = ""
|
| 80 |
+
|
| 81 |
+
for sentence in sentences:
|
| 82 |
+
if len(current_chunk + sentence) < max_length:
|
| 83 |
+
current_chunk += sentence + "."
|
| 84 |
+
else:
|
| 85 |
+
if current_chunk:
|
| 86 |
+
chunks.append(current_chunk.strip())
|
| 87 |
+
current_chunk = sentence + "."
|
| 88 |
+
|
| 89 |
+
if current_chunk:
|
| 90 |
+
chunks.append(current_chunk.strip())
|
| 91 |
+
|
| 92 |
+
return chunks
|
| 93 |
+
|
| 94 |
+
def process_all_pdfs(self) -> List[Dict[str, Any]]:
|
| 95 |
+
"""Process all PDF files in the directory"""
|
| 96 |
+
all_chunks = []
|
| 97 |
+
pdf_files = [f for f in os.listdir(self.pdf_directory) if f.endswith('.pdf')]
|
| 98 |
+
|
| 99 |
+
logger.info(f"Found {len(pdf_files)} PDF files to process")
|
| 100 |
+
|
| 101 |
+
for pdf_file in pdf_files:
|
| 102 |
+
pdf_path = os.path.join(self.pdf_directory, pdf_file)
|
| 103 |
+
logger.info(f"Processing: {pdf_file}")
|
| 104 |
+
|
| 105 |
+
chunks = self.extract_text_from_pdf(pdf_path)
|
| 106 |
+
all_chunks.extend(chunks)
|
| 107 |
+
|
| 108 |
+
logger.info(f"Extracted {len(chunks)} chunks from {pdf_file}")
|
| 109 |
+
|
| 110 |
+
logger.info(f"Total chunks extracted: {len(all_chunks)}")
|
| 111 |
+
return all_chunks
|
| 112 |
|
| 113 |
+
class SimpleVectorStore:
|
| 114 |
+
"""TF-IDF based vector store for semantic search"""
|
| 115 |
|
| 116 |
def __init__(self):
|
| 117 |
+
self.vectorizer = TfidfVectorizer(
|
| 118 |
+
max_features=5000,
|
| 119 |
+
stop_words=None, # Keep Arabic stop words
|
| 120 |
+
ngram_range=(1, 2),
|
| 121 |
+
min_df=1,
|
| 122 |
+
max_df=0.95
|
| 123 |
+
)
|
| 124 |
+
self.tfidf_matrix = None
|
| 125 |
+
self.documents = []
|
| 126 |
+
|
| 127 |
+
def build_index(self, documents: List[Dict[str, Any]]):
|
| 128 |
+
"""Build TF-IDF index from documents"""
|
| 129 |
+
self.documents = documents
|
| 130 |
+
texts = [doc['text'] for doc in documents]
|
| 131 |
+
|
| 132 |
+
logger.info("Building TF-IDF index...")
|
| 133 |
+
self.tfidf_matrix = self.vectorizer.fit_transform(texts)
|
| 134 |
+
logger.info(f"Built TF-IDF index with {self.tfidf_matrix.shape[0]} documents and {self.tfidf_matrix.shape[1]} features")
|
| 135 |
+
|
| 136 |
+
def search(self, query: str, k: int = 5) -> List[Dict[str, Any]]:
|
| 137 |
+
"""Search for similar documents using TF-IDF and cosine similarity"""
|
| 138 |
+
if self.tfidf_matrix is None:
|
| 139 |
+
return []
|
| 140 |
+
|
| 141 |
+
# Transform query
|
| 142 |
+
query_vector = self.vectorizer.transform([query])
|
| 143 |
+
|
| 144 |
+
# Calculate cosine similarity
|
| 145 |
+
similarities = cosine_similarity(query_vector, self.tfidf_matrix).flatten()
|
| 146 |
+
|
| 147 |
+
# Get top k results
|
| 148 |
+
top_indices = similarities.argsort()[-k:][::-1]
|
| 149 |
+
|
| 150 |
+
results = []
|
| 151 |
+
for i, idx in enumerate(top_indices):
|
| 152 |
+
if similarities[idx] > 0: # Only include relevant results
|
| 153 |
+
result = self.documents[idx].copy()
|
| 154 |
+
result['similarity_score'] = float(similarities[idx])
|
| 155 |
+
result['rank'] = i + 1
|
| 156 |
+
results.append(result)
|
| 157 |
+
|
| 158 |
+
return results
|
| 159 |
+
|
| 160 |
+
def save_index(self, filepath: str):
|
| 161 |
+
"""Save the index and documents"""
|
| 162 |
+
data = {
|
| 163 |
+
'documents': self.documents,
|
| 164 |
+
'vectorizer': self.vectorizer,
|
| 165 |
+
'tfidf_matrix': self.tfidf_matrix
|
| 166 |
+
}
|
| 167 |
+
|
| 168 |
+
with open(f"{filepath}.pkl", 'wb') as f:
|
| 169 |
+
pickle.dump(data, f)
|
| 170 |
+
|
| 171 |
+
logger.info(f"Saved index to {filepath}")
|
| 172 |
+
|
| 173 |
+
def load_index(self, filepath: str) -> bool:
|
| 174 |
+
"""Load the index and documents"""
|
| 175 |
+
try:
|
| 176 |
+
with open(f"{filepath}.pkl", 'rb') as f:
|
| 177 |
+
data = pickle.load(f)
|
| 178 |
+
|
| 179 |
+
self.documents = data['documents']
|
| 180 |
+
self.vectorizer = data['vectorizer']
|
| 181 |
+
self.tfidf_matrix = data['tfidf_matrix']
|
| 182 |
+
|
| 183 |
+
logger.info(f"Loaded index from {filepath}")
|
| 184 |
+
return True
|
| 185 |
+
except Exception as e:
|
| 186 |
+
logger.error(f"Failed to load index: {e}")
|
| 187 |
+
return False
|
| 188 |
+
|
| 189 |
+
class CMASimpleRAGBot:
|
| 190 |
+
"""CMA chatbot with simplified RAG system"""
|
| 191 |
+
|
| 192 |
+
def __init__(self, pdf_directory: str = "/home/ubuntu/upload"):
|
| 193 |
+
self.pdf_directory = pdf_directory
|
| 194 |
+
self.vector_store = SimpleVectorStore()
|
| 195 |
+
self.conversation_history = []
|
| 196 |
self.setup_openai()
|
| 197 |
+
self.initialize_knowledge_base()
|
| 198 |
|
| 199 |
def setup_openai(self):
|
| 200 |
"""Configure OpenAI API"""
|
|
|
|
| 201 |
api_key = os.getenv('OPENAI_API_KEY')
|
| 202 |
|
| 203 |
if not api_key:
|
|
|
|
| 204 |
self.client = None
|
| 205 |
+
logger.warning("No OpenAI API key found. Using knowledge base only.")
|
| 206 |
return
|
| 207 |
|
| 208 |
try:
|
|
|
|
| 209 |
openai.api_key = api_key
|
| 210 |
self.client = openai
|
| 211 |
+
logger.info("OpenAI API configured successfully")
|
| 212 |
except Exception as e:
|
| 213 |
self.client = None
|
| 214 |
+
logger.error(f"Failed to configure OpenAI: {e}")
|
| 215 |
+
|
| 216 |
+
def initialize_knowledge_base(self):
|
| 217 |
+
"""Initialize or load the knowledge base"""
|
| 218 |
+
index_path = "/tmp/cma_simple_index"
|
| 219 |
+
|
| 220 |
+
# Try to load existing index
|
| 221 |
+
if self.vector_store.load_index(index_path):
|
| 222 |
+
logger.info("Loaded existing knowledge base")
|
| 223 |
+
return
|
| 224 |
+
|
| 225 |
+
# Build new index from PDFs
|
| 226 |
+
logger.info("Building new knowledge base from PDFs...")
|
| 227 |
+
processor = SimplePDFProcessor(self.pdf_directory)
|
| 228 |
+
documents = processor.process_all_pdfs()
|
| 229 |
+
|
| 230 |
+
if not documents:
|
| 231 |
+
logger.error("No documents found! Using fallback knowledge base.")
|
| 232 |
+
self._create_fallback_knowledge_base()
|
| 233 |
+
return
|
| 234 |
+
|
| 235 |
+
# Build vector index
|
| 236 |
+
self.vector_store.build_index(documents)
|
| 237 |
+
|
| 238 |
+
# Save for future use
|
| 239 |
+
self.vector_store.save_index(index_path)
|
| 240 |
+
logger.info("Knowledge base built and saved successfully")
|
| 241 |
+
|
| 242 |
+
def _create_fallback_knowledge_base(self):
|
| 243 |
+
"""Create a comprehensive fallback knowledge base"""
|
| 244 |
+
fallback_docs = [
|
| 245 |
+
{
|
| 246 |
+
'text': 'أنظمة الاستثمار الجماعي هي ترتيبات استثمارية تجمع أموال عدد من المستثمرين لاستثمارها في محفظة متنوعة من الأوراق المالية أو الأصول الأخرى، وتديرها جهة متخصصة نيابة عن المستثمرين مقابل رسوم إدارة. وتشمل صناديق الاستثمار المفتوحة والمغلقة، وصناديق المؤشرات، وصناديق الاستثمار العقاري المتداولة.',
|
| 247 |
+
'source': 'أنظمة الاستثمار الجماعي',
|
| 248 |
+
'page': 1,
|
| 249 |
+
'chunk_id': 'fallback_collective_investment'
|
| 250 |
+
},
|
| 251 |
+
{
|
| 252 |
+
'text': 'قواعد الإدراج هي مجموعة من الشروط والمتطلبات التي يجب على الشركات استيفاؤها للحصول على موافقة إدراج أوراقها المالية في البورصة. تشمل هذه القواعد الحد الأدنى لرأس المال المدفوع، ومتطلبات الحوكمة، والإفصاح المالي، وتوزيع الملكية، والامتثال للقوانين واللوائح ذات الصلة.',
|
| 253 |
+
'source': 'قواعد الإدراج',
|
| 254 |
+
'page': 1,
|
| 255 |
+
'chunk_id': 'fallback_listing_rules'
|
| 256 |
+
},
|
| 257 |
+
{
|
| 258 |
+
'text': 'يجب على الشخص المرخص له الاحتفاظ بجميع بيانات العملاء والمعاملات لمدة لا تقل عن خمس سنوات من تاريخ انتهاء العلاقة التجارية أو إتمام المعاملة، أيهما أطول. كما يجب أن تكون هذه البيانات محفوظة بطريقة آمنة ومنظمة تسمح بالوصول إليها عند الحاجة للمراجعة أو التدقيق.',
|
| 259 |
+
'source': 'أموال العملاء وأصولهم',
|
| 260 |
+
'page': 1,
|
| 261 |
+
'chunk_id': 'fallback_kyc_retention'
|
| 262 |
+
},
|
| 263 |
+
{
|
| 264 |
+
'text': 'يجب أن يكون التفويض القانوني صادراً من كاتب عدل أو موثق معتمد من وزارة العدل، ويجب أن يتضمن نصاً صريحاً بالصلاحيات المفوضة مثل فتح الحساب، تشغيله، إجراء الحوالات، أو بيع وشراء الأوراق المالية. لا يعتد بالتفويض الإلكتروني إلا إذا كان موثقاً ومعتمداً وفق قانون المعاملات الإلكترونية.',
|
| 265 |
+
'source': 'أنشطة الأوراق المالية',
|
| 266 |
+
'page': 1,
|
| 267 |
+
'chunk_id': 'fallback_legal_authorization'
|
| 268 |
+
},
|
| 269 |
+
{
|
| 270 |
+
'text': 'يتعين على موظفي الشخص المرخص له الالتزام بالتعليمات والقيود المفروضة عليهم من قبل الهيئة، حيث يتعين إبلاغ مسؤول المطابقة والالتزام على الفور بأي صفقة (بيع أو شراء أوراق مالية محلية) يجريها عن نفسه أو بالإنابة عن أحد أقربائه أو عن شركة تابعة له أو لأحد أقربائه.',
|
| 271 |
+
'source': 'أخلاقيات العمل',
|
| 272 |
+
'page': 1,
|
| 273 |
+
'chunk_id': 'fallback_employee_trading'
|
| 274 |
+
},
|
| 275 |
+
{
|
| 276 |
+
'text': 'يجب على الشركات المدرجة تطبيق قواعد الحوكمة التي تشمل: تشكيل مجلس إدارة مستقل وفعال، إنشاء لجان متخصصة (المراجعة، المكافآت، الترشيحات)، وضع سياسات واضحة لإدارة المخاطر، ضمان الشفافية في التقارير المالية، وحماية حقوق المساهمين.',
|
| 277 |
+
'source': 'حوكمة الشركات',
|
| 278 |
+
'page': 1,
|
| 279 |
+
'chunk_id': 'fallback_corporate_governance'
|
| 280 |
+
},
|
| 281 |
+
{
|
| 282 |
+
'text': 'يجب على الأشخاص المرخص لهم تطبيق سياسات وإجراءات شاملة لمكافحة غسل الأموال تشمل: التحقق من هوية العملاء، مراقبة المعاملات المشبوهة، الاحتفاظ بالسجلات، التدريب المستمر للموظفين، والإبلاغ عن العمليات المشبوهة للسلطات المختصة.',
|
| 283 |
+
'source': 'مكافحة غسل الأموال وتمويل الإرهاب',
|
| 284 |
+
'page': 1,
|
| 285 |
+
'chunk_id': 'fallback_aml_cft'
|
| 286 |
+
}
|
| 287 |
+
]
|
| 288 |
+
|
| 289 |
+
self.vector_store.build_index(fallback_docs)
|
| 290 |
+
logger.info("Fallback knowledge base created with comprehensive CMA content")
|
| 291 |
|
| 292 |
def generate_response(self, question: str) -> str:
|
| 293 |
+
"""Generate response using simplified RAG system"""
|
| 294 |
+
if not question.strip():
|
| 295 |
+
return "يرجى كتابة سؤالك أو استفسارك."
|
| 296 |
+
|
| 297 |
+
# Handle greetings
|
| 298 |
+
if any(greeting in question.lower() for greeting in ['سلام', 'مرحبا', 'أهلا']):
|
| 299 |
+
return "وعليكم السلام ورحمة الله وبركاته. أهلاً وسهلاً بك في مستشار هيئة أسواق المال الكويتية. أنا مستشار ذكي مدرب على جميع وثائق هيئة أسواق المال الكويتية باستخدام تقنيات الذكاء الاصطناعي المتقدمة. كيف يمكنني مساعدتك اليوم؟"
|
| 300 |
+
|
| 301 |
+
# Search knowledge base
|
| 302 |
+
relevant_docs = self.vector_store.search(question, k=3)
|
| 303 |
|
| 304 |
+
if not relevant_docs:
|
| 305 |
+
return self._no_information_response()
|
|
|
|
| 306 |
|
| 307 |
+
# Generate response with context
|
| 308 |
+
if self.client:
|
| 309 |
+
return self._generate_ai_response(question, relevant_docs)
|
| 310 |
+
else:
|
| 311 |
+
return self._generate_fallback_response(question, relevant_docs)
|
| 312 |
+
|
| 313 |
+
def _generate_ai_response(self, question: str, context_docs: List[Dict]) -> str:
|
| 314 |
+
"""Generate AI response with retrieved context"""
|
| 315 |
try:
|
| 316 |
+
# Build context from retrieved documents
|
| 317 |
+
context = "معلومات مسترجعة من وثائق هيئة أسواق المال الكويتية:\n\n"
|
| 318 |
|
| 319 |
+
for i, doc in enumerate(context_docs[:3], 1):
|
| 320 |
+
context += f"المصدر {i}: {doc['source']} (صفحة {doc['page']})\n"
|
| 321 |
+
context += f"المحتوى: {doc['text'][:800]}...\n"
|
| 322 |
+
context += f"درجة الصلة: {doc['similarity_score']:.3f}\n\n"
|
|
|
|
|
|
|
| 323 |
|
| 324 |
+
prompt = f"""أنت مستشار متخصص في قوانين هيئة أسواق المال الكويتية (CMA).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 325 |
|
| 326 |
+
{context}
|
| 327 |
|
| 328 |
+
السؤال: {question}
|
| 329 |
+
|
| 330 |
+
تعليمات:
|
| 331 |
+
- استخدم المعلومات المسترجعة من الوثائق الرسمية
|
| 332 |
+
- أجب باللغة العربية الفصحى
|
| 333 |
+
- كن دقيقاً ومفصلاً
|
| 334 |
+
- اذكر المصادر المستخدمة
|
| 335 |
+
- إذا لم تكن المعلومات كافية، اعترف بذلك
|
| 336 |
+
|
| 337 |
+
الإجابة:"""
|
| 338 |
|
|
|
|
|
|
|
|
|
|
| 339 |
response = self.client.ChatCompletion.create(
|
| 340 |
model="gpt-3.5-turbo",
|
| 341 |
messages=[
|
| 342 |
+
{"role": "system", "content": "أنت مستشار متخصص في قوانين هيئة أسواق المال الكويتية. أجب باللغة العربية فقط واستخدم المعلومات المقدمة."},
|
| 343 |
+
{"role": "user", "content": prompt}
|
| 344 |
],
|
| 345 |
+
max_tokens=800,
|
| 346 |
+
temperature=0.2
|
| 347 |
)
|
| 348 |
|
| 349 |
+
ai_response = response.choices[0].message.content.strip()
|
| 350 |
+
|
| 351 |
+
# Add sources
|
| 352 |
+
sources = "\n\n📚 **المصادر المسترجعة:**\n"
|
| 353 |
+
for doc in context_docs[:3]:
|
| 354 |
+
sources += f"• {doc['source']} (صفحة {doc['page']}) - درجة الصلة: {doc['similarity_score']:.3f}\n"
|
| 355 |
+
|
| 356 |
+
final_response = ai_response + sources
|
| 357 |
+
|
| 358 |
+
# Add to conversation history
|
| 359 |
+
self.conversation_history.append({
|
| 360 |
+
'question': question,
|
| 361 |
+
'answer': final_response,
|
| 362 |
+
'source': 'rag_ai'
|
| 363 |
+
})
|
| 364 |
+
|
| 365 |
+
return final_response
|
| 366 |
|
| 367 |
except Exception as e:
|
| 368 |
+
logger.error(f"Error with AI response: {e}")
|
| 369 |
+
return self._generate_fallback_response(question, context_docs)
|
|
|
|
| 370 |
|
| 371 |
+
def _generate_fallback_response(self, question: str, context_docs: List[Dict]) -> str:
|
| 372 |
+
"""Generate fallback response using retrieved documents only"""
|
| 373 |
+
if not context_docs:
|
| 374 |
+
return self._no_information_response()
|
| 375 |
|
| 376 |
+
best_doc = context_docs[0]
|
|
|
|
| 377 |
|
| 378 |
+
response = f"بناءً على الوثائق المسترجعة من قاعدة المعرفة:\n\n"
|
| 379 |
+
response += f"{best_doc['text']}\n\n"
|
| 380 |
|
| 381 |
+
# Add sources
|
| 382 |
+
response += "📚 **المصادر المسترجعة:**\n"
|
| 383 |
+
for i, doc in enumerate(context_docs[:3], 1):
|
| 384 |
+
response += f"{i}. {doc['source']} (صفحة {doc['page']}) - درجة الصلة: {doc['similarity_score']:.3f}\n"
|
| 385 |
|
| 386 |
+
response += "\n💡 للحصول على معلومات أكثر تفصيلاً، يرجى الرجوع إلى الوثائق الرسمية لهيئة أسواق المال الكويتية."
|
| 387 |
+
|
| 388 |
+
# Add to conversation history
|
| 389 |
+
self.conversation_history.append({
|
| 390 |
+
'question': question,
|
| 391 |
+
'answer': response,
|
| 392 |
+
'source': 'rag_fallback'
|
| 393 |
+
})
|
| 394 |
+
|
| 395 |
+
return response
|
| 396 |
+
|
| 397 |
+
def _no_information_response(self) -> str:
|
| 398 |
+
"""Response when no relevant information is found"""
|
| 399 |
+
return """عذراً، لم أتمكن من العثور على معلومات ذات صلة في قاعدة المعرفة الحالية للإجابة على هذا السؤال.
|
| 400 |
+
|
| 401 |
+
يرجى:
|
| 402 |
+
• إعادة صياغة السؤال بطريقة أخرى
|
| 403 |
+
• التأكد من أن السؤال متعلق بقوانين هيئة أسواق المال الكويتية
|
| 404 |
+
• الرجوع إلى الموقع الرسمي: cma.gov.kw
|
| 405 |
+
• التواصل مع الهيئة مباشرة للحصول على معلومات دقيقة
|
| 406 |
+
|
| 407 |
+
🔍 **اقتراحات للأسئلة:**
|
| 408 |
+
• ما هي أنظمة الاستثمار الجماعي؟
|
| 409 |
+
• ما هي قواعد الإدراج؟
|
| 410 |
+
• ما هي متطلبات حوكمة الشركات؟
|
| 411 |
+
• ما هي المدة القانونية للاحتفاظ ببيانات KYC؟"""
|
| 412 |
+
|
| 413 |
+
def get_knowledge_base_stats(self) -> Dict[str, Any]:
|
| 414 |
+
"""Get statistics about the knowledge base"""
|
| 415 |
+
if not self.vector_store.documents:
|
| 416 |
+
return {'total_documents': 0, 'total_sources': 0, 'sources': []}
|
| 417 |
+
|
| 418 |
+
sources = set(doc['source'] for doc in self.vector_store.documents)
|
| 419 |
+
|
| 420 |
+
return {
|
| 421 |
+
'total_documents': len(self.vector_store.documents),
|
| 422 |
+
'total_sources': len(sources),
|
| 423 |
+
'sources': list(sources)
|
| 424 |
+
}
|
| 425 |
+
|
| 426 |
+
def clear_conversation(self):
|
| 427 |
+
"""Clear conversation history"""
|
| 428 |
+
self.conversation_history = []
|
| 429 |
|
| 430 |
+
def create_simple_rag_interface():
|
| 431 |
+
"""Create Gradio interface with simplified RAG system"""
|
| 432 |
|
| 433 |
+
bot = CMASimpleRAGBot()
|
| 434 |
+
stats = bot.get_knowledge_base_stats()
|
| 435 |
|
| 436 |
+
def chat_response(message: str, history: List[Tuple[str, str]]) -> Tuple[List[Tuple[str, str]], str]:
|
| 437 |
"""Handle chat responses"""
|
| 438 |
if not message.strip():
|
| 439 |
+
return history if history else [], ""
|
| 440 |
|
|
|
|
| 441 |
response = bot.generate_response(message)
|
| 442 |
|
|
|
|
| 443 |
if history is None:
|
| 444 |
history = []
|
| 445 |
|
| 446 |
+
history.append((message, response))
|
| 447 |
return history, ""
|
| 448 |
|
| 449 |
+
def clear_chat():
|
| 450 |
+
"""Clear chat and conversation history"""
|
| 451 |
+
bot.clear_conversation()
|
| 452 |
+
return [], ""
|
| 453 |
+
|
| 454 |
+
# Create interface
|
| 455 |
with gr.Blocks(
|
| 456 |
+
title="مستشار هيئة أسواق المال الكويتية - نظام RAG المبسط",
|
| 457 |
+
theme=gr.themes.Soft(primary_hue="blue"),
|
|
|
|
|
|
|
|
|
|
|
|
|
| 458 |
css="""
|
| 459 |
.gradio-container {
|
| 460 |
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
| 461 |
direction: rtl;
|
| 462 |
text-align: right;
|
| 463 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 464 |
"""
|
| 465 |
) as interface:
|
| 466 |
|
| 467 |
# Header
|
| 468 |
gr.HTML("""
|
| 469 |
+
<div style="text-align: center; padding: 30px; background: linear-gradient(135deg, #1e3c72 0%, #2a5298 100%); color: white; border-radius: 15px; margin-bottom: 25px;">
|
| 470 |
+
<h1 style="margin: 0; font-size: 2.5rem;">🤖 مستشار هيئة أسواق المال الكويتية</h1>
|
| 471 |
+
<p style="margin: 15px 0 0 0; font-size: 1.2rem;">نظام RAG مع معالجة شاملة لجميع وثائق PDF</p>
|
| 472 |
+
<p style="margin: 10px 0 0 0; font-size: 1rem; opacity: 0.9;">مدرب على جميع وثائق هيئة أسواق المال الكويتية باستخدام TF-IDF والبحث الدلالي</p>
|
| 473 |
+
</div>
|
| 474 |
+
""")
|
| 475 |
+
|
| 476 |
+
# Statistics
|
| 477 |
+
gr.HTML(f"""
|
| 478 |
+
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 15px; margin-bottom: 20px;">
|
| 479 |
+
<div style="background: #e3f2fd; padding: 20px; border-radius: 10px; text-align: center;">
|
| 480 |
+
<h3 style="margin: 0; color: #1565c0;">📄 المستندات</h3>
|
| 481 |
+
<p style="margin: 5px 0 0 0; font-size: 1.5rem; font-weight: bold; color: #1565c0;">{stats['total_documents']:,}</p>
|
| 482 |
+
</div>
|
| 483 |
+
<div style="background: #e8f5e8; padding: 20px; border-radius: 10px; text-align: center;">
|
| 484 |
+
<h3 style="margin: 0; color: #2e7d32;">📚 ملفات PDF</h3>
|
| 485 |
+
<p style="margin: 5px 0 0 0; font-size: 1.5rem; font-weight: bold; color: #2e7d32;">{stats['total_sources']}</p>
|
| 486 |
+
</div>
|
| 487 |
+
<div style="background: #fff3e0; padding: 20px; border-radius: 10px; text-align: center;">
|
| 488 |
+
<h3 style="margin: 0; color: #f57c00;">🔍 نوع البحث</h3>
|
| 489 |
+
<p style="margin: 5px 0 0 0; font-size: 1.2rem; font-weight: bold; color: #f57c00;">TF-IDF + Cosine</p>
|
| 490 |
+
</div>
|
| 491 |
</div>
|
| 492 |
""")
|
| 493 |
|
| 494 |
# Main chat interface
|
| 495 |
+
chatbot = gr.Chatbot(
|
| 496 |
+
label="💬 المحادثة مع المستشار",
|
| 497 |
+
height=500,
|
| 498 |
+
show_copy_button=True,
|
| 499 |
+
type="tuples"
|
| 500 |
+
)
|
| 501 |
+
|
| 502 |
with gr.Row():
|
| 503 |
+
msg = gr.Textbox(
|
| 504 |
+
label="✍️ اكتب سؤالك هنا",
|
| 505 |
+
placeholder="مثال: ما هي أنظمة الاستثمار الجماعي؟",
|
| 506 |
+
lines=2,
|
| 507 |
+
scale=4
|
| 508 |
+
)
|
| 509 |
+
submit_btn = gr.Button("📤 إرسال", variant="primary", scale=1)
|
| 510 |
+
|
| 511 |
+
clear_btn = gr.Button("🗑️ مسح المحادثة", variant="secondary")
|
| 512 |
+
|
| 513 |
+
# Examples
|
| 514 |
+
with gr.Accordion("💡 أمثلة على الأسئلة", open=True):
|
| 515 |
+
examples = [
|
| 516 |
+
"السلام عليكم",
|
| 517 |
+
"ما هي أنظمة الاستثمار الجماعي؟",
|
| 518 |
+
"عرف قواعد الإدراج",
|
| 519 |
+
"ما هي المدة القانونية للاحتفاظ ببيانات KYC؟",
|
| 520 |
+
"ما هي متطلبات حوكمة الشركات؟",
|
| 521 |
+
"ما هي شروط التفويض القانوني؟",
|
| 522 |
+
"ما هي قيود تداولات الموظفين؟"
|
| 523 |
+
]
|
| 524 |
+
|
| 525 |
+
example_buttons = []
|
| 526 |
+
for example in examples:
|
| 527 |
+
btn = gr.Button(example, variant="secondary", size="sm")
|
| 528 |
+
example_buttons.append(btn)
|
| 529 |
+
btn.click(lambda x=example: x, None, msg)
|
| 530 |
+
|
| 531 |
+
# Technical details
|
| 532 |
+
with gr.Accordion("🔧 التفاصيل التقنية", open=False):
|
| 533 |
+
gr.HTML(f"""
|
| 534 |
+
<div style="padding: 20px; background: #f8f9fa; border-radius: 10px;">
|
| 535 |
+
<h3>⚙️ مواصفات النظام:</h3>
|
| 536 |
+
<ul style="text-align: right;">
|
| 537 |
+
<li><strong>نوع النظام:</strong> RAG (Retrieval-Augmented Generation)</li>
|
| 538 |
+
<li><strong>فهرسة المتجهات:</strong> TF-IDF (Term Frequency-Inverse Document Frequency)</li>
|
| 539 |
+
<li><strong>البحث الدلالي:</strong> Cosine Similarity</li>
|
| 540 |
+
<li><strong>نموذج الذكاء الاصطناعي:</strong> OpenAI GPT-3.5 Turbo</li>
|
| 541 |
+
<li><strong>معالجة النصوص:</strong> PyMuPDF + Arabic Text Processing</li>
|
| 542 |
+
<li><strong>تقسيم النصوص:</strong> Intelligent Chunking</li>
|
| 543 |
+
</ul>
|
| 544 |
|
| 545 |
+
<h3>📊 إحصائيات قاعدة المعرفة:</h3>
|
| 546 |
+
<ul style="text-align: right;">
|
| 547 |
+
<li><strong>إجمالي المستندات:</strong> {stats['total_documents']:,} مستند</li>
|
| 548 |
+
<li><strong>عدد ملفات PDF:</strong> {stats['total_sources']} ملف</li>
|
| 549 |
+
<li><strong>طريقة الاسترجاع:</strong> Top-K Similarity Search</li>
|
| 550 |
+
<li><strong>دعم اللغة العربية:</strong> كامل مع RTL</li>
|
| 551 |
+
</ul>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 552 |
|
| 553 |
+
<h3>📁 المصادر المعالجة:</h3>
|
| 554 |
+
<div style="max-height: 200px; overflow-y: auto;">
|
| 555 |
+
<ul style="text-align: right;">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 556 |
""")
|
| 557 |
+
|
| 558 |
+
for source in stats.get('sources', []):
|
| 559 |
+
gr.HTML(f"<li>{source}</li>")
|
| 560 |
+
|
| 561 |
gr.HTML("""
|
| 562 |
+
</ul>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 563 |
</div>
|
| 564 |
</div>
|
| 565 |
""")
|
| 566 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 567 |
# Event handlers
|
| 568 |
msg.submit(chat_response, [msg, chatbot], [chatbot, msg])
|
| 569 |
submit_btn.click(chat_response, [msg, chatbot], [chatbot, msg])
|
| 570 |
+
clear_btn.click(clear_chat, None, [chatbot, msg])
|
| 571 |
|
| 572 |
+
# Clear input after submit
|
| 573 |
msg.submit(lambda: "", None, msg)
|
| 574 |
submit_btn.click(lambda: "", None, msg)
|
| 575 |
|
| 576 |
return interface
|
| 577 |
|
| 578 |
+
# Launch application
|
| 579 |
if __name__ == "__main__":
|
| 580 |
+
print("🚀 Starting CMA Simple RAG Chatbot...")
|
| 581 |
+
print("📚 Processing all PDF documents with TF-IDF indexing...")
|
|
|
|
| 582 |
|
| 583 |
+
interface = create_simple_rag_interface()
|
| 584 |
interface.launch(
|
| 585 |
+
share=False,
|
| 586 |
server_name="0.0.0.0",
|
| 587 |
server_port=7860,
|
| 588 |
show_error=True
|
requirements.txt
CHANGED
|
@@ -1,3 +1,6 @@
|
|
| 1 |
gradio>=4.0.0
|
| 2 |
-
openai>=
|
| 3 |
python-dotenv>=1.0.0
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
gradio>=4.0.0
|
| 2 |
+
openai>=0.28.0
|
| 3 |
python-dotenv>=1.0.0
|
| 4 |
+
numpy>=1.21.0
|
| 5 |
+
scikit-learn>=1.0.0
|
| 6 |
+
PyMuPDF>=1.23.0
|