מדריך קלוד קוד בעברית

תיעוד 70

בדיקת תוספים עם evals

כתוב מקרי eval לתוסף Claude Code שלך, הרץ אותם עם claude plugin eval, דרג את התוצאות, השווה מול קו בסיס בלי תוסף, ושער את CI לפי הציון.

פקודת המעטפת claude plugin eval מריצה את התוסף שלך מול חבילת מקרי בדיקה ומנקדת את התוצאות. כל מקרה הוא prompt מציאותי ועוד grader אחד או יותר. grader הוא בדיקת עבר או נכשל על מה שClaude ייצר, למשל regex על התשובה, האם כלי מסוים נקרא, או rubric שמודל שני שופט לפיה את התשובה.

אין חובה לכתוב את החבילה ידנית. claude plugin eval init שואל אותך על התוסף, מציע את המקרים ואת ה-graders, מנסה אותם, וכותב את הקבצים. אפשר גם לבקש מClaude לעשות את אותו הדבר מתוך סשן שכבר פתוח אצלך.

השתמש ב-evals כדי:

  • למדוד עד כמה התוסף מוביל את Claude באופן אמין לתוצאה הנכונה
  • לתפוס רגרסיות כשאתה משנה את התוסף או כשיוצא מודל חדש
  • לראות מה התוסף תורם לעומת הרצה בלי תוסף

הדף הזה מיועד למחברי תוספים ומיומנויות שיש להם תוסף עובד והם רוצים לבדוק את ההתנהגות שלו, ולצוותים ששערים שינויי תוסף ב-CI. לאיטרציה על מיומנות אחת בתוך שיחת Claude Code, תוסף skill-creator מריץ השוואה דומה עם פורמט evals/evals.json משלו, ואף אחד משני הכלים לא קורא את קובצי המקרים של השני. כדי ליצור תוסף, ראה יצירת תוסף. כדי לבדוק את קובצי התוסף לשגיאות תחביר וסכמה, ולא את ההתנהגות שלו, השתמש ב-claude plugin validate.

[!NOTE]

כל הרצת eval וכל grader מסוג judge הם קריאת מודל אמיתית בחשבון שלך, שנמנית מול מגבלות השימוש של התוכנית או מול חשבון ה-API, אז בדוק קודם את הדרישות. אחר כך צור את חבילת ה-eval הראשונה שלך, או עבור אל הרצת evals ב-CI אם כבר יש לך חבילה.

#דרישות

כדי להריץ evals של תוסף צריך:

  • Claude Code בגרסה v2.1.269 או חדשה יותר. הרץ claude --version כדי לבדוק ו-claude update כדי לשדרג.
  • Git בגרסה 2.31 או חדשה יותר, אם git מותקן. הרץ git --version כדי לבדוק. עם git ישן יותר, claude plugin eval נעצר לפני הרצת מקרה כלשהו. בלי git, ההרצה מתבצעת כרגיל.
  • ספריית תוסף עם מניפסט plugin.json או .claude-plugin/plugin.json, או תוסף מסוג ספריית מיומנויות.
  • אותו אימות ואותו ספק מודל שהסשנים הרגילים שלך ב-Claude Code משתמשים בהם. הרצות eval, graders שמנוקדים על ידי judge, ו-claude plugin eval init קוראים למודל עם פרטי הגישה שלך, ולכן הם נמנים מול מגבלות השימוש של התוכנית או מול חשבון ה-API. כשהפקודה מדווחת על עלות, הנתון הוא הערכת מחיר מחירון של הקריאות האלה. אם אתה מריץ את Claude Code על Amazon Bedrock, Agent Platform של Google Cloud, או Microsoft Foundry, הרץ את החבילה ממעטפת שמייצאת את אותם משתני ספק כמו בסשנים הרגילים שלך, כי כל הרצה יורשת אותם מהמעטפת הזו כפי ששדה env מתאר.

#איך הרצת eval עובדת

חבילת eval נמצאת בספרייה בשם evals/ בתוך התוסף, בפריסה שכתיבה ושיפור מקרים מראה. לכל מקרה יש תת ספרייה משלו עם prompt ועם grader אחד או יותר. ה-prompt הוא משהו שאדם שמשתמש בתוסף שלך עשוי להקליד, למשל בקשה שאחת המיומנויות שלו אמורה לטפל בה.

#מה קורה בהרצה

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

#איך מקרה מקבל ציון

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

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

#קו הבסיס בלי תוסף

ציון גבוה לבדו לא אומר שהתוסף עזר, כי Claude עשוי להצליח באותה מידה גם בלעדיו. כדי להפריד בין השניים, הרצות המקרה חוזרות בלי תוסף טעון, ומתקבלים שני ציונים, WITH ו-W/OUT. ההפרש ביניהם, Δ, הוא מה שהתוסף תרם. אם מקרה מקבל 1.0 גם עם התוסף וגם בלעדיו, התוסף אינו מה שגרם לו לעבור.

שתי קבוצות ההרצות נקראות with-arm ו-without-arm. ניקוד מול קו הבסיס בלי תוסף מכסה אילו מקרים מריצים רק את ה-with-arm ואיך graders מנוקדים בשתי הזרועות.

#יצירת חבילת ה-eval הראשונה שלך

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

  • Claude Code בגרסה v2.1.269 או חדשה יותר ואת שאר הדרישות
  • מסוף פתוח בספריית השורש של התוסף, זו שמכילה את plugin.json או את .claude-plugin/plugin.json
  • מיומנות אחת בתוסף שאתה רוצה לבדוק, ובקשה שמשתמש היה מקליד ואמורה להפעיל אותה

1. יצירת המקרים

משורש התוסף, הרץ:

claude plugin eval init

אם Claude Code עדיין לא נותן אמון בספרייה הזו, הוא שואל קודם Trust this plugin directory?. ענה y.

אחר כך נפתח סשן אינטראקטיבי של Claude Code. Claude קורא את התוסף ושואל אותך איך נראית תוצאה טובה, מציע prompts שצריכים ולא צריכים להפעיל את התוסף, מתכנן graders לכל אחד, מריץ אותם פעם אחת כניסיון כדי לבדוק שהם מתנהגים כצפוי, וכותב ספריית מקרה אחת לכל prompt תחת evals/, כל אחת בשם שנגזר מה-prompt שלה.

כשClaude אומר שהחבילה מוכנה, צא מהסשן עם /exit או Ctrl+D כדי לחזור למעטפת.

אם כבר יש לך סשן Claude Code פתוח בשורש התוסף, אפשר במקום זאת לבקש שם מClaude להריץ את claude plugin eval init. Claude מריץ את הפקודה ואז שואל אותך את אותן שאלות באותה שיחה.

אם אתה מעדיף לכתוב מקרה בעצמך כדי לראות בדיוק מה הקבצים מכילים, עקוב אחרי כתיבת מקרה ידנית וחזור לכאן כדי להריץ אותו.

2. הרצת החבילה

חזרה במעטפת, בשורש התוסף, הרץ כל מקרה תחת evals/:

claude plugin eval .

כבר נתת אמון בספרייה הזו בשלב 1, ולכן ההרצה מתחילה מיד. אם כתבת את המקרה ידנית במקום זאת, ההרצה שואלת קודם Trust this plugin directory? [y/N]. ענה y. למה הרצה יכולה לגשת מסביר למה אתה מסכים.

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

3. קריאת הסיכום

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

CASE        WITH  W/OUT Δ      RUNS COST    NOTES
first-case  1.00  0.33  +0.67  6    $0.41

1 case(s) · mean Δ +0.67 · 74s · $0.41
Report: /Users/you/my-plugin/evals/results/2026-09-10T17-02-11-482Z/report.html
Published: https://claude.ai/... · keep local next time with --no-publish

WITH הוא הציון של המקרה כשהתוסף טעון, W/OUT הוא הציון בלעדיו, ו-Δ חיובי אומר שהתוסף העלה את הציון. COST הוא הערכת מחיר מחירון של קריאות המודל, ו-NOTES מציג את ההסבר של ה-grader הכושל בעל המשקל הגבוה ביותר, או את שגיאת ההרצה, מתוך ה-with-arm.

4. פתיחת הדוח ואיטרציה

פתח את כתובת Published:, או את הנתיב Report: כשאין שורת Published:, כדי לראות את פסק הדין וההסבר של כל grader בכל הרצה, ועבור graders מסוג llm גם את הקולות של ה-judge ואת הקטע שהוא שפט. שורת Published: מופיעה רק כשהחשבון שלך יכול לפרסם דוחות.

הממצא הראשון הנפוץ ביותר הוא Δ קרוב לאפס כשה-grader tool_used: Skill של המקרה נכשל, וזה אומר שClaude לא בוחר את המיומנות שלך בניסוח טבעי. התאם את ה-description של המיומנות, הרץ שוב claude plugin eval ., והשווה.

