המודל האחרון שלנו, קלוד 3.5 סונט המשודרג, הגיע ל-49% במדד SWE-bench Verified, הערכת ביצועים בהנדסת תוכנה, ובכך עקף את המודל המוביל בתחומו הקודם שהשיג 45%. כתבה זו מסבירה את ה"סוכן" שבנינו סביב המודל, ונועדה לסייע למפתחים להפיק את הביצועים המיטביים מקלוד 3.5 סונט.

SWE-bench הוא מדד ביצועים להערכת AI, הבוחן את יכולתו של מודל להשלים משימות הנדסת תוכנה מעולם האמיתי. ספציפית, הוא בודק כיצד המודל יכול לפתור בעיות GitHub ממאגרי קוד פתוח פופולריים של Python. עבור כל משימה במדד, מודל ה-AI מקבל סביבת פיתוח Python מוכנה ועותק עבודה מקומי (checkout) של המאגר מנקודת הזמן שלפני פתרון הבעיה. המודל נדרש אז להבין, לשנות ולבדוק את הקוד לפני הגשת הפתרון המוצע שלו.

כל פתרון נבדק מול בדיקות היחידה (unit tests) האמיתיות מבקשת המשיכה (pull request) שסגרה את בעיית ה-GitHub המקורית. בדיקה זו בוחנת האם מודל ה-AI הצליח להשיג את אותה פונקציונליות כמו המפתח האנושי המקורי שיצר את בקשת המשיכה.

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

קיימים מדדי ביצועים רבים אחרים ליכולות הקידוד של מודלי שפה גדולים (LLM), אך SWE-bench צבר פופולריות מכמה סיבות:

  1. הוא משתמש במשימות הנדסיות אמיתיות מפרויקטים קיימים, ולא בשאלות בסגנון תחרויות או ראיונות;
  2. הוא עדיין לא רווי – יש הרבה מקום לשיפור. אף מודל עדיין לא חצה את רף ה-50% במדד SWE-bench Verified (אף שקלוד 3.5 סונט המשודרג עומד, נכון לכתיבת שורות אלו, על 49%);
  3. הוא מודד "סוכן" שלם, ולא מודל בבידוד. מפתחי קוד פתוח וסטארט-אפים זכו להצלחה רבה באופטימיזציה של מעטפות תוכנה כדי לשפר באופן ניכר את הביצועים סביב אותו מודל.

יש לציין כי מערך הנתונים המקורי של SWE-bench מכיל כמה משימות שאינן ניתנות לפתרון ללא הקשר נוסף מחוץ לבעיית ה-GitHub (לדוגמה, לגבי הודעות שגיאה ספציפיות להחזרה). SWE-bench-Verified הוא תת-קבוצה של 500 בעיות מתוך SWE-bench שנבדקו על ידי בני אדם כדי לוודא שהן ניתנות לפתרון, ובכך הוא מספק את המדד הברור ביותר לביצועי סוכני קידוד. זהו מדד הביצועים אליו נתייחס בכתבה זו.

השגת ביצועים מובילים

סוכן המשתמש בכלים

פילוסופיית העיצוב שלנו בעת יצירת מעטפת הסוכן שעברה אופטימיזציה עבור קלוד 3.5 סונט המשודרג הייתה להעניק כמה שיותר שליטה למודל השפה עצמו, ולשמור על מעטפת מינימלית. הסוכן כולל פרומפט, כלי Bash (Bash Tool) לביצוע פקודות Bash, וכלי עריכה (Edit Tool) לצפייה ועריכת קבצים וספריות. אנו ממשיכים לדגום עד שהמודל מחליט שהוא סיים, או חורג מאורך חלון ההקשר (context length) שלו העומד על 200 אלף טוקנים. מעטפת זו מאפשרת למודל להשתמש בשיקול דעתו כיצד לטפל בבעיה, במקום להיות מקודד מראש (hardcoded) לדפוס או זרימת עבודה ספציפית.

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

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

<uploaded_files>
{location}
</uploaded_files>
I've uploaded a python code repository in the directory {location} (not in /tmp/inputs). Consider the following PR description:

<pr_description>
{pr_description}
</pr_description>

Can you help me implement the necessary changes to the repository so that the requirements specified in the <pr_description> are met?
I've already taken care of all changes to any of the test files described in the <pr_description>. This means you DON'T have to modify the testing logic or any of the tests in any way!

Your task is to make the minimal changes to non-tests files in the {location} directory to ensure the <pr_description> is satisfied.

