מבדיקות אווירה להערכת סוכנים מבוססת-נתונים

1. מבוא

סקירה כללית

השיעור הזה הוא המשך לשיעור יצירת מערכות מרובות סוכנים באמצעות ADK.

במעבדה הזו, בניתם מערכת ליצירת קורסים המורכבת מ:

  1. סוכן חוקר: שימוש ב-google_search כדי למצוא מידע עדכני.
  2. סוכן שופטים: ביקורת על איכות ושלמות המחקר.
  3. סוכן בונה תוכן: הפיכת המחקר לקורס מובנה.
  4. סוכן Orchestrator: מנהל את תהליך העבודה והתקשורת בין המומחים האלה.

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

Researcher,‏ Judge ו-Content Builder נפרסים כסוכני A2A בשירותי Cloud Run נפרדים. Orchestrator הוא עוד שירות Cloud Run עם ADK Service API.

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

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

במעבדה זו, תחליפו "בדיקות ויברציה" סובייקטיביות בהערכה מבוססת נתונים באמצעות שירות ההערכה של Vertex AI Gen AI. תטמיעו מדדים אדפטיביים להערכה ומדדים לאיכות השימוש בכלי כדי להעריך באופן מדויק את מערכת מרובת הסוכנים המבוזרת שנבנתה במעבדה 1. לבסוף, תהפכו את התהליך הזה לאוטומטי בצינור CI/CD, כדי להבטיח שכל פריסה תשמור על האמינות והדיוק של סוכני הייצור שלכם.

תבנו פייפליין להערכה מתמשכת של הסוכנים. במאמר הזה תלמדו איך:

  1. פריסת הסוכנים לגרסה מתויגת פרטית ב-Google Cloud Run (פריסת צל).
  2. הפעל חבילת הערכה אוטומטית כנגד הגרסה הספציפית הזו באמצעות Vertex AI Gen AI Evaluation Service.
  3. דמיינו ונתחו את התוצאות.
  4. משתמשים בהערכה כחלק מצינור ה-CI/CD.

2. מושגים מרכזיים: תיאוריה של הערכת תפקוד הסוכנים

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

ההבדל העיקרי הוא בין גילוי לבין הגנה:

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

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

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

המכשול 'Vibe Check'

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

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

מלכודת ה-Vibe Check

