תשתית בינה מלאכותית: שימוש ב-CI להכוונת אייג׳נטים

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

לא מזמן ישבתי על פרויקט עם מתכנתת שאני עובד איתה על מספר פרויקטים והיא אמרה לי שקשה לעבוד על פרויקט שהוא לא AI Ready.

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

הגישה הנאיבית – לא רעיון טוב

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

  1. עם פחות שגיאות.
  2. עם קוד שבאמת קולע למטרה ולא עושה שטויות.
  3. פחות איטרציות של המודל.
  4. חסכון בזמן ובכסף.

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

אז איך עושים את זה? איך בונים תשתית שנותנת לאייג׳נטים שלנו – ולא משנה אם זה קורסור או קלוד קוד או ווטבר – לעבוד כמו שצריך.

תשתיות CI חזקות – הבסיס להכל

חלק מהדברים שמאד עוזרים לכלי LLM זה התשתיות הותיקות. נכון, אנחנו כבר לא נדרשים לכתוב את התשתיות האלו אבל אנחנו כן נדרשים להגדיר אותן ולהבין מצוין מה הן עושות. מה זה תשתיות CI?

לינטרים

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

בדיקות יחידה

בדיקות unit test היו מקור לפולמוס משמעותי בקרב מתכנתים אבל היום כשכל כך זול לכתוב את הקוד, יש כבר תמימות דעים שכדאי לשים אותן. מעבר לכך שבאמת אפשר למנוע רגרסיות, בדיקות יחידה טובות גם מקילות על מודלי שפה גדולים להבין את הקוד וגם להוסיף לו. בבדיקות יחידה צריך להגדיר גם את הפריימוורק וגם את דרך ההרצה וכן הכיסוי הנדרש. צריך לזכור ש unit זה לא בדיקות מאד bullet proof אבל הן עוזרות במיוחד בעידן החדש והמבלבל הזה.

בדיקות E2E

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

בדיקות נוספות

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

הרוטב הסודי: hooks

הבדיקות האלו צריכות לרוץ אוטומטית כאשר מבצעים פעולות בגיט. למשל לפני קומיטים? שהבדיקות של הלינטרים יעברו. לפני פושים? שכל בדיקות התוכנה יעברו.
למה? מעבר לעובדה שזה נוח, כאשר אנחנו מריצים אייג׳נט, אנחנו פשוט רק צריכים לומר לו שיש hooks וכשהוא עושה פעולות בגיט, שיהיה מוכן לזה. זה הכל. כאשר אנחנו אומרים לסוכן "תוודא שהקוד תקין", אנחנו מסתמכים על כושר השיפוט שלו, שעלול להשתנות או להישחק בקונטקסט נרקב. לעומת זאת, הטמעת הבדיקות בתוך התהליכים הבסיסיים של שמירת העבודה דומה להצבת שומר בשער של מבצר. הסוכן אינו צריך להחליט האם לציית לכללים, אלא הוא פשוט לא יכול להתקדם הלאה מבלי לעמוד בהם. לא עובר לינט? אין קומיט. לא עובר טסטים? אין פוש. הסביבה עצמה הופכת למורה המעניק משוב מיידי ובלתי מתפשר. עבור סוכן אוטונומי, התקלות ב git hook שנכשל הן מידע חשוב כי אז יש אילוץ שמכריח אותו לבצע הערכה מחדש של הפתרון שלו בזמן אמת, בדיוק בנקודה שבה הטעות נוצרה.

ההגיון מאחורי ההוקים וה-CI

אם בעבר דיברנו על ״הפרומפט המושלם״ ו prompt engineering, היום הגישה היא יותר flow enginnering. זו גישה המעבירה את המוקד מהניסיון ליצור את ההנחיה המושלמת לבנייה של מסלול עבודה עם כמה שלבים ויותר מובנה שבו ה-LLM יעבוד יותר טוב. במקום לצפות מהבינה המלאכותית לספק פתרון מדויק בפעולה אחת, ה-flow engineering מפרקת את המשימה לסדרה של צעדים קטנים הכוללים תכנון, הרצת בדיקות וביצוע תיקונים על בסיס משוב חיצוני – שזה בדיוק ההוקים שלנו. וכך יש סביבה שבה המודל נכשל בנקודות זמן ספציפיות וזה מפחית את הסיכוי לטעויות ויוצר איכות. האיכות נובעת מהמבנה הסדור של התהליך.

זה כמובן מתועד היטב וזו לא המצאה שלי.

חלק מכם יגידו: ״מה הבעיה? שים את זה בהוראות של הפרויקט!״. אבל בעידן שבו הקונטקסט יקר וגם עודף קונטקסט הוא לא יעיל, כדאי מאד להעביר דברים שגם ככה עושים למקומות דטרמניסטיים כי אז לא בונים על האייג׳נט. כאשר אנחנו מקבעים את חוקי המשחק בתוך התשתית עצמה, אנחנו הופכים את הציפיות שלנו ממלל מופשט שעלול להתפרש לא נכון, למציאות פיזית שהאייג׳נט חייב להתיישר לפיה. במקום להעמיס על חלונית ההקשר הנחיות חוזרות ונשנות לגבי סגנון כתיבה או תקינות לוגית, אנחנו מאפשרים לכלים דטרמניסטיים לתת את המשוב הזה בזמן אמת. בדרך זו, המודל לא צריך "לזכור" לכתוב לפי הכללים. הוא פשוט פועל בתוך מערכת שבה הדרך היחידה להתקדם היא לכתוב קוד איכותי שעובר את הבדיקות. זהו המעבר מניהול של הבטחות לניהול של עובדות, וזה בדיוק מה שמבדיל בין שימוש חובבני בצ'אט לבין הנדסת תוכנה מקצועית בעידן הבינה המלאכותית.

תיעוד

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

פוסטים נוספים שכדאי לקרוא

יסודות בתכנות

איך TCP עובד? מבט מעמיק

הסבר מעמיק מתחת למנוע על איך תקשורת TCP עובדת כולל ניתוח פקטות.

גלילה לראש העמוד