منصة تعليمية تفاعلية تحوّل دروس الأطفال إلى قصص ومغامرات، أحداثها في مناطق المملكة العربية السعودية.
🔗 المنصة: https://medad-five.vercel.app
في ميزة "موضوع خاص"، الطفل يكتب عنوان أي درس ويختار صفه الدراسي، فيكتب له نموذج علام (ALLaM-7B) المدرّب على داتا مداد قصة تعليمية جديدة. القصة تنعرض مقسّمة أجزاء، وبينها أسئلة فهم قصيرة، ومعه البطل اللي اختاره. وفي النهاية تطلع له نفس شاشة النتيجة حقت القصص الجاهزة.
الطفل يكتب عنوان الدرس + الصف
│
▼
واجهة React (Vite) ──POST /generate──► سيرفر FastAPI على Modal (GPU L4)
▲ │
│ ▼
│ نموذج علام المدرّب (merged, float16)
│ │
│ ▼
│ تنظيف النص + تقسيمه لأجزاء
│ │
│ ▼
│ توليد أسئلة الفهم (نفس النموذج، prompt ثاني)
│ │
└──── Story JSON (أجزاء + أسئلة) ◄──────┘
{ lesson, grade } للسيرفر.midad-allam-merged-inference).Story بنفس الهيكل اللي تستخدمه القصص الجاهزة في src/data/stories.ts، فالتطبيق يعرضه على طول.النقاط: المجموع ٤٠٠ مثل القصص الجاهزة. كل سؤال صحيح = ١٠٠ نقطة (والغلط ١٠)، والباقي يتوزع على أجزاء القصة. يعني كل الأسئلة صح = ١٠٠٪، وكلها غلط ≈ ٥٥٪.
للتحكم في الأسئلة من server/modal_app.py: ENABLE_QUESTIONS = False يطفيها، و MAX_QUESTIONS يحدد عددها. كل سؤال يزيد تقريباً ١٠–١٥ ثانية على وقت التوليد.
السيرفر يشتغل لحاله وقت الطلب، ويطفي نفسه بعد ٥ دقائق بدون استخدام. أول طلب بعد فترة توقف ياخذ تقريباً دقيقة إلى دقيقتين عشان يحمّل النموذج، وبعدها كل قصة تاخذ تقريباً ٢٠ إلى ٤٠ ثانية. التطبيق يرسل طلب "تصحية" للسيرفر أول ما يفتح، عشان يكون جاهز غالباً لما يوصل الطفل لـ"موضوع خاص".
Medad-main/
├── server/
│ └── modal_app.py # سيرفر علام على Modal (تنزيل النموذج + الـ API)
├── src/
│ ├── api/
│ │ └── storyApi.ts # طلب القصة من السيرفر + تصحية السيرفر
│ ├── assets/characters/ # صور الأبطال الأربعة (Git LFS)
│ ├── components/
│ │ ├── GeneratingScreen.tsx # شاشة انتظار توليد القصة (مع حالة الخطأ وإعادة المحاولة)
│ │ ├── AdventureScreen.tsx # شاشة القصة والأسئلة (لقصص علام: البطل المرافق)
│ │ ├── CompanionSpeech.tsx # فقاعة كلام البطل المرافق
│ │ ├── ResultsScreen.tsx # شاشة النتيجة (لكل القصص)
│ │ └── ...
│ ├── data/
│ │ ├── stories.ts # القصص الجاهزة + هيكل Story
│ │ └── characters.ts # بيانات الأبطال
│ └── App.tsx
├── AI-work/ # داتا التدريب ونوتبوك التدريب
├── docs/ # التقرير (المنهجية) ومخطط سير العمل والبوستر
├── legacy/ # نسخ قديمة ما عاد تُستخدم (سيرفر Kaggle + tunnel، نوتبوك قديم)
├── .env.example # مثال لرابط السيرفر
└── README.md
المتطلبات: Node.js 18 أو أحدث.
npm install
سوّوا ملف .env في مجلد المشروع الرئيسي، فيه رابط السيرفر. تقدرون تنسخون .env.example:
VITE_API_URL=https://<workspace>--medad-medad-web.modal.run
رابط السيرفر الحالي عند صاحبة البرانش. اطلبوه منها، لأن ملف
.envما يترفع على GitHub.
npm run dev
بعدها افتحوا http://localhost:8443.
هذا القسم تحتاجونه بس إذا بتنشرون السيرفر من حسابكم. إذا بتجربون الواجهة بس، استخدموا الرابط الموجود.
المتطلبات: Python 3.10 أو أحدث، وحساب على Modal مضاف له بطاقة. التكلفة تنخصم من الرصيد المجاني، ويُفضّل تحطون حد صرف من إعدادات Billing.
pip install modal
python -m modal setup
١. احفظوا مفتاح Kaggle في Modal. تجيبونه من kaggle.com: Settings ثم API ثم Create New Token.
python -m modal secret create medad-secrets KAGGLE_USERNAME=... KAGGLE_KEY=...
الحساب لازم يقدر يوصل للنموذج lama377/midad-allam-7b على Kaggle.
٢. نزّلوا النموذج. هذي الخطوة مرة وحدة بس، والنموذج تقريباً 13GB.
cd server
python -m modal run modal_app.py::download_model
٣. انشروا السيرفر:
python -m modal deploy modal_app.py
الرابط اللي يطلع لكم هو قيمة VITE_API_URL.
مستخدمي Windows: إذا طلع خطأ ترميز بسبب الحروف العربية، شغّلوا هذا الأمر قبل أوامر Modal:
$env:PYTHONIOENCODING="utf-8"
| الطريقة | المسار | الوصف |
|---|---|---|
GET | /health | فحص إن السيرفر شغال، ويستخدم لتصحيته. يرجع {"status":"ok"} |
POST | /generate | يولّد قصة |
مثال طلب:
{ "lesson": "دورة الماء", "grade": "الصف الرابع الابتدائي", "subject": "" }
الرد: كائن Story، والأجزاء فيه عقد من نوع narrative، وبينها أسئلة من نوع question، وآخرها ending:
{
"id": "custom-1a2b3c4d",
"title": "دورة الماء",
"region": "الدمام",
"grade": "الصف الرابع الابتدائي",
"startNode": "part_1",
"nodes": {
"part_1": { "type": "narrative", "title": "الجزء الأول", "text": "...", "next": "question_1" },
"question_1": {
"type": "question", "title": "سؤال سريع", "text": "...؟",
"choices": [
{ "text": "...", "correct": true, "points": 100, "feedback": "...", "next": "part_2" },
{ "text": "...", "correct": false, "points": 10, "feedback": "الإجابة الصحيحة: ...", "next": "part_2" },
{ "text": "...", "correct": false, "points": 10, "feedback": "الإجابة الصحيحة: ...", "next": "part_2" }
]
},
"part_2": { "type": "ending", "title": "الخاتمة", "text": "..." }
}
}
الواجهة منشورة على Vercel من الـ fork: FajerAlanazi/Medad. أي push على main ينشر الموقع تلقائياً، وأي برانش ثاني يطلع له رابط Preview مستقل.
VITE_API_URL لازم ينضاف في Vercel (Settings → Environment Variables)، لأن ملف .env ما ينرفع. تأكدوا إن الرابط ما فيه مسافات ولا / في آخره.server/modal_app.py غيّروا APP_NAME = "medad" إلى "medad-dev".modal deploy modal_app.py: يطلع سيرفر تجريبي برابط فيه medad-dev. النموذج ما يحتاج تنزيل ثاني، لأن النسختين يستخدمون نفس الـ Volume.medad-dev في VITE_API_URL لبيئة Preview بس، وارفعوا البرانش وجربوا رابط الـ Preview.APP_NAME = "medad"، و modal deploy، وادمجوا في main، وبعدها modal app stop medad-dev.لمتابعة ردود النموذج على الأسئلة: modal app logs medad (أو medad-dev). كل سؤال يطلع جنبه ✅ إذا انقبل، أو ❌ إذا صيغته غلط.
git lfs install. إذا حمّلتوا المشروع كملف ZIP من GitHub، الصور بتنزل فاضية، حجم كل وحدة تقريباً 131 بايت.transformers: النموذج انحفظ على Kaggle بإصدار 5، فالسيرفر لازم يستخدم transformers>=5. الإصدار 4 يعطي الخطأ 'list' object has no attribute 'keys'.| الملف | التغيير |
|---|---|
server/modal_app.py | سيرفر علام على Modal + توليد أسئلة الفهم بين الأجزاء + توزيع النقاط (المجموع ٤٠٠) |
src/App.tsx | ربط "موضوع خاص" بالسيرفر، وشاشة النتيجة نفسها لكل القصص |
src/components/AdventureScreen.tsx | النقاط تظهر لكل القصص، وزر "اعرض نتيجتي" في النهاية. قصص علام معها البطل المرافق |
src/components/ResultsScreen.tsx | تشتغل لقصص "موضوع خاص" بعد (تستخدم عنوان الدرس في التقييم) |
src/api/storyApi.ts | طلب القصة وتصحية السيرفر. يشيل المسافات من الرابط تلقائياً |
src/components/GeneratingScreen.tsx | تنتظر رد السيرفر الحقيقي، وفيها حالة خطأ وإعادة محاولة |
src/components/CompanionSpeech.tsx | فقاعة كلام البطل المرافق |
src/data/stories.ts | شلنا صور الخلفية اللي ما لها علاقة بالقصص الجاهزة |
src/data/characters.ts | تصحيح ترحيب ملك: "ومتحمسة أتعلم وياك اليوم" |
src/assets/characters/*.png | صور الأبطال (Git LFS) |
.env.example، .gitignore | مثال الرابط، والسماح برفع .env.example |
منصة تعليمية تفاعلية تحوّل دروس الأطفال إلى قصص ومغامرات، أحداثها في مناطق المملكة العربية السعودية.
🔗 المنصة: https://medad-five.vercel.app
في ميزة "موضوع خاص"، الطفل يكتب عنوان أي درس ويختار صفه الدراسي، فيكتب له نموذج علام (ALLaM-7B) المدرّب على داتا مداد قصة تعليمية جديدة. القصة تنعرض مقسّمة أجزاء، وبينها أسئلة فهم قصيرة، ومعه البطل اللي اختاره. وفي النهاية تطلع له نفس شاشة النتيجة حقت القصص الجاهزة.
الطفل يكتب عنوان الدرس + الصف
│
▼
واجهة React (Vite) ──POST /generate──► سيرفر FastAPI على Modal (GPU L4)
▲ │
│ ▼
│ نموذج علام المدرّب (merged, float16)
│ │
│ ▼
│ تنظيف النص + تقسيمه لأجزاء
│ │
│ ▼
│ توليد أسئلة الفهم (نفس النموذج، prompt ثاني)
│ │
└──── Story JSON (أجزاء + أسئلة) ◄──────┘
{ lesson, grade } للسيرفر.midad-allam-merged-inference).Story بنفس الهيكل اللي تستخدمه القصص الجاهزة في src/data/stories.ts، فالتطبيق يعرضه على طول.النقاط: المجموع ٤٠٠ مثل القصص الجاهزة. كل سؤال صحيح = ١٠٠ نقطة (والغلط ١٠)، والباقي يتوزع على أجزاء القصة. يعني كل الأسئلة صح = ١٠٠٪، وكلها غلط ≈ ٥٥٪.
للتحكم في الأسئلة من server/modal_app.py: ENABLE_QUESTIONS = False يطفيها، و MAX_QUESTIONS يحدد عددها. كل سؤال يزيد تقريباً ١٠–١٥ ثانية على وقت التوليد.
السيرفر يشتغل لحاله وقت الطلب، ويطفي نفسه بعد ٥ دقائق بدون استخدام. أول طلب بعد فترة توقف ياخذ تقريباً دقيقة إلى دقيقتين عشان يحمّل النموذج، وبعدها كل قصة تاخذ تقريباً ٢٠ إلى ٤٠ ثانية. التطبيق يرسل طلب "تصحية" للسيرفر أول ما يفتح، عشان يكون جاهز غالباً لما يوصل الطفل لـ"موضوع خاص".
Medad-main/
├── server/
│ └── modal_app.py # سيرفر علام على Modal (تنزيل النموذج + الـ API)
├── src/
│ ├── api/
│ │ └── storyApi.ts # طلب القصة من السيرفر + تصحية السيرفر
│ ├── assets/characters/ # صور الأبطال الأربعة (Git LFS)
│ ├── components/
│ │ ├── GeneratingScreen.tsx # شاشة انتظار توليد القصة (مع حالة الخطأ وإعادة المحاولة)
│ │ ├── AdventureScreen.tsx # شاشة القصة والأسئلة (لقصص علام: البطل المرافق)
│ │ ├── CompanionSpeech.tsx # فقاعة كلام البطل المرافق
│ │ ├── ResultsScreen.tsx # شاشة النتيجة (لكل القصص)
│ │ └── ...
│ ├── data/
│ │ ├── stories.ts # القصص الجاهزة + هيكل Story
│ │ └── characters.ts # بيانات الأبطال
│ └── App.tsx
├── AI-work/ # داتا التدريب ونوتبوك التدريب
├── docs/ # التقرير (المنهجية) ومخطط سير العمل والبوستر
├── legacy/ # نسخ قديمة ما عاد تُستخدم (سيرفر Kaggle + tunnel، نوتبوك قديم)
├── .env.example # مثال لرابط السيرفر
└── README.md
المتطلبات: Node.js 18 أو أحدث.
npm install
سوّوا ملف .env في مجلد المشروع الرئيسي، فيه رابط السيرفر. تقدرون تنسخون .env.example:
VITE_API_URL=https://<workspace>--medad-medad-web.modal.run
رابط السيرفر الحالي عند صاحبة البرانش. اطلبوه منها، لأن ملف
.envما يترفع على GitHub.
npm run dev
بعدها افتحوا http://localhost:8443.
هذا القسم تحتاجونه بس إذا بتنشرون السيرفر من حسابكم. إذا بتجربون الواجهة بس، استخدموا الرابط الموجود.
المتطلبات: Python 3.10 أو أحدث، وحساب على Modal مضاف له بطاقة. التكلفة تنخصم من الرصيد المجاني، ويُفضّل تحطون حد صرف من إعدادات Billing.
pip install modal
python -m modal setup
١. احفظوا مفتاح Kaggle في Modal. تجيبونه من kaggle.com: Settings ثم API ثم Create New Token.
python -m modal secret create medad-secrets KAGGLE_USERNAME=... KAGGLE_KEY=...
الحساب لازم يقدر يوصل للنموذج lama377/midad-allam-7b على Kaggle.
٢. نزّلوا النموذج. هذي الخطوة مرة وحدة بس، والنموذج تقريباً 13GB.
cd server
python -m modal run modal_app.py::download_model
٣. انشروا السيرفر:
python -m modal deploy modal_app.py
الرابط اللي يطلع لكم هو قيمة VITE_API_URL.
مستخدمي Windows: إذا طلع خطأ ترميز بسبب الحروف العربية، شغّلوا هذا الأمر قبل أوامر Modal:
$env:PYTHONIOENCODING="utf-8"
| الطريقة | المسار | الوصف |
|---|---|---|
GET | /health | فحص إن السيرفر شغال، ويستخدم لتصحيته. يرجع {"status":"ok"} |
POST | /generate | يولّد قصة |
مثال طلب:
{ "lesson": "دورة الماء", "grade": "الصف الرابع الابتدائي", "subject": "" }
الرد: كائن Story، والأجزاء فيه عقد من نوع narrative، وبينها أسئلة من نوع question، وآخرها ending:
{
"id": "custom-1a2b3c4d",
"title": "دورة الماء",
"region": "الدمام",
"grade": "الصف الرابع الابتدائي",
"startNode": "part_1",
"nodes": {
"part_1": { "type": "narrative", "title": "الجزء الأول", "text": "...", "next": "question_1" },
"question_1": {
"type": "question", "title": "سؤال سريع", "text": "...؟",
"choices": [
{ "text": "...", "correct": true, "points": 100, "feedback": "...", "next": "part_2" },
{ "text": "...", "correct": false, "points": 10, "feedback": "الإجابة الصحيحة: ...", "next": "part_2" },
{ "text": "...", "correct": false, "points": 10, "feedback": "الإجابة الصحيحة: ...", "next": "part_2" }
]
},
"part_2": { "type": "ending", "title": "الخاتمة", "text": "..." }
}
}
الواجهة منشورة على Vercel من الـ fork: FajerAlanazi/Medad. أي push على main ينشر الموقع تلقائياً، وأي برانش ثاني يطلع له رابط Preview مستقل.
VITE_API_URL لازم ينضاف في Vercel (Settings → Environment Variables)، لأن ملف .env ما ينرفع. تأكدوا إن الرابط ما فيه مسافات ولا / في آخره.server/modal_app.py غيّروا APP_NAME = "medad" إلى "medad-dev".modal deploy modal_app.py: يطلع سيرفر تجريبي برابط فيه medad-dev. النموذج ما يحتاج تنزيل ثاني، لأن النسختين يستخدمون نفس الـ Volume.medad-dev في VITE_API_URL لبيئة Preview بس، وارفعوا البرانش وجربوا رابط الـ Preview.APP_NAME = "medad"، و modal deploy، وادمجوا في main، وبعدها modal app stop medad-dev.لمتابعة ردود النموذج على الأسئلة: modal app logs medad (أو medad-dev). كل سؤال يطلع جنبه ✅ إذا انقبل، أو ❌ إذا صيغته غلط.
git lfs install. إذا حمّلتوا المشروع كملف ZIP من GitHub، الصور بتنزل فاضية، حجم كل وحدة تقريباً 131 بايت.transformers: النموذج انحفظ على Kaggle بإصدار 5، فالسيرفر لازم يستخدم transformers>=5. الإصدار 4 يعطي الخطأ 'list' object has no attribute 'keys'.| الملف | التغيير |
|---|---|
server/modal_app.py | سيرفر علام على Modal + توليد أسئلة الفهم بين الأجزاء + توزيع النقاط (المجموع ٤٠٠) |
src/App.tsx | ربط "موضوع خاص" بالسيرفر، وشاشة النتيجة نفسها لكل القصص |
src/components/AdventureScreen.tsx | النقاط تظهر لكل القصص، وزر "اعرض نتيجتي" في النهاية. قصص علام معها البطل المرافق |
src/components/ResultsScreen.tsx | تشتغل لقصص "موضوع خاص" بعد (تستخدم عنوان الدرس في التقييم) |
src/api/storyApi.ts | طلب القصة وتصحية السيرفر. يشيل المسافات من الرابط تلقائياً |
src/components/GeneratingScreen.tsx | تنتظر رد السيرفر الحقيقي، وفيها حالة خطأ وإعادة محاولة |
src/components/CompanionSpeech.tsx | فقاعة كلام البطل المرافق |
src/data/stories.ts | شلنا صور الخلفية اللي ما لها علاقة بالقصص الجاهزة |
src/data/characters.ts | تصحيح ترحيب ملك: "ومتحمسة أتعلم وياك اليوم" |
src/assets/characters/*.png | صور الأبطال (Git LFS) |
.env.example، .gitignore | مثال الرابط، والسماح برفع .env.example |