שתי דרכים לדרג את ביצועי הסוכן

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

  1. בודקי תשובות מבוססי-קוד (דטרמיניסטיים):
    • מה הם מודדים: אילוצים מחמירים (למשל, 'האם הוחזר JSON תקין?', האם הוא הפעיל את הכלי search?").
    • יתרונות: מהיר, זול, מדויק ב-100%.
    • חסרונות: אי אפשר לשפוט ניואנסים או איכות.
  2. מדרגים מבוססי מודל (הסתברותי):
    • ידוע גם בשם "LLM-כמו-שופט". אנחנו משתמשים במודל חזק (כמו Gemini 3 Pro) כדי להעריך את הפלט של הסוכן.
    • מה הם מודדים: ניואנסים, חשיבה רציונלית, יעילות, בטיחות.
    • יתרונות: יכול להעריך משימות מורכבות ופתוחות.
    • חסרונות: איטי יותר, יקר יותר, דורש הנדסת פרומפטים מדויקת לשופט.

מדדי הערכה של Vertex AI

בשיעור ה-Lab הזה נשתמש בVertex AI Gen AI Evaluation Service, שמספק מדדים מנוהלים כך שלא תצטרכו לכתוב כל שופט מאפס.

ישנן מספר דרכים לקבץ מדדים להערכת תפקוד הסוכנים:

  • מדדים מבוססי קריטריונים: שילוב של מודלי שפה גדולים בתהליכי עבודה של הערכה.
    • קריטריונים להערכה שמותאמים לכל שאלה: קריטריונים להערכה נוצרים באופן דינמי לכל הנחיה. התשובות נבדקות באמצעות משוב מפורט וברור, שכולל ציון הצלחה או כישלון שספציפי להנחיה.
    • רובריקות סטטיות: רובריקות מוגדרות במפורש ואותה רובריקה חלה על כל ההנחיות. התשובות נבדקות באמצעות אותה קבוצה של בודקים שמבוססים על ניקוד מספרי. ציון מספרי יחיד (למשל 1-5) לכל הנחיה. כשנדרשת הערכה בממד ספציפי מאוד או כשנדרשת אותה רובריקה בדיוק בכל ההנחיות.
  • השוואות מבוססות-מחשוב: הערכת תגובות באמצעות אלגוריתמים דטרמיניסטיים, בדרך כלל באמצעות אמת קרקע (ground truth). ציון מספרי (למשל 0.0 עד 1.0) לכל הנחיה. כשיש נתוני אמת שאפשר להתאים להם שיטה דטרמיניסטית.
  • מדדים של פונקציות מותאמות אישית: אפשר להגדיר מדד משלכם באמצעות פונקציית Python.

מדדים ספציפיים שבהם נשתמש:

  • Final Response Match: (מבוסס-הפניה) האם התשובה תואמת ל'תשובה המושלמת' שלנו?
  • Tool Use Quality: (ללא הפניות) האם הסוכן השתמש בכלים רלוונטיים בצורה נכונה?
  • Hallucination: (ללא הפניה) האם הטענות בתשובה נתמכות על ידי ההקשר שאוחזר?
  • Tool Trajectory Precision ו-Tool Trajectory Recall (מבוסס על הפניה) האם הסוכן בחר את הכלי הנכון וסיפק טיעונים תקפים? בניגוד ל-Tool Use Quality, המדדים המותאמים אישית האלה משתמשים בנתיב התייחסות – רצף של קריאות צפויות לכלים וארגומנטים.

3. הגדרה

הגדרות אישיות

  1. פותחים את Cloud Shell: לוחצים על הסמל Activate Cloud Shell (הפעלת Cloud Shell) בפינה הימנית העליונה של מסוף Google Cloud.
  2. מריצים את הפקודה הבאה כדי לרענן את הכניסה ולעדכן את Application Default Credentials (ADC):
    gcloud auth login --update-adc
    
    פועלים לפי ההוראות כדי להשלים את הכניסה בדפדפן.
  3. מגדירים פרויקט פעיל ל-CLI של gcloud.מריצים את הפקודה הבאה כדי לקבל את הפרויקט הנוכחי של gcloud:
    gcloud config get-value project
    
    אם היא לא מוגדרת, מריצים את הפקודה הבאה:
    gcloud config set project YOUR_PROJECT_ID
    
    מחליפים את YOUR_PROJECT_ID במזהה הפרויקט.
  1. מגדירים את אזור ברירת המחדל שבו ייפרסו שירותי Cloud Run.
    gcloud config set run/region us-west1
    
    בִּמקוֹםus-west1, אתה יכול להשתמש בכלאזור Cloud Run קרוב יותר אליך.

קוד ויחסי תלות

  1. משכפלים את קוד נקודת ההתחלה ומעבירים את הספרייה לשורש הפרויקט.
    cd ~
    git clone https://github.com/vladkol/agent-evaluation-lab -b starter
    cd agent-evaluation-lab
    
  2. יצירת קובץ .env:
    echo "GOOGLE_GENAI_USE_VERTEXAI=true" > .env
    echo "GOOGLE_CLOUD_PROJECT=$(gcloud config get-value project -q)" >> .env
    echo "GOOGLE_CLOUD_REGION=$(gcloud config get-value run/region -q)" >> .env
    echo "GOOGLE_CLOUD_LOCATION=global" >> .env
    
  3. התקן תלויות על ידי הפעלת הפקודה הבאה בחלון הטרמינל:
    uv sync
    

4. הסבר על פריסה בטוחה

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

תגי גרסה ופריסה שקופה

‫Google Cloud Run תומך בגרסאות. בכל פעם שמבצעים פריסה, נוצרת גרסה חדשה שלא ניתן לשנות. אתם יכולים להקצות תגים לגרסאות האלה כדי לגשת אליהן דרך כתובת URL ספציפית, גם אם הן מקבלות 0% מהתנועה הציבורית.

למה לא להריץ הערכות באופן מקומי?

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

  1. שוויון בין סביבות: סביבות מקומיות שונות (רשת שונה, מעבד/זיכרון שונים, סודות שונים). בדיקה בענן מבטיחה שהסוכן שלך יעבוד בסביבת זמן הריצה בפועל (בדיקת מערכת).
  2. אינטראקציה בין סוכנים מרובים: במערכת מבוזרת, סוכנים משוחחים דרך HTTP. בבדיקות 'מקומיות' בדרך כלל מדמים את החיבורים האלה. בפריסת צללים נבדקים זמן האחזור האמיתי של הרשת, הגדרות הזמן הקצוב לתפוגה והאימות בין המיקרו-שירותים.
  3. סודות והרשאות: מאמת שלחשבון השירות שלך יש את ההרשאות הנדרשות (למשל, לקרוא ל-Vertex AI או לקרוא מ-Firestore).

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

תהליך העבודה של CI/CD: פריסה, הערכה, קידום

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

  1. שמירה: משנים את ההנחיה של הסוכן ודוחפים אותה למאגר.
  2. פריסה (מוסתרת): פעולה זו מפעילה פריסה של גרסה חדשה שתויגה עם קוד ה-commit (לדוגמה, c-abc1234). גרסה זו מקבלת 0% מהתנועה הציבורית.
  3. הערכה: סקריפט ההערכה מטַרגט את כתובת ה-URL הספציפית של הגרסה https://c-abc1234---researcher-xyz.run.app.
  4. קידום: אם (ורק אם) ההערכה עוברת בהצלחה ובדיקות אחרות מצליחות, מעבירים את התנועה לגרסה החדשה הזו.
  5. חזרה לגרסה קודמת: אם הפריסה נכשלת, המשתמשים אף פעם לא ראו את הגרסה הבעייתית, ואפשר פשוט להתעלם מהגרסה הבעייתית או למחוק אותה.

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

ניתוח של evaluate.sh

פתיחת evaluate.sh. הסקריפט הזה מבצע את התהליך באופן אוטומטי.

export COMMIT_SHORT_HASH=$(git rev-parse --short HEAD)
export COMMIT_REVISION_TAG="c-${COMMIT_SHORT_HASH}"

# ...

# Deploy services with a revision tag and NO traffic
source ./deploy.sh --revision-tag $COMMIT_REVISION_TAG --no-redeploy

# Run the evaluation against that specific tag
uv run -m evaluator.evaluate_agent

ה-deploy.sh מטפל בפריסת עדכונים עם האפשרויות --no-traffic ו---tag. אם כבר פועל שירות, הוא לא יושפע. הגרסה החדשה שמוגדרת כ'מוסתרת' לא תקבל תנועה, אלא אם תפעילו אותה באופן מפורש באמצעות כתובת URL מיוחדת שמכילה את תג הגרסה (למשל, https://c-abc1234---researcher-xyz.run.app)

5. הטמעה של סקריפט ההערכה

עכשיו, בואו נכתוב את הקוד שבאמת מריץ את הבדיקות.

  1. פתיחת evaluator/evaluate_agent.py.
  2. תראו ייבוא ​​והגדרות, אבל המדדים ולוגיקת הביצוע חסרים.

הגדרת המדדים

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

אנחנו רוצים למדוד:

  • התאמה לתשובה הסופית: (יכולת) האם התשובה תואמת לתשובה הצפויה? זהו מדד שמבוסס על הפניה. הוא משתמש ב-LLM של שופט כדי להשוות את תפוקת הסוכן לתשובה הצפויה. הוא לא מצפה שהתשובה תהיה זהה, אלא דומה מבחינה סמנטית ועובדתית.
  • Tool Use Quality (איכות השימוש בכלי): מדד מותאם אישית שמיועד להערכת הבחירה של כלים מתאימים, השימוש הנכון בפרמטרים וההקפדה על רצף הפעולות שצוין.
  • המסלול של השימוש בכלי: (Trace) שני מדדים מותאמים אישית שמודדים את המסלול של השימוש בכלי על ידי הסוכן (דיוק והחזרה) בהשוואה למסלולים הצפויים. המדדים האלה מיושמים ב-shared/evaluation/tool_metrics.py כפונקציות בהתאמה אישית. בניגוד לאיכות השימוש בכלים, המדד הזה הוא מדד דטרמיניסטי מבוסס-הפניה – הקוד בודק באופן מילולי אם הקריאות בפועל לכלים תואמות לנתוני ההפניה (reference_trajectory בנתוני ההערכה).

מדדים מותאמים אישית של מסלול השימוש בכלי

כדי ליצור מדדים מותאמים אישית של מסלול השימוש בכלי, יצרנו קבוצה של פונקציות Python ב-shared/evaluation/tool_metrics.py. כדי לאפשר ל-Vertex AI Gen AI Evaluation Service לבצע את הפונקציות הללו, עלינו להעביר אליו את קוד הפייתון.

כדי לעשות את זה, מגדירים אובייקט EvaluationRunMetric עם הגדרות UnifiedMetric ו-CustomCodeExecutionSpec. הפרמטר remote_custom_function הוא מחרוזת המכילה את קוד הפייתון של הפונקציה. יש לקרוא לפונקציה כ-evaluate:

def evaluate(
    instance: dict
) -> float:
    ...

יצרנו עוזר get_custom_function_metric (ב-shared/evaluation/evaluate.py) שממיר פונקציית Python למדד הערכה מותאם אישית של קוד.

היא מקבלת את הקוד של מודול הפונקציה (כדי לתעד יחסי תלות מקומיים), יוצרת פונקציה נוספת evaluate שקוראת לפונקציה המקורית, ומחזירה אובייקט EvaluationRunMetric עם CustomCodeExecutionSpec.

import inspect
module_source = inspect.getsource(
    inspect.getmodule(metrics_function)
)
module_source += (
    "\n\ndef evaluate(instance: dict) -> float:\n"
    f"    return {metrics_function.__name__}(instance)\n"
)
return types.EvaluationRunMetric(
    metric=metric_name,
    metric_config=types.UnifiedMetric(
        custom_code_execution_spec=types.CustomCodeExecutionSpec(
            remote_custom_function=module_source
        )
    )
)

שירות ההערכה של Gen AI יבצע את הקוד הזה בסביבת ביצוע ארגז חול (sandbox), ויעביר אליו את נתוני ההערכה.

הוספת המדדים וקוד ההערכה

מוסיפים את הקוד הבא אל evaluator/evaluate_agent.py אחרי השורה if __name__ == "__main__":.

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

    eval_data_researcher = os.path.dirname(__file__) + "/eval_data_researcher.json"
    metrics=[
        # Compares the agent's output against a "Golden Answer"
        types.RubricMetric.FINAL_RESPONSE_MATCH,
        # Did the agent use the tools effectively?
        types.RubricMetric.TOOL_USE_QUALITY,
        # Custom metrics for tools trajectory analysis
        get_custom_function_metric("trajectory_precision", trajectory_precision_func),
        get_custom_function_metric("trajectory_recall", trajectory_recall_func)
    ]

    print("🧪 Running Researcher Evaluation...")
    eval_results = asyncio.run(
        # Run the evaluation and retrieve the results.
        evaluate_agent(
            agent_api_server=RESEARCHER_URL, # Agent Service URL (in Cloud Run).
            agent_name="agent", # Agent name as it's exposed by the server.
            evaluation_data_file=eval_data_researcher, # Evaluation data file.
            # GCS location for the Evaluation Service to store the result to.
            evaluation_storage_uri=f"gs://{GOOGLE_CLOUD_PROJECT}-agents/evaluation",
            metrics=metrics, # Metrics to use when evaluating the agent.
            project_id=GOOGLE_CLOUD_PROJECT,
            location=GOOGLE_CLOUD_REGION
        )
    )
    print(f"\n🧪 Researcher Evaluation results:\n{eval_results}")
    print(f"Evaluation Run ID: {eval_results.run_id}")

בצינור עיבוד נתונים אמיתי של ייצור, צריך קריטריונים להערכה. אחרי שההערכה מסתיימת והמדדים מוכנים. יהיה לך כאן שלב שער (Gating Step). לדוגמה: "אם הציון של Final Response Match נמוך מ-0.75, הבנייה נכשלת". כך נמנע מצב שבו גרסאות לא טובות יקבלו תנועה.

מוסיפים את הקוד הבא ל-evaluator/evaluate_agent.py:

    METRIC_THRESHOLD = 0.75
    researcher_eval_failed = False
    if eval_results.state != types.EvaluationRunState.SUCCEEDED:
        print(f"🛑 Researcher Evaluation failed with state {eval_results.state}.")
        researcher_eval_failed = True
    else:
        for metric_name, metric_values in eval_results.metrics.items():
            if metric_values["mean"] < METRIC_THRESHOLD:
                print(f"🛑 Researcher Evaluation failed with metric `{metric_name}` below {METRIC_THRESHOLD} threshold.")
                researcher_eval_failed = True
    if researcher_eval_failed:
        exit(1)

בכל פעם שמְמוּצָע הערך של כל אחד ממדדי ההערכה נמוך מ-סַף (0.75 ), הפריסה אמורה להיכשל.

[אופציונלי] הוספת הערכה באמצעות מדדים ללא הפניה עבור כלי התזמור

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

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

מוסיפים את הלוגיקה של ההערכה ל-Orchestrator:

    eval_data_orchestrator = os.path.dirname(__file__) + "/eval_data_orchestrator.json"
    metrics=[
        types.RubricMetric.HALLUCINATION,
    ]

    print("🧪 Running Orchestrator Evaluation...")
    eval_results = asyncio.run(evaluate_agent(
        agent_api_server=ORCHESTRATOR_URL,
        agent_name="agent",
        evaluation_data_file=eval_data_orchestrator,
        evaluation_storage_uri=f"gs://{GOOGLE_CLOUD_PROJECT}-agents/evaluation",
        metrics=metrics,
        project_id=GOOGLE_CLOUD_PROJECT,
        location=GOOGLE_CLOUD_REGION
    ))
    print(f"\n🧪 Orchestrator Evaluation results:\n{eval_results}")
    print(f"Evaluation Run ID: {eval_results.run_id}")
    METRIC_THRESHOLD = 0.75
    orchestrator_eval_failed = False
    if eval_results.state != types.EvaluationRunState.SUCCEEDED:
        print(f"🛑 Orchestrator Evaluation failed with state {eval_results.state}.")
        orchestrator_eval_failed = True
    else:
        for metric_name, metric_values in eval_results.metrics.items():
            if metric_values["mean"] < METRIC_THRESHOLD:
                print(f"🛑 Orchestrator Evaluation failed with metric `{metric_name}` below {METRIC_THRESHOLD} threshold.")
                orchestrator_eval_failed = True
    if orchestrator_eval_failed:
        exit(1)

בדיקת נתוני ההערכה

פותחים את הספרייה evaluator/. יוצגו שני קובצי נתונים:

  • eval_data_researcher.json: הנחיות והפניות לנתוני אמת (Golden/Ground-Truth) עבור החוקר.
  • eval_data_orchestrator.json: הנחיות ל-Orchestrator (אנחנו מבצעים הערכה ללא הפניה רק ל-Orchestrator).

כל רשומה בדרך כלל כוללת:

  • prompt: ההנחיה לסוכן.
  • reference: התשובה האידיאלית (האמת הבסיסית), אם רלוונטי.
  • reference_trajectory: הרצף הצפוי של קריאות לכלי.

6. הסבר על קוד ההערכה

פתיחת shared/evaluation/evaluate.py. המודול הזה מכיל את לוגיקת הליבה להפעלת הערכות. פונקציית המקש היא evaluate_agent.

הקוד מבצע את הפעולות הבאות:

  1. טעינת נתונים: קריאה של מערך הנתונים של ההערכה (הנחיות והפניות) מקובץ.
  2. הסקת מסקנות מקבילית: הרצת הסוכן מול מערך הנתונים במקביל. הוא מטפל ביצירת סשן, שולח הנחיות ולוכד הן את התגובה הסופית והן את מעקב ביצוע הכלי הביניים.
  3. הערכת בינה מלאכותית של Vertex: מאחד את נתוני ההערכה המקוריים עם התגובות הסופיות ומעקב ביצוע הכלי הביניים, ושולח את התוצאות ל-שירות ההערכה של בינה מלאכותית של Vertex עם לקוח GenAI ב-Vertex AI SDK. שירות זה מפעיל את המדדים שתצורתם נקבעה כדי לדרג את ביצועי הסוכן.

הרגע המרכזי בשלב האחרון הוא קריאה לפונקציה create_evaluation_run של מודול ה-eval של Gen AI SDK:

evaluation_run = client.evals.create_evaluation_run(
    dataset=agent_dataset_with_inference,
    agent_info=agent_info,
    metrics=metrics,
    dest=evaluation_storage_uri
)

אנו עושים זאת בפונקציה evaluate_agent ב-shared/evaluation/evaluate.py.

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

‫Agent Info API

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

אבל איך אנחנו משיגים את המידע הזה? אנחנו הופכים אותו לחלק מ-ADK Service API.

פותחים את shared/adk_app.py ומחפשים את def agent_info. אפשר לראות שאפליקציית ה-ADK חושפת נקודת קצה (endpoint) של כלי עזר:

@app.get("/apps/{agent_name}/agent-info")
async def agent_info(agent_name: str) -> typing.Dict[str, typing.Any]:
    # ...
    return {
        "name": agent.name,
        "instruction": str(getattr(agent, "instruction", None)),
        "tool_declarations": tools_dict_list
    }

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

7. הרצת ההערכה

אחרי שמטמיעים את כלי ההערכה, מפעילים פתרונות חכמים.

  1. מריצים את סקריפט ההערכה מהרמה הבסיסית (root) של המאגר:
    ./evaluate.sh
    
    מה קורה אחר כך?
    1. הפונקציה מחזירה את הגיבוב (hash) הנוכחי של ה-commit ב-Git.
    2. זה מפעיל את deploy.sh כדי לפרוס גרסה עם תג המבוסס על ה-commit hash.
    3. לאחר הפריסה, הוא מתחיל ב-evaluator.evaluate_agent.
    4. תראו סרגלי התקדמות בזמן שהמערכת תפעיל את מקרי הבדיקה מול שירות הענן שלכם.
    5. בסוף, הוא מדפיס סיכום של התוצאות ב-JSON.
    יכול להיות שתופיע ההודעה הבאה כשמריצים את הסקריפט:
    Deploying from source requires an Artifact Registry Docker repository to store built containers. A repository named [cloud-run-source-deploy] in region [us-west1] will be created.
    
    Do you want to continue (Y/n)?
    
    מקישים על Enter כדי לאפשר יצירה של המאגר.
    הערה: יכול להיות שההרצה הראשונה תימשך כמה דקות עד לפריסת השירותים.

8. הצגת תוצאות במחברת

קשה לקרוא את פלט ה-JSON הגולמי. לקוח Gen AI ב-Vertex AI SDK מספק דרך לעקוב אחר הריצות הללו לאורך זמן. נשתמש ב-notebook של Colab כדי להציג את התוצאות באופן חזותי.

  1. אפשר לפתוח את evaluator/show_evaluation_run.ipynb ב-Google Colab באמצעות הקישור הזה.
  2. הגדר את המשתנים GOOGLE_CLOUD_PROJECT, GOOGLE_CLOUD_REGION ו-EVAL_RUN_ID למזהה הפרויקט, האזור ומזהה הריצה שלך.
  1. התקן תלויות ואימות.

אחזור של ההרצה של ההערכה והצגת התוצאות

אנחנו צריכים לאחזר את נתוני ההרצה של ההערכה מ-Vertex AI. מחפשים את התא בקטע Retrieve Evaluation Run and Display Results ומחליפים את השורה # TODO בבלוק הקוד הבא:

from google.genai import types as genai_types
from vertexai import Client

# Initialize SDK
client = Client(
    project=GOOGLE_CLOUD_PROJECT,
    location=GOOGLE_CLOUD_REGION,
    http_options=genai_types.HttpOptions(api_version="v1beta1"),
)

evaluation_run = client.evals.get_evaluation_run(
    name=EVAL_RUN_ID,
    include_evaluation_items=True
)
evaluation_run.show()

פירוש התוצאות

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

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

Pass@1 לעומת Pass@k: כשמריצים בדיקה מסוימת פעם אחת, מקבלים את הציון Pass@1. אם סוכן נכשל, יכול להיות שהסיבה לכך היא חוסר דטרמיניזם. בהגדרות מתוחכמות, ייתכן שתפעילו כל בדיקה k פעמים (למשל, 5 פעמים) ותחשבו pass@k (האם היא הצליחה לפחות פעם אחת?) או pass^k (האם היא הצליחה בכל פעם?). זה מה שמדדים רבים כבר עושים מתחת למכסה המנוע. לדוגמה, types.RubricMetric.FINAL_RESPONSE_MATCH (התאמה לתשובה הסופית) מבצע 5 קריאות למודל שפה גדול (LLM) שמשמש כשופט כדי לקבוע את ציון ההתאמה לתשובה הסופית.

9. אינטגרציה רציפה ופריסה רציפה (CI/CD)

במערכת ייצור, הערכת תפקוד הסוכנים צריכה להתבצע כחלק מצינור ה-CI/CD. Cloud Build היא אפשרות טובה לכך.

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

הערכה מתמשכת

הגדרות Cloud Build

כעת, בואו ניצור סקריפט תצורה לפריסה של Cloud Run שיבצע את השלבים הבאים:

  1. פריסת שירותים לגרסה פרטית.
  2. מריץ הערכה של הסוכן.
  3. אם ההערכה עוברת, היא "מקדמת" פריסות גרסאות כך שיספקו 100% מהתנועה.

יצירה cloudbuild.yaml:

steps:
- name: gcr.io/google.com/cloudsdktool/google-cloud-cli:latest
  entrypoint: /bin/bash
  env:
      - 'BUILD_ID=$BUILD_ID'
  args:
      - "-c"
      - |
        if [[ "$_COMMIT_SHORT_HASH" != "" ]]; then
          export COMMIT_SHORT_HASH=$_COMMIT_SHORT_HASH
        else
          export COMMIT_SHORT_HASH=$SHORT_SHA
        fi
        export COMMIT_REVISION_TAG="c-$${COMMIT_SHORT_HASH}"
        echo "Deploying with revision tag: $$COMMIT_REVISION_TAG"
        set -e
        # Install uv and sync dependencies.
        curl -LsSf https://astral.sh/uv/install.sh | sh
        source $$HOME/.local/bin/env
        uv sync

        # Deploy services with the revision tag.
        source ./deploy.sh --revision-tag $$COMMIT_REVISION_TAG --no-redeploy

        # Run evaluation.
        uv run -m evaluator.evaluate_agent
        # If evaluation fails, the deployment will stop here.

        # If evaluation passes, it will continue with promoting the revisions to serve 100% of traffic.
        echo "Promoting revisions $$COMMIT_REVISION_TAG to serve 100% of traffic."
        gcloud run services update-traffic researcher --to-tags $$COMMIT_REVISION_TAG=100 --region $$GOOGLE_CLOUD_REGION --project $$GOOGLE_CLOUD_PROJECT
        gcloud run services update-traffic judge --to-tags $$COMMIT_REVISION_TAG=100 --region $$GOOGLE_CLOUD_REGION --project $$GOOGLE_CLOUD_PROJECT
        gcloud run services update-traffic content-builder --to-tags $$COMMIT_REVISION_TAG=100 --region $$GOOGLE_CLOUD_REGION --project $$GOOGLE_CLOUD_PROJECT
        gcloud run services update-traffic orchestrator --to-tags $$COMMIT_REVISION_TAG=100 --region $$GOOGLE_CLOUD_REGION --project $$GOOGLE_CLOUD_PROJECT
        gcloud run services update-traffic course-creator --to-tags $$COMMIT_REVISION_TAG=100 --region $$GOOGLE_CLOUD_REGION --project $$GOOGLE_CLOUD_PROJECT

options:
  substitutionOption: 'ALLOW_LOOSE'
  defaultLogsBucketBehavior: REGIONAL_USER_OWNED_BUCKET

הפעלת הפייפליין

בסוף, אפשר להריץ את צינור ההערכה.

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

  1. יצירת סקריפט run_cloud_build.sh:
    #!/bin/bash
    
    set -e
    source .env
    
    BUILD_SA_NAME="agent-eval-build-sa"
    BUILD_SA_EMAIL="${BUILD_SA_NAME}@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com"
    COMMIT_SHORT_HASH=$(git rev-parse --short HEAD)
    
    # Creating service account for Cloud Build. Granting necessary roles to it.
    if ! gcloud iam service-accounts describe "${BUILD_SA_EMAIL}" --project "${GOOGLE_CLOUD_PROJECT}" &> /dev/null; then
        echo "Creating service account ${BUILD_SA_NAME} for Cloud Build."
        gcloud iam service-accounts create ${BUILD_SA_NAME} --project "${GOOGLE_CLOUD_PROJECT}" --display-name "Agent Build Service Account"
    
        echo "Granting roles to service account ${BUILD_SA_NAME}..."
        sleep 10
        ROLES=(
            "roles/cloudbuild.builds.builder"
            "roles/run.admin"
            "roles/run.invoker"
            "roles/iam.serviceAccountOpenIdTokenCreator"
            "roles/iam.serviceAccountUser"
            "roles/serviceusage.serviceUsageAdmin"
            "roles/serviceusage.serviceUsageConsumer"
            "roles/aiplatform.user"
            "roles/logging.logWriter"
            "roles/storage.admin"
            "roles/artifactregistry.writer"
        )
    
        # Loop through and grant each role
        for ROLE in "${ROLES[@]}"; do
            gcloud projects add-iam-policy-binding "$GOOGLE_CLOUD_PROJECT" \
                --member="serviceAccount:$BUILD_SA_EMAIL" \
                --role="$ROLE" \
                --condition=None \
                --quiet
        done
    
        echo "Waiting for 60 seconds for the permission changes to propagate..."
        sleep 60
    fi
    
    gcloud builds submit --config cloudbuild.yaml \
        --service-account="projects/${GOOGLE_CLOUD_PROJECT}/serviceAccounts/${BUILD_SA_EMAIL}" \
        --machine-type=e2-highcpu-32 \
        --timeout=120m \
        --substitutions _COMMIT_SHORT_HASH=$COMMIT_SHORT_HASH,_GOOGLE_CLOUD_PROJECT=$GOOGLE_CLOUD_PROJECT,_GOOGLE_CLOUD_LOCATION=$GOOGLE_CLOUD_LOCATION,_GOOGLE_CLOUD_REGION=$GOOGLE_CLOUD_REGION
    
    
    הסקריפט הזה:
    • יוצר חשבון שירות ייעודי agent-eval-build-sa.
    • מקצה לו את התפקידים הנדרשים (roles/run.admin,‏ roles/aiplatform.user וכו'). ‫*. שולח את ה-build ל-Cloud Build.
  2. מריצים את הפייפליין:
    chmod +x run_cloud_build.sh
    ./run_cloud_build.sh
    

אפשר לצפות בהתקדמות הבנייה במסוף או ללחוץ על הקישור למסוף Cloud.

הערה: בסביבת ייצור אמיתית, היית מגדיר Cloud Build Trigger כדי להפעיל זאת באופן אוטומטי בכל git push. תהליך העבודה זהה: הטריגר יופעל cloudbuild.yaml, וכך יובטח שכל פעולת commit תיבדק.

10. סיכום

יצרתם בהצלחה צינור עיבוד נתונים להערכה.

  • פריסה: השתמשתם בתגי עדכון עם גיבוב של git commit כדי לפרוס סוכנים בצורה בטוחה בסביבה אמיתית לצורך בדיקה, בלי להשפיע על פריסות בייצור.
  • הערכה: הגדרתם מדדי הערכה, ויצרתם אוטומציה של תהליך ההערכה באמצעות שירות ההערכה של Vertex AI ל-AI גנרטיבי.
  • ניתוח: השתמשתם במחברת Colab כדי להציג את תוצאות ההערכה ולשפר את הסוכן שלכם.
  • השקה: השתמשתם ב-Cloud Build כדי להפעיל את צינור העיבוד של ההערכה באופן אוטומטי, וקידמתם את הגרסה הכי טובה כדי להציג אותה ל-100% מהתנועה.

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