כדי לבצע איטרציה על מקרה אחד עם פחות הרצות, הרץ זרוע אחת פעם אחת. הרצה בודדת רועשת, אז אשר כל שינוי בשלוש ההרצות של ברירת המחדל לפני שאתה סומך עליו. עם זרוע אחת הטבלה מציגה עמודות SCORE ו-PASS% במקום WITH, W/OUT ו-Δ:

claude plugin eval . --case <case-name> --runs 1 --ablation none

החלף את <case-name> באחד משמות הספריות תחת evals/.

#כתיבה ושיפור של מקרים

המקרים ש claude plugin eval init כותב הם קבצים רגילים שאפשר לפתוח, לשנות ולהוסיף אליהם. מקרה הוא תיקייה תחת תיקיית ה eval של התוסף שמכילה prompt.md, case.yaml, או את שניהם. תנו לכל מקרה לפחות grader אחד, כקובץ graders/<name>.md או כרשומת graders: ב case.yaml, כי מקרה בלי אחד נכשל בטעינה. כדי לקבץ מקרים, קננו אותם תחת תיקייה שאינה בעצמה מקרה. כל מה שבתוך תיקיית מקרה, כמו graders/ וקבצי fixture, שייך לאותו מקרה.

זהו המבנה ש claude plugin eval init כותב, ובו יש להשתמש לסוויטות חדשות. ב הפניה לסוויטת eval מופיע העץ המלא, כולל mocks ותוצאות:

my-plugin/
├── .claude-plugin/plugin.json
├── skills/...
└── evals/
    ├── first-case/
    │   ├── prompt.md          # frontmatter: case fields; body: the prompt
    │   ├── graders/
    │   │   ├── criteria.md    # frontmatter: type + options; body: rubric or pattern
    │   │   └── skill-fired.md
    │   └── case.yaml          # optional: only for context.* fields
    ├── ignores-unrelated-request/
    │   └── ...
    └── results/               # written by each run; add to .gitignore

#כתיבת מקרה ידנית

הדרך המומלצת היא ש Claude יכתוב את המקרים עם claude plugin eval init. כדי לכתוב מקרה בעצמכם, התחילו מתבנית ריקה. הפקודה הבאה כותבת מקרה בשם first-case עם prompt.md ממלא מקום ו grader ממלא מקום אחד, ולא מריצה דבר:

claude plugin eval init --bare first-case
evals/first-case/
├── prompt.md            # the prompt sent to Claude, plus run limits
└── graders/
    └── criteria.md      # one grader: how to score the result

ב prompt.md כותבים את ההודעה ש Claude מקבל בכל ריצה, ומגדירים ב frontmatter את מגבלות הריצה ואת הכלים שהמקרה רשאי להשתמש בהם. פתחו את evals/first-case/prompt.md והחליפו את גוף ממלא המקום בבקשה שאחד מה skills שלכם אמור לטפל בה, מנוסחת כפי שמשתמש היה מקליד אותה, בלי לנקוב בשם ה skill. הדוגמה הזו היא ל skill שמנסח הודעות commit. השתמשו בבקשה שלכם:

---
max_turns: 10
allowed_tools: [Read, Glob, Grep, Skill]
---

Write me a commit message for this change: I renamed getUser to fetchUser and updated the three call sites.

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

הרשימה המלאה של שדות ה frontmatter מכסה את המודל, timeout, תגיות ומשתני סביבה.

כל קובץ תחת graders/ הוא בדיקה אחת שמופעלת אחרי הריצה. פתחו את evals/first-case/graders/criteria.md והחליפו את ממלא המקום במחוון למודל השופט, כתוב כתנאי PASS ו FAIL קונקרטיים:

---
type: llm
---

PASS if <what a correct response contains>.
FAIL if <what a wrong or missing response looks like>.

אחר כך הוסיפו grader שני שבודק אם ה skill שלכם הוא מה שייצר את התשובה. צרו את evals/first-case/graders/skill-fired.md, והחליפו את your-skill-name בשם התיקייה של ה skill תחת skills/, שהוא השם שבו Claude מפעיל אותו:

---
type: tool_used
tool: Skill
input_match: '"skill"\s*:\s*"(?:[\w-]+:)?your-skill-name"'
---

זה עובר כש Claude הפעיל את ה skill הזה לפחות פעם אחת במהלך הריצה, כולל בצורה עם מרחב השמות plugin-name:skill-name.

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

אחרי ששני הקבצים נשמרו, הריצו את המקרה כמו ב התחלה המהירה, עם claude plugin eval . משורש התוסף.

#הגדרת מגבלות ריצה וכלים ב prompt.md

הגדירו את max_turns, timeout_seconds, model, tags ואת allowed_tools שהמקרה רשאי להשתמש בהם ב frontmatter של prompt.md. ההפניה frontmatter של prompt.md מפרטת כל שדה ואת ברירת המחדל שלו.

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

#בחירה ושקלול של graders

ה frontmatter של grader מגדיר את type שלו, ואפשר גם weight שגורם לו להיחשב יותר בציון הריצה, ו arm שקובע איך הוא מנוקד מול ה baseline. מבין ששת הסוגים, regex, tool_used, tool_order ו file_exists מחושבים מהתמליל ומהקבצים ואינם עולים דבר, בעוד llm ו baseline קוראים למודל שופט ומוסיפים לעלות הריצה.

אין graders של קוד מותאם אישית.

סוגי grader מפרטים את האפשרויות ואת תנאי המעבר של כל סוג, ו מה grader יכול לבדוק מפרט את הערכים ש target ו focus מקבלים.

כברירת מחדל, השופט של graders מסוג llm ו baseline הוא המודל ש Claude Code משתמש בו למשימות רקע. העבירו --judge-model sonnet או מזהה מודל מלא כדי לבחור את השופט בעצמכם.

#בחירת graders שנותנים אות יציב

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

  • לפלט ארוך כמו קובץ שנוצר, דרגו אותו עם grader מסוג regex על תוכן הקובץ, שבודק את כל הקובץ באותו אופן בכל פעם. השאירו graders מסוג llm לפלטים קצרים, עם מחוונים כתובים כתנאי PASS ו FAIL קונקרטיים.
  • תנו לכל מקרה grader אחד על התוצאה, כמו ההודעה הסופית או קובץ שנוצר, ואחד על הצעדים ש Claude עשה כדי לייצר אותה, כמו tool_used או tool_order. יחד הם אומרים גם אם התשובה הייתה נכונה וגם אם התוסף שלכם ייצר אותה.
  • אם grader של tool_used: Skill במקרה עובר אבל Δ שלילי, חשדו בשופט לפני התוסף. מודל שופט קטן יכול לסמן תשובה נכונה כשגויה כי העיצוב שלה שונה ממה שהמחוון מתאר. הריצו שוב עם --judge-model sonnet, והדקו את המחוון כך שעיצוב לא יקבע את פסק הדין.
  • כדי לבדוק ש build או בדיקה עברו בתוך הריצה, בקשו בפרומפט מ Claude להריץ אותם ולכתוב את התוצאה לקובץ, דרגו את הקובץ, ואשרו שהפקודה רצה עם grader מסוג tool_used שה input_match שלו מנקוב בשם הפקודה.

#ניקוד מול baseline בלי תוסף

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

במצבים האלה מקרה מריץ רק את זרוע ה with, ולכן אין לו ציון W/OUT או Δ:

  • מעבירים --ablation none: כל מקרה מריץ זרוע אחת, וזה חוצה את העלות כשאין צורך בהשוואה, למשל בזמן איטרציה על graders.
  • המקרה ממשיך תמליל והיעד הוא נתיב: עם יעד כמו . ולא שם של תוסף מותקן, מקרה עם context.history_file מריץ זרוע אחת כברירת מחדל, מתוך הנחה שהשיחה המוקלטת כבר משקפת את התוסף. הריצה מדפיסה הודעת single-arm (no Δ) ב stderr שמנקבת בשמות המקרים האלה. כדי להשוות את התור שנמשך עם התוסף ובלי התוסף, העבירו --ablation with-without.
  • לא נמצא תוסף למקרה: כשהיעד הוא נתיב, מקרה שהתוסף שלו Claude Code לא הצליח לאתר גם הוא מריץ זרוע אחת כברירת מחדל. ראו זרוע ה baseline לא מציגה תוסף כדי לתקן זאת.

