הערכות LLM — Evals
הצעד שמפריד בין חובבים למקצוענים. בלי דרך למדוד אם המערכת עובדת טוב, כל שינוי הוא הימור. כך בונים מדידה שיטתית.
הבעיה שאין לה פתרון בלי מדידה
הדפוס מוכר לכל מי שהריץ מערכת LLM בייצור. משנים מילה בהנחיה, בודקים שלוש שאלות, זה נראה טוב יותר, מעלים. שבוע אחרי מתלונן מישהו על משהו שעבד קודם, ואין דרך לדעת אם השינוי ההוא אשם.
הסיבה שזה קורה אינה רשלנות. מערכות LLM לא נשברות — הן משתנות. שינוי בהנחיה משפר סוג אחד של שאלות ופוגע בסוג אחר, והצוות רואה רק את הסוג שהוא בדק. בקוד רגיל יש שגיאה שעוצרת; כאן יש תשובה שנראית סבירה והיא פחות טובה מקודמתה.
מכאן מגיעה ההגדרה המעשית: Eval הוא מערך קלטים קבוע עם ציפייה מסומנת, שרץ אחרי כל שינוי. לא מדד איכות מוחלט, לא ציון לפרסום — מנגנון שאומר אם השינוי האחרון שיפר או החמיר.
וזה גם מה שהופך אותו לצעד הראשון ולא לאחרון. מערכת בלי evals אפשר לשנות, אבל אי אפשר לשפר — כי שיפור מחייב יכולת להשוות שני מצבים, ובלי מערך קבוע אין שני מצבים אלא שתי התרשמויות.
אם בשיחה על המערכת נאמר "נראה לי שזה השתפר" — זה בדיוק הרגע. המשפט הזה אינו מידע.
ויש לזה תוצר לוואי שמפתיע צוותים רבים: ברגע שיש מערך, הדיון על המערכת משתנה. במקום ויכוח בין שתי התרשמויות יש שאלה אחת — מה אומרת ההרצה. זה חוסך זמן לפחות כמו שהוא חוסך טעויות.
ההחלטה מה נחשב נכון היא העבודה
כאן נמצא החלק שכמעט כל מדריך מדלג עליו. הריצה קלה — יש ספריות, יש כלים, זה יום עבודה. מה שקשה הוא להחליט מה נחשב תשובה נכונה, וזו החלטה מוצרית ולא טכנית.
קחו שאלה פשוטה שמגיעה לבוט שירות: "כמה זמן לוקח משלוח?" האם תשובה שאומרת "3-5 ימי עסקים" נכונה כשהמדיניות אומרת "עד 5 ימי עסקים"? האם תשובה שמוסיפה "תלוי באזור" טובה יותר או מיותרת? האם תשובה שמפנה לדף המדיניות במקום לענות היא כישלון או התנהגות רצויה?
לכל אחת מהשאלות האלה יש תשובה — אבל היא תלויה במה שהמוצר שלכם אמור לעשות, ומישהו צריך להכריע בה פעם אחת. ברגע שהכרעתם, המערך מודד; לפני כן הוא רק נראה כמו מדידה.
הדרך המעשית לעשות את זה: לשבת עם עשרים פלטים אמיתיים ולסמן אותם ידנית, ולכתוב לצד כל החלטה למה. אחרי עשרים מקרים מתגבשים שלושה עד חמישה כללים ברורים, וזה מסמך ההגדרה שכל השאר נשען עליו — כולל, בהמשך, ההנחיה שתינתן לשופט האוטומטי.
מאיפה מגיעים המקרים
הטעות הנפוצה ביותר בבניית מערך היא להמציא אותו. מקרים שנכתבו בישיבה מייצגים את מה שהצוות מדמיין שמשתמשים שואלים, וזה כמעט תמיד גרסה מנוסחת יפה מדי של המציאות.
המקור הנכון הוא לוג הייצור. ואם עדיין אין ייצור — שיחות עם משתמשים ראשונים, פניות לתמיכה, הודעות וואטסאפ שהגיעו לעסק. הטקסט האמיתי מכיל שגיאות כתיב, ניסוח חלקי, שתי שאלות במשפט אחד ודברים שהצוות לא היה חושב עליהם.
ומה צריך להיות בפנים, מעבר למקרה הרגיל:
- מה שקרה באמת. השאלות הנפוצות, בניסוח שבו נשאלו.
- מקרי קצה שנפלו. כל תלונה היא מקרה בדיקה מוכן. זה המקור הזול והשימושי ביותר.
- שאלות שאין עליהן תשובה. כדי לוודא שהמערכת אומרת שאינה יודעת במקום להמציא.
- שאלות מחוץ לתחום. כדי לראות שהיא לא נגררת.
- קלט עוין. ניסיונות לגרום למערכת לחרוג מתפקידה — prompt injection בסיסי לכל הפחות.
וכלל אחד שמונע בזבוז: כל תקלה בייצור נכנסת למערך לפני שמתקנים אותה. כך המערך גדל בדיוק במקומות שבהם המערכת נכשלת בפועל, ולא במקומות שנוח לבדוק.
ומה לא נכנס פנימה
לוג ייצור הוא מקור מצוין וגם מקור לבעיה, כי הוא מכיל אנשים אמיתיים. מקרה בדיקה שמכיל שם, טלפון, מספר הזמנה או כתובת של לקוח הופך את מערך ההערכה למאגר מידע אישי — שמועתק למחשבים של מפתחים, נכנס למערכת בקרת גרסאות ונשלח לספק מודל בכל הרצה.
הפתרון לא דורש לוותר על המקרה: מחליפים את הערכים בערכים בדויים ועקביים. "דנה כהן" הופכת ל"רונית לוי" בכל מופע, ומספר ההזמנה למספר קבוע. המבנה הלשוני נשמר, וזה מה שנבדק ממילא.
ושני דברים נוספים שלא שייכים למערך: מקרים שהתשובה הנכונה בהם משתנה עם הזמן — מחיר, מלאי, שעות פעילות — כי הם ייכשלו בעוד חודש בלי שהמערכת השתנתה; ומקרים כפולים, שרק מנפחים את הציון בלי להוסיף כיסוי.
כמה מקרים מספיקים
התשובה מאכזבת בפשטותה: הרבה פחות ממה שנדמה. חמישים מקרים מסומנים היטב שווים יותר מחמש מאות שנאספו אוטומטית ואיש לא בדק.
הסיבה היא ששימושיות המערך תלויה באיכות הסימון, לא בכמות. מערך גדול עם סימון רשלני מייצר ציון שנראה מדעי ואינו מודד כלום, ומערך קטן ומדויק תופס נסיגות אמיתיות ביום הראשון.
קצב שעובד: מתחילים בעשרים עד שלושים מקרים שמכסים את התרחישים המרכזיים, מריצים, ומגלים מיד שני דברים — אילו מקרים חסרים, ואילו כללי סימון לא היו ברורים מספיק. מוסיפים, ומגיעים תוך שבועיים לחמישים עד מאה. משם המערך גדל לבד מכל תקלה בייצור.
ומה שכן חשוב יותר מהגודל: שהמערך יהיה קבוע. מערך שמישהו משנה בכל שבוע אינו נקודת ייחוס, וההשוואה בין הרצות מאבדת משמעות. שינוי במערך הוא אירוע מתועד, כמו שינוי בגרסה.
סימון ידני בלי לשרוף שבוע
החלק הידני מרתיע, והוא קצר בהרבה ממה שנדמה — בתנאי שעושים אותו נכון.
מה שהופך אותו לאיטי הוא לסמן ולנסח כללים בו זמנית. הדרך המהירה הפוכה: לעבור על שלושים מקרים ולסמן במהירות טוב או לא טוב, בלי להתלבט. המקרים שגרמו להיסוס מסומנים בנפרד — הם, ולא האחרים, מכילים את ההחלטות שצריך לקבל.
אחרי הסבב הזה יושבים עשרים דקות על ערימת ההיסוסים בלבד, ומנסחים ממנה את הכללים. ואז עוברים שוב על השלושים, הפעם לפי הכללים — וזה הסבב שקובע, כי הוא עקבי.
ונקודה שמשתלמת בצוות: שני אנשים מסמנים את אותם עשרה מקרים בנפרד. כל מקום שבו הם חלוקים הוא מקום שבו ההגדרה לא ברורה, וגילוי שלו עכשיו זול בהרבה מגילוי שלו אחרי שהשופט האוטומטי כבר רץ על מאה מקרים.
שלוש שכבות של בדיקה
לא כל בדיקה דורשת מודל. למעשה, הבדיקות הזולות והאמינות ביותר הן אלה שאינן משתמשות בו כלל, וכדאי למצות אותן קודם.
- בדיקות דטרמיניסטיות. האם הפלט תקין מבנית, האם הוא מכיל את מזהה ההזמנה שהופיע בקלט, האם הוא לא מכיל מספר טלפון, האם אורכו סביר. מהיר, חינם, ואפס אי-ודאות. בקוד רגיל, לא במודל.
- התאמה לציפייה. כשיש תשובה נכונה בדידה — סיווג, חילוץ שדה, בחירת כלי — פשוט משווים. כאן פלט מובנה משתלם פעמיים, כי ההשוואה הופכת אוטומטית לחלוטין.
- שיפוט איכותי. רק מה שנשאר: האם התשובה מועילה, נאמנה למקור, בטון הנכון. כאן נכנס שופט אוטומטי, ורק כאן.
הסדר הזה אינו עניין של טעם. כל בדיקה שאפשר לעשות בקוד ונעשית במודל היא בדיקה שהוספתם לה רעש ועלות בלי סיבה. מערכות רבות מריצות שופט יקר על שאלה שאפשר לענות עליה בביטוי רגולרי.
שופט אוטומטי, בלי לרמות את עצמך
LLM-as-Judge הוא מודל שמדרג פלט של מודל אחר. הוא עובד טוב יותר ממה שנשמע — ורק אם מטפלים בשלוש נקודות.
הראשונה: קריטריון אחד לכל שיפוט. "דרג את איכות התשובה מ-1 עד 10" מייצר מספרים שקשה להסביר. "האם התשובה נתמכת במקור שצורף — כן או לא" מייצר תוצאה שאפשר לפעול לפיה.
השנייה: נימוק לפני הכרעה. שופט שמחזיר קודם ציון ואז הסבר מייצר הצדקה בדיעבד. סדר השדות בסכמה קובע כאן ממש.
והשלישית: השופט צריך את המקור. כדי לקבוע אם תשובה נאמנה, צריך לראות למה היא אמורה להיות נאמנה.
JUDGE = """אתה בודק תשובות של בוט שירות.
בהינתן השאלה, המקור והתשובה — קבע האם
כל טענה בתשובה נתמכת במקור.
החזר JSON:
reason – הסבר קצר, נכתב ראשון
grounded – true / false
unsupported – רשימת טענות לא נתמכות
"""
def judge(question, source, answer):
out = call_model(
system=JUDGE,
user=f"שאלה: {question}\n\n"
f"מקור: {source}\n\n"
f"תשובה: {answer}",
schema=JUDGE_SCHEMA, # reason לפני grounded
temperature=0,
)
return out
שימו לב למה שאין כאן: אין ציון מ-1 עד 10, אין שאלה כפולה, ויש טמפרטורה אפס. שופט אמור להיות משעמם וחזרתי — כל יצירתיות בו היא רעש במדידה.
ומי בודק את השופט
זו השאלה שמפרידה בין מדידה אמיתית לבין תחושת ביטחון. שופט אוטומטי הוא מודל, ולכן הוא טועה — והשאלה היא כמה, ובאיזה כיוון.
הכיול פשוט ולוקח שעה: קחו שלושים מקרים, סמנו אותם ידנית, והריצו עליהם את השופט. אם הוא מסכים עם הסימון האנושי ברוב המכריע — אפשר לסמוך עליו על המערך כולו. אם לא, ההנחיה שלו לא ברורה מספיק, וזה תיקון בהנחיה ולא במודל.
ושתי הטיות ידועות ששווה לבדוק מולן: שופטים נוטים להעדיף תשובות ארוכות, גם כשהקצרה נכונה לא פחות; ומודל שמשמש כשופט לפלט של עצמו נוטה להיות סלחני. אם אפשר — השופט הוא ממשפחת מודלים אחרת מזו שנבדקת.
להשוות שתי גרסאות, לא לתת ציון
לרוב השאלה אינה "כמה טובה המערכת" אלא "האם הגרסה החדשה טובה מהישנה". והשאלה השנייה קלה בהרבה למדידה.
ציון מוחלט דורש סולם, והסולם נודד. השוואה זוגית — שתי תשובות לאותה שאלה, איזו עדיפה — יציבה הרבה יותר, גם אצל שופט אוטומטי וגם אצל בני אדם. אין צורך להסכים מה זה "7 מתוך 10" כדי להסכים ש-א׳ עדיפה על ב׳.
פרט טכני שמשנה תוצאה: להחליף את סדר ההצגה בחצי מהמקרים. שופטים, אנושיים וגם אוטומטיים, מושפעים מהמיקום. בלי החלפה מקבלים תוצאה שנראית מובהקת ואינה.
ומתי כן צריך ציון מוחלט: כשרוצים לדעת אם המערכת עומדת ברף לפני העלאה לייצור. אז הבדיקות הדטרמיניסטיות וההתאמה לציפייה עושות את רוב העבודה, והשיפוט האיכותי מוסיף שכבה.
כמה הבדל הוא בכלל הבדל
שתי גרסאות נבדקו על חמישים מקרים. הישנה קיבלה 41, החדשה 43. האם החדשה טובה יותר?
התשובה הזהירה היא שלא ידוע. הפרש של שני מקרים מתוך חמישים יכול לנבוע מרעש — המודל אינו דטרמיניסטי לחלוטין, והשופט גם הוא מודל. מי שמעלה גרסה על סמך הפרש כזה מקבל תוצאה אקראית בחצי מהמקרים.
שלוש דרכים מעשיות להתמודד, בלי סטטיסטיקה מסובכת: להריץ פעמיים ולראות כמה הציון זז בלי ששינו דבר — זה הרעש הבסיסי של המערכת, וכל הפרש קטן ממנו אינו ממצא; להסתכל על אילו מקרים השתנו ולא על כמה — שני מקרים שהתהפכו מסיבה מובנת שווים יותר מעשרה שהתהפכו אקראית; ולהגדיל את המערך כשההחלטה חשובה, כי הפרש קטן על מערך גדול משמעותי יותר מאותו הפרש על מערך קטן.
מה שונה כשהמערכת בעברית
רוב הכלים והמאמרים בתחום נכתבו על מערכות באנגלית, וארבע נקודות משתנות בעברית.
- השוואת מחרוזות כמעט חסרת ערך. "הזמנה נשלחה" ו"ההזמנה נשלחה" זהות במשמעות ושונות כטקסט. מדדי חפיפה מילוליים שעובדים סביר באנגלית מייצרים בעברית תוצאה מטעה, בגלל אותיות השימוש. השוואה סמנטית או שיפוט, לא התאמת טקסט.
- המערך חייב להיות בעברית אמיתית. מקרי בדיקה שתורגמו מאנגלית מנוסחים כמו אנגלית, ולא תופסים את מה שנשבר בניסוח עברי טבעי.
- כתיב חסר ומלא, וטעויות הקלדה. "מיליון" מול "מליון", "צריך" מול "צריח". זה חלק מהקלט האמיתי וצריך להיות במערך.
- השופט מקבל הנחיה בעברית. אם הפלט שנבדק הוא עברי, הנחיה עברית לשופט נותנת בדרך כלל שיפוט מדויק יותר — ובכל מקרה, שווה לבדוק את שתי האפשרויות מול הסימון הידני במקום להניח.
להריץ את זה בלי לזכור להריץ
מערך שרצים עליו כשנזכרים אינו שונה בהרבה מלא להריץ בכלל. מה שהופך אותו למנגנון הוא שהוא רץ מעצמו בנקודה קבועה — לפני מיזוג שינוי, או לפחות לפני כל העלאה לייצור.
שלוש החלטות שצריך לקבל כדי שזה יחזיק:
- מה נחשב כישלון. לא כל ירידה. סף מוחלט על בדיקות קריטיות — אלה שאסור שייכשלו אף פעם — וסף יחסי על השאר.
- מה עולה להריץ. מערך של מאתיים מקרים עם שופט הוא עלות אמיתית בכל הרצה. דפוס נפוץ: תת-מערך מהיר על כל שינוי, המערך המלא לפני העלאה.
- מה שומרים. את הפלטים עצמם, לא רק את הציון. כשהציון יורד, השאלה הראשונה היא מה השתנה בתשובות — ובלי הפלטים אין תשובה.
וכלל שחוסך שעות: שינוי אחד בכל הרצה. הנחיה חדשה ומודל חדש יחד מייצרים תוצאה שאי אפשר לייחס לאף אחד מהם, וזה בדיוק המצב שבו מתחילים לנחש שוב.
כשהמודל משתנה מתחתיכם
יש סוג שינוי אחד שאינכם עושים ובכל זאת משפיע: הספק מעדכן את המודל. גרסה מתעדכנת, התנהגות זזה מעט, וההנחיה שכוונה בקפידה לגרסה הקודמת מתנהגת אחרת.
זו בדיוק הנקודה שבה מערך הערכה מחזיר את ההשקעה בפעם אחת. בלעדיו התסמין הוא תלונות מפוזרות שאיש לא מקשר ביניהן; איתו זו הרצה אחת שמראה מיד מה השתנה ובאיזו קטגוריה.
שני הרגלים שהופכים את זה לניתן לניהול: לקבע את מזהה הגרסה של המודל שאתם קוראים לו, כשהספק מאפשר, במקום להסתמך על כינוי שמצביע תמיד על החדש ביותר; ולהריץ את המערך על הגרסה החדשה לפני שעוברים אליה, ולא אחרי. מעבר גרסה הוא שינוי לכל דבר, ומגיע לו אותו יחס ששינוי בהנחיה מקבל.
איך קוראים תוצאה
ציון כולל הוא מספר יחיד, ומספר יחיד מסתיר בדיוק את מה שמעניין. מערכת שירדה מ-88% ל-86% נראית כמעט זהה — עד שמתברר ששיעור ההצלחה בשאלות על החזרים ירד ממאה לשישים, והשאר עלה מעט וכיסה על זה.
לכן שלושה הרגלי קריאה:
- פילוח לפי קטגוריה. לתייג כל מקרה בסוג שלו, ולהסתכל על הפילוח לפני הממוצע.
- לקרוא את הכישלונות, אחד אחד. עשרה כישלונות שנקראו ידנית מלמדים יותר מכל גרף. בדרך כלל שלושה מהם הם אותה בעיה.
- להסתכל על מה שהשתנה, לא רק על מה שנכשל. מקרה שעבר קודם ונכשל עכשיו חשוב יותר ממקרה שנכשל תמיד.
ועוד נקודה שקל לפספס: גם שיפור חד מחייב בדיקה. קפיצה פתאומית היא לעתים קרובות סימן שמשהו במערך נשבר — למשל שהשופט התחיל להחזיר ברירת מחדל.
מה שמערך קבוע לא יתפוס
מערך הבדיקה מודד מול מה שידעתם לשאול בזמן שבניתם אותו. מה שמשתמשים שואלים בפועל זז, ולכן צריך גם מדידה בייצור — לא במקום המערך, אלא לצדו.
שלושה אותות שלא דורשים סימון ידני ומספקים ערך מיידי: שיעור השאלות שהמערכת ענתה עליהן "אין לי מידע", כי עלייה בו אומרת שהתוכן חסר; שיעור הפניות שהועברו לאדם; וחזרה על אותה שאלה בניסוח אחר באותה שיחה, שהיא הסימן הברור ביותר לכך שהתשובה הראשונה לא הספיקה.
ומה שמחבר בין השניים: כל דפוס שמתגלה בייצור חוזר למערך כמקרה בדיקה. זה מה שהופך את המערך למשהו שמשתפר עם הזמן במקום להתיישן.
מה זה עולה, ואיך מוזילים
מערך עם שופט הוא קריאות מודל נוספות, ובקצב פיתוח מהיר זה מצטבר. ארבע דרכים להוזיל בלי לוותר על המדידה:
- למצות את הבדיקות שאינן דורשות מודל. הן חינם, והן תופסות חלק ניכר מהתקלות.
- תת-מערך מהיר לעבודה יומית. עשרים מקרים מייצגים על כל שינוי, המערך המלא לפני העלאה.
- מודל קטן יותר לשופט. שיפוט לפי קריטריון אחד ברור הוא משימה קלה בהרבה מהמשימה שנבדקת. כיילו מול הסימון הידני ובדקו אם זה מחזיק — לעתים קרובות כן.
- לשמור תוצאות. מקרה שלא השתנה ומודל שלא השתנה אינם דורשים הרצה מחדש.
ומול זה שווה להחזיק את הפרופורציה: העלות של יום אחד של דיבוג מערכת שנשברה בלי שאיש ידע מתי גבוהה מעלות ההרצות של חודש. זו ההשוואה הנכונה, לא ההשוואה מול אפס.
מתי זה מיותר
- אב טיפוס שעדיין לא הוחלט אם ייבנה. קודם לבדוק אם הרעיון עובד בכלל.
- כשהפלט הולך ישירות לעיני אדם שמאשר. אם ממילא בן אדם קורא כל תשובה לפני שהיא יוצאת, המדידה כבר קיימת.
- כשהמשימה דטרמיניסטית. חילוץ מפורמט קבוע נבדק בבדיקות יחידה רגילות, בלי מערך הערכה ובלי שופט.
- כשאין עדיין מה למדוד. מערכת שמשתמשים בה שלושה אנשים — עדיף לדבר איתם.
טעויות שחוזרות
- להתחיל בלי להחליט מה נחשב נכון. אז המערך מודד את חוסר ההסכמה שלכם.
- מקרים שהומצאו בישיבה. מנוסחים יפה מדי ולא מייצגים.
- מערך שמשתנה בכל שבוע. אין השוואה בין הרצות.
- שופט שלא כויל מול בני אדם. מדידה שאיש לא בדק אם היא מודדת.
- שופט מאותה משפחת מודלים. נוטה להיות סלחני כלפי פלט שדומה לשלו.
- ציון מ-1 עד 10. קשה להסבר, לא יציב, ולא מוביל לפעולה.
- להסתכל רק על הממוצע. נסיגה בקטגוריה אחת נעלמת בתוכו.
- לשנות שני דברים יחד. תוצאה שאי אפשר לייחס.
- לא לשמור את הפלטים. ואז ירידה בציון היא חידה במקום ממצא.
- להשוות מחרוזות בעברית. אותיות שימוש הופכות תשובות זהות לשונות.