LLM Evals للمتوسط: إزاي تعرف إن تطبيق الذكاء الاصطناعي بتاعك بيتحسّن ولا بيسوء
المستوى المطلوب: متوسط. المقال ده مكتوب لحد بنى تطبيق فيه LLM (شات بوت، RAG، أو agent) وبيغيّر فيه، بس معندوش طريقة يقيس هل التغيير حسّن ولا خرّب. مش محتاج تكون خبير ML، بس محتاج تعرف تكتب دالة Python وتنده API.
لو بتعدّل البرومبت أو بتبدّل الموديل وبتقرّر إنه "بقى أحسن" بإحساسك بعد ما تجرّب 3 أو 4 أسئلة بإيدك، انت بتقامر. الـ Evals بيحوّل "أحسن" الغامضة دي لرقم واحد بيتحسب على مئات الحالات في كل build، فتعرف بالظبط كسبت ولا خسرت قبل ما توصل للمستخدم.
المشكلة باختصار
تطبيقات الـ LLM مش deterministic زي الكود العادي. نفس الإدخال ممكن يديك مخرجات مختلفة. عشان كده الـ unit test التقليدي (اللي بيقارن مخرج بمخرج متوقع بالظبط) بيفشل هنا في معظم الحالات المفيدة.
اللي بيحصل فعلاً في الفرق الصغيرة: حد يعدّل كلمة في الـ system prompt عشان يظبط حالة واحدة اشتكى منها عميل. الحالة دي بتتظبط، بس 6 حالات تانية بتخرب في صمت. محدش بيلاحظ غير لما شكوى تانية توصل بعد أسبوعين. ده اسمه regression صامت، والـ Evals هو اللي بيمسكه.
يعني إيه Eval أصلاً؟ (بمثال المدرّس)
تخيّل مدرّس عنده 200 ورقة امتحان يصحّحها. مش هيقعد يقرا كل ورقة ويحكم بمزاجه. هو ماسك نموذج إجابة: لكل سؤال فيه العناصر اللي لازم تكون موجودة، وبيدّي درجة لكل ورقة على أساسها. آخر اليوم عنده متوسط درجات الفصل: رقم واحد بيقوله الفصل فاهم ولا لأ.
الـ Eval هو بالظبط نموذج الإجابة ده، بس لتطبيق الـ AI بتاعك. علميًا: الـ Eval هو مجموعة حالات اختبار ثابتة (dataset)، بيتشغّل عليها تطبيقك، وبيتحسبله درجة آلية لكل حالة عبر قاعدة أو حَكَم (judge)، ثم بتتجمّع الدرجات في مقياس واحد بتقارن بيه الإصدارات. المدرّس بيصحّح مرة في السنة، وانت بتشغّل الـ Eval كل build.
المكوّنات الأربعة لأي نظام Eval
- Dataset (بنك الحالات): مجموعة إدخالات + المتوقّع منها. ابدأ بـ 20 حالة وكبّرها لـ 200. كل شكوى عميل بتتحوّل لحالة جديدة تتضاف هنا.
- Run (التشغيل): تطبيقك بياخد كل إدخال ويطلّع مخرج. ثبّت الـ temperature والـ seed عشان النتيجة تكون قابلة للمقارنة.
- Judge (الحَكَم): بيدّي درجة لكل مخرج. نوعين: قواعد صارمة (هل فيه الرقم ده؟ هل الـ JSON صحيح؟)، أو حَكَم LLM للأمور الذوقية (هل الإجابة مؤدبة ومسندة للسياق؟).
- Gate (البوابة): رقم واحد بيقرّر ship ولا no-ship. مثلاً: لو الدرجة الإجمالية أقل من 85، اكسر الـ CI وامنع النشر.
كود شغّال: harness في 30 سطر
ده harness كامل بالقواعد الصارمة. انسخه، بدّل call_your_llm بتطبيقك، وشغّله:
# eval.py — harness بسيط بقواعد (rule-based)
golden = [
{"q": "كام عدد محافظات مصر؟", "must_include": ["27"]},
{"q": "اطلع لي ايميل من: تواصل معايا على a@b.com", "must_include": ["a@b.com"]},
{"q": "رد باختصار: تمام؟", "must_not": ["as an AI"]},
]
def call_your_llm(q):
# ناديت هنا الـ pipeline بتاعك بالكامل
...
def score(output, case):
inc = case.get("must_include", [])
exc = case.get("must_not", [])
hit = sum(kw in output for kw in inc)
bad = any(kw in output for kw in exc)
base = hit / len(inc) if inc else 1.0
return 0.0 if bad else base
results = [score(call_your_llm(c["q"]), c) for c in golden]
overall = 100 * sum(results) / len(results)
print("eval score:", round(overall, 1))
GATE = 85
raise SystemExit(0 if overall >= GATE else 1)