בריצה עם שתי זרועות, חלק מה graders מדווחים עם scored: false. בדיקה כמו "the skill was invoked" לא יכולה לעבור בלי התוסף, ולכן ספירה שלה הייתה דוחפת את זרוע ה without לכיוון אפס ומנפחת את Δ. כדי לשמור על שתי הזרועות בנות השוואה, Claude Code מוציא graders כאלה מהציון בשתי הזרועות ומדווח עליהם בזרוע ה with כמחווני עבר או נכשל בלבד. זה כולל:

  • כל grader מסוג tool_used שה tool שלו הוא Skill
  • כל grader מסוג regex עם target: mock_calls וכל grader מסוג llm עם focus: mock_calls, כשכל שרת מדומה במקרה הוא שרת שהתוסף שלכם מצהיר עליו
  • כל grader שמסמנים arm: with-only

שלוש הגדרות משנות את ההחרגה הזו:

  • כל grader מוחרג: אם כל grader במקרה נמצא בקבוצה המוחרגת, הם מנוקדים כרגיל, כי לא היה נשאר מה לנקד.
  • arm: both: הגדירו arm: both על grader כדי לנקד אותו בשתי הזרועות בכל מקרה, וזה מה שרוצים לבדיקה של "אסור להפעיל את ה skill" עם min: 0 ו max: 0.
  • --ablation none: תחת --ablation none שום דבר לא מוחרג, ולכן אותה סוויטה יכולה להפיק ציון מוחלט שונה בשני המצבים.

#שימוש בתיקיית eval אחרת

אם evals/ כבר תפוס על ידי כלי אחר, שמרו את הסוויטה בתיקייה אחרת. אפשר לרשום את התיקייה הזו ב plugin.json של התוסף כדי שכל ריצה וכל משתף פעולה ישתמשו בה, או להעביר אותה בשורת הפקודה לריצה אחת:

  • ב plugin.json: הוסיפו "experimental": { "evals": "quality/evals" }.
  • בשורת הפקודה: העבירו --eval-dir quality/evals גם ל claude plugin eval וגם ל claude plugin eval init.

אם מגדירים את שניהם, נעשה שימוש בתיקייה של הדגל. תנו נתיב יחסי של שמות תיקיות פשוטים כמו qa או quality/evals. נתיב מוחלט או נתיב שמכיל .. לא מתקבל: כערך דגל זו שגיאה, וכערך manifest בלתי שמיש מודפסת שורת Warning: והריצה משתמשת ב evals/. המקרים, התוצאות ופלט init עוברים כולם לתיקייה הזו.

#הגדרת fixtures ו mocks

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

#זריעת סביבת העבודה או השיחה

כל ריצה מתחילה בסביבת עבודה ריקה. כשמקרה צריך יותר מהפרומפט, הוסיפו case.yaml ליד prompt.md עם בלוק context:

  • קבצי fixture או מאגר git: כתבו סקריפט Bash בתיקיית המקרה ונקבו בשמו ב context.scaffold_script. הסקריפט רץ בתורכם, מחוץ ל sandbox של הסוכן, ורק כשמעבירים --scaffold, אז העבירו את הדגל הזה רק לסוויטות שאתם או הארגון שלכם כתבתם.
  • שיחה קודמת להמשך: שמרו את התמליל כקובץ .jsonl ונקבו בשמו ב context.history_file, והפרומפט של המקרה הופך לתור המשתמש הבא. כשהיעד הוא נתיב, מקרה כזה רץ בלי זרוע baseline כברירת מחדל.
  • תיקיות fixture ש Claude יכול לקרוא במהלך הריצה: פרטו אותן ב context.add_dirs.

case.yaml צריך גם schema_version: "1.1" ו name. להפניה שדות case.yaml יש את הרשימה המלאה.

ה case.yaml הזה זורע סביבת עבודה מסקריפט ונותן ל Claude לקרוא fixtures מתיקיית resources/:

schema_version: "1.1"
name: changelog-from-diff
tags: [smoke]
context:
  scaffold_script: fixture.sh
  add_dirs: [resources]

סקריפט scaffold מתחיל בסביבת העבודה הריקה עם סביבה קבועה קטנה: PATH של המעטפת שלכם, HOME שמוגדר לתיקיית הבית הזמנית של הריצה, TMPDIR, וכמה קבועים כמו TERM=dumb. שום דבר אחר מהמעטפת שלכם לא מגיע אליו, וגם לא משתני EVAL_* של המקרה. אם הסקריפט יוצא עם קוד שאינו אפס או רץ יותר מ 120 שניות, הריצה הזו מקבלת ציון 0 עם שגיאת scaffold failed. השתמשו בסקריפט רק לקבצים ולמצב git, כי תצורת פרויקט שהוא כותב לא נטענת.

#הדמיית שרתי MCP

אפשר להעריך תוסף שה skills שלו קוראים לכלי MCP בלי השירות האמיתי מאחוריהם. שימו קובץ Markdown אחד לכל כלי תחת evals/mocks/<server>/<tool>.md לכל הסוויטה, או תחת תיקיית mocks/ של המקרה עצמו למקרה אחד, כאשר <server> הוא שם השרת ב תצורת MCP של התוסף.

ריצה אף פעם לא מפעילה את שרתי ה MCP האמיתיים של התוסף אלא אם מבקשים זאת. Claude Code רושם שרת תחליפי תחת השם של כל שרת. כלים עם קובץ mock עונים ממנו ומותרים בלי הרשאת --allow-tools, וכלי בלי קובץ mock אינו זמין ל Claude. שרת בלי mocks בכלל מופיע בשורת ההתקדמות mocked: של המקרה כ plugin_<plugin>_<server>[not started: no mock].

גוף הקובץ הוא מה שהכלי מחזיר ל Claude. ה mock הזה מחליף כלי create_issue בשרת בשם tracker, בודק את הקלט ש Claude שולח, ומחזיר את הכותרת. שמרו אותו כ evals/mocks/tracker/create_issue.md:

---
expect:
  title: string
  priority: [low, medium, high]
---

Created issue #4821: {{input.title}}

הגוף וה frontmatter של קובץ mock מקבלים את האפשרויות האלה:

  • החלפות: הכניסו שדות מהקלט של הקריאה עם {{input.<field>}}, ואת תוכן קובץ fixture ליד ה mock עם {{file:fixtures/{input.<field>}.json}}.
  • expect:: בלוק expect: שומר על הקלט. אם קריאה מפרה אותו, הריצה נעצרת עם ציון 0 ורושמת למה, כך שמקרה יכול לאמת מה התוסף ביקש מהשרת לעשות.
  • error: true: הגדירו error: true כדי להחזיר את הגוף כשגיאת כלי.
  • type: agent: הגדירו type: agent כדי שמודל השופט יענה בתור השרת לפי הוראות בגוף. קריאות ל mocks מסוג agent חולקות תקציב אחד לכל ריצה של פי ארבעה מ max_turns של המקרה, וקריאה מעבר לו עוצרת את הריצה עם ציון 0.

הפניה לקובץ mock מפרטת כל מפתח ואת הקבצים _server.md ו _tools.json.

כדי לדרג את הקריאות עצמן, כוונו grader אל target: mock_calls.

כדי לרוץ מול שרתי ה MCP האמיתיים של התוסף, העבירו אחד מהדגלים האלה. בכל אחת מהדרכים התהליכים האלה רצים בתורכם, מחוץ ל sandbox של הריצה, והכלים שלהם צריכים הרשאת --allow-tools:

  • --allow-real-servers: הפעילו את התהליך האמיתי לכל שרת שלא יצרתם לו mock, והמשיכו לענות לכלים עם mock מהקבצים שלהם
  • --mocks off: התעלמו מ mocks/ לגמרי והפעילו כל שרת שהתוסף מצהיר עליו

#השמעה חוזרת של תשובות mock מסוג agent

mock מסוג type: agent עונה בקריאה ל --judge-model, ולכן הפלט שלו משתנה בין ריצות ומשתנה אם משנים את השופט. כשריצה מסתיימת בלי שגיאה או עצירה, Claude Code שומר כל תשובה ש mock מסוג agent נתן תחת תיקיית התוצאות ב mock-recordings/.

פתחו שם את ADOPT.txt כדי לראות כל הקלטה ואת תיקיית .replay/<server>/ שאליה יש להעתיק אותה, ליד ה mock שייצר אותה. אחרי שמעתיקים הקלטה לשם, ריצות מאוחרות עונות לקריאה הזהה ממנה בלי קריאה למודל. בצעו commit ל mocks/.replay/ עם שאר mocks/ כדי שריצות CI יהיו ניתנות לחזרה.

#הרצת הערכות

אחרי שקיימת חבילה, claude plugin eval מריץ אותה. בוחרים איזה תוסף ואילו מקרים רצים באמצעות ארגומנט היעד, מעניקים כלים שהמקרים צריכים מעבר לקבוצת הקריאה בלבד עם --allow-tools, ושולטים במספר ההרצות, במודלים, בעלות ובפלט עם שאר האפשרויות.

#בחירת מה להעריך

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