Follow these steps to resolve the issue:
1. As a first step, it might be a good idea to explore the repo to familiarize yourself with its structure.
2. Create a script to reproduce the error and execute it with `python <filename.py>` using the BashTool, to confirm the error
3. Edit the sourcecode of the repo to resolve the issue
4. Rerun your reproduce script and confirm that the error is fixed!
5. Think about edgecases and make sure your fix handles them as well

Your thinking should be thorough and so it's fine if it's very long.

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

להלן מוצגת מפרט כלי ה-Bash (Bash Tool):

{
   "name": "bash",
   "description": "Run commands in a bash shell\n\n* When invoking this tool, the contents of the \"command\" parameter does NOT need to be XML-escaped.\n\n* You don't have access to the internet via this tool.\n\n* You do have access to a mirror of common linux and python packages via apt and pip.\n\n* State is persistent across command calls and discussions with the user.\n\n* To inspect a particular line range of a file, e.g. lines 10-25, try 'sed -n 10,25p /path/to/the/file'.\n\n* Please avoid commands that may produce a very large amount of output.\n\n* Please run long lived commands in the background, e.g. 'sleep 10 &' or start a server in the background.",
   "input_schema": {
       "type": "object",
       "properties": {
           "command": {
               "type": "string",
               "description": "The bash command to run."
           }
       },
       "required": ["command"]
   }
}

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

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

הקוד הבא מציג את תיאור כלי העריכה (Edit Tool) שלנו:

{
   "name": "str_replace_editor",
   "description": "Custom editing tool for viewing, creating and editing files\n\n* State is persistent across command calls and discussions with the user\n\n* If `path` is a file, `view` displays the result of applying `cat -n`. If `path` is a directory, `view` lists non-hidden files and directories up to 2 levels deep\n\n* The `create` command cannot be used if the specified `path` already exists as a file\n\n* If a `command` generates a long output, it will be truncated and marked with `<response clipped>` \n\n* The `undo_edit` command will revert the last edit made to the file at `path`\n\n\n
Notes for using the `str_replace` command:\n\n* The `old_str` parameter should match EXACTLY one or more consecutive lines from the original file. Be mindful of whitespaces!\n\n* If the `old_str` parameter is not unique in the file, the replacement will not be performed. Make sure to include enough context in `old_str` to make it unique\n\n* The `new_str` parameter should contain the edited lines that should replace the `old_str`",
...

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

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

מפרט כלי העריכה (Edit Tool) שלנו מוצג להלן:

...
   "input_schema": {
       "type": "object",
       "properties": {
           "command": {
               "type": "string",
               "enum": ["view", "create", "str_replace", "insert", "undo_edit"],
               "description": "The commands to run. Allowed options are: `view`, `create`, `str_replace`, `insert`, `undo_edit`."
           },
           "file_text": {
               "description": "Required parameter of `create` command, with the content of the file to be created.",
               "type": "string"
           },
           "insert_line": {
               "description": "Required parameter of `insert` command. The `new_str` will be inserted AFTER the line `insert_line` of `path`.",
               "type": "integer"
           },
           "new_str": {
               "description": "Required parameter of `str_replace` command containing the new string. Required parameter of `insert` command containing the string to insert.",
               "type": "string"
           },
           "old_str": {
               "description": "Required parameter of `str_replace` command containing the string in `path` to replace.",
               "type": "string"
           },
           "path": {
               "description": "Absolute path to file or directory, e.g. `/repo/file.py` or `/repo`.",
               "type": "string"
           },
           "view_range": {
               "description": "Optional parameter of `view` command when `path` points to a file. If none is given, the full file is shown. If provided, the file will be shown in the indicated line number range, e.g. [11, 12] will show lines 11 and 12. Indexing at 1 to start. Setting `[start_line, -1]` shows all lines from `start_line` to the end of the file.",
               "items": {
                   "type": "integer"
               },
               "type": "array"
           }
       },
       "required": ["command", "path"]
   }
}

תוצאות

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

דוגמאות להתנהגות סוכן

להרצת מדד הביצועים, השתמשנו ב-framework של SWE-Agent כבסיס לקוד הסוכן שלנו. ביומנים שלנו למטה, אנו מציגים את פלט הטקסט של הסוכן, קריאות הכלים ותגובות הכלים כ-THOUGHT, ACTION ו-OBSERVATION, אף על פי שאיננו מגבילים את המודל לסדר קבוע.

בלוקי הקוד הבאים יציגו מקרה אופייני שבו סונט 3.5 פותר בעיית SWE-bench.

