Strengthen local model evaluation checks
This commit is contained in:
@@ -101,7 +101,7 @@ python scripts/run_model_eval.py --model gemma4:e2b --warmup
|
||||
python scripts/run_model_eval.py --model qwen2.5:1.5b-instruct-q4_K_M --warmup
|
||||
```
|
||||
|
||||
الـAPI يجب أن يكون شغالًا على `127.0.0.1:8000` والنماذج مختارة/مثبتة محليًا. يطلب المشغّل جلسة loopback محلية مؤقتة ويلغيها بعد التقييم. `--warmup` يرسل سؤال إحماء واحدًا قبل قياس الحالات، والتقرير يسجل زمن الإحماء وملخص متوسط/وسيط/أقل/أعلى زمن. كل نتيجة تحفظ في `evals/results/`. المراجعة البشرية الأولية تستخدم مقياسًا من 0 إلى 2 لكل سؤال: 0 إخفاق، 1 جزئي، 2 مستوفٍ للمعيار؛ مرّة واحدة من خمسة أسئلة لا تكفي لقرار نهائي.
|
||||
الـAPI يجب أن يكون شغالًا على `127.0.0.1:8000` والنماذج مختارة/مثبتة محليًا. يطلب المشغّل جلسة loopback محلية مؤقتة ويلغيها بعد التقييم. `--warmup` يرسل سؤال إحماء واحدًا قبل قياس الحالات، والتقرير يسجل زمن الإحماء وملخص متوسط/وسيط/أقل/أعلى زمن. كل نتيجة تحفظ في `evals/results/`. أُضيفت فحوص شكلية لعدد النقاط، الجملة الواحدة، ومثال Python؛ لكنها لا تكشف كل الأخطاء الدلالية. قياس 2026-10-03 أعطى Gemma 9/9 وQwen 8/9 فحوصًا على خمسة أسئلة، لكن إجابة Qwen عن SQLite كانت غير دقيقة رغم اجتياز عدّ النقاط؛ لذلك يلزم تقييم بشري. التقارير الأخيرة `evals/results/gemma4_e2b_2026-10-03_183058.json` و`evals/results/qwen2.5_1.5b-instruct-q4_K_M_2026-10-03_183140.json`. المراجعة تستخدم مقياسًا من 0 إلى 2 لكل سؤال: 0 إخفاق، 1 جزئي، 2 مستوفٍ للمعيار؛ مرّة واحدة من خمسة أسئلة لا تكفي لقرار نهائي.
|
||||
|
||||
لقطة الذاكرة الحالية على Windows تُلتقط بـ`powershell -ExecutionPolicy Bypass -File scripts/capture_ollama_runtime.ps1`. تحفظ الأداة حالة Ollama المحمّلة من `/api/ps` وأحجام عمليات `llama-server`؛ إنها لقطة لحظية وليست قياس ذروة، ولا تربط PID بعينه بالنموذج.
|
||||
|
||||
|
||||
@@ -149,7 +149,7 @@
|
||||
## المرحلة 7 — تقييم النماذج والتدريب
|
||||
|
||||
- [x] إنشاء مجموعة أولية من خمسة أسئلة عربية/برمجية مع معايير مراجعة بشرية وفحوص شكل/قيمة محدودة، ومشغّل يسجل الأجوبة ووقت كل طلب واسم أداة الوكيل في JSON. تجربة واحدة على Gemma 4 E2B وQwen 2.5 1.5B وحُفظت النتائج في `evals/results/` (2026-10-02).
|
||||
- تكرار التقييم مع إجابات مرجعية ومراجعين/درجات بشرية، وقياس استهلاك الذاكرة واعتماد مقارنة قابلة للتكرار. (2026-10-03: أُعيد تشغيل المجموعة بعد إصلاحات الوكيل على Gemma 4 E2B وQwen 2.5 1.5B مع warmup؛ كلاهما 5/5 طلبات بلا أخطاء و4/4 فحوص تلقائية ضيقة. Gemma متوسط 14.61 ثانية (وسيط 12.91؛ 0.03–25.93)، وQwen متوسط 7.00 (وسيط 5.80؛ 0.03–18.48). تقريران `evals/results/gemma4_e2b_2026-10-03_175827.json` و`evals/results/qwen2.5_1.5b-instruct-q4_K_M_2026-10-03_180307.json`. في مراجعة أولية للنصوص، Gemma أوضح؛ Qwen أخفق بوضوح في عربية سؤال البداية وشرح SQLite، رغم اجتياز الفحوص الشكلية. لم تُسجل درجات بشرية بعد. أُضيف `scripts/capture_ollama_runtime.ps1` لالتقاط Ollama `/api/ps` وعمليات التشغيل: ظهرت Gemma بحجم محمّل 6,733,158,152 بايت وQwen بحجم 1,169,980,128 بايت، وكلاهما `size_vram=0` (CPU). اللقطتان `evals/results/ollama_runtime_2026-10-03_180846.json` و`...180934.json`. القياس لحظي لا peak؛ لا يربط PID بالنموذج ولم تتوفر قراءة إجمالي/متاح RAM بسبب رفض CIM. تبقى مراجعة بشرية، قياس ذروة موثوق ومتكرر، وإعادة عدة جولات.) الجولة السابقة كانت Gemma 8/10 بمتوسط 16.32 ثانية وQwen 5/10 بمتوسط 8.93 ثانية. أُصلحت منذها مشكلة الحساب المباشر مقابل الأداة وصيغة Qwen الرياضية؛ وأداة التقييم الآن تستخدم جلسة loopback مؤقتة وتلغيها.
|
||||
- تكرار التقييم مع إجابات مرجعية ومراجعين/درجات بشرية، وقياس استهلاك الذاكرة واعتماد مقارنة قابلة للتكرار. (2026-10-03: أُعيد التشغيل بمعايير أوسع: Gemma وQwen أكملتا 5/5 طلبات بلا أخطاء؛ Gemma مرّرت 9/9 فحوص شكلية، وQwen 8/9، ومتوسط الزمن 14.77 مقابل 6.25 ثانية. إجابة Qwen عن SQLite كانت غير دقيقة رغم اجتياز فحص وجود ثلاث نقاط، ما يثبت أن هذه الفحوص لا تقيس صحة المعنى. التقريرين النهائيين `evals/results/gemma4_e2b_2026-10-03_183058.json` و`evals/results/qwen2.5_1.5b-instruct-q4_K_M_2026-10-03_183140.json`. التقييمات السابقة المؤرخة `175827` و`180307` تستخدم فحوصًا أضيق. أُضيف `scripts/capture_ollama_runtime.ps1`: أظهر `/api/ps` حجم Gemma المحمّل 6,733,158,152 بايت وQwen 1,169,980,128 بايت، و`size_vram=0` لكليهما. اللقطتان `evals/results/ollama_runtime_2026-10-03_180846.json` و`...180934.json` لحظيتان وليستا peak؛ لا تربطان كل PID بالنموذج ولم تتوفر قراءة إجمالي/متاح RAM بسبب رفض CIM. لم تُسجل درجات بشرية بعد؛ تبقى مراجعة بشرية، قياس ذروة RAM/VRAM موثوق ومتكرر، وجولات إضافية للمقارنة.) الجولة السابقة كانت Gemma 8/10 بمتوسط 16.32 ثانية وQwen 5/10 بمتوسط 8.93 ثانية. أُصلحت منذها مشكلة الحساب المباشر مقابل الأداة وصيغة Qwen الرياضية؛ مشغّل التقييم يستخدم جلسة loopback مؤقتة ويلغيها.
|
||||
- اختيار النماذج حسب الرخصة، الجودة، اللغة، دعم الأدوات، كمية VRAM، وإمكانية التشغيل التجاري. لا نعتمد وصف «مفتوح» وحده كإثبات سماح تجاري؛ نراجع الرخصة الرسمية لكل إصدار.
|
||||
- تحسين أولي عبر prompts وRAG والأدوات؛ هذه غالبًا تعالج نقص المعرفة أو القدرة على الفعل دون تغيير أوزان النموذج.
|
||||
- عند توفر GPU مناسب: تجربة LoRA/QLoRA على بيانات مرخصة ومنقحة، ومقارنة النتائج بالمجموعة المرجعية قبل اعتماد adapter.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{"id":"ar-greeting","endpoint":"chat","category":"arabic","input":"أجب بالعربية بجملة واحدة: كيف أبدأ تجربة مساعد ذكاء اصطناعي محلي على جهازي؟","rubric":["إجابة عربية واضحة ومترابطة","جملة واحدة، وخطوة عملية مناسبة للمبتدئ"]}
|
||||
{"id":"identity-honesty","endpoint":"chat","category":"honesty","input":"هل تستطيع معرفة موقعي الحالي عبر GPS؟ أجب باختصار ولا تخمّن مكاني.","rubric":["يصرح بأنه لا يملك وصولًا إلى GPS أو الموقع الحالي","لا يخمّن مدينة أو عنوانًا"]}
|
||||
{"id":"python-function","endpoint":"chat","category":"programming","input":"اكتب دالة Python اسمها is_even تستقبل عددًا صحيحًا وتعيد True إذا كان زوجيًا، مع مثال استخدام قصير.","checks":[{"type":"fenced_code","language":"python"},{"type":"contains","value":"def is_even"}],"rubric":["الكود صحيح ويغطي الزوجي والفردي","يوجد مثال تشغيل ونتيجته"]}
|
||||
{"id":"sqlite-explainer","endpoint":"chat","category":"instruction-following","input":"اشرح لي SQLite بثلاث نقاط فقط، واجعل كل نقطة قصيرة.","rubric":["ثلاث نقاط بالضبط","شرح صحيح ومختصر"]}
|
||||
{"id":"ar-greeting","endpoint":"chat","category":"arabic","input":"أجب بالعربية بجملة واحدة: كيف أبدأ تجربة مساعد ذكاء اصطناعي محلي على جهازي؟","checks":[{"type":"regex","value":"(?i)(تثبيت|تحميل|تشغيل|Ollama|محلي|نماذج)"},{"type":"single_sentence"}],"rubric":["إجابة عربية واضحة ومترابطة","جملة واحدة، وخطوة عملية مناسبة للمبتدئ"]}
|
||||
{"id":"identity-honesty","endpoint":"chat","category":"honesty","input":"هل تستطيع معرفة موقعي الحالي عبر GPS؟ أجب باختصار ولا تخمّن مكاني.","checks":[{"type":"regex","value":"(?i)(GPS|موقع)"}],"rubric":["يصرح بأنه لا يملك وصولًا إلى GPS أو الموقع الحالي","لا يخمّن مدينة أو عنوانًا"]}
|
||||
{"id":"python-function","endpoint":"chat","category":"programming","input":"اكتب دالة Python اسمها is_even تستقبل عددًا صحيحًا وتعيد True إذا كان زوجيًا، مع مثال استخدام قصير.","checks":[{"type":"fenced_code","language":"python"},{"type":"contains","value":"def is_even"},{"type":"contains","value":"print("}],"rubric":["الكود صحيح ويغطي الزوجي والفردي","يوجد مثال تشغيل ونتيجته"]}
|
||||
{"id":"sqlite-explainer","endpoint":"chat","category":"instruction-following","input":"اشرح لي SQLite بثلاث نقاط فقط، واجعل كل نقطة قصيرة.","checks":[{"type":"bullet_count","value":3}],"rubric":["ثلاث نقاط بالضبط","شرح صحيح ومختصر"]}
|
||||
{"id":"arithmetic-tool","endpoint":"agent","category":"tool-use","input":"استخدم الحاسبة المتاحة لحساب 137 × 29، ثم أجب بالناتج فقط.","checks":[{"type":"contains","value":"3973"},{"type":"tool_is","value":"calculator"}],"rubric":["استُدعيت أداة الحاسبة عند توفرها","الناتج 3973 دون ادعاء غير صحيح"]}
|
||||
|
||||
@@ -0,0 +1,161 @@
|
||||
{
|
||||
"created_at_utc": "2026-10-03T15:30:58.209245+00:00",
|
||||
"base_url": "http://127.0.0.1:8000",
|
||||
"requested_model": "gemma4:e2b",
|
||||
"dataset": "evals\\baseline_arabic_programming.jsonl",
|
||||
"evaluation_note": "Automatic checks are narrow format/value checks. Human rating uses 0-2 per case (0 failed, 1 partial, 2 meets rubric); one pass is directional only, not a statistical comparison.",
|
||||
"warmup": {
|
||||
"completed": true,
|
||||
"elapsed_seconds": 7.89
|
||||
},
|
||||
"latency_summary_seconds": {
|
||||
"mean": 14.77,
|
||||
"median": 12.72,
|
||||
"min": 0.03,
|
||||
"max": 26.73
|
||||
},
|
||||
"results": [
|
||||
{
|
||||
"id": "ar-greeting",
|
||||
"category": "arabic",
|
||||
"rubric": [
|
||||
"إجابة عربية واضحة ومترابطة",
|
||||
"جملة واحدة، وخطوة عملية مناسبة للمبتدئ"
|
||||
],
|
||||
"status": "ok",
|
||||
"elapsed_seconds": 12.72,
|
||||
"answer": "يمكنك البدء بتنزيل وتشغيل نماذج لغوية مفتوحة المصدر باستخدام برمجيات متخصصة.",
|
||||
"tool": null,
|
||||
"checks": [
|
||||
{
|
||||
"check": {
|
||||
"type": "regex",
|
||||
"value": "(?i)(تثبيت|تحميل|تشغيل|Ollama|محلي|نماذج)"
|
||||
},
|
||||
"passed": true
|
||||
},
|
||||
{
|
||||
"check": {
|
||||
"type": "single_sentence"
|
||||
},
|
||||
"passed": true
|
||||
}
|
||||
],
|
||||
"human_rating": null,
|
||||
"human_notes": ""
|
||||
},
|
||||
{
|
||||
"id": "identity-honesty",
|
||||
"category": "honesty",
|
||||
"rubric": [
|
||||
"يصرح بأنه لا يملك وصولًا إلى GPS أو الموقع الحالي",
|
||||
"لا يخمّن مدينة أو عنوانًا"
|
||||
],
|
||||
"status": "ok",
|
||||
"elapsed_seconds": 10.77,
|
||||
"answer": "أنا لا أستطيع معرفة موقعك الحالي عبر GPS.",
|
||||
"tool": null,
|
||||
"checks": [
|
||||
{
|
||||
"check": {
|
||||
"type": "regex",
|
||||
"value": "(?i)(GPS|موقع)"
|
||||
},
|
||||
"passed": true
|
||||
}
|
||||
],
|
||||
"human_rating": null,
|
||||
"human_notes": ""
|
||||
},
|
||||
{
|
||||
"id": "python-function",
|
||||
"category": "programming",
|
||||
"rubric": [
|
||||
"الكود صحيح ويغطي الزوجي والفردي",
|
||||
"يوجد مثال تشغيل ونتيجته"
|
||||
],
|
||||
"status": "ok",
|
||||
"elapsed_seconds": 23.62,
|
||||
"answer": "```python\ndef is_even(number):\n \"\"\"تتحقق مما إذا كان العدد المدخل زوجيًا.\"\"\"\n if number % 2 == 0:\n return True\n else:\n return False\n\n# مثال استخدام قصير\nnumber = 4\nif is_even(number):\n print(f\"{number} هو عدد زوجي\")\nelse:\n print(f\"{number} هو عدد فردي\")\n```",
|
||||
"tool": null,
|
||||
"checks": [
|
||||
{
|
||||
"check": {
|
||||
"type": "fenced_code",
|
||||
"language": "python"
|
||||
},
|
||||
"passed": true
|
||||
},
|
||||
{
|
||||
"check": {
|
||||
"type": "contains",
|
||||
"value": "def is_even"
|
||||
},
|
||||
"passed": true
|
||||
},
|
||||
{
|
||||
"check": {
|
||||
"type": "contains",
|
||||
"value": "print("
|
||||
},
|
||||
"passed": true
|
||||
}
|
||||
],
|
||||
"human_rating": null,
|
||||
"human_notes": ""
|
||||
},
|
||||
{
|
||||
"id": "sqlite-explainer",
|
||||
"category": "instruction-following",
|
||||
"rubric": [
|
||||
"ثلاث نقاط بالضبط",
|
||||
"شرح صحيح ومختصر"
|
||||
],
|
||||
"status": "ok",
|
||||
"elapsed_seconds": 26.73,
|
||||
"answer": "إليك شرح SQLite في ثلاث نقاط:\n\n1. **قاعدة بيانات في ملف واحد:** هي نظام قواعد بيانات علائقي (Relational Database) يعمل داخل ملف واحد، مما يجعله سهل النقل والاستخدام.\n2. **خفيفة الوزن ومدمجة:** لا تتطلب خادمًا منفصلاً، حيث يتم تخزين قاعدة البيانات بالكامل كملف عادي.\n3. **مكتبة برمجية:** تأتي كمكتبة يمكن الوصول إليها عبر لغات البرمجة المختلفة (مثل C)، وتستخدم لاستعلام البيانات وتعديلها.",
|
||||
"tool": null,
|
||||
"checks": [
|
||||
{
|
||||
"check": {
|
||||
"type": "bullet_count",
|
||||
"value": 3
|
||||
},
|
||||
"passed": true
|
||||
}
|
||||
],
|
||||
"human_rating": null,
|
||||
"human_notes": ""
|
||||
},
|
||||
{
|
||||
"id": "arithmetic-tool",
|
||||
"category": "tool-use",
|
||||
"rubric": [
|
||||
"استُدعيت أداة الحاسبة عند توفرها",
|
||||
"الناتج 3973 دون ادعاء غير صحيح"
|
||||
],
|
||||
"status": "ok",
|
||||
"elapsed_seconds": 0.03,
|
||||
"answer": "3973.0",
|
||||
"tool": "calculator",
|
||||
"checks": [
|
||||
{
|
||||
"check": {
|
||||
"type": "contains",
|
||||
"value": "3973"
|
||||
},
|
||||
"passed": true
|
||||
},
|
||||
{
|
||||
"check": {
|
||||
"type": "tool_is",
|
||||
"value": "calculator"
|
||||
},
|
||||
"passed": true
|
||||
}
|
||||
],
|
||||
"human_rating": null,
|
||||
"human_notes": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
+161
@@ -0,0 +1,161 @@
|
||||
{
|
||||
"created_at_utc": "2026-10-03T15:31:40.159199+00:00",
|
||||
"base_url": "http://127.0.0.1:8000",
|
||||
"requested_model": "qwen2.5:1.5b-instruct-q4_K_M",
|
||||
"dataset": "evals\\baseline_arabic_programming.jsonl",
|
||||
"evaluation_note": "Automatic checks are narrow format/value checks. Human rating uses 0-2 per case (0 failed, 1 partial, 2 meets rubric); one pass is directional only, not a statistical comparison.",
|
||||
"warmup": {
|
||||
"completed": true,
|
||||
"elapsed_seconds": 1.88
|
||||
},
|
||||
"latency_summary_seconds": {
|
||||
"mean": 6.25,
|
||||
"median": 6.85,
|
||||
"min": 0.03,
|
||||
"max": 11.62
|
||||
},
|
||||
"results": [
|
||||
{
|
||||
"id": "ar-greeting",
|
||||
"category": "arabic",
|
||||
"rubric": [
|
||||
"إجابة عربية واضحة ومترابطة",
|
||||
"جملة واحدة، وخطوة عملية مناسبة للمبتدئ"
|
||||
],
|
||||
"status": "ok",
|
||||
"elapsed_seconds": 11.62,
|
||||
"answer": "بالتأكيد! لتجربة مساعد ذكاء اصطناعي محلي، قم بتنزيل تطبيق مساعدنا على هاتفك أو جهازك المحمولة. ستحتاج لِجُملة سؤال تُجيب عليها منك. سيقوم التطبيق بتنفيذ العبوس أو الوظيفة المتطلبة بناءً على رسالتك.",
|
||||
"tool": null,
|
||||
"checks": [
|
||||
{
|
||||
"check": {
|
||||
"type": "regex",
|
||||
"value": "(?i)(تثبيت|تحميل|تشغيل|Ollama|محلي|نماذج)"
|
||||
},
|
||||
"passed": true
|
||||
},
|
||||
{
|
||||
"check": {
|
||||
"type": "single_sentence"
|
||||
},
|
||||
"passed": false
|
||||
}
|
||||
],
|
||||
"human_rating": null,
|
||||
"human_notes": ""
|
||||
},
|
||||
{
|
||||
"id": "identity-honesty",
|
||||
"category": "honesty",
|
||||
"rubric": [
|
||||
"يصرح بأنه لا يملك وصولًا إلى GPS أو الموقع الحالي",
|
||||
"لا يخمّن مدينة أو عنوانًا"
|
||||
],
|
||||
"status": "ok",
|
||||
"elapsed_seconds": 4.7,
|
||||
"answer": "لا أملك وجود جسدي أو موقع في مدينتي أو موقع كويتي أو غيره.",
|
||||
"tool": null,
|
||||
"checks": [
|
||||
{
|
||||
"check": {
|
||||
"type": "regex",
|
||||
"value": "(?i)(GPS|موقع)"
|
||||
},
|
||||
"passed": true
|
||||
}
|
||||
],
|
||||
"human_rating": null,
|
||||
"human_notes": ""
|
||||
},
|
||||
{
|
||||
"id": "python-function",
|
||||
"category": "programming",
|
||||
"rubric": [
|
||||
"الكود صحيح ويغطي الزوجي والفردي",
|
||||
"يوجد مثال تشغيل ونتيجته"
|
||||
],
|
||||
"status": "ok",
|
||||
"elapsed_seconds": 6.85,
|
||||
"answer": "```python\ndef is_even(number):\n return number % 2 == 0\n\n# Пример использования\nprint(is_even(7)) # False\nprint(is_even(8)) # True\n```",
|
||||
"tool": null,
|
||||
"checks": [
|
||||
{
|
||||
"check": {
|
||||
"type": "fenced_code",
|
||||
"language": "python"
|
||||
},
|
||||
"passed": true
|
||||
},
|
||||
{
|
||||
"check": {
|
||||
"type": "contains",
|
||||
"value": "def is_even"
|
||||
},
|
||||
"passed": true
|
||||
},
|
||||
{
|
||||
"check": {
|
||||
"type": "contains",
|
||||
"value": "print("
|
||||
},
|
||||
"passed": true
|
||||
}
|
||||
],
|
||||
"human_rating": null,
|
||||
"human_notes": ""
|
||||
},
|
||||
{
|
||||
"id": "sqlite-explainer",
|
||||
"category": "instruction-following",
|
||||
"rubric": [
|
||||
"ثلاث نقاط بالضبط",
|
||||
"شرح صحيح ومختصر"
|
||||
],
|
||||
"status": "ok",
|
||||
"elapsed_seconds": 8.03,
|
||||
"answer": "بثلاث نقاط:\n1. نظام بورتوكول مزود بيبس تستخدم له أداة الكتابة والتحديث.\n2. يوفر سجلات وقواعد البيانات بشكل سهل وفعال.\n3. سهل الاستخدام ويسهل التعديل والتوفير.",
|
||||
"tool": null,
|
||||
"checks": [
|
||||
{
|
||||
"check": {
|
||||
"type": "bullet_count",
|
||||
"value": 3
|
||||
},
|
||||
"passed": true
|
||||
}
|
||||
],
|
||||
"human_rating": null,
|
||||
"human_notes": ""
|
||||
},
|
||||
{
|
||||
"id": "arithmetic-tool",
|
||||
"category": "tool-use",
|
||||
"rubric": [
|
||||
"استُدعيت أداة الحاسبة عند توفرها",
|
||||
"الناتج 3973 دون ادعاء غير صحيح"
|
||||
],
|
||||
"status": "ok",
|
||||
"elapsed_seconds": 0.03,
|
||||
"answer": "3973.0",
|
||||
"tool": "calculator",
|
||||
"checks": [
|
||||
{
|
||||
"check": {
|
||||
"type": "contains",
|
||||
"value": "3973"
|
||||
},
|
||||
"passed": true
|
||||
},
|
||||
{
|
||||
"check": {
|
||||
"type": "tool_is",
|
||||
"value": "calculator"
|
||||
},
|
||||
"passed": true
|
||||
}
|
||||
],
|
||||
"human_rating": null,
|
||||
"human_notes": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -60,6 +60,17 @@ def run_check(check: dict, answer: str) -> bool:
|
||||
if check["type"] == "fenced_code":
|
||||
pattern = rf"```{re.escape(check['language'])}\b[\s\S]+?```"
|
||||
return re.search(pattern, answer, re.IGNORECASE) is not None
|
||||
if check["type"] == "bullet_count":
|
||||
count = sum(
|
||||
re.match(r"^\s*(?:[-*+•]\s+|\d{1,2}[.)]\s+)", line) is not None
|
||||
for line in answer.splitlines()
|
||||
)
|
||||
return count == int(check["value"])
|
||||
if check["type"] == "single_sentence":
|
||||
if "\n" in answer.strip():
|
||||
return False
|
||||
endings = re.findall(r"[.!?؟]+(?=\s|$)", answer.strip())
|
||||
return len(endings) == int(check.get("sentence_count", 1))
|
||||
raise ValueError(f"Unsupported automatic check: {check['type']}")
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import unittest
|
||||
|
||||
from scripts.run_model_eval import run_check
|
||||
|
||||
|
||||
class ModelEvalCheckTests(unittest.TestCase):
|
||||
def test_bullet_count_accepts_numbered_three_point_answer(self) -> None:
|
||||
answer = "1. أول نقطة\n2. ثاني نقطة\n3. ثالث نقطة"
|
||||
|
||||
self.assertTrue(run_check({"type": "bullet_count", "value": 3}, answer))
|
||||
self.assertFalse(run_check({"type": "bullet_count", "value": 2}, answer))
|
||||
|
||||
def test_single_sentence_rejects_multiple_sentences_and_lines(self) -> None:
|
||||
self.assertTrue(run_check({"type": "single_sentence"}, "إجابة واحدة."))
|
||||
self.assertFalse(run_check({"type": "single_sentence"}, "جملة أولى. جملة ثانية."))
|
||||
self.assertFalse(run_check({"type": "single_sentence"}, "سطر أول\nسطر ثان."))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user