יעדמה רץ
ספריית השורש של תוסף, למשל .כל מקרה תחת ספריית ה-eval שלו, כשהתוסף הזה טעון
קובץ prompt.md או case.yaml יחידהמקרה הזה, כשהתוסף שעוטף אותו טעון
תוסף מותקן לפי שם, name או name@marketplaceהתוסף והמקרים בספריית ה-eval שלו, שנקראים במקום או מהעותק המותקן. התוצאות נכתבות תחת ./evals/results/ בתיקייה הנוכחית, או ./<dir>/results/ עם --eval-dir
name@skills-dirאותו דבר, עבור תוסף של ספריית skills
הושמטהתיקייה הנוכחית כנתיב

מוסיפים --case <glob> כדי לסנן לפי שם מקרה, ו---tag <tag> כדי להשאיר מקרים שיש להם אחד מהתגים שניתנו.

שמים את היעד לפני --tag, --allow-tools ו---json. שני הראשונים מקבלים רשימה, ו---json מקבל נתיב אופציונלי, ולכן כל אחד מהם קורא יעד שבא אחריו כערך משלו.

#הענקת כלים

הרצות אף פעם לא עוצרות כדי לבקש הרשאה. כלים מובנים שצריכים הענקה שלא נתתם, כמו Bash, Write, Edit, WebFetch ו-WebSearch, מוסרים מהסשן, ולכן Claude לא יכול לקרוא להם בכלל.

הרצה מתירה רק את כלי הקריאה בלבד שהמקרה מציין ב-allowed_tools, מתוך Read, Glob, Grep, NotebookRead, Skill, AskUserQuestion, Agent, TodoWrite, וכלי המשימות TaskCreate, TaskGet, TaskList, TaskUpdate ו-TaskStop, ובנוסף כל מה שמעניקים עם --allow-tools. ההענקה הזו חלה על כל מקרה בהרצה. כדי לאפשר למקרים להשתמש ב-Bash, Write, Edit, WebFetch או WebSearch, מעניקים אותם בעצמכם:

claude plugin eval . --allow-tools Write Edit "Bash(npm test *)"

כשמקרה ביקש כלי שלא הענקתם, פלט ההתקדמות מציין אותו כ-not granted. כלים בשרת MCP מדומה לא צריכים הענקה. כלים בשרת MCP אמיתי של תוסף צריכים גם שהשרת יופעל, עם --allow-real-servers או --mocks off, וגם הענקה לפי שם, למשל --allow-tools "mcp__plugin_my-plugin_github__*". כלי MCP של תוסף נקראים mcp__plugin_<plugin>_<server>__<tool>.

כשמעניקים Bash בכל צורה, כל פקודה רצה תחת ארגז החול ברמת מערכת ההפעלה של Claude Code. כתיבות מוגבלות למרחב העבודה של ההרצה, תיקיית הבית והגדרות Claude Code לא ניתנות לקריאה, והגישה לרשת מוגבלת לדומיינים שמעניקים עם --allow-tools "WebFetch(domain:example.com)". אם מעניקים Bash או PowerShell במכונה בלי מנגנון ארגז חול, Claude Code מסרב לכל הרצה במקום להריץ אותה בלי הגבלה, והמקרה מציג שגיאת הרצה ובדרך כלל מקבל ציון 0. ל-Windows מקורי אין מנגנון, ולכן מריצים חבילות שמעניקות מעטפת תחת WSL2. ב-Linux מתקינים קודם bubblewrap ו-socat. ראו את דרישות הקדם של ארגז החול.

#אפשרויות הפקודה

הטבלה הזו מכסה את האפשרויות למספר הרצות, מודלים, ניקוד, עלות, הענקות כלים, הדמיות ופלט. הריצו claude plugin eval --help לרשימה המלאה, שכוללת גם את --case, --tag, --eval-dir, --no-scaffold, --report ו---verbose.

אפשרותברירת מחדלהשפעה
--runs <n>ה-runs של כל מקרה, אחרת 3הרצות לכל מקרה לכל זרוע
-j, --concurrency <n>1להריץ עד מספר זה של הרצות סוכן בבת אחת, מ-1 עד 8. הן חולקות את מגבלת הקצב של החשבון, ולכן זה מקצר את זמן השעון ולא מעלה את התפוקה מעבר למגבלה הזו. התוצאות שומרות על סדר המקרים
--model <model>ה-model של כל מקרה, אחרת ANTHROPIC_MODEL אם הוגדר, אחרת ברירת המחדל של Claude Codeהמודל של הסוכן שנבדק. קבעו אותו ב-CI כדי שפריסת מודל לא תתפרש בטעות כרגרסיה של התוסף
--judge-model <model>המודל למשימות רקעהמודל למדרגים מסוג llm ו-baseline
--ablation <mode>נקבע לכל מקרה. ראו ניקוד מול קו הבסיס בלי התוסףהאם להריץ גם כל מקרה בלי התוסף כדי למדוד מה הוא מוסיף. none מריץ זרוע אחת. with-without מוסיף את קו הבסיס בלי התוסף
--threshold <0..1>1.0מקרה עובר כשציון זרוע ה-with שלו הוא לפחות הערך הזה. כל מקרה מתחתיו גורם לפקודה לצאת עם קוד 1
--max-cost-usd <usd>אין תקרהתקרה על אומדן עלות מחירון של ההרצה, לא על שימוש בתוכנית. נבדקת לפני שכל הרצה מתחילה. אחרי שהסכום נוצל, שום דבר נוסף לא מתחיל. הרצות שכבר התחילו מסתיימות, ולכן ההוצאה יכולה לעבור את התקרה בגלל ההרצות האלה. אם נשארה הרצה שלא התחילה, הפקודה יוצאת עם קוד 2 ועם תוצאות חלקיות
--allow-tools <tools...>איןהענקת כלים מעבר לקבוצת הקריאה בלבד. ראו הענקת כלים
--scaffoldכבוילהריץ את scaffold_script של כל מקרה
--trust-pluginכבוילדלג על בקשת האמון בהרצה הראשונה עבור תוסף שהקוד והחבילה שלו הייתם מריצים בעצמכם. מעבירים אותו ב-CI כדי שהמשימה לא תסורב על ידי הבקשה ולא תישאר ממתינה בה. ראו למה הרצה יכולה לגשת
--mocks <mode>recordrecord עונה לקריאות כלי MCP מהדמיות, לא מפעיל את השרתים האמיתיים של התוסף, ושומר תשובות של הדמיית סוכן להפעלה חוזרת. off מתעלם מהדמיות ומפעיל את שרתי ה-MCP האמיתיים של התוסף
--allow-real-serversכבויעם --mocks record, גם להפעיל את שרתי ה-MCP האמיתיים של התוסף עבור שרתים שאין להם הדמיה
--json [path]כבוילהדפיס את מסמך התוצאה ל-stdout, או לכתוב אותו לנתיב שמסתיים ב-.json. ההרצה שקטה: אין שורות התקדמות ואין טבלת סיכום
--output-dir <dir><eval dir>/results/<timestamp>/לאן הולכים aggregate-result.json ו-report.html
--no-publishלהשאיר את דוח ה-HTML מקומי. ראו דוח HTML
--publish-reportלפרסם את הדוח גם במקום שבו הוא היה נשאר מקומי כברירת מחדל, למשל הרצה שסשן של Claude Code התחיל
--keep-tempכבוילשמור את ספריית ארגז החול של כל הרצה ולהדפיס את הנתיב שלה, לניפוי שגיאות של מה ש-Claude יצר

#הרצת הערכות ב-CI

במשימת ה-CI, מריצים את החבילה עם --json כדי לכתוב את התוצאה לארכיון, ומכשילים את הבנייה לפי קוד היציאה. מעבירים --trust-plugin כדי שהמשימה לעולם לא תמתין בבקשת האמון בהרצה הראשונה, מקבעים את שני המודלים כדי שהציונים יהיו בני השוואה לאורך זמן, משאירים את הדוח מקומי, וקובעים תקרת עלות כגבול עליון:

claude plugin eval . \
  --trust-plugin \
  --json results.json \
  --threshold 0.8 \
  --model claude-sonnet-5 \
  --judge-model claude-haiku-4-5 \
  --no-publish \
  --max-cost-usd 20

קוד היציאה של המשימה אומר מה קרה:

קוד יציאהמשמעות
0החבילה עברה: כל מקרה קיבל ציון שווה ל---threshold או מעליו, ושום דבר אחר לא הכשיל את ההרצה
1מקרה קיבל ציון מתחת לסף, קובץ מקרה נכשל בטעינה, לא נמצאו מקרים, לא ניתן היה להתחיל הרצה, או הרצה הסתיימה בשגיאת API. מוחזר גם כשספריית התוסף לא מהימנה ו---trust-plugin לא הועבר, כשאפשרות לא תקינה, או כשלא ניתן היה לכתוב את תוצאת ה---json
2הרצה חלקית: תקרת --max-cost-usd הושגה, או שהאישור נדחה לפני ההרצה הראשונה או בה. results.json עדיין נכתב עם partial: true והסיבה
130הופסק. נכתבות תוצאות חלקיות
143הופסק, למשל על ידי פסק זמן של CI

הדלתא של with פחות without מדווחת אבל אף פעם לא משנה את קוד היציאה, וגם בעיות בכתיבה או בפרסום של דוח ה-HTML לא משנות אותו.

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

רץ CI צריך גם את אלה במקום:

  • התקנה ואישורים: רץ CI צריך התקנה של Claude Code ואישורים בסביבה, כמו ANTHROPIC_API_KEY או המשתנים של ספק הענן.
  • אמון: בלי --trust-plugin, משימה שספריית ה-checkout שלה Claude Code עדיין לא סומך עליה צריכה את בקשת האמון בהרצה הראשונה, והרצה שלא יכולה לשאול מסורבת עם קוד יציאה 1.
  • init ב-CI: claude plugin eval init צריך מסוף כדי לשאול את השאלות. ב-CI מריצים claude plugin eval init --bare <name> כדי לקבל את התבנית הריקה.

כדי לשמור על עלויות צפויות, נותנים לחבילות מהירות של כל שינוי רק מדרגים שלא קוראים לשופט, משתמשים ב---ablation none במקום שבו לא צריך Δ, ומשאירים מסמכים עם partial: true והרצות עם skippedPaidGraders מחוץ לכל מגמה שמשרטטים.

#קריאת התוצאות

כל הרצה עם לפחות מקרה אחד כותבת ספריית results/<timestamp>/ בתוך ספריית ה-eval, שמכילה את aggregate-result.json ואת report.html. עבור יעד נתיב זה תחת התוסף. עבור תוסף שציינתם בשם, זה תחת התיקייה הנוכחית, כמו שטבלת היעדים מראה. טבלת הסיכום, ה-JSON והדוח מציגים כולם את אותם נתוני תוצאה.

#דוח HTML

report.html הוא קובץ יחיד עצמאי שלא שולח בקשות חיצוניות, ולכן אפשר לצרף אותו למשימת CI או לפתוח אותו מהדיסק. הדוגמה הזו היא החלק העליון של דוח לחבילה של שלושה מקרים שהורצה עם --threshold 0.8. העלות שמוצגת היא אומדן מחירון, והיא משתנה לפי המודל ולפי מספר המקרים:

חלק עליון של דוח הערכה: שורת פסק שקוראת "Plugin effect: +33.3 pts vs baseline, improved 2, flat 1, regressed 0 of 3 cases", חמישה אריחי סיכום לציון החבילה, דלתא של ablation, ציון קו הבסיס, מקרים שעוברים את הסף, והרצות מושלמות, ואז המקרה הראשון עם הדלתא שלו, פס ציון, והרצה אחת ששני המדרגים שלה מציגים pass

קוראים אותו מלמעלה למטה:

  • שורת הפסק והאריחים עונים אם התוסף עזר על פני כל החבילה. ציון החבילה הוא הממוצע של ציוני with-plugin לכל מקרה, Ablation Δ הוא כמה זה מעל או מתחת לציון קו הבסיס, ו-Cases סופר כמה עמדו בסף. Perfect runs הוא החלק של הרצות with-plugin שבהן כל מדרג עבר.
  • כל כרטיס מקרה מציג את ה-Δ של המקרה ואת ציון ה-with-plugin, עם סימון על הפס בסף. מקרה שה-Δ שלו שלילי מקבל קצה שמאלי אדום, כך שרגרסיות בולטות בגלילה.
  • בתוך מקרה, הרצות ה-with-plugin באות קודם והרצות קו הבסיס אחריהן. כל הרצה מפרטת את המדרגים שלה עם שבב pass או fail. מדרג שנכשל כבר פתוח עם ההסבר שלו, ומדרג llm מציג גם את ההצבעות של השופט ואת הראיות שהוצגו לו, ושם מגלים למה הרצה קיבלה ציון נמוך. מדרגים שלא נספרים בציון, כמו tool_used: Skill, נושאים תג plugin-fired indicator.
  • Prompt ו-Graders, מתחת להרצות, מציגים את הפרומפט של המקרה ואת המחוון או התבנית של כל מדרג, כדי שמי שקורא את הדוח בלי החבילה יוכל לראות מה נשאל ומה נחשב טוב.

אם מחוברים עם מנוי claude.ai וartifacts זמינים לחשבון, Claude Code גם מפרסם את הדוח כ-artifact פרטי ומדפיס Published: <url>. מעבירים --no-publish כדי להשאיר אותו מקומי. אם לא מופיעה שורת Published:, למשל עם אימות במפתח API, הקובץ המקומי הוא הדוח.

הרצה שסשן של Claude Code התחיל, למשל כשמבקשים מ-Claude להריץ את החבילה, גם נשארת מקומית, ושורת ה-Report: שלה אומרת kept local. מוסיפים --publish-report לפקודה הזו כדי לפרסם אותה.

#תוצאת JSON

aggregate-result.json, ופלט --json, הוא מסמך עם גרסה ועם schemaVersion: 1 כדי שסקריפטים של CI ינתחו אותו. שמות השדות הם camelCase, ושדות חדשים מתווספים בלי שינוי שם של שדות קיימים, ולכן כותבים את הסקריפט כך שיתעלם משדות שהוא לא מזהה.

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

שדהמשמעות
partial, partialReasontrue עם cost_ceiling, interrupted או auth_failed כשהחבילה לא הסתיימה. משאירים תוצאות חלקיות מחוץ לתרשימי מגמה
aggregates.overallScoreציון מקרה ממוצע על פני החבילה
aggregates.casesPassed, aggregates.casesTotalמקרים ב---threshold או מעליו, והסך הכולל
aggregates.meanDeltaממוצע Δ על פני המקרים, במצב שתי הזרועות
cases[].nameשם המקרה
cases[].aggregates.scoreציון הרצה ממוצע של זרוע ה-with עבור המקרה
cases[].aggregates.deltaציון זרוע ה-with פחות ציון זרוע ה-without. מושמט כשהמקרה רץ בזרוע אחת או כשהזרועות לא בנות השוואה
cases[].arms.with[].errornull, או למה הרצה הסתיימה באופן חריג, למשל timed out after 300s. חלק מההרצות האלה עדיין מדורגות לפי מה שהן ייצרו, ולכן שגיאה שאינה null לא מרמזת על ציון 0. הרצה שהסתיימה בשגיאת API מקבלת ציון 0 בלי דירוג
cases[].arms.with[].abortedקיים כשהדמיה עצרה את ההרצה דרך expect:, abort_when, או תקציב הקריאות של הדמיית הסוכן, עם server, tool ו-reason. ההרצה מקבלת ציון 0 ו-error נשאר null
cases[].arms.with[].skippedPaidGraderstrue כשתקרת העלות דילגה על מדרגי השופט של ההרצה הזו, ולכן הציון שלה לא בר השוואה
costUsd, durationSeconds, claudeVersionעלות משוערת במחיר מחירון כולל קריאות שופט, שניות שעון, וגרסת Claude Code שהריצה את החבילה

#למה לריצה יש גישה

claude plugin eval טוען את ה-skills, ה-hooks וה-agents של תוסף היעד ומריץ את חבילת ה-eval שלו על המכונה שלך, בתור אתה. הפניה שלו אל תוסף היא אותה החלטת אמון כמו claude --plugin-dir, לכן הערך רק תוספים שאתה סומך עליהם.

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

#אמון בתיקיית התוסף

בפעם הראשונה שאתה מריץ את claude plugin eval מול תיקייה, Claude Code שואל Trust this plugin directory? לפני שהוא טוען ממנה משהו, אלא אם כבר אישרת את בקשת האמון שם בסשן claude אינטראקטיבי. בתוך מאגר git, תשובה של כן נותנת אמון במאגר כולו, גם לסשנים אינטראקטיביים. כאשר stdin או stdout אינם מסוף, או תחת --json, הריצה לא יכולה לשאול והיא נדחית עם יציאה 1. העבר --trust-plugin כדי לאשר את האמון בעצמך, רק עבור תוסף שהיית מריץ על המכונה שלך. יעד שאתה מציין בשם ולא כנתיב, כלומר תוסף מותקן או תוסף של תיקיית skills, מדלג על הבקשה.

חלקים מסוימים של התוסף והחבילה רצים רק כאשר אתה מעביר את הדגל שלהם לאותה ריצה:

allowed_tools של מקרה ו-frontmatter של allowed-tools של skill עצמו לא יכולים להרחיב אף אחד מהם.