בבלוק הראשון הזה, תוכלו לראות חלק מהפרומפט הראשוני שניתן למודל, כאשר {pr_description} מולא בערך האמיתי ממשימת SWE-bench. חשוב לציין, משימה זו מכילה שלבים לשחזור הבעיה, מה שיעניק למודל נקודת פתיחה חשובה לחקירה.

<uploaded_files>
/repo
</uploaded_files>
I've uploaded a python code repository in the directory /repo (not in /tmp/inputs). Consider the following PR description:

<pr_description>
linear_model.RidgeClassifierCV's Parameter store_cv_values issue
#### Description
Parameter store_cv_values error on sklearn.linear_model.RidgeClassifierCV

#### Steps/Code to Reproduce
import numpy as np
from sklearn import linear_model as lm

#test database
n = 100
x = np.random.randn(n, 30)
y = np.random.normal(size = n)

rr = lm.RidgeClassifierCV(alphas = np.arange(0.1, 1000, 0.1), normalize = True, 
                                         store_cv_values = True).fit(x, y)

#### Expected Results
Expected to get the usual ridge regression model output, keeping the cross validation predictions as attribute.

#### Actual Results
TypeError: __init__() got an unexpected keyword argument 'store_cv_values'

lm.RidgeClassifierCV actually has no parameter store_cv_values, even though some attributes depends on it.

המודל מגיב ומשתמש תחילה בכלי העריכה (Edit Tool) כדי לצפות במבנה המאגר. תוכלו לראות את פלט הטקסט של המודל ואת ארגומנטי קריאת הכלי תחת THOUGHT ו-ACTION, וחלק מפלט הכלי תחת OBSERVATION:

THOUGHT:
I'll help you implement the changes to satisfy the PR description. Let's follow the steps you outlined.

1. First, let's explore the repo structure:
ACTION (str_replace_editor):
command:
view

path:
/repo

OBSERVATION:
Here's the files and directories up to 2 levels deep in /repo, excluding hidden items:
/repo
/repo/PULL_REQUEST_TEMPLATE.md
/repo/ISSUE_TEMPLATE.md
/repo/sklearn
/repo/sklearn/decomposition
/repo/sklearn/kernel_ridge.py
/repo/sklearn/model_selection
/repo/sklearn/covariance
/repo/sklearn/cluster
/repo/sklearn/base.py
/repo/sklearn/dummy.py
/repo/sklearn/naive_bayes.py
...

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

THOUGHT:
Based on the repository structure, we'll need to modify the Ridge Classifier in the `sklearn/linear_model` directory.

Let's create a script to reproduce the error:
ACTION (str_replace_editor):
command:
create

path:
/repo/reproduce_error.py

file_text:
import numpy as np
from sklearn import linear_model as lm

#test database
n = 100
x = np.random.randn(n, 30)
y = np.random.normal(size = n)

rr = lm.RidgeClassifierCV(alphas=np.arange(0.1, 1000, 0.1), normalize=True, 
                         store_cv_values=True).fit(x, y)

OBSERVATION:
File created successfully at: /repo/reproduce_error.py

לאחר מכן, המודל משתמש בכלי ה-Bash (Bash Tool) כדי להריץ את הסקריפט שכתב, ומשחזר בהצלחה את הבעיה מהמשימה:

THOUGHT:
Let's verify that we can reproduce the error:
ACTION (bash):
command:
python3 /repo/reproduce_error.py

OBSERVATION:
Traceback (most recent call last):
  File "/repo/reproduce_error.py", line 10, in <module>
    store_cv_values=True).fit(x, y)
TypeError: __init__() got an unexpected keyword argument 'store_cv_values'

מכאן ואילך, המודל משתמש בכלי העריכה (Edit Tool) כדי לשנות את קוד המקור במאגר ומריץ מחדש את הסקריפט שלו כדי לוודא אם השינוי פתר את הבעיה:

THOUGHT:
The error is reproducible. Looking at the code, I can see that the issue is that while the `RidgeClassifierCV` class inherits from `_BaseRidgeCV`, it fails to pass the `store_cv_values` parameter from its constructor through to the base class constructor.

Let's fix this:
ACTION (str_replace_editor):
command:
str_replace

path:
/repo/sklearn/linear_model/ridge.py

old_str:
    def __init__(self, alphas=(0.1, 1.0, 10.0), fit_intercept=True,
                 normalize=False, scoring=None, cv=None, class_weight=None):
        super(RidgeClassifierCV, self).__init__(
            alphas=alphas, fit_intercept=fit_intercept, normalize=normalize,
            scoring=scoring, cv=cv)
        self.class_weight = class_weight

