סוכן התמיכה ענה לבקשת משתמש לאיפוס אימות דו-שלבי (two-factor authentication) באמצעות שלבים שפשוט לא קיימים. התגובה נראתה בטוחה, בקשת ה-HTTP החזירה 200 OK, השיהוי (latency) היה תקין וכל גרף הניטור נשאר ירוק.

סוכן תמיכה מבוסס AI הפיק "הזיה" (hallucination) מכיוון שהבדיקות הפנימיות שאמורות היו לתפוס את השגיאה מעולם לא רצו. לוחות הבקרה (dashboards) שעליהם מסתמכים המהנדסים דיווחו על ריצה מושלמת, בעוד הסוכן בנה פתרון מומצא בשקט.

למה לוחות בקרה מסורתיים מפספסים הזיות AI

רוב מערכות ה-observability מתייחסות לסוכן AI כמו לכל microservice אחר: בקשה נכנסת אחת ותגובה יוצאת אחת. הן מתעדות את סטטוס ה-HTTP, זמן התגובה ומספר השגיאות. הן לא מתעדות את השלבים הנסתרים בתוך הבקשה – שליפת מסמכים חיצוניים, הקריאות למודלי שפה גדולים (LLMs), השימוש בכלים עזר וכל לוגיקת guard-rail שמאמתת את הפלט.

כאשר שלב השליפה מחזיר תוצאה ריקה, המודל נוטה לעיתים קרובות "למלא את הפער" בטקסט שנשמע סביר. מנקודת המבט של מערכת הניטור, הקריאה הצליחה, כי דבר לא קרס וקוד הסטטוס נשאר 200. ההזיה נשארת בלתי נראית, והסימפטום היחיד הוא תשובה שגויה שמגיעה למשתמש.

להפוך קופסה שחורה לעץ קריא

הצעד הראשון לניפוי שגיאות (debugging) אמין הוא להפסיק להתייחס לסוכן כאל קריאה מונוליטית ולהתחיל להציג כל פעולה פנימית כשורה נפרדת בטבלת trace. ריצה טיפוסית מתפרקת ל:

  • זימון הסוכן ברמת העל (top-level agent invocation)
  • שלב השליפה שמושך תיעוד רלוונטי
  • כל תהליך הסקה (inference) של מודל שפה המעבד את הנתונים שנשלפו
  • כל קריאה לכלי (למשל, חיפוש במסד נתונים, בקשת API)
  • בדיקות guard-rail שאוכפות עובדתיות או עמידה במדיניות

כל שורה מתעדת את חותמת הזמן (timestamp), דגל ההצלחה וה-payload שעבר דרך אותו שלב. עם המבנה הזה, הביצוע הופך לעץ שניתן לבחון שורה אחר שורה במקום לנחש על סמך הפלט הסופי.

הבאג שחמק

באינטראקציית התמיכה השגויה, ה-trace נראה כך:

  1. השליפה רצה אך לא החזירה מסמכים.
  2. השלב הבא המשיך למרות זאת, והעביר הקשר (context) ריק למודל.
  3. המודל יצר תשובה שמילאה את המידע החסר בשלבים מומצאים.
  4. המערכת החזירה 200 מכיוון שהתהליך (pipeline) לא נתקל בחריגה (exception).

ההזיה לא הייתה פגם במודל השפה עצמו; היא הייתה חוסר ב-guard-rail בין שלבי השליפה לשלבי הגנרציה (generation). הסוכן ענה גם כשלא היה לו שום בסיס (grounding) לתשובתו.

guard-rails פשוטים שעוצרים הזיות

שני שינויים קונקרטיים ביטלו את הבעיה:

  • ביטול במקרה של שליפה ריקה – אם מאגר המסמכים לא מחזיר דבר, הסוכן חייב לענות "לא מצאתי את המידע שאתה צריך" במקום להמשיך לשלב הגנרציה.
  • בדיקת grounding – לאחר שהמודל מייצר תגובה, יש לוודא שכל טענה עובדתית מופיעה בתוכן שנשלף. אם הבדיקה נכשלת, יש לדחות את התשובה ולעבור לתגובת "לא ניתן לענות".

תהליך עבודה מעשי לניפוי שגיאות מהיר יותר

  1. עקבו אחר כל קריאה פנימית – הטמיעו (instrument) בתוך הסוכן מנגנון כך שכל שליפה, הסקה של מודל ושימוש בכלי יכתבו שורה ללוג קבוע.
  2. שמרו ריצות שנכשלו – אחסנו את ה-trace המלא של כל אינטראקציה שהמשתמש מדווח עליה כשגויה. מחיקתם כדי לחסוך בשטח אחסון מסתירה את הנתונים הדרושים למציאת רגרסיות.
  3. תייגו ריצות עם מידע על גרסה – כללו את מזהה הגרסה וכל מצב של feature-flag בכל שורת trace. זה מאפשר לכם לקשר באג חדש לשינוי קוד אחרון.
  4. מדדו איכות, לא רק מהירות – הוסיפו מדדים (metrics) שבודקים עד כמה התשובה עוקבת אחר ההוראה ונשארת מבוססת (grounded) על התוכן שנשלף. תפוקה גבוהה (throughput) משמעותה מעט אם התשובות שגויות.
  5. סקרו כשלים מדי יום – סקירה קצרה וקבועה של כשלים שנשמרו חושפת לעיתים קרובות דפוסים (למשל, סוג מסוים של שאילתה שמחזיר באופן עקבי שליפות ריקות) לפני שהם משפיעים על משתמשים רבים.

על ידי הפיכת הסטטוס מ"ירוק" ל"מאומת", צוותים יכולים לתפוס הזיות בשלב מוקדם ולשמור על חווית משתמש אמינה.

המחיר של התעלמות מכשלים פנימיים

כאשר לוחות בקרה מדווחים על הצלחה בשכבת ה-HTTP בלבד, ארגונים פורסים סוכנים שנראים אמינים אך מספקים הנחיות שגויות באופן קבוע.

מה כדאי לעקוב אחריו בהמשך

עד שדברים אלו יהפכו לנפוצים, הגישה הבטוחה ביותר היא להתייחס לכל פעולה פנימית ככזו שניתן לצפות בה ולהיכשל מהר כאשר חסרות הראיות.

בשורה התחתונה: לוח בקרה ירוק אומר לך שהתשתית עובדת; הוא אינו מבטיח שהתשובה נכונה. באמצעות מעקב אחר כל שליפה, קריאה למודל ובדיקת מנגנוני הגנה, אתה הופך הזיות נסתרות לכשלים גלויים שניתן לתקן לפני שהם מגיעים למשתמש.