כאשר התוסף כולל hooks שלא כתבת, או שאתה מפעיל את שרתי ה-MCP האמיתיים שלו, התייחס לציונים שלו כמייעצים אלא אם הרצת אותו בסביבה מבודדת כגון קונטיינר או רץ CI, משום ש-hooks ושרתים רצים מחוץ ל-sandbox של הסוכן ויכולים לשנות את הקבצים שה-graders קוראים.

#איך ריצות מבודדות

כל ריצה מקבלת תיקיית home זמנית, תיקיית עבודה ותצורת Claude Code, והסוכן הנבדק רץ שם כתהליך בן claude -p כשרק התוסף שלך טעון. שמור את ההשלכות האלה בראש כאשר אתה כותב מקרים:

  • שום דבר אישי או ברמת הפרויקט לא נטען. הגדרות המשתמש שלך, hooks, קבצי CLAUDE.md, שרתי MCP, תוספים מותקנים אחרים, memory ו-skills נעדרים. גם תצורה בתחום הפרויקט לא נקראת בשום מקום: לא נטענים תיקיית .claude/, CLAUDE.md או .mcp.json מעל סביבת העבודה או בתוכה, גם לא אחד ש-scaffold_script כתב, ותיקיות add_dirs מעניקות גישת קריאה בלבד. גם רוב סביבת ה-shell שלך נמנעת. רק רשימת היתר ומשתני EVAL_* מגיעים לריצה. כלול בתוסף הנבדק כל skills, agents, hooks או שרתי MCP שמקרה תלוי בהם, משום ש-scaffold_script יכול לספק רק קבצים ומצב git.
  • מדיניות מנוהלת עדיין יכולה להגביל ריצה. הגבלות ב-הגדרות מנוהלות שמנהל פרס למכונה חלות בתוך ריצה, ולכן תוצאות על מכונה מנוהלת יכולות להיות שונות ממכונה לא מנוהלת לפי אותה מדיניות.
  • כלי ה-Artifact כבוי. skill שמפרסם artifact אפשר לדרג רק לפי מה שהוא מייצר לפני אותו שלב.
  • הגדרות המקרה מוסתרות מהסוכן. ריצה לא יכולה לקרוא את תיקיית ה-eval, ולכן Claude לא יכול לראות את ה-prompt של המקרה, את ה-graders שלו או מקרים אחים.
  • אין sandbox רשת מחוץ לפקודות shell. פקודות shell שאתה מעניק רצות לפי כללי הרשת של ה-sandbox. הענקה של WebFetch(domain:…) מגיעה לאותו דומיין ישירות, וה-hooks של התוסף עצמו וכל שרתי MCP אמיתיים שאתה מפעיל יכולים להגיע לכל מארח.

#הפניה לחבילת eval

כל מה שחבילת eval יכולה להכיל נמצא תחת תיקיית ה-eval של התוסף, evals/ אלא אם הגדרת אחרת. תיקייה נחשבת למקרה כאשר היא מחזיקה prompt.md או case.yaml, ומקרה בלי grader אחד לפחות נכשל בטעינה עם שגיאת invalid case.yaml שמציינת את graders. העץ הזה מציג כל קובץ ש-claude plugin eval קורא או כותב בתיקיית ה-eval:

evals/
├── <case>/                        # תיקייה אחת לכל מקרה; קנן תחת תיקייה שאינה מקרה כדי לקבץ
│   ├── prompt.md                  # frontmatter: שדות מקרה וריצה; גוף: ה-prompt
│   ├── case.yaml                  # אופציונלי: שדות context.*, או המקרה כולו בקובץ אחד
│   ├── graders/
│   │   └── <name>.md              # grader אחד לכל קובץ; frontmatter: type ואפשרויות; גוף: rubric
│   ├── mocks/                     # אופציונלי: mocks למקרה זה בלבד, אותו מבנה כמו למטה
│   └── <fixtures, scripts, transcripts referenced by case.yaml>
├── mocks/                         # אופציונלי: MCP mocks לכל החבילה
│   ├── <server>/
│   │   ├── <tool>.md              # כלי מדומה אחד; גוף: תוצאת הכלי
│   │   ├── _server.md             # אופציונלי: סוכן אחד שעונה לכמה כלים
│   │   ├── _tools.json            # אופציונלי: תגובת tools/list שמורה לתיאורים ולסכמות אמיתיים
│   │   └── fixtures/              # קבצים שמוכנסים עם {{file:fixtures/...}}
│   └── .replay/<server>/          # הקלטות agent-mock שאומצו, נענות בלי קריאת מודל
└── results/<timestamp>/           # נכתב על ידי כל ריצה; הוסף את results/ ל-.gitignore
    ├── aggregate-result.json
    ├── report.html
    └── mock-recordings/           # תשובות agent-mock מריצות נקיות, עם ADOPT.txt

#frontmatter של prompt.md

ה-frontmatter של prompt.md מקבל את השדות האלה. מפתח לא מוכר הוא שגיאה:

שדהברירת מחדלמטרה
schema_version"1.1", מוגדר עבורךגרסת פורמט המקרה. מקרים שנכתבו כ-prompt.md מקבלים אותו אוטומטית, ולכן אתה ממעט להגדיר אותו
nameשם התיקייהשם המקרה. תבניות glob של --case מתאימות לו והדוח משתמש בו כמפתח
descriptionלבני אדם. לא בשימוש בזמן ריצה
tags[]תוויות לסינון --tag. מקרה רץ אם אחת מהתוויות שלו תואמת
pluginsהתוסף העוטף הקרוב ביותרתיקיות תוספים בבדיקה, יחסית לתיקיית המקרה. הגדר plugins: ["../.."] כאשר זיהוי אוטומטי לא מוצא את התוסף שלך. ראה התוסף לא נטען
runs3ריצות לכל זרוע, 1 עד 50. --runs דורס אותו
expected_outcomeלבני אדם. לא בשימוש בזמן ריצה
modelברירת המחדל של סשן הבןמודל לסוכן הנבדק. --model דורס אותו
max_turns10תקרת תורות, עד 200. פגיעה בה נרשמת כשגיאת ריצה ובדרך כלל מורידה את הציון, לכן הגדר אותה בנדיבות
timeout_seconds300תקרת שעון קיר לכל ריצה, עד 3600
allowed_tools[]כלים שהמקרה רוצה, כגון [Read, Glob, Grep, Skill]. כלים לקריאה בלבד מוענקים כאשר הם רשומים כאן. לכל דבר אחר, ראה הענקת כלים
append_system_promptטקסט שמתווסף ל-system prompt של סשן הבן
env{}משתני סביבה נוספים לסשן הבן. מפתחות חייבים להתאים ל-EVAL_[A-Z0-9_]*. כל מפתח אחר מכשיל את הריצה. הריצה יורשת מה-shell שלך רק רשימת היתר: יסודות כגון PATH ו-locale, הגדרות proxy ותעודה, המשתנים שבוחרים ומאמתים את ספק המודל שלך, רוב תצורת ANTHROPIC_* ו-CLAUDE_CODE_*, ו-EVAL_*. כדי להעביר לתוסף כל דבר אחר, כגון הגדרת toolchain, יצא אותו כמשתנה EVAL_*

#שדות case.yaml