new_str:
    def __init__(self, alphas=(0.1, 1.0, 10.0), fit_intercept=True,
                 normalize=False, scoring=None, cv=None, class_weight=None,
                 store_cv_values=False):
        super(RidgeClassifierCV, self).__init__(
            alphas=alphas, fit_intercept=fit_intercept, normalize=normalize,
            scoring=scoring, cv=cv, store_cv_values=store_cv_values)
        self.class_weight = class_weight

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

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

אתגרים

SWE-bench Verified הוא מדד הערכה רב עוצמה, אך הוא גם מורכב יותר להרצה מאשר הערכות פשוטות של סיבוב יחיד. אלו כמה מהאתגרים שאיתם התמודדנו בשימוש בו – אתגרים שמפתחי AI אחרים עשויים גם הם לפגוש.

  1. משך זמן ועלויות טוקנים גבוהות. הדוגמאות לעיל הן ממקרה שהושלם בהצלחה ב-12 שלבים. עם זאת, הרצות מוצלחות רבות דרשו מאות סיבובים עד שהמודל פתר את הבעיה, ולמעלה מ-100 אלף טוקנים. קלוד 3.5 סונט המשודרג הוא עיקש: הוא יכול לעיתים קרובות למצוא את דרכו לפתרון בעיה בהינתן מספיק זמן, אך הדבר עלול להיות יקר;
  2. ניקוד. בעת בדיקת משימות שנכשלו, מצאנו מקרים שבהם המודל התנהג נכון, אך היו בעיות בהגדרת הסביבה, או בעיות עם תיקוני התקנה שהוחלו פעמיים. פתרון בעיות מערכת אלו חיוני לקבלת תמונה מדויקת של ביצועי סוכן AI.
  3. בדיקות נסתרות. מכיוון שהמודל אינו יכול לראות את הבדיקות שלפיהן הוא מקבל ציון, הוא לעיתים קרובות "חושב" שהצליח כאשר המשימה למעשה נכשלה. חלק מהכישלונות הללו נובעים מכך שהמודל פתר את הבעיה ברמת הפשטה שגויה (יישום פתרון מהיר במקום רפקטור עמוק יותר). כישלונות אחרים מרגישים פחות הוגנים: הם פותרים את הבעיה, אך אינם תואמים את בדיקות היחידה מהמשימה המקורית.
  4. רב-מודאליות. למרות שקלוד 3.5 סונט המשודרג ניחן ביכולות ראייה ורב-מודאליות מצוינות, לא יישמנו דרך שבה הוא יוכל לצפות בקבצים שנשמרו במערכת הקבצים או אליהם מתייחסים כ-URL. זה הפך את איתור הבאגים במשימות מסוימות (במיוחד אלו מ-Matplotlib) לקשה במיוחד, וגם גרם ל"הזיות" (hallucinations) במודל. בהחלט יש כאן "פירות נמוכים" (low-hanging fruit) למפתחים לשפר – ו-SWE-bench השיק הערכה חדשה המתמקדת במשימות רב-מודאליות. אנו מצפים לראות מפתחים משיגים ציונים גבוהים יותר בהערכה זו עם קלוד בעתיד הקרוב.

קלוד 3.5 סונט המשודרג השיג 49% במדד SWE-bench Verified, ובכך עקף את המודל המוביל בתחומו הקודם (45%), באמצעות פרומפט פשוט ושני כלים לשימוש כללי. אנו בטוחים שמפתחים שיבנו עם קלוד 3.5 סונט החדש ימצאו במהרה דרכים חדשות וטובות יותר לשפר את ציוני SWE-bench מעבר למה שהצגנו כאן בתחילה.

תודות

אריק שלונץ (Erik Schluntz) ביצע אופטימיזציה לסוכן SWE-bench וכתב פוסט זה בבלוג. סיימון ביגס (Simon Biggs), דון דריין (Dawn Drain), ואריק כריסטיאנסן (Eric Christiansen) סייעו ביישום מדד הביצועים. שאונה קרבק (Shauna Kravec), דון דריין, פליפה רוסו (Felipe Rosso), נובה דאסארמה (Nova DasSarma), ון צ'נדרסקראן (Ven Chandrasekaran), ורבים אחרים תרמו לאימון קלוד 3.5 סונט להיות מצוין בקידוד סוכני.