AST + AI — שימוש ב-Abstract Syntax Trees עם Coding Agents

מאת צוות מדיה דיל · 09.08.2026 · AI · 7 דק׳

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

סוכן AI מקבל בקשה: "שנה את שם הפרמטר userId ל-accountId בכל מקום שהפונקציה הזו נקראת". גישה נאיבית מבוססת חיפוש-והחלפה טקסטואלי (find-and-replace) עלולה לפגוע במחרוזת בתוך לוג, בהערה בקוד, או במשתנה אחר שבמקרה נקרא באותו שם בהקשר אחר לגמרי. גישה שמבינה את מבנה הקוד - לא רק את הטקסט שלו - יודעת בדיוק אילו אזכורים הם אכן קריאות לאותו פרמטר, ואילו לא. זה בדיוק התפקיד של Abstract Syntax Tree (AST) בעולם של סוכני קוד: להפוך "מחרוזת של תווים" ל"מבנה שיש לו משמעות תחבירית מוגדרת", ולתת למודל AI כלים לפעול על המשמעות הזו ולא רק על הטקסט הגולמי.

מה זה AST, בקצרה טכנית

חשוב להבדיל בין שלושה ייצוגים שלעיתים מתבלבלים ביניהם: Tokens (רצף המילים והסימנים הגולמי לאחר lexing, בלי מבנה), Parse Tree (עץ מלא שכולל כל פרט תחבירי כולל סוגריים ופסיקים), ו-AST עצמו שהוא גרסה "מזוקקת" של עץ הפרסינג - משמיט פרטים תחביריים לא חיוניים ומשאיר רק את המבנה הסמנטי הרלוונטי. ה-AST הוא הרובד שמודלים וכלי ניתוח בפועל עובדים עליו, כי הוא מאזן בין דיוק מלא לבין קומפקטיות שמאפשרת עיבוד מהיר.

AST הוא ייצוג עצי של קוד מקור, שבו כל צומת מייצג קונסטרוקט תחבירי - הצהרת פונקציה, לולאה, קריאה לפונקציה, ביטוי חיבור. פרסר (parser) הופך טקסט קוד למבנה הזה על ידי ניתוח לפי דקדוק השפה. לדוגמה, הביטוי const total = price * quantity; הופך לעץ עם צומת VariableDeclaration, שמכיל צומת Identifier בשם "total" וצומת BinaryExpression עם אופרטור "*" ושני Identifiers. ההבדל המהותי מטיפול טקסטואלי הוא שה-AST "יודע" שה-total הזה הוא הצהרת משתנה, בעוד אזכור אחר של אותה מחרוזת "total" בתוך הערה או מחרוזת טקסט הוא לא אותו דבר מבחינה תחבירית, גם אם הוא זהה מבחינה מחרוזתית.

למה סוכני קוד צריכים AST ולא רק Regex

עריכת קוד מבוססת Regex או חיפוש-והחלפה עובדת טוב למקרים פשוטים, אבל נשברת בקנה מידה: היא לא מבדילה בין scope (משתנה מקומי לעומת גלובלי עם אותו שם), לא מבינה shadowing, ולא יודעת להבדיל בין קריאה לפונקציה להגדרתה. AST פותר את כל אלה כי הוא נותן scope-awareness אמיתי - אפשר לשאול "כל האזכורים של המשתנה הזה בתוך ה-scope הנוכחי בלבד", לא "כל מקום שהמחרוזת הזו מופיעה בקובץ". זה הבסיס הטכני שעליו נבנות יכולות refactoring אמינות בכלי IDE מסורתיים, וכיום גם ביכולות של סוכני קוד אוטונומיים שצריכים לבצע שינויים מדויקים בלי לשבור קוד שלא קשור.

שימושים מרכזיים ב-AST בהקשר של AI

  • עריכה מדויקת (structural editing) - במקום להחליף טקסט, הסוכן מזהה את הצומת המדויק בעץ ומחליף אותו, תוך שמירה על תחביר תקין מובטחת.
  • בניית אינדקס סמלים - מעבר על ה-AST של כל קובץ מפיק רשימה מדויקת של פונקציות, מחלקות ומשתנים גלובליים, הבסיס למיפוי ריפו.
  • ניתוח סטטי לפני הרצה - זיהוי שגיאות תחביר, משתנים לא בשימוש, או קריאה לפונקציה עם מספר ארגומנטים שגוי - עוד לפני שהקוד רץ בכלל.
  • חילוץ chunks בעלי משמעות - לצורך chunking ו-embeddings, גבולות ה-AST מבטיחים שכל chunk הוא יחידה שלמה (פונקציה, מחלקה) ולא קטע תלוש.
  • Diff סמנטי - השוואה בין שתי גרסאות קוד ברמת המבנה, לא רק ברמת שורות טקסט - כך אפשר להבדיל בין "שינוי אמיתי בלוגיקה" ל"שינוי פורמט בלבד" (רווחים, סדר imports).

איך זה עובד בפועל - Tree-sitter כדוגמה

ספריות כמו Tree-sitter הפכו לתקן דה-פקטו ליצירת AST מהיר ואמין לתמיכה רב-שפתית, במיוחד בכלים שמיועדים לעבוד על ריפואים אמיתיים בזמן אמת. Tree-sitter בונה עץ פרסינג incremental - כשקובץ משתנה, הוא לא צריך לפרסר את הקובץ כולו מחדש, אלא רק את החלק שהשתנה, מה שהופך אותו למתאים במיוחד לשילוב עם עורכי קוד וסוכני AI שצריכים תגובה מהירה. פלט הפרסר הוא עץ שאפשר לשאול (query) בשפת query ייעודית, למשל "מצא את כל צמתי function_declaration שיש להם פרמטר בשם userId".

(function_declaration
  parameters: (formal_parameters
    (identifier) @param (#eq? @param "userId"))
  body: (statement_block) @body)

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

AST ואימות (Validation) לפני הגשה

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

AST כשכבת בטיחות ל-Tool Calling

כשסוכן קוד פועל דרך function calling מובנה, ה-AST יכול לשמש גם שכבת בקרה על הפלט של המודל עצמו. לפני שקריאה לכלי edit_file מתבצעת בפועל, אפשר לוודא שהטקסט שהמודל ביקש להחליף (old_text) אכן תואם צומת AST קיים ובעל משמעות ברורה - ולא סתם מחרוזת מקרית שתפגע במקום הלא נכון. זו שכבת אימות זולה שרצה במילישניות ותופסת קטגוריה שלמה של טעויות לפני שהן הופכות לשינוי בפועל בקובץ, עוד לפני שמגיעים לשלב היקר של הרצת בדיקות.

Trade-off: מהירות מול כיסוי רב-שפתי

לכל שפת תכנות יש דקדוק משלה, ולכן AST הוא תמיד ספציפי לשפה - אין "עץ אוניברסלי" שמכסה את כולן. בפרויקטים פוליגלוט (שירות אחד ב-Python, אחר ב-Go, פרונט ב-TypeScript) צריך פרסר נפרד לכל שפה, ולעיתים איכות התמיכה שונה בין שפות - שפות פופולריות כמו JavaScript ו-Python נהנות מפרסרים בוגרים ומדויקים, בעוד שפות נישתיות יותר עלולות לסבול מכיסוי חלקי. זה שיקול מעשי שצריך לקחת בחשבון: מערכת שמסתמכת רק על AST לכל הפעולות עלולה "להתעוור" בשפה שהתמיכה בה חלשה, ולכן כדאי לשמור על נתיב גיבוי מבוסס טקסט/regex לשפות שוליות בארגון.

AST כשכבת ביניים בין קוד לגרפים ולמודלים

מעבר לשימוש הישיר בעריכה ואימות, ה-AST משמש גם כשכבת ביניים שממנה נגזרים ייצוגים נוספים שסוכני AI צורכים. גרף התלויות שתואר במיפוי ריפו נבנה במעשה על בסיס מעבר על עצי ה-AST של כל קבצי הריפו וחילוץ צמתי import/export מהם. באופן דומה, אינדקס הסמלים (symbols) שממפה כל פונקציה ומחלקה למיקומה המדויק, מבוסס על מעבר שיטתי על העץ וזיהוי צמתי הצהרה. אפילו שכבת ה-embeddings שמשמשת לחיפוש סמנטי נהנית מ-AST: חיתוך קוד ל-chunks לפי גבולות פונקציה ומחלקה, במקום לפי מספר שורות שרירותי, מבטיח שכל embedding מייצג יחידת קוד שלמה ובעלת משמעות. במילים אחרות, AST הוא לא "עוד כלי" אלא תשתית-על שרוב הכלים האחרים בערימת ה-AI-for-code נשענים עליה, במישרין או בעקיפין.

דוגמה מהשטח: Refactoring רחב בבטחה

נניח שצוות מחליט להחליף ספריית HTTP client ישנה בחדשה בכל הריפו - שינוי שנוגע בעשרות קבצים. גישה טקסטואלית תחפש את שם הספריה הישנה ותחליף, אבל תפספס מקרים שבהם השם משמש כ-alias שונה, או תפגע בקוד בתוך הערות ותיעוד שמזכיר את הספרייה הישנה בכוונה (למשל migration guide). גישה מבוססת AST פועלת אחרת: היא מזהה את כל צמתי ה-import שמצביעים על החבילה הישנה, עוברת על כל קריאה לפונקציות מהחבילה בתוך ה-scope הרלוונטי בלבד, ומחליפה כל אחת בקריאה המקבילה בחבילה החדשה - תוך שמירה על סדר הארגומנטים והתאמת חתימות שונות במקרה הצורך. סוכן קוד שמבצע את המשימה הזו יריץ קודם שאילתת AST לאיתור כל האתרים הרלוונטים, יבנה תוכנית שינוי לכל אחד, יבצע את השינויים אחד-אחד, ורק בסוף יריץ את סוויטת הבדיקות המלאה לאימות - במקום לנחש ולתקן בדיעבד.

טעויות נפוצות

  • שימוש ב-Regex לעריכות שדורשות scope-awareness - מוביל לבאגים סמויים שקשה לתפוס בבדיקת קוד רגילה.
  • התעלמות מהבדלי גרסאות שפה - תחביר חדש (למשל optional chaining) עלול לשבור פרסר ישן שלא מכיר אותו.
  • בניית AST מחדש לכל הריפו בכל שינוי - במקום פרסינג incremental, מה שהופך את המערכת לאיטית מדי לשימוש בזמן אמת.
  • הסתמכות רק על AST בלי הרצה בפועל - קוד יכול להיות תקין תחבירית אך שגוי לוגית; AST הוא שכבת סינון ראשונה, לא תחליף לבדיקות.

שאלות נפוצות

האם AST מחליף את הצורך בבדיקות (Tests)?

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

מה ההבדל בין AST ל-Embeddings לחיפוש קוד?

AST נותן מבנה מדויק (תחביר, scope, קשרים) בעוד embeddings נותנים דמיון סמנטי (משמעות, כוונה). שילוב שניהם - כפי שמפורט בחיפוש קוד סמנטי - נותן את התוצאה הטובה ביותר.

האם צריך AST לכל שפה בנפרד?

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

האם AST עוזר גם בזיהוי קוד כפול (duplication)?

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

מה קורה כשהקוד לא תקין תחבירית ואי אפשר לבנות AST שלם?

פרסרים מודרניים כמו Tree-sitter תוכננו להיות error-tolerant - הם ממשיכים לבנות עץ חלקי גם כשיש שגיאת תחביר, ומסמנים את האזור הבעייתי כצומת שגיאה. זה חשוב מאוד לסוכני AI שעובדים על קוד באמצע עריכה, שלעיתים נמצא במצב לא תקין זמנית.

האם כדאי לבנות פרסר AST בעצמי לפרויקט ספציפי?

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

שילוב נכון של ניתוח AST בתשתית פיתוח מבוססת AI הוא הבדל בין סוכן שעורך קוד "בערך נכון" לסוכן שאפשר לסמוך עליו על ריפו production. מדיה דיל בונה תשתיות פיתוח מבוססות AI כחלק מפתרונות ה-AI שלנו - לשיחה אפשר לפנות בוואטסאפ.

תגיות: AST · Abstract Syntax Tree · Coding Agents · Tree-sitter · Structural Editing · Static Analysis · AI

← חזרה לבלוג · צור קשר