case.yaml הוא חלופה או בן לוויה ל-prompt.md: הוא מתאר מקרה ב-YAML ומוסיף את השדות שמצביעים על קבצים אחרים. הוא דורש schema_version: "1.1" ו-name. השדות של prompt.md שהם description, tags, plugins, runs ו-expected_outcome נמצאים ברמה העליונה. model, max_turns, timeout_seconds, allowed_tools, append_system_prompt ו-env נמצאים תחת execution:. כאשר שני הקבצים קיימים, ה-frontmatter של prompt.md דורס את שדות case.yaml התואמים, גוף prompt.md הוא ה-prompt, ו-graders/*.md מתווספים אחרי כל graders שרשומים ב-case.yaml.

השדות האלה קיימים רק ב-case.yaml:

שדהמטרה
context.scaffold_scriptסקריפט Bash בתיקיית המקרה שרץ בסביבת העבודה הריקה לפני ש-Claude מתחיל, כדי ליצור קבצי fixture או מאגר git. הוא רץ רק כאשר אתה מעביר --scaffold, עם סביבה מזערית ומגבלה של 120 שניות, ויציאה שאינה אפס מכשילה את הריצה
context.history_fileתמליל .jsonl בתיקיית המקרה לחידוש. ה-prompt של המקרה הופך לתור המשתמש הבא
context.add_dirsתיקיות בתוך תיקיית המקרה ש-Claude רשאי לקרוא במהלך הריצה, מוענקות לקריאה בלבד
execution.promptה-prompt, כאשר אתה שומר את המקרה כולו ב-case.yaml ומשמיט את prompt.md
gradersרשימת graders, כל אחד עם name ועם אותם מפתחות שקובץ graders/*.md מקבל ב-frontmatter. עבור graders מסוג llm, שים את ה-rubric ב-criteria

#frontmatter של grader

כל קובץ grader תחת graders/ מקבל את המפתחות האלה ב-frontmatter, ועוד האפשרויות של הסוג שלו. שם ה-grader הוא שם הקובץ בלי .md:

מפתחברירת מחדלמטרה
typeנדרשאחד מסוגי ה-grader
weight1משקל יחסי בציון הריצה. כל מספר חיובי
armלא מוגדרwith-only מוציא את ה-grader מהניקוד בריצה בשתי זרועות. both כופה על grader ש-Claude Code היה מוציא אחרת להיות מנוקד בשתי הזרועות

#על מה grader יכול להסתכל

graders מסוג regex מקבלים target ו-graders מסוג llm מקבלים focus. שניהם מקבלים את אותם ערכים:

ערךמה ה-grader רואה
last_messageטקסט התגובה הסופית של Claude. זו ברירת המחדל
traceהסשן כ-JSON, הודעה אחת בכל שורה. grader מסוג regex רואה כל הודעה. שופט llm רואה את 12 הראשונות ואת 12 האחרונות. מרכאות ושורות חדשות בתוכו מוברחות כ-JSON, ולכן regex מתאים ל-\" במקום "
filesרשימת הנתיבים ש-Claude יצר במהלך הריצה, אחד בכל שורה. לא התוכן שלהם, ולא קבצים ש-scaffold יצר או ש-Claude רק שינה
{ source: file, path: <path> }התוכן של קובץ אחד בסביבת העבודה אחרי הריצה. השתמש בזה כדי לדרג את מה שהתוסף ייצר. קובץ PNG, JPEG, GIF או WebP מוצג לשופט llm כתמונה. שופט llm מסרב לקבצים בינאריים אחרים כגון .pptx או PDF. רנדר אותם לתמונה או כתוב אותם כטקסט ודרג את זה
mock_callsכל קריאה ש-Claude ביצע לכלי MCP מדומה, עם הקלט שלה והתשובה של ה-mock

#סוגי grader

כל סוג grader למטה מפרט את האפשרויות שלו ומתי הוא עובר:

סוגאפשרויותעובר כאשר
regexpattern, flags, match, targetה-regex של JavaScript בשם pattern נמצא ביעד. הגדר match: not_contains כדי לדרוש היעדרות או match: "count:N" כדי לדרוש בדיוק N התאמות. שים אי רגישות לאותיות ב-flags: i. (?i) בתוך התבנית לא נתמך
tool_usedtool, input_match, min, maxמספר הקריאות ל-tool שהקלט שלהן, מקודד כ-JSON, תואם ל-regex האופציונלי input_match נמצא בין min, ברירת מחדל 1, ל-max, ברירת מחדל ללא הגבלה. כדי לקבוע שכלי לא נקרא מעולם, הגדר גם min: 0 וגם max: 0
tool_orderbefore, afterשני הכלים נקראו והקריאה הראשונה שתואמת ל-before קודמת לקריאה הראשונה שתואמת ל-after. כל אחד הוא שם כלי או { tool, input_match }
file_existspath, existsקובץ ש-Claude יצר תואם ל-glob של path, או שאף אחד לא תואם עם exists: false. רק קבצים שנוצרו במהלך הריצה נספרים
llmcriteria, focusמודל שופט מצביע PASS על ה-rubric לפחות בשתיים מתוך שלוש הצבעות. בפריסת .md גוף הקובץ הוא ה-criteria
baselinebaseline_file, criteriaשופט מוצא שהריצה מקיימת את ה-criteria לפחות באותה מידה כמו תמליל הייחוס ב-baseline_file, קובץ .jsonl בתיקיית המקרה

#קבצי mock

קובץ <tool>.md תחת mocks/<server>/ עונה לכלי אחד. הגוף שלו הוא תוצאת הכלי, עם החלפות {{input.<field>}} ו-{{file:fixtures/<name>}}. ה-frontmatter שלו מקבל את המפתחות האלה:

מפתחברירת מחדלמטרה
typefixedfixed מחזיר את הגוף כפי שנכתב. agent מתייחס לגוף כהוראות למודל השופט, שפועל כשרת עבור הריצה ורואה קריאות קודמות כהיסטוריה
expectלא מוגדרמיפוי מנתיבי קלט מנוקדים לשם סוג כגון string, number, boolean, array או object, ל-/regex/, לליטרל, או לרשימת ליטרלים מותרים. קריאה שמפרה אותו מבטלת את הריצה עם ציון 0 ומדווחת כ-aborted עם השרת, הכלי והסיבה
errorfalseרק fixed. החזר את הגוף כשגיאת כלי
abort_whenלא מוגדררק agent. פרוזה שמפרטת את התנאים היחידים שבהם הסוכן רשאי לבטל את הריצה

שני קבצים אופציונליים יושבים לצד קבצי הכלים בתיקיית שרת:

  • _server.md: mock יחיד מסוג type: agent שעונה לכמה כלים, הרשומים במפתח ה-frontmatter tools: שלו. <tool>.md לאותו כלי מקבל עדיפות. בדיקת expect: כאן היא שגיאת טעינה אלא אם tools: מפרט כלי יחיד, לכן שים את הבדיקה על <tool>.md הבודד
  • _tools.json: תגובת tools/list שמורה מהשרת האמיתי, כדי שכלים מדומים ישאו את התיאורים וסכמות הקלט האמיתיים שלהם במקום placeholder מתירני

תיקיית mocks/ של המקרה עצמו משתמשת באותו מבנה ודורסת את ה-mocks של החבילה קובץ אחר קובץ.

#תבניות regex ב-expect

ערך /regex/ ב-expect: משתמש בניב קטן ש-Claude Code בודק כאשר הוא טוען את החבילה:

  • תווים ליטרליים, ., בריחות כגון \d, ומחלקות תווים כגון [a-z]
  • הכמתים *, +, ?, והצורות {m,n}, כל אחד על תו יחיד, בריחה או מחלקה
  • ^ אופציונלי בהתחלה ו-$ בסוף
  • הדגלים i ו-s בלבד

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

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

#פתרון בעיות

אלה הבעיות שמחברים נתקלים בהן לרוב, לפי מה שאתם רואים.

#"plugin eval is currently in early access"

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

#"plugin eval is currently unavailable"

Anthropic כיבתה את הפקודה בצד השרת. שום דבר במכונה שלכם לא מדליק אותה בחזרה. הריצו claude update ונסו שוב בסשן חדש מאוחר יותר.

#"is not a trusted plugin directory, and this run cannot stop to ask you about it"

זו הריצה הראשונה מול ספרייה ש Claude Code עדיין לא סומך עליה, והוא לא יכול לשאול אתכם כי stdin או stdout אינם מסוף, או שהעברתם --json. הריצו claude plugin eval <dir> פעם אחת במסוף וענו להנחיה, או העבירו --trust-plugin אם אתם סומכים על הקוד ועל חבילת הבדיקות של התוסף. ראו למה ריצה יכולה לגשת.

#"is too old for claude plugin eval"

ה git שב PATH שלכם ישן מ 2.31, ולכן claude plugin eval נעצר לפני הרצת מקרה כלשהו ויצא עם קוד 1 עם הודעה שמציינת את הגרסה שלכם:

git 2.30 is too old for claude plugin eval: it ignores the environment configuration (GIT_CONFIG_COUNT, added in git 2.31) that switches off the repository's git hooks and helper programs for the run. Install git 2.31 or newer.

בכל ריצה, Claude Code מכבה git hooks, credential helpers ותוכניות אחרות שתצורת git של מאגר יכולה להפעיל. הוא עושה זאת דרך תצורת סביבה ש git קורא רק מגרסה 2.31. git ישן יותר מתעלם מהתצורה הזו, ולכן החבילה נעצרת במקום לתת ציון לריצות שבהן התוכניות האלה יכולות לרוץ. התקינו git 2.31 או חדש יותר והריצו את החבילה שוב.

לפני v2.1.283, claude plugin eval לא בדק את גרסת git, וב git ישן יותר החבילה רצה כשהתוכניות האלה נשארו דלוקות.

#"cannot be evaluated where sessions load it"

הפעלתם claude plugin eval במעטפת עם שם של תוסף שהותקן מ marketplace שהוספתם מנתיב מקומי. אף סשן לא יכול לטעון את התוסף מאותו marketplace עכשיו, ולכן הפקודה מסרבת עם ההודעה הזו ויוצאת עם קוד 1.

Error: formatter@my-marketplace cannot be evaluated where sessions load it: no session could load it now. Its marketplace is a folder whose catalog could not be read, or lists it no more. Check the marketplace with `claude plugin marketplace list`, or pass ./<dir> to evaluate a directory.

המשפט אחרי no session could load it now. מציין מה נכשל.

הריצו claude plugin marketplace list במעטפת ותקנו את מה שההודעה מציינת. לדוגמה, שחזרו את ספריית ה marketplace או החזירו את רשומת התוסף לקטלוג שלו. כדי להעריך את מקור התוסף בלי ה marketplace, העבירו את הספרייה שלו כיעד, למשל claude plugin eval ./plugins/formatter.

#"No eval cases found"

אין <case>/prompt.md או <case>/case.yaml מתחת לספריית ה eval שבתוקף, או שמסנני --case ו --tag שלכם לא התאימו לאף מקרה. הריצו משורש התוסף, או הריצו claude plugin eval init כדי ליצור חבילה.

#זרוע הבסיס לא מציגה תוסף, או שהדלתא היא אפס

אם בסיכום אין עמודת W/OUT, או שמקרה נכשל עם "ablation requested but no plugin resolved", הסיבה הרגילה היא שלא נמצא תוסף למקרה. אם כל מקרה ממשיך תמליל דרך context.history_file, העמודה החסרה צפויה במקום זאת, כי המקרים האלה רצים בזרוע אחת כברירת מחדל. אחרת, הוסיפו plugins: ["../.."] למקרה, עם הנתיב מספריית המקרה אל ספריית התוסף.

אם התוסף כן נטען ו Δ עדיין קרוב לאפס כשה grader של tool_used: Skill נכשל, זו בדרך כלל ממצא אמיתי, כלומר ה description של ה skill לא מופעל על הניסוח של ה prompt. התאימו את ה description והריצו שוב את אותה חבילה.

#"Agent type '...' not found" עבור אחד מה agents של התוסף שלכם

כברירת מחדל כל מקרה רץ גם עם התוסף שלכם וגם בלעדיו, והריצות בלעדיו הן קו הבסיס בלי תוסף. כש Claude משגר אחד מה agents של התוסף שלכם בריצת בסיס, קריאת כלי Agent נכשלת עם Agent type '<plugin>:<agent-name>' not found. Available agents: .... הרשימה מציינת רק agents שקיימים בלי התוסף, כמו subagents מובנים.

השגיאה צפויה, כי Δ משווה את הריצות של התוסף שלכם מול קו הבסיס. בתוצאת ה JSON, ריצות הבסיס נמצאות תחת cases[].arms.without.

בריצות שבהן התוסף שלכם טעון, מקרה שמציין Agent ב allowed_tools יכול לשגר אחד מה agents של התוסף לפי השם עם מרחב השמות, למשל my-plugin:code-reviewer עבור ה agent code-reviewer בתוסף בשם my-plugin. כדי לדלג על ריצות הבסיס, העבירו --ablation none.

#הכל מקבל ציון אפס למרות שנוצרו הקבצים הנכונים

ה graders שלכם מכוונים ל files, רשימת הנתיבים שנוצרו, כשהתכוונתם לתוכן הקובץ. השתמשו ב { source: file, path: <path> } בתור target או focus.

בנפרד, file_exists סופר רק קבצים שנוצרו במהלך הריצה, ולכן קובץ שה scaffold יצר או ש Claude רק ערך אינו נראה לו. תנו ציון לתוכן שלו, או השתמשו ב tool_used על Edit.

#regex על ה trace לא תואם לטקסט שאני רואה

  • יעד שגוי: ברירת המחדל של target היא last_message, לא ה trace.
  • בריחת JSON: כשאתם מגדירים target ל trace, זה JSON בכל שורה, ולכן מרכאות מופיעות כ \".
  • תחביר regex: regex משתמשים בתחביר JavaScript, ולכן שימו i ב flags במקום לכתוב (?i).

#כלים נדחים, כלי MCP חסרים, או ש Bash לא רץ

כל דבר מעבר לערכת הקריאה בלבד דורש את האישור שלכם, למשל --allow-tools Bash Write. שרתי ה MCP האישיים שלכם אף פעם לא נטענים בריצה. השרתים של התוסף עצמו לא מתחילים אלא אם בחרתם להצטרף, ואז גם הכלים שלהם צריכים אישור --allow-tools "mcp__plugin_<plugin>_<server>__*". כלי מדומה לא צריך אף אחד מהם.

#הריצה יוצאת עם 1 אבל התוצאות נראות תקינות

כשכל מקרה קיבל ציון ב --threshold או מעליו והפקודה עדיין יוצאת עם 1, שורה ב stderr מציינת את הסיבה, גם תחת --json. לדוגמה, קובץ מקרה נכשל בטעינה, או שריצות הסתיימו בשגיאת API. לשאר הסיבות, ראו את קודי היציאה.

ברירת המחדל של --threshold היא 1.0, ולכן הפקודה יוצאת עם 1 כשמקרה כלשהו מקבל ציון מתחת למושלם. הגדירו סף שמתאים לציון שאתם דורשים.

#--json output path must end in .json

שמתם את היעד אחרי --json, ולכן הוא נקרא כנתיב הפלט. שימו את היעד קודם, כמו ב claude plugin eval . --json, או תנו ל --json נתיב .json מפורש.

#grader מציג passed: false תחת ריצה שקיבלה ציון 1.0

ה grader הזה מוחרג מהציון במכוון בריצה עם שתי זרועות, ושדה scored שלו הוא false. ראו ציון מול קו הבסיס בלי תוסף.

#"ended on an API error and scored 0 without grading"

אזהרת stderr הזו אומרת שה API ענה לבקשה האחרונה של לפחות ריצה אחת בשגיאת HTTP, למשל הגבלת קצב או הגבלת שימוש בתוכנית. ריצה כזו מקבלת ציון 0 בלי תוצאות grader, והפקודה יוצאת עם קוד שאינו אפס, בלי קשר לציונים ול --threshold.

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

  • בלי --json: השגיאה נמצאת בסוף שורת ההתקדמות של הריצה, אחרי error:
  • תחת --json: השגיאה נמצאת בתוצאת ה JSON, ב cases[].arms.with[].error או ב cases[].arms.without[].error

הספירה כוללת גם ריצות בסיס. הציונים שלהן אף פעם לא קובעים אם מקרה עובר, ולכן כל מקרה יכול לעבור בזמן שהפקודה עדיין נכשלת. בריצה עם שתי זרועות, W/OUT ו Δ של המקרה המושפע מודפסים כ ,. להגבלת שימוש או קצב, ראו ריצות נכשלות עם שגיאת הגבלת שימוש או הגבלת קצב.

לפני v2.1.299, ריצה כזו קיבלה ציון לפי מה שהיא הספיקה לייצר.

#ריצות נכשלות עם שגיאת הגבלת שימוש או הגבלת קצב באמצע הדרך

ריצות שמסתיימות בשגיאת הגבלת שימוש או הגבלת קצב מקבלות ציון 0 בלי דירוג, והפקודה יוצאת עם קוד שאינו אפס. החבילה עדיין מסתיימת ואינה מסומנת partial. הריצו שוב אחרי שההגבלה מתאפסת, עם --runs 1 או מסנן --case אם אתם צריכים להישאר מתחתיה.

#"mock call budget exceeded"

כל type: agent mock בריצה שואב מתקציב קריאות אחד של פי ארבעה מ max_turns של המקרה, שהוא 40 קריאות בברירת המחדל של 10. גם קריאות שנענו מהקלטות .replay/ נספרות, ושורת ההתקדמות mock budget של המקרה מדפיסה את התקציב. קריאה מעבר לו מפסיקה את הריצה עם ציון 0 והסיבה הזו, ולכן העלו את max_turns במקרה עבור skill שעושה קריאות רבות ל agent mocks.

#ריצות מגיעות ל timeout או לתקרת התורות

ברירות המחדל הן 10 תורות ו 300 שניות. העלו את max_turns ואת timeout_seconds במקרה עבור משימות שצריכות יותר, והשתמשו ב --max-cost-usd כתקרת העלות במקום מגבלות הדוקות לכל ריצה.

#ראו גם

  • יצירת תוסף: בנו את התוסף שאתם בודקים, וטענו אותו עם --plugin-dir במהלך הפיתוח
  • הפניה לפקודות תוסף: רשומות הפקודות plugin eval ו plugin eval init. המפתח experimental.evals של ה manifest נמצא בהפניה ל manifest
  • Skills: איך ה description של skill מחליט מתי Claude מפעיל אותו, וזה מה שמקרה שבודק אם ה skill מופעל מודד
  • Sandboxing: ה sandbox ברמת מערכת ההפעלה שחל כשאתם מעניקים Bash לריצה
  • פרסום תוסף: פרסמו את התוסף ברגע שהחבילה שלו עוברת
  • מדידת עלות ושימוש של תוסף: מה התוסף מוסיף להקשר של כל סשן והאם אנשים עדיין משתמשים בו