Code RAG: אינדוקס קוד עם Tree-sitter ו-AST

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

תחומי פיתוח AI

שאלות נפוצות

העבודות האחרונות

  • פיתוח אפליקציית ווב עבור FEEDME
    פיתוח אפליקציית ווב עבור FEEDME
    1365
  • פיתוח חנות מקוונת לחברת FURNORO
    פיתוח חנות מקוונת לחברת FURNORO
    1330
  • עיצוב לוגו לחברת B2B Advance
    עיצוב לוגו לחברת B2B Advance
    776
  • פיתוח אפליקציית ווב עבור Enviok
    פיתוח אפליקציית ווב עבור Enviok
    1077
  • פיתוח לוגו חברת AIDER
    פיתוח לוגו חברת AIDER
    1022
  • פיתוח CRM עבור Chasseurs
    פיתוח CRM עבור Chasseurs
    1113

אינדוקס מאגר הקוד: מדוע חיפוש לפי קוד אינו עובד

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

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

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

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

מה נותן ניתוח התחביר: אפקט מדיד

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

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

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

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

דרכי ניתוח קוד: השוואת גישות

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

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

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

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

שלבי הטמעה: מביקורת מאגרים ועד מסירה

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

רצף העבודות

  1. ביקורת מאגרים. סופרים מספר מאגרים, שפות ונפח קוד, מפרידים ספריות מחוללות וספריות ספק, מסמנים ענפים נטושים: זבל באינדקס יוצר התראות שווא.
  2. תכנון סכימת הניתוח. קובעים את יחידת המשמעות — פונקציה, מחלקה, מודול. החיתוך נעשה לפי עץ התחביר תוך שמירת קשרים בין קריאות, ולא לפי מספר שורות קבוע.
  3. בניית האינדקס. בנייה ראשונית על פני כל מאגר הקוד עם הצמדה לקומיט: כל תוצאה ניתנת להתאמה לגרסת קוד מסוימת.
  4. הגדרת עדכון. האינדקס נבנה מחדש לפי אירוע במערכת בקרת הגרסאות — נקודתית, לפי קבצים שהשתנו, ולא במלואו.
  5. מדידות איכות. מכינים סט שאלות ייחוס למאגר הקוד, מחשבים את שיעור הפגיעות במודול הנכון ואת שלמות ההקשר בתשובה.
  6. הפרדת הרשאות גישה. ההרשאות עוברות בירושה מהמאגרים ומהענפים: החיפוש אינו מחזיר קוד שאסור לעובד לראות.
  7. מסירת תיעוד. מוסרים תקנוני עדכון, סכימת ניתוח ומטריקות שלפיהן הצוות עצמו שולט באיכות האינדקס.

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

ארטיפקטים של הטמעה: סכימות, כללי גישה, תקנונים

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

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

מקרה בוחן: חיפוש במונורפו לפני ואחרי

מונורפו עם כמה אלפי חבילות ועשרות מיליוני שורות: תשובה לשאלה «היכן זה מוגדר ומי קורא לזה» ארכה למהנדס עד שבע דקות — עם מעבר בין תיקיות, קריאה של ישויות דומות בשם ובדיקה ידנית.

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

מה שינינו

הוספנו ניתוח מבנה דרך Tree-sitter: מעבר על קבצים, בניית עץ ניתוח ואינדקס ישויות — מודולים, מחלקות, מתודות, קריאות וייבוא עם הצמדה לטווחי שורות. השאילתה רצה לא על טקסט, אלא על גרף קשרים: «מי קורא למתודה הזו», «היכן ממומשק הממשק», «מה יושפע משינוי חתימה». אינדוקס מחדש הוא אינקרמנטלי: אחרי קומיט מתעדכן רק הענף הפגוע בעץ, ולא נדרש מעבר מלא.

מטריקות לפני ואחרי

מטריקה לפני אחרי
זמן למצוא את הקטע הדרוש כ-7 דקות כ-40 שניות
התאמות רלוונטיות בחמישייה המובילה 31% 82%
שגיאות «הפניה למתודה שלא קיימת» כ-35 בשבוע בודדות

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

במה נבדל אינדוקס אינקרמנטלי מבנייה מחדש מלאה?

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

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

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

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

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

ספקות טכניים לפני תחילת הפרויקט

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

האינדקס משותף — האם לא יקרה שקוד ידלוף בין צוותים?

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

מה קורה בעת כתיבה מחדש של ההיסטוריה ודחיפה בכפייה של ענף?

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

יש לנו כמה שפות ומונורפו גדול — האם זה יעמוד בכך?

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

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

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

נדון במשימה: נחזור עם הערכה ותוכנית

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

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

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

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

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