קוד שנראה טוב אבל הוא בעצם לא (היוש AI)
בוובינר השבוע אראה איך לעבוד עם סוכני AI בענן. הם מדהימים ויכולים לעשות דברים מאוד יצירתיים אבל חופש הפעולה שלהם עובד הרבה פעמים גם נגדנו. שואלים אותי לפעמים בוובינרים אם צריך לקרוא כל שורה ש AI כותב ואני חושב שהתשובה מורכבת. מצד אחד אי אפשר להכנס "לראש של AI" כי זה מכונה וקל מאוד לפספס דברים קטנים שהוא עושה לא בסדר. מצד שני רוב הפעמים ה AI משאיר סימנים לטעויות שלו, סימנים שאנחנו יכולים לראות בקוד ש AI כותב ושעוזרים לנו להיות יותר חשדנים לגבי התוצאה. דוגמה? ברור בשביל זה באנו.
כאן יש PR לפיצ'ר שביקשתי מקופיילוט לכתוב על פרויקט קוד פתוח שלי שנקרא לנגלטס. ביקשתי שיבנה מסך נגן שיראה שיר מיוטיוב ולידו את המילים, התרגום, טבלה עם אוצר מילים מהשיר ואפשרות להוריד את אוצר המלים לקובץ CSV: https://github.com/ynonp/langlets-rails/pull/71
הוא עשה עבודה מדהימה - מתיאור של כמה מילים הוא יצר לי מסכים עובדים בתוך הפרויקט. אני יכול למשוך את ה PR ולראות את זה לייב על המחשב שלי. למתכנת אנושי זה היה לוקח יותר זמן. לי, שאני מכיר ריילס טוב ומכיר את הפרויקט טוב זה היה לוקח יותר זמן. חלק מזה זה באמת הכח של AI לכתוב קוד מהר וחלק אחר מזה זה חוסר היכולת של AI לקבל החלטות טובות. בואו נקרא את ה PR לראות מה עובד ומה שבור שם.
1. הצגת הנגן
הקובץ הראשון שה AI כתב נקרא play_controller.rb. זה הקובץ שמטפל בהצגת הנגן וכבר פה יש לי בעיה עם השם. השם play_controller אולי מתאים ל URL שביקשתי אבל הוא לא שם אינפורמטיבי ולא ברור ממנו מה המטרה של הקונטרולר. שם טוב יותר היה StandalonePlayerController - ואכן זה השם שהוא יבחר לקובץ ה JavaScript המקביל בהמשך ה PR. תכף נגיע לזה.
כבר אחרי שליפת הרשומה מבסיס הנתונים אנחנו רואים את הבעיה הראשונה: אם הרשומה לא נמצאה קופיילוט החליט לענות ב Redirect למסך הבית. אני לא ביקשתי את זה ונתיבים אחרים במערכת זורקים Exception במצב כזה. נשים לב שהרבה פעמים החלטות כאלה אפשר לשפר באמצעות יצירת קבצי "חוקים ל AI". הבלוק הבא הוא לדעתי האתגר הכי גדול שלנו בקוד ש AI יוצר ואני מדביק אותו כאן:
# Get the medium from the first lesson to access phrases
first_lesson = @course.lessons.first
unless first_lesson
redirect_to root_path, alert: "No lessons found for this course"
return
end
@medium = first_lesson.medium
זה בדיוק קוד שנראה טוב אבל הוא בעצם לא. מצד אחד הוא מבין שבשביל להגיע מקורס לוידאו הוא צריך לעבור דרך lessons, לקחת את השעור הראשון ושם לגשת לשדה medium. הוא גם מבין שיכול להיות שלא יהיו שעורים בקורס ואז אי אפשר להציג את הנגן. הקוד עובד, מטפל בכל המקרים וגם ברור ולכן נראה טוב. אבל מפתחי ריילס יודעים שלא כדאי להעמיס לוגיקה על הקונטרולר כי אי אפשר להשתמש בה שוב במקומות אחרים במערכת ולכן מפתחי ריילס טובים היו מכניסים את הבלוק הזה לפונקציה חדשה של course. "אה, אני רואה שצריך להגיע מקורס לוידאו אז אני אכתוב לזה פונקציה לגישה קלה" זה משהו ש AI כמעט ולא אומר.
דבר שני ששבור כאן זה השימוש ב @. בריילס אנחנו מסמנים ב @ משתנים שאנחנו רוצים לגשת אליהם מקוד התבנית ושישפיעו בהמשך על ה HTML שיווצר. למשתנה @medium ב PR אף אחד לא ניגש אחרי הקונטרולר ולכן נכון יותר לסמן אותו כמשתנה פנימי של הפונקציה. הפונקציה מסתיימת בשורות:
# Prepare vocabulary data - all token translations from the medium
@vocabulary = @phrases.flat_map(&:token_translations).uniq { |tt| [tt.original_text.downcase, tt.translation.downcase] }
שהיו עובדות הרבה יותר טוב בתור מתודה חדשה של Medium או באיזשהו Service Object כך שאפשר היה לבדוק אותן. עם שם טוב לפונקציה גם לא היינו צריכים את ההערה.
2. שמירת אוצר המלים כ CSV
הפונקציה השניה שה AI בנה נקראת vocabulary_csv. גם פה בעזרת Rules File אפשר היה לשכנע אותו ליצור קונטרולר חדש בשביל ה CSV הזה עם שם טוב כמו StandalonePlayerCsvController אבל בסדר גם רוב המפתחים האנושיים שאני מכיר לא היו מוסיפים קובץ קונטרולר חדש רק בשביל "הורדה כ CSV". מה שיותר מטריד זה שהפונקציה השניה מכילה הרבה קוד שנמצא כבר בפונקציה הראשונה. ברור לגמרי למה ה AI עושה את זה ושסוכן AI לעתים נדירות מצליח לראות אבסטרקציות. זאת הסיבה שאנחנו חייבים לעבור על הקוד ולתקן.
3. קובץ ה JavaScript
את ה JavaScript של הפיצ'ר הסוכן כתב בתוך קובץ בשם standalone_player_controller.js. זה שם הרבה יותר טוב ממה שהוא בחר לרובי ונשאר לשאול רק למה צריך כמה שמות לאותו פיצ'ר. מבחינת הקוד עיקר קוד ה JavaScript מתעסק בהגדרת קלאסים לאלמנטים ב HTML לדוגמה:
// Update tab styles
this.lyricsTabTarget.classList.add("bg-slate-700/50", "text-white", "border-b-2", "border-blue-500")
this.lyricsTabTarget.classList.remove("text-slate-400")
מפתחי ריילס יודעים שלסטימולוס (תשתית ה JavaScript של ריילס) יש מנגנון מובנה לשמירת שמות הקלאסים האלה ב HTML בשביל לא ללכלך את ה JavaScript אבל AI עדיין לא למד את זה. אפילו אם הוא לא מכיר מנגנון מובנה היינו מצפים למצוא את רשימות הקלאסים האלה במקום אחד מרכזי בקובץ, אבל החזרתיות היא לצערי אחד המאפיינים של קוד AI ושוב משהו שאנחנו בני אדם נדרשים לנקות אחריו.
בעיה אחרונה בקובץ הזה קשורה שוב לקבלת החלטות - הבחירה להגדיר את הטאב הפעיל בתור סטייט נסתר ב DOM ולא לשמור אותו כחלק מה URL אומרת שמשתמשים שירעננו את העמוד יחזרו תמיד לטאב ברירת המחדל. אני לא בטוח שזאת ההתנהגות שהייתי בוחר ובכל מקרה זה משהו שצריך להתלבט עליו.
4. קובץ ה HTML
לריילס יש מנגנון שנקרא Layouts שמאפשר להגדיר תבנית כללית לאתר וכל דף ישתול את התוכן שלו בתוך אותה תבנית כללית. מיותר לציין שב PR שקיבלתי ה AI כתב את העמוד המלא בתוך הקובץ show.html.erb ופשוט העתיק את ה Layout המלא של העמוד.
5. קובץ הבדיקה
לסיום אפשר לשמוח שה AI כתב בדיקה אבל כדאי לשים לב לכמה סימנים מדאיגים:
הבדיקה לא אפקטיבית. באמת. הוא בודק דברים מאוד פשוטים ואם משהו יישבר בעמוד הבדיקות שהוא כתב לא יעלו על זה.
בקובץ הבדיקה הוא רשם את התחילית:
self.use_transactional_tests = true
# Clear fixtures for this test class
def self.fixture_table_names
[]
end
שתי אופציות שאנחנו לא רגילים למצוא בקוד בדיקה. האופציה הראשונה לפעמים נמצאת בקובץ ההגדרות של הבדיקות והמנגנון השני כמעט לא נמצא בשימוש ואין לו צורך גם בבדיקה הזו. הנטיה של AI להכניס קוד מוזר שאנחנו לא צריכים ורבים מאתנו לא מבינים היא בעיה גדולה בקוד ש AI מייצר ואני מאוד ממליץ להזהר ממנה ספציפית ולמחוק כל שורה ש AI כותב שאתם לא הייתם כותבים לבד.
ובכל זאת? העובדה שיש לי מנגנון שעובד לפיצ'ר שרציתי כולל HTML, CSS ו JavaScript וקוד צד שרת ובסיס לבדיקה תוך חצי שעה היא לא פחות ממדהימה. בעבודה רגילה על פרויקט בנקודה הזאת אני לוקח את הקוד אליי, מנקה, מארגן אבסטרקציות ומתקן קבצי Rules כדי שפעם הבאה ה AI ייצור קוד שאני יותר אוהב. למרות שהייתי צריך לקרוא כל שורה שהוא כתב למדתי מהמשחק והתאמנתי על המיומנות החשובה לעתיד של המקצוע - חשיבה על מערכת, יצירת אבסטרקציות, בניית תשתית להתקדמות מהירה.
רוצים לראות לייב איך אני שולח את ה AI למשימות ולעבור איתי יחד על התוצאות שלו ועל מהלך הפיתוח? לא מסובך צריך רק להצטרף לקבוצת הוובינרים ואני שולח לכם במייל את הקישור לזום. נרשמים כאן: