3.6 אימות הרצאה
כל ההרצאות הקודמות בפרק הזה - הבנת קוד, כתיבת פיצ'ר, דיבוג, git, בדיקות ו-refactoring - חוזרות שוב ושוב לאותו רעיון: לא מקבלים תוצאה מהagent בלי לבדוק אותה. ההרצאה הזו הופכת את הרעיון החוזר הזה לנושא בפני עצמו, כי הוא המיומנות הכי חשובה בכל הקורס. הagent AI טוב מאוד בלייצר קוד שנראה נכון, מוסבר בביטחון, ומגיע עם משפט כמו "הפיצ'ר מוכן ועובד". המשפט הזה הוא טענה, לא עובדה. התפקיד שלכם, בכל אינטראקציה, הוא לדרוש הוכחה לפני שאתם מאמינים לו. בהרצאה הזו נבנה את המשמעת הזו: למה "done" לא אומר כלום, איך קוד משכנע-אך-שגוי נוצר בכלל, ואיך מריצים, קוראים ודורשים ראיות בכל שלב - עד לרשימת תיוג שאפשר להשתמש בה בכל פעם שagent אומר שהוא סיים.
למה "עשיתי את זה" לא אומר כלום - why "done" means nothing¶
כשagent מסיים משימה, הוא כמעט תמיד יסכם אותה בביטחון: "הוספתי את הפונקציה, עדכנתי את הבדיקות, הכל עובד". הבעיה היא שהמשפט הזה נוצר באותו אופן שבו נוצרת כל תשובה של מודל שפה - כניסיון לחזות מה תשובה טובה נראית, לא בהכרח כתוצאה של בדיקה אמיתית שהתבצעה. לפעמים הagent באמת הריץ את הקוד וראה שהוא עובד. לפעמים הוא רק כתב קוד שנראה כאילו הוא אמור לעבוד, והמשפט "הכל עובד" הוא ניחוש סביר, לא דיווח על עובדה.
ההבדל בין השניים לא ניכר מהניסוח - שתי התשובות נשמעות זהות בביטחון שלהן. הדרך היחידה להבדיל היא לדרוש ראיה: פלט של הרצה אמיתית, תוצאת בדיקה שרצה בפועל, diff שאפשר לקרוא. "בואו נריץ ונראה" הוא המשפט החשוב ביותר בכל הזרימה הזו.
איך נוצר קוד משכנע אבל שגוי - plausible but wrong¶
חשוב להבין את המנגנון, לא רק לזכור את הכלל. מודל שפה מייצר טקסט (וקוד הוא טקסט) על ידי חיזוי הרצף הכי סביר בהינתן הcontext. כשהcontext כולל תיאור ברור של מה שצריך לקרות, הקוד שנוצר בדרך כלל נכון - כי "קוד שממש עושה את זה" הוא ההמשך הכי סביר. אבל יש כמה מצבים שבהם הביטחון של המודל לא תואם לנכונות בפועל:
- פרטים שלא נבדקו בפועל: הagent "יודע" איך ספרייה מסוימת עובדת מהאימון שלו, אבל הגרסה שמותקנת אצלכם שונה, וה-API השתנה. הקוד ייכתב בביטחון מלא לפי הגרסה הישנה.
- הנחות שלא אומתו מול הקוד האמיתי: הagent מניח שפונקציה מסוימת מחזירה
nullכשלא נמצא ערך, בלי לבדוק את המימוש בפועל - וזה נכון בהרבה ספריות, אבל לא בכולן. - אינטראקציה בין חלקים שלא נבדקה יחד: כל פונקציה בנפרד נכונה, אבל השילוב ביניהן - למשל טיפוסי נתונים שלא תואמים בדיוק - נשבר רק בזמן ריצה.
- טעויות שקטות בגבולות:
>במקום>=, אינדקס שמתחיל מ-0 מול 1, timezone שלא הובא בחשבון. אלה בדיוק סוגי הבאגים שקוד "נראה נכון" בקריאה ראשונה אך נכשל בהרצה.
בכל המקרים האלה, הקוד שנוצר נראה סביר לחלוטין - יש לו מבנה נכון, שמות משתנים הגיוניים, לוגיקה שנשמעת נכונה בקריאה. רק הרצה בפועל, או קריאה קפדנית מול המקור האמיתי (לא מהזיכרון), חושפת את הפער.
הרצת הקוד בפועל - run it, don't imagine it¶
הצעד הראשון והבסיסי ביותר: אם אפשר להריץ - מריצים. לא "נראה שזה אמור לעבוד", אלא הרצה ממשית עם פלט אמיתי.
> Run the function applyDiscountCode with a real discount code
that exists in the DB, and show me the exact output it returns.
$ node -e "
const { applyDiscountCode } = require('./dist/services/discountService');
applyDiscountCode('SUMMER10', 200).then(console.log);
"
{ originalPrice: 200, discountedPrice: 180, discountApplied: 10 }
זו הוכחה. "אני מאמין שזה יחזיר הנחה של 10%" היא לא הוכחה, גם אם היא נכתבת באותו ביטחון. אותו עיקרון חל על פיצ'רים שדורשים שרת רץ, interface, או תהליך ידני - כשלא ניתן להריץ הכל אוטומטית, לפחות מריצים את מה שניתן, ומבקשים מהagent להסביר בדיוק מה הוא לא בדק ולמה:
> You're saying the feature is "ready". Tell me explicitly: what
did you actually check (running it, automated tests) and what
remains untested that I need to check manually.
השאלה הזו מכריחה הבחנה בין "נבדק" ל"סביר שעובד", וזו הבחנה שסוכנים לא תמיד עושים מיוזמתם.
כשמדובר בפיצ'ר שדורש בדיקה ידנית - למשל interface משתמש, אינטגרציה עם שירות חיצוני, או תרחיש שדורש חשבון משתמש אמיתי - אי אפשר להסתמך על הרצה אוטומטית בלבד. במקרים כאלה, הדרישה למינימום לא משתנה: מבקשים מהagent הוראות מדויקות לבדיקה ידנית, ומבצעים אותה בעצמכם לפני שמאשרים שהפיצ'ר מוכן.
> Give me an exact list of steps for manually testing this feature
in the browser - what to click, what to expect to see at each
step, and what error I'd expect to see if something is broken.
רשימה כזו הופכת בדיקה ידנית מ"אני חושב שזה עובד" למשהו שיטתי וניתן לחזרה - וגם שימושית להעביר לחבר צוות אחר שיבדוק בעצמו, בלי לסמוך רק על העין שלכם.
קריאת ה-diff - read the diff, every time¶
הרצה מוכיחה שתרחיש ספציפי עובד. קריאת diff מוכיחה שאתם יודעים מה בדיוק השתנה - כולל שינויים שלא ביקשתם, קבצים שלא ציפיתם שייגעו בהם, או הסרה בשוגג של קוד שהיה שם מסיבה.
קריאת diff היא לא פורמליות - זו הדרך שבה מגלים שהagent, כדי לתקן דבר קטן, שינה גם קובץ קונפיגורציה גלובלי, או מחק שורה שנראתה לו "מיותרת" אך בפועל טיפלה במקרה קצה נדיר. ככל שהשינוי קטן ומובן יותר, קל יותר לקרוא אותו במלואו - וזו עוד סיבה לעבוד בצעדים קטנים (כפי שראינו בהרצאות 3.2 ו-3.5) במקום לתת לagent לרוץ הרחק לפני שעוצרים לבדוק.
דרישת ראיות - demand evidence, not confidence¶
מעבר להרצה ול-diff, יש מקומות שדורשים ראיה ספציפית לפי סוג הטענה:
| הטענה של הagent | הראיה שדורשים |
|---|---|
| "הבדיקות עוברות" | פלט מלא של הרצת הבדיקות, לא סיכום מילולי |
| "תיקנתי את הבאג" | שחזור שהיה נכשל, ועכשיו עובר (ראו הרצאה 3.3) |
| "זה לא שובר כלום אחר" | הרצת כל חבילת הבדיקות, לא רק הבדיקות החדשות |
| "הפונקציה הזו לא נקראת משום מקום אחר" | חיפוש ממצה בכל הריפו, לא הערכה |
| "זה תואם את הקונבנציה של הפרויקט" | דוגמה קונקרטית מהקוד הקיים שמראה את אותה קונבנציה |
| "הנתונים נשמרו נכון ב-DB" | שאילתה בפועל שמראה את הרשומה שנוצרה |
הרעיון המשותף: כל טענה כללית ("עובד", "לא שובר", "תואם") אפשר לתרגם לבקשת ראיה קונקרטית וממוקדת. ברגע שתופסים את ההרגל הזה, קל להבחין - הagent תמיד יכול לספק את הראיה אם היא באמת נכונה, ולפעמים כשמנסים לספק אותה מתגלה שהטענה המקורית לא הייתה מדויקת.
מתי אתם הכי פגיעים - overconfidence after a long session¶
יש רגע ספציפי שבו המשמעת הזו נשברת הכי הרבה: אחרי session ארוך ומוצלח. עברתם שעה עם הagent, הוא פתר כמה בעיות קשות, כתב קוד טוב, הרים בדיקות שעברו - ואז מגיעה הבקשה האחרונה והקטנה יחסית, ובדיוק שם מוותרים על הרגלי האימות כי "הוא כבר הוכיח את עצמו הפעם". זו בדיוק המלכודת. הביצועים של הagent לא מצטברים - כל תשובה חדשה היא הזדמנות חדשה לטעות, בלי קשר לכמה תשובות נכונות קדמו לה.
תופעה דומה קורית כשהcontext של השיחה מתמלא. ככל שהשיחה ארוכה יותר, כך גדל הסיכוי שהagent "שוכח" פרט שהוזכר מוקדם, או מבלבל בין שני קטעי קוד דומים ששניהם הוזכרו בשיחה. אם שיחה נמשכת הרבה זמן וכוללת הרבה קבצים, שווה מדי פעם לעצור ולבקש מהagent לסכם במפורש את המצב הנוכחי, כדי לוודא שההבנה שלו עדיין מדויקת:
> Before we continue, summarize for me: what exactly changed so far
in this session, and what's still open or unfinished.
אם הסיכום לא תואם את מה שאתם זוכרים שקרה, זה סימן שהcontext החל להתפורר, ושווה לשקול לפתוח שיחה חדשה עם סיכום נקי במקום להמשיך על בסיס context לא אמין (נרחיב על ניהול context בפרק הבא).
איתות אזהרה נוסף הוא כשהagent מתחיל להשתמש בניסוחים גורפים במיוחד - "זה בוודאות יעבוד", "אין שום בעיה כאן", "זה הפתרון היחיד". שפה בטוחה מדי, בלי הסתייגות, היא בדיוק המקום שבו כדאי להאט ולבדוק, כי בעיות אמיתיות נוטות להיות מלוות בניואנס ("זה אמור לעבוד במקרה הרגיל, אבל לא בדקתי X"), לא בביטחון מוחלט.
רשימת תיוג לאימות עבודת agent - the verification checklist¶
הנה רשימה שכדאי להריץ, לפחות במחשבה, בכל פעם שagent מכריז שמשימה הושלמה:
[ ] I ran the code for real (or ran whatever part of it can be run), not just
read its description
[ ] I read the full diff, line by line, not just the summary
[ ] I ran the entire test suite, not just tests specific to the change
[ ] If there was a bug fix - there's a test that reproduced it before the fix
and passes now
[ ] I checked that there are no changes in files I didn't expect to change
[ ] If the claim touched performance/security/permissions - I asked for
specific evidence, not a general explanation
[ ] I explicitly asked "what wasn't tested" and not just "what was tested"
[ ] If something in the code is unclear to me - I read it myself, I didn't
settle for the agent's explanation
הרשימה הזו לא צריכה לקחת יותר מכמה דקות לרוב המשימות. ההשקעה קטנה ביחס לעלות של לגלות בפרודקשן שמשהו "שהושלם" בכלל לא עבד.
דוגמה: איך אימות תופס בעיה שהייתה עוברת בלי שאלות¶
1. < agent: "I finished adding a cache for calculateShipping's
results. This should improve performance significantly and not
change behavior."
2. > Show me an actual measurement - response time before and
after, not an estimate.
3. < agent runs a benchmark:
Before: ~45ms average
After: ~2ms average (from the cache)
[But...] I noticed while running it that a second call with
identical parameters but a different country returned the same
result from the cache - it looks like the cache key doesn't
include country.
4. > Good catch. Fix the cache key and rerun this specific test -
two different countries with the same weight should return
different results from the cache.
5. < [fix, rerun, output shown demonstrating different results per
country]
השורה הקריטית כאן היא "תראה לי מדידה בפועל". בלעדיה, "שיפרתי ביצועים משמעותית" היה נשמע כמו הצלחה, וה-cache השבור - שהיה מחזיר מחיר משלוח שגוי למדינות שונות - היה מגיע לפרודקשן.
סיכום¶
- "עשיתי את זה" או "זה עובד" הם טענות של הagent, לא עובדות מוכחות - הביטחון בניסוח לא מבדיל בין קוד שנבדק בפועל לקוד שרק נראה נכון.
- קוד משכנע-אך-שגוי נוצר כשהagent מסתמך על ידע לא מעודכן, הנחות שלא אומתו מול הקוד האמיתי, אינטראקציות בין חלקים שלא נבדקו יחד, או טעויות שקטות בגבולות.
- הצעד הראשון תמיד: להריץ את הקוד בפועל ולראות פלט אמיתי, במקום להסתפק בהסבר שהוא "אמור" לעבוד.
- קוראים את ה-diff המלא שורה-שורה בכל פעם - זה חושף שינויים לא צפויים בקבצים שלא ציפיתם שייגעו בהם.
- לכל סוג טענה יש ראיה ספציפית שאפשר לדרוש: פלט בדיקות מלא לטענת "עובר", שחזור-ואז-תיקון לטענת "תוקן", חיפוש ממצה לטענת "לא נקרא משום מקום אחר".
- רשימת תיוג קצרה - הרצה, diff, בדיקות מלאות, שחזור לפני תיקון, "מה לא נבדק" - שווה להפעיל בכל פעם שagent מדווח שמשימה הושלמה.
- הדוגמה בהרצאה הראתה איך בקשת מדידה בפועל תפסה cache שבור לפי מדינה - בעיה שהייתה עוברת חלק בלי שאלות אם היו מסתפקים בהצהרה "שיפרתי ביצועים".
- העיקרון המנחה של כל הפרק: הagent הוא כלי חזק שמאיץ כמעט כל שלב בעבודה, אבל האחריות לוודא שהקוד באמת נכון נשארת, תמיד, אצל המהנדס האנושי.