AI Observability
— לראות מה הסוכן עושה
סוכן AI ש"סתם לא עובד" הוא סיוט לדיבוג — קלט לא-דטרמיניסטי, שרשרת קריאות ל-LLM וכלים, ועלויות שמטפסות. Observability הופך את הקופסה השחורה לשקופה: כל trace, כל prompt, כל טוקן ועלות. במדריך: ארבעת עמודי התווך, tracing ו-evals, ו-LangSmith מול Langfuse.
למה ניטור רגיל לא מספיק כאן
במערכת תוכנה רגילה תקלה מכריזה על עצמה. יש חריגה, יש קוד 500, יש בקשה שלא חזרה. הניטור מחפש את הסימנים האלה ומוצא אותם.
מערכת LLM נשברת אחרת. הבקשה חוזרת 200, בזמן סביר, עם תשובה שנראית תקינה לגמרי — והיא שגויה. אין חריגה לתפוס ואין מדד שקפץ. הלוג מראה מערכת בריאה, והמשתמש מקבל מידע לא נכון.
מכאן ההבדל שקובע את כל השאר: ניטור רגיל שואל אם המערכת ענתה. תצפית על מערכת LLM חייבת לשאול מה היא ענתה — וזה אומר לשמור את התשובה עצמה, לא רק את העובדה שהייתה.
יש כאן גם היפוך מוכר. בתוכנה רגילה מתחילים ממדדים ויורדים ללוגים כשצריך. במערכת LLM זה הפוך: הטקסט הוא הנתון העיקרי, והמדדים הם התוספת. צוות שהתקין לוח מחוונים עם זמני תגובה ושיעורי שגיאה, ולא שמר את הפלטים, בנה בדיוק את החלק שלא יעזור לו.
לקוח מתלונן על תשובה שקיבל אתמול. תוך כמה זמן תוכלו לראות בדיוק מה נשלח למודל ומה חזר? אם התשובה אינה "דקה" — זה הפער.
היחידה שהכל נבנה סביבה
המושג המרכזי הוא trace: התיעוד המלא של בקשה אחת מתחילתה ועד סופה, על כל השלבים שבדרך.
בשאלה אחת של משתמש מתרחשים בדרך כלל חמישה עד עשרה דברים — שליפה ממאגר הידע, בניית ההקשר, קריאה למודל, קריאה לכלי, עוד קריאה למודל, ניסוח התשובה. כשמשהו יוצא שגוי, השאלה היא באיזה מהם זה קרה, וזו שאלה שאי אפשר לענות עליה מתוך לוג של שורות מפוזרות.
מה שהופך trace לשימושי הוא שלושה דברים פשוטים: מזהה אחד שמקשר את כל השלבים, שלבים מקוננים שמראים מה קרה בתוך מה, ותזמון לכל שלב. עם שלושת אלה, השאלה "למה זה לקח אחת עשרה שניות" הופכת מחקירה לקריאה.
וזה גם הסדר הנכון להיכנס לתחום: trace ראשון, מדדים אחר כך. אפשר לגזור מדדים מתוך traces; אי אפשר לשחזר trace ממדדים.
מה נשמר בכל שלב
ההבדל בין תיעוד שפותר תקלה לתיעוד שרק תופס מקום הוא מה נכנס לתוכו. שבעה פריטים שמכסים כמעט כל מקרה:
- ההקשר המלא שנשלח למודל. לא ההנחיה כפי שכתובה בקוד — הטקסט הסופי אחרי כל ההצבות. זה הפריט החשוב ביותר ברשימה, והוא גם זה שהכי הרבה מערכות מדלגות עליו.
- הפלט המלא. כולל מקרים שנפלו בוולידציה, ודווקא הם.
- איזה מודל וגרסה. בלי זה אי אפשר לקשר שינוי התנהגות לעדכון גרסה.
- פרמטרים. טמפרטורה, תקרת טוקנים, סכמה. מסבירים תוצאות מוזרות בתדירות גבוהה.
- טוקנים, בכניסה וביציאה. מכאן נגזרות העלות והתשובה לשאלה אם ההקשר תפח.
- מה נשלף ובאיזה דירוג. במערכת RAG זה מפריד מיד בין בעיית שליפה לבעיית ניסוח.
- קריאות כלים — פרמטרים, תוצאה, וזמן. בסוכן זה בדרך כלל המקום שבו הדברים באמת נשברים.
ופריט שמנוסח בדרך כלל גרוע: סיבת הסיום. תשובה שנקטעה כי נגמרה תקרת הטוקנים נראית בלוג כמו תשובה קצרה, ולא כמו תקלה. בלי השדה הזה מחפשים את הבעיה במקום הלא נכון.
איך זה נראה בקוד
אין כאן קסם, וזה שווה להראות כי רבים מדמיינים תשתית מסובכת יותר ממה שנדרש. שלב הוא רשומה עם זמן התחלה, זמן סיום ומזהה אב:
@contextmanager
def step(trace_id, name, parent=None):
rec = {"trace_id": trace_id, "name": name,
"parent": parent, "started": now(),
"meta": {}}
try:
yield rec
rec["status"] = "ok"
except Exception as e:
rec["status"] = "error"
rec["error"] = str(e)
raise
finally:
rec["ended"] = now()
store(rec) # טבלה, לא שירות חיצוני
with step(tid, "answer") as root:
with step(tid, "retrieve", root) as s:
docs = search(q)
s["meta"]["ids"] = [d.id for d in docs]
with step(tid, "generate", root) as s:
s["meta"]["prompt"] = final_prompt
out = call_model(final_prompt)
s["meta"]["output"] = out
s["meta"]["tokens"] = out.usage
שני פרטים בקוד הזה חשובים יותר מהמבנה. הכתיבה ב-finally — שלב שנפל הוא בדיוק השלב שרוצים לראות, ותיעוד שנכתב רק בהצלחה מאבד אותו. והשמירה היא לטבלה: אפשר להחליף אותה בכלי ייעודי בהמשך, ואין סיבה להתחיל משם.
ומה אסור לשמור
כאן נכנס השיקול שרוב המדריכים הטכניים מדלגים עליו, והוא לא פחות חשוב. תיעוד מלא של מערכת שירות הוא מאגר של שיחות עם אנשים אמיתיים — שמות, טלפונים, כתובות, לעתים פרטי תשלום או מידע רפואי.
זה הופך את מערכת התצפית עצמה לנכס רגיש. ובישראל, מערכת שאוגרת מידע כזה כפופה לחוק הגנת הפרטיות, בין אם מי שבנה אותה חשב על כך ובין אם לא.
ארבע החלטות שכדאי לקבל לפני שמתחילים לתעד, ולא אחרי:
- לסנן לפני הכתיבה. זיהוי דפוסים מוכרים — תעודת זהות, טלפון, כרטיס אשראי — והחלפתם בסימון, בקוד שרץ לפני שהנתון יוצא מהמערכת שלכם.
- תקופת שמירה מוגדרת. traces שימושיים ימים עד שבועות. שמירה לנצח היא סיכון שמצטבר בלי תועלת מקבילה.
- הרשאות נפרדות. מי שמסתכל על מדדים לא חייב גישה לתוכן השיחות.
- לדעת איפה זה יושב. כלי תצפית מנוהל הוא צד שלישי שמקבל את כל הטקסט שעובר במערכת. בתוכן רגיש זו החלטה שצריכה להתקבל במפורש.
לא חייבים לשמור הכל
תיעוד מלא של כל בקשה גדל מהר, ובמערכת עם תעבורה אמיתית הוא הופך בעצמו לעלות ולסיכון. הפתרון הוא דגימה, והיא עובדת רק כשהיא לא אחידה.
הכלל: לשמור הכל ממה שמעניין, ומעט ממה שלא. כל בקשה שנכשלה, כל בקשה שהופעל בה ניסיון חוזר, כל שיחה שהועברה לנציג, וכל מה שחרג מסף זמן או עלות — נשמרים במלואם. מהמקרים הרגילים מספיק אחוז קטן, וגם הוא בעיקר כדי לראות איך נראה תקין.
ושתי רמות שכדאי להפריד ביניהן: מטא-נתונים לכולם — זמנים, טוקנים, עלות, סטטוס, שהם קטנים וזולים לשמירה ומהם נגזרים כל המדדים; והטקסט המלא לפי דגימה. כך שומרים על יכולת המדידה בלי לצבור מאגר ענק של שיחות.
המספרים שבאמת מעניינים
אחרי ה-traces מגיעים המדדים, ורק חלק מהם משנים החלטות:
- זמן עד הטוקן הראשון. זה מה שהמשתמש חווה כ"מהירות", לא הזמן הכולל. מערכת שמתחילה לענות מהר נתפסת כמהירה גם כשהיא מסיימת לאט.
- זמן כולל לפי אחוזון, לא ממוצע. הממוצע מסתיר בדיוק את הזנב שבו יושבים המשתמשים שמתלוננים.
- טוקנים בכניסה לפי שלב. העלייה שלהם היא הסימן המוקדם לכך שההקשר תפח, כפי שמוסבר בContext Engineering.
- שיעור כשל בקריאות כלים. בסוכן זה המדד הכי מנבא תקלות, והכי פחות מנוטר.
- שיעור ניסיונות חוזרים. עלייה כאן אומרת שמשהו במבנה הפלט הפסיק להתאים.
- עלות לשיחה. לא עלות לקריאה — לשיחה שלמה. זה המספר שאפשר להשוות לערך העסקי.
ומה ששווה פחות ממה שנדמה: שיעור שגיאות HTTP. במערכת LLM הוא כמעט תמיד אפס גם כשהאיכות קורסת, וזה בדיוק הטעם של הסעיף הראשון.
איפה הכסף באמת הולך
כשמסתכלים לראשונה על פילוח עלות של מערכת LLM, התוצאה כמעט תמיד מפתיעה — ובאותם כיוונים.
הכניסה עולה יותר מהיציאה. לא לטוקן, אלא בסך הכל: תשובה בת מאתיים מילים יושבת על הקשר של אלפי טוקנים. מכאן שכל אופטימיזציה על אורך התשובה כמעט לא משפיעה, וכל צמצום של ההקשר משפיע ישירות.
מיעוט מהשיחות אחראי לרוב ההוצאה. שיחות ארוכות, סוכנים שנכנסו ללולאה, מסמכים גדולים. פילוח לפי עלות לשיחה מאתר אותן תוך דקות, ולרוב מגלה באג ולא שימוש כבד.
וניסיונות חוזרים אינם נראים בשום מקום. קריאה שנכשלה בוולידציה ורצה שוב נספרת פעמיים בחשבון ופעם אחת בלוג, אלא אם תיעדתם אותה במפורש.
המדד שמסכם את הכל הוא עלות לשיחה מוצלחת — ההוצאה הכוללת חלקי מספר השיחות שהסתיימו בתשובה טובה. הוא היחיד שאפשר להשוות ישירות לערך העסקי, ולכן גם היחיד שאפשר לנהל לפיו.
אותות איכות שלא דורשים סימון ידני
מדידת איכות אמיתית נעשית במערך הערכה, וזה נושא נפרד שמפורט בEvals. אבל יש אותות שזורמים מהייצור בחינם, בלי שאיש יסמן דבר, והם מספקים התרעה מוקדמת מצוינת:
- שיעור תשובות "אין לי מידע". עלייה פתאומית בו אומרת בדרך כלל שהשליפה נשברה, לא שהתוכן נעלם.
- שאלה שחוזרת בניסוח אחר באותה שיחה. הסימן הברור ביותר לכך שהתשובה הראשונה לא הספיקה — וזה אות שאף אחד לא צריך לסמן.
- העברה לנציג אנושי. כשקיימת, זו מדידת כישלון ישירה.
- שיחות שנקטעות. משתמש שעזב באמצע אמר משהו.
- אורך התשובה לאורך זמן. שינוי הדרגתי בלי שאיש שינה הנחיה הוא לעתים קרובות הסימן הראשון לכך שמשהו בצד הספק זז.
הערך של הרשימה הזו הוא בכיוון: היא לא אומרת מה שגוי, היא אומרת איפה להסתכל. וזה בדיוק מה שחסר כשאין שום אות והתלונה הראשונה מגיעה מלקוח.
משוב מהמשתמש, ומה הוא שווה
כפתור אגודל למעלה ולמטה הוא התוספת הזולה ביותר שאפשר לעשות, ורוב המערכות מתייחסות אליו לא נכון.
מה שהוא לא: מדד איכות. רק מיעוט קטן של משתמשים לוחץ, והם אינם מדגם מייצג — לוחצים בעיקר כשמשהו היה גרוע במיוחד או טוב במיוחד. שיעור שביעות רצון שנגזר ממנו אינו מספר אמיתי.
ומה שהוא כן, וזה שווה הרבה: מנגנון איתור. כל אגודל למטה הוא הצבעה על trace ספציפי שכדאי לקרוא, וכל אחד מהם הוא מקרה בדיקה מוכן למערך ההערכה. עשרים כאלה בשבוע הם יותר ממה שרוב הצוותים מצליחים לאסוף באופן יזום.
שני פרטים שמכפילים את התועלת: לקשר את הלחיצה ל-trace המלא ולא רק לשמור ספירה, ולאפשר לכתוב מילה. "לא ענה על מה ששאלתי" ו"התשובה ארוכה מדי" הן שתי בעיות שונות לגמרי שנראות זהות במספר.
על מה להתריע
התרעה שמצלצלת הרבה הופכת מהר להתרעה שמתעלמים ממנה, ואז אין התרעה בכלל. שלוש קטגוריות ששוות את זה:
- קריסות אמיתיות. שיעור שגיאות מהספק, זמני תגובה שקפצו, מכסה שנגמרה. אלה מדדים רגילים, והם עדיין נחוצים.
- קפיצות עלות. עלייה חדה בטוקנים או בהוצאה היא כמעט תמיד באג — לולאה שלא נעצרה, הקשר שתפח, ניסיונות חוזרים בלי תקרה. זו ההתרעה עם ההחזר הגבוה ביותר.
- אותות איכות שזזו חזק. קפיצה בשיעור "אין לי מידע" או בהעברות לנציג.
ומה שעדיף להשאיר לדוח יומי במקום להתרעה מיידית: שינויים קטנים והדרגתיים. הם אמיתיים ושווים מבט, והם לא דורשים להעיר מישהו.
וכלל אחד שמונע את רוב הרעש: התרעה חייבת לומר מה לעשות. "שיעור השגיאות עלה" היא הודעה; "שיעור כשל בקריאות לכלי התשלום עלה מ-1% ל-12% בשעה האחרונה" היא התחלה של תיקון.
להשוות שתי גרסאות בייצור
מערך הערכה אומר אם שינוי משפר את מה שידעתם לבדוק. תצפית אומרת מה הוא עשה למשתמשים אמיתיים — ואלה שתי שאלות שונות שכדאי לשאול את שתיהן.
המנגנון פשוט: לתייג כל trace בגרסה שייצרה אותו — גרסת ההנחיה, המודל, ההגדרות — ואז אפשר לפלח כל מדד לפי הגרסה. עלייה בעלות, ירידה בזמן, קפיצה בהעברות לנציג, כולם נעשים ניתנים לייחוס במקום להיות תחושה.
ומכאן אפשר גם להעלות שינוי לחלק מהתעבורה ולהשוות. שני סייגים שמונעים מסקנות שגויות: לתת לזה זמן, כי דפוסי שימוש משתנים בין שעות היום ובין ימי השבוע; ולפלח לפי סוג שאלה, כי שינוי שמשפר קטגוריה אחת ופוגע באחרת נראה במספר הכולל כמו שום שינוי.
מה קורה כשמגיעה תלונה
הערך של כל מה שנבנה עד כאן נמדד ברגע אחד: מישהו אומר שהמערכת ענתה לו לא נכון, ואתם צריכים לדעת למה. הנה הסדר שעובד, ולמה הוא מה שהוא.
- למצוא את ה-trace. לפי מזהה שיחה, משתמש או זמן. אם זה לוקח יותר מדקה — הבעיה הראשונה היא באינדוקס, לא בתשובה.
- לקרוא את ההקשר שנשלח בפועל. לא את התבנית — את הטקסט הסופי. במקרים רבים כאן זה נגמר: השדה שהיה אמור להיכנס היה ריק.
- לבדוק מה נשלף. אם הקטע הנכון לא היה שם, זו בעיית שליפה והתשובה של המודל הייתה סבירה בהינתן מה שקיבל.
- לבדוק את קריאות הכלים. פרמטרים שגויים, תוצאה ריקה שנראתה כמו הצלחה, שגיאה שנבלעה.
- ורק עכשיו להסתכל על הפלט עצמו. אם ההקשר היה נכון, השליפה הייתה נכונה והכלים החזירו נכון — אז זו באמת התנהגות של המודל, וזה תיקון בהנחיה או במערך ההערכה.
הסדר הזה חשוב יותר משנראה: הוא ממוין מהסביר לנדיר. האינסטינקט הוא להתחיל מהמודל, וזה גם המקום שהכי פחות סביר שבו הבעיה — וגם היחיד שבו אין תיקון ודאי.
כלים: מה באמת ההבדל
יש כמה כלים ייעודיים לתחום, והבחירה ביניהם פחות קריטית ממה שנדמה. מה שכן שווה להשוות:
- אירוח עצמי או שירות מנוהל. השאלה המכרעת בתוכן רגיש. כלי בקוד פתוח שרץ אצלכם שומר את הטקסט בבית.
- תלות בספרייה מסוימת. כלי שמשתלב אוטומטית עם מסגרת עבודה אחת חוסך עבודה — וכובל אתכם אליה. תקן פתוח לתיעוד מבוזר מקל על החלפה בהמשך.
- האם הוא מחבר בין תצפית להערכה. היכולת לקחת trace מהייצור ולהפוך אותו למקרה בדיקה בלחיצה היא ההבדל המעשי הגדול ביותר בין הכלים.
- מה עולה בהיקף. תמחור לפי כמות traces מתנהג אחרת לגמרי בעשרת אלפים ובמיליון.
ומה שכדאי לדעת לפני שבוחרים בכלל: תיעוד בסיסי לטבלה במסד הנתונים שלכם הוא צעד ראשון לגיטימי לחלוטין. הוא יספיק להרבה יותר זמן ממה שנדמה, והוא ילמד אתכם מה אתם באמת צריכים לפני שתשלמו על זה.
כשהתקלה אינה אצלכם
חלק מהתקלות מקורן בצד הספק, ותצפית טובה מבדילה ביניהן לבין תקלות שלכם — הבדל שמכריע אם מבזבזים שעתיים על דיבוג או שולחים הודעה ומחכים.
שלושה דברים ששווה לתעד במפורש כדי שההבחנה תהיה מיידית: קוד התשובה וזמן התגובה של הספק בנפרד מהזמן הכולל, כי עלייה שמופיעה רק שם אינה קשורה לקוד שלכם; מגבלות קצב ומכסה, שנראות בלוג רגיל כמו שגיאה כללית; ומזהה הבקשה שהספק מחזיר, שהוא מה שיידרש אם תפתחו פנייה.
ופרט מעשי אחרון: כשהמערכת עוברת למודל חלופי בגלל עומס או תקלה, זה חייב להופיע בתיעוד. אחרת התסמין הוא "המערכת התנהגה אחרת במשך שעה" בלי שום הסבר בקוד ובלי שום שינוי שמישהו עשה — וזו אחת החקירות המתסכלות שיש.
איך מתחילים ביום אחד
הטעות הנפוצה היא לתכנן מערכת תצפית מלאה ולא לבנות אותה. סדר שמחזיר ערך מיד:
- לשמור את מה שנשלח ומה שחזר, לטבלה. עם מזהה, חותמת זמן, מודל וספירת טוקנים. זה שעה עבודה וזה פותר את רוב המקרים.
- להוסיף מזהה שמקשר את כל השלבים של בקשה אחת. מכאן זה כבר trace, גם אם עדיין בלי כלי.
- לחשב עלות מהטוקנים. מספר אחד שמקבל תשומת לב מיידית, וגם תופס באגים.
- להוסיף את אותות האיכות. שיעור "אין לי מידע" והעברות לנציג — שתי שאילתות.
- ורק אז לשקול כלי ייעודי. כשידוע מה חסר, הבחירה קלה ומהירה.
מי מסתכל על זה, ומתי
הכשל הנפוץ ביותר במערכות תצפית אינו טכני: הן נבנות, ואיש לא פותח אותן. לוח מחוונים שנצפה פעם בחודש הוא ארכיון, לא תצפית.
מה שמחזיק לאורך זמן הוא הרגל קבוע וקטן, לא כלי טוב יותר. שלוש נקודות שעובדות:
- סיכום יומי אוטומטי שנשלח לאן שהצוות ממילא מסתכל — מספר שיחות, עלות, שיעור כשלים, וקישור לחמש השיחות היקרות או האיטיות ביותר. המאמץ הוא אפס, ומשם מגיעים רוב הממצאים.
- קריאה שבועית של עשר שיחות אמיתיות. לא מדדים — שיחות. חצי שעה, ובכל פעם מתגלה משהו שאף מדד לא הראה.
- בדיקה אחרי כל שינוי. העליתם הנחיה חדשה — פותחים את המדדים למחרת. זה לא תהליך, זו תזכורת.
והדבר היחיד שבאמת הכרחי: שמישהו אחד יהיה אחראי לזה בשם. תצפית שהיא באחריות כולם אינה באחריות איש, וזה הרגע שבו התלונה הראשונה מגיעה מלקוח במקום מהמערכת.
מתי זה מוגזם
- אב טיפוס שרץ אצלכם במחשב. הדפסה למסך מספיקה. תשתית בשלב הזה היא דחיינות בתחפושת.
- כשהמערכת לא בשימוש. חמישה משתמשים — עדיף לדבר איתם מאשר לנתח את הלוגים שלהם.
- קריאה אחת פשוטה ללא שלבים. סיכום טקסט בלי שליפה ובלי כלים אינו זקוק ל-trace מקונן.
- לפני שיש מה למדוד. תשתית מלאה על מערכת שעדיין משנים בה הכל כל יום תתיישן לפני שתשמש.
טעויות שחוזרות
- לנטר זמינות ולא תוכן. המערכת תמיד תיראה בריאה.
- לשמור את התבנית ולא את ההקשר הסופי. ואז הפריט החשוב ביותר חסר בדיוק כשצריך אותו.
- לתעד רק הצלחות. הכישלונות הם הנתון.
- בלי מזהה מקשר. שורות לוג מפוזרות שאי אפשר לחבר לסיפור.
- מידע אישי שנשמר בלי סינון. מערכת התצפית הופכת לחשיפה בפני עצמה.
- שמירה ללא תקופה מוגדרת. סיכון שמצטבר בלי תועלת מקבילה.
- ממוצעים במקום אחוזונים. מסתירים בדיוק את מי שמתלונן.
- לא לתעד את גרסת המודל. ואז שינוי בצד הספק נראה כמו תעלומה.
- התרעות על כל דבר. שקולות לאפס התרעות.
- לבחור כלי לפני שיודעים מה חסר. לוח מחוונים יפה שלא עונה על השאלה שבאמת נשאלת.