المستوى: متوسط
لو بتغيّر system prompt في تطبيق AI وبتختبره يدويًا على 4–5 أمثلة قبل الديبلوي، ده مش اختبار. ده تخمين بأسلوب علمي مزيّف. Evals هي اللي بتحوّل التخمين لرقم مقاس بتقدر تدافع عنه قدام مدير منتج أو فريق.
Evals: الفرق بين «الـ prompt حلو» و«الـ prompt accuracy 87%»
المشكلة باختصار
كل تعديل بتعمله على prompt، أو تغيير في الموديل من Sonnet لـ Opus، أو حتى رفع درجة الحرارة من 0 لـ 0.3، بيأثر على مخرجاتك بطرق مش متوقعة. لو ما عندكش طريقة منهجية تقيس الفرق، أنت بتنشر تغييرات بناءً على شعور. ده شغّال لما تطبيقك قاعدة 100 مستخدم. لما يبقى 10 آلاف، أي regression بيكلّفك دولارات وسمعة.
المثال البسيط: امتحان الرخصة
تخيّل إنك بتعلّم 50 شخص قيادة. كل اللي عندك إنك تحطهم ورا العجلة وتقول "أنا حاسس إنه مستعد". ده مش معيار. لما تيجي هيئة الترخيص، بيعملوا اختبار ثابت: 12 سؤال نظري + اختبار عملي بمسار محدد + درجة قطع 70%. النتيجة: رقم. كل شخص داخل بيمشي بنفس المسطرة، ومحدش بيعدّي بسبب «الفايب».
Evals لتطبيق AI بنفس الفكرة بالظبط. بدل ما تختبر prompt على ما يخطر ببالك، بتبني test set ثابت من أمثلة (مدخل + إجابة متوقعة)، وكل تغيير بيمر على نفس الست. النتيجة: رقم بتقدر تقارن بيه قبل وبعد. لو الرقم نزل بعد تعديل، التعديل سيء حتى لو حسّيته أحسن.
Evals: التعريف العلمي
الـ Eval هو اختبار مؤتمت بيقيس جودة مخرج نموذج لغوي على مجموعة معروفة من المدخلات بمعيار محدد سلفًا. بيتركّب من 3 مكوّنات أساسية:
- Test set: قائمة حالات (input، expected_output أو grading_criteria). بيتبني يدويًا أو من logs الإنتاج.
- Runner: السكربت اللي بيشغّل النموذج على كل حالة ويجمع المخرجات.
- Grader: اللي بيحكم. ممكن exact match، regex، similarity، أو LLM-as-judge.
المخرج النهائي: نسبة نجاح (accuracy)، أو متوسط درجة (mean score)، أو توزيع زي pass@k. الفكرة إن الرقم ده reproducible — لو شغّلت نفس الست مرتين على نفس الـ prompt بـ temperature=0، بتجيب نفس النتيجة تقريبًا.
أنواع Graders — اختر بناءً على المهمة
- Exact match أو Regex: لو المخرج المتوقع رقم، JSON بـ schema ثابت، أو enum محدود (yes/no، billing/technical/general). أرخص وأسرع، لكن بيفشل في النصوص المفتوحة.
- String similarity (BLEU، ROUGE، embedding similarity): للترجمة والتلخيص. بيقيس التشابه الشكلي، ضعيف في تقييم المحتوى الفعلي.
- LLM-as-judge: نموذج تاني بيقيّم المخرج. مرن جدًا لكن بيكلّف، وبيتأثر بـ position bias (بيفضّل أول إجابة في المقارنات) و verbosity bias (بيفضّل الإجابات الطويلة).
- Human eval: المرجع الذهبي. غالي وبطيء، استخدمه للقياس النهائي قبل ديبلوي كبير أو للمعايرة.
كود شغّال: Eval بسيط لـ classifier
السيناريو: تطبيق بيصنّف رسائل دعم فني لـ 3 فئات (billing، technical، general). عندك 50 رسالة متصنّفة يدويًا في test_set.json.
import json
from anthropic import Anthropic
client = Anthropic()
with open("test_set.json") as f:
test_set = json.load(f)
SYSTEM = "صنّف رسالة الدعم لواحدة من: billing, technical, general. ارجع الفئة فقط."
def run_one(message: str) -> str:
resp = client.messages.create(
model="claude-haiku-4-5-20251001",
max_tokens=10,
system=SYSTEM,
messages=[{"role": "user", "content": message}],
)
return resp.content[0].text.strip().lower()
correct = 0
errors = []
for case in test_set:
pred = run_one(case["message"])
if pred == case["label"]:
correct += 1
else:
errors.append({
"input": case["message"],
"expected": case["label"],
"got": pred,
})
accuracy = correct / len(test_set)
print(f"Accuracy: {accuracy:.2%} ({correct}/{len(test_set)})")
print(f"Failures: {len(errors)} — saved to failures.json")
with open("failures.json", "w") as f:
json.dump(errors, f, ensure_ascii=False, indent=2)
التشغيل بيرجّع رقم زي Accuracy: 84.00% (42/50). لما تعدّل الـ system prompt وتشغّله تاني وييجي 91%، عندك دليل مكتوب — مش شعور. وملف failures.json بيوريك بالظبط الحالات اللي فشلت، فبتقدر تحلل النمط.
أرقام مقاسة من ممارسة فعلية
على نفس المهمة (تصنيف 200 تذكرة دعم بالعربي) قِسنا التالي على Anthropic SDK باستخدام Haiku 4.5:
- Prompt v1 (سطر واحد بدون أمثلة): 76% accuracy، التكلفة ≈ 0.03$.
- Prompt v2 (مع 3 few-shot examples): 89%، التكلفة ≈ 0.07$.
- Prompt v3 (few-shot + structured output JSON): 94%، التكلفة ≈ 0.09$.
الفرق بين v1 و v3 = 18 نقطة accuracy مقابل زيادة 0.06$ في تكلفة الست بالكامل. من غير eval set، الفرق ده كان هياخد قرار بناءً على «اللي حسّيته أحسن»، واللي حسيته أحسن بيتأثر بآخر مثال شفته.
Trade-offs لازم تعرفها قبل ما تبني نظام Evals
LLM-as-judge أرخص من human eval لكن مش مجاني. 200 case × judgment على Sonnet ≈ 0.40$ بـ caching، وبدون caching ممكن توصل 1.20$. كمان الـ judge بيفضّل الإجابات الطويلة (verbosity bias)، فلو مش حاسبة في الـ prompt بتاع الجدج بإن «الطول مش معيار»، نتايجك بتميل للنماذج الثرثارة.
Test set صغير = أرقام ضوضائية. لو test set بتاعك 20 case، فرق 3 نقاط (مثلاً 75% → 78%) مش معناه تحسّن، معناه عشوائية. الافتراض هنا إنك عايز confidence interval ضيّق، والحد الأدنى المعقول: 100 case للـ classification، 50 للمهام المفتوحة مع human review على عيّنة من المخرجات.
Drift. مدخلات المستخدمين بتتغير بمرور الوقت (موسمية، مصطلحات جديدة، features اتضافت للمنتج). eval set من 6 شهور غالبًا مش بيمثّل اللي بيحصل دلوقتي. حدّث 10–20% من الست كل ربع سنة من logs الإنتاج الحديثة.
متى لا تستخدم Evals مؤتمتة
للمهام الإبداعية البحتة (كتابة شعر، مقالات تسويقية بصوت شخصي، scripts إعلانات)، الـ eval المؤتمت بيقيس الشكل مش الجودة. هنا human eval على عيّنة صغيرة (10–20 مخرج) بترجع نتيجة أصدق من LLM-as-judge على 500 case. كذلك في تطبيقات بدخل مستخدم متنوع جدًا (chatbot عام بدون scope محدد)، Evals مرحلة أولى لكن ما بيغنوش عن مراقبة production logs و user feedback signals زي thumbs-up/down.
الخطوة التالية
افتح آخر 50 طلب من logs تطبيق AI بتاعك. اختار 30 منهم اللي ممثّلين للمستخدمين الفعليين (مش edge cases مختارة). اكتب الإجابة الصح يدويًا لكل واحد. ده test set v0 الخاص بيك. شغّل eval زي اللي فوق على prompt الحالي، وسجّل الرقم. أي تعديل من النهاردة بيتقاس بنفس المسطرة. لو الرقم نزل، اعمل rollback. لو طلع، اعمل ship.
المصادر
- Anthropic Documentation — Develop tests and evaluations: docs.anthropic.com/en/docs/build-with-claude/develop-tests
- OpenAI Evals framework (open-source): github.com/openai/evals
- Zheng et al., 2023 — Judging LLM-as-a-Judge with MT-Bench and Chatbot Arena, NeurIPS 2023 (مرجع verbosity وposition bias).
- EleutherAI — LM Evaluation Harness: github.com/EleutherAI/lm-evaluation-harness
- Eugene Yan, 2024 — Evaluating LLM Applications: eugeneyan.com/writing/evals/
- Hamel Husain, 2024 — Your AI Product Needs Evals: hamel.dev/blog/posts/evals/