Build AI Agents with the Agent Development Kit (ADK) for Java

1. ברוכים הבאים, מפתחים של נציגי תמיכה מבוססי-AI!

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

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

d666c455bb267688.png

מה תלמדו

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

הדרישות

  • דפדפן אינטרנט שנשתמש בו במצב פרטי.
  • חשבון Gmail אישי.
  • פרויקט חדש ב-Google Cloud שמשויך לחשבון Gmail האישי שלכם.
  • חשבון לחיוב שנוצר באמצעות קרדיטים ל-Google Cloud שמומשו.
  • כלי שורת הפקודה של git כדי לבדוק את קוד המקור.
  • ‫Java 17 ואילך ו-Apache Maven.
  • עורך טקסט או סביבת פיתוח משולבת (IDE), כמו IntelliJ IDEA או VS Code.

אפשר להשתמש בעורך VS Code המובנה ב-Cloud Shell, במסוף Google Cloud.

2. הגדרה: הסביבה שלכם

מימוש קרדיטים ב-Google Cloud לסדנה

e3e67ae61e86ec9f.png

אם אתם משתתפים בסדנה בהנחיית מדריך, תקבלו קישור לאתר שבו תוכלו לממש קרדיטים ל-Google Cloud לשימוש בסדנה.

  • שימוש בחשבון Google אישי: חשוב להשתמש בחשבון Google אישי (למשל כתובת עם הסיומת gmail.com), כי כתובות אימייל של חברות או בתי ספר לא יפעלו.
  • שימוש ב-Google Chrome במצב פרטי: מומלץ להשתמש במצב הזה כדי ליצור סשן נקי ולמנוע התנגשויות עם חשבונות Google אחרים.
  • שימוש בקישור לאירוע מיוחד: צריך להשתמש בקישור מיוחד לאירוע, שדומה לקישור https://trygcp.dev/event/xxx ואחריו קוד האירוע (בדוגמה הזו, xxx).
  • מאשרים את התנאים וההגבלות: אחרי הכניסה לחשבון, מוצגים התנאים וההגבלות של Google Cloud Platform, שצריך לאשר כדי להמשיך.
  • יצירת פרויקט חדש: צריך ליצור פרויקט חדש וריק מתוך מסוף Google Cloud.
  • קישור חשבון לחיוב: מקשרים את הפרויקט החדש לחשבון לחיוב.
  • אישור הזיכוי: בסרטון הבא מוסבר איך בודקים שהזיכוי חל על הפרויקט. לשם כך, צריך לעיין בקטע 'זיכויים' בדף החיוב.

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

יצירה והגדרה של מפתח API

כדי לאמת את סוכני ה-AI של ADK באמצעות Gemini API בסדנת התכנות הזו, תשתמשו במפתח API שמשויך לפרויקט שלכם ב-Google Cloud.

  1. יצירת מפתח API:
  • עוברים אל Google AI Studio ולוחצים על הקישור 'קבלת מפתח API' בתחתית החלונית הימנית.
  • בוחרים באפשרות פרויקטים ולוחצים על הלחצן ייבוא פרויקטים.
  • מחפשים את פרויקט Google Cloud שרוצים לייבא ובוחרים בו, ואז לוחצים על הלחצן ייבוא.
  • אחרי שמייבאים את הפרויקט, עוברים לדף API Keys בתפריט Dashboard ויוצרים מפתח API בפרויקט שזה עתה ייבאתם.
  • רושמים את מפתח ה-API.
  1. מגדירים את משתנה הסביבה: לסוכן שלכם צריכה להיות גישה למפתח הזה. הדרך הרגילה היא להגדיר משתנה סביבה בשם GOOGLE_API_KEY.
  • macOS / Linux: פותחים את הטרמינל ומריצים את הפקודה הבאה, ומחליפים את "your-api-key" במפתח שהעתקתם. כדי להפוך את השינוי הזה לקבוע, מוסיפים את השורה הזו לקובץ ההפעלה של המעטפת (לדוגמה, ~/.bash_profile, ~/.zshrc).
export GOOGLE_API_KEY="your-api-key"
  • Windows (שורת פקודה): פותחים שורת פקודה חדשה ומריצים:
setx GOOGLE_API_KEY "your-api-key"
  • כדי שהשינוי ייכנס לתוקף, צריך להפעיל מחדש את שורת הפקודה.
  • Windows (PowerShell): פותחים טרמינל ב-PowerShell ומריצים:
$env:GOOGLE_API_KEY="your-api-key"
  • כדי שהשינוי הזה יהיה קבוע ב-PowerShell, צריך להוסיף אותו לסקריפט הפרופיל.

3. תחילת העבודה: הנציג הראשון

f814ab5b7614e071.png

הדרך הכי טובה להתחיל פרויקט חדש היא באמצעות תבנית ADK for Java GitHub. הוא מספק את מבנה הפרויקט ואת כל יחסי התלות הנדרשים.

אם יש לכם חשבון ב-GitHub, אתם יכולים לבצע את הפעולות הבאות: Use this template > Create a new repository, ואז להוציא את הקוד באופן מקומי באמצעות הפקודה git clone.

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

1613a3d0ddc66dc5.png

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

git clone https://github.com/glaforge/adk-java-maven-template.git

ואז cd אל adk-java-maven-template.

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

mvn compile

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

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

ניצור מחלקה חדשה ב-Java, בחבילה com.example.agent, ונקרא לה ScienceTeacher.

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

// src/main/java/com/example/agent/ScienceTeacher.java
package com.example.agent;

import com.google.adk.agents.LlmAgent;
import com.google.adk.web.AdkWebServer;

public class ScienceTeacher {
    public static void main(String[] args) {
        AdkWebServer.start(
            LlmAgent.builder()
                .name("science-teacher")
                .description("A friendly science teacher")
                .instruction("""
                    You are a science teacher for teenagers.
                    You explain science concepts in a simple, concise and direct way.
                    """)
                .model("gemini-2.5-flash")
                .build()
        );
    }
}

הגדרת הסוכן מבוסס ה-AI מתבצעת באמצעות השיטה LlmAgent.builder(). הפרמטרים name(), description() ו-model() הם חובה, וכדי להגדיר לסוכן אישיות ספציפית והתנהגות מתאימה, תמיד צריך לספק הנחיות מפורטות באמצעות השיטה instruction().

במקרה הזה בחרנו להשתמש במודל Gemini 2.5 Flash, אבל אתם יכולים לנסות גם את Gemini 2.5 Pro למשימות מורכבות יותר.

הנציג הזה עטוף בשיטה AdkWebServer.start(). זהו ממשק הצ'אט שנקרא ממשק המשתמש של ADK Dev. הוא מאפשר לכם לשוחח עם הנציג באמצעות ממשק צ'אט רגיל. בנוסף, הוא יכול לעזור לכם להבין מה קורה מאחורי הקלעים, כמו כל האירועים שזורמים דרך המערכת, הבקשות והתשובות שנשלחות ל-LLM.

כדי לקמפל ולהריץ את הסוכן הזה באופן מקומי, מריצים את הפקודה הבאה:

mvn compile exec:java -Dexec.mainClass=com.example.agent.ScienceTeacher

אחר כך עוברים לדפדפן בכתובת http://localhost:8080. ממשק המשתמש אמור להיראות כמו בצילום המסך שלמטה. אפשר לשאול את הסוכן שאלות שקשורות למדע.

da094da276ba15d6.png

4. מתן כלים לסוכנים

cd5c5798a0861a1c.png

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

שלב קוד: יצירת כלי בהתאמה אישית (StockTicker)

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

// src/main/java/com/example/agent/StockTicker.java
package com.example.agent;

import com.google.adk.agents.LlmAgent;
import com.google.adk.tools.Annotations.Schema;
import com.google.adk.tools.FunctionTool;
import com.google.adk.web.AdkWebServer;
import java.util.Map;

public class StockTicker {
    public static void main(String[] args) {
        AdkWebServer.start(
            LlmAgent.builder()
                .name("stock_agent")
                .instruction("""
                    You are a stock exchange ticker expert.
                    When asked about the stock price of a company,
                    use the `lookup_stock_ticker` tool to find the information.
                    """)
                .model("gemini-2.5-flash")
                .tools(FunctionTool.create(StockTicker.class, "lookupStockTicker"))
                .build()
        );
    }

    @Schema(
        name = "lookup_stock_ticker",
        description = "Lookup stock price for a given company or ticker"
    )
    public static Map<String, String> lookupStockTicker(
        @Schema(name = "company_name_or_stock_ticker", description = "The company name or stock ticker")
        String ticker) {
        // ... (logic to return a stock price)
    }
}

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

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

חשוב לציין את name וdescription של השיטה ושל הפרמטרים שלה באמצעות ההערה @Schema, כי המידע הזה ישמש את מודל ה-LLM הבסיסי כדי להבין מתי ואיך הוא צריך להפעיל שיטה מסוימת.

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

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

  • אם הפעולה מצליחה, מוחזרת התוצאה: {"stock_price": 123}
  • במקרה של שגיאה, החזרה: {"error": "Impossible to retrieve stock price for XYZ"}

לאחר מכן מריצים את המחלקה באמצעות הפקודה הבאה:

mvn compile exec:java -Dexec.mainClass=com.example.agent.StockTicker

5. הכוח של חיפוש Google בחיפוש מידע עדכני

ae215e7b7cde02ab.png

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

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

בואו נסתכל על סוכן פשוט לחיפוש חדשות:

// src/main/java/com/example/agent/LatestNews.java
package com.example.agent;

import java.time.LocalDate;
import com.google.adk.agents.LlmAgent;
import com.google.adk.tools.GoogleSearchTool;
import com.google.adk.web.AdkWebServer;

public class LatestNews {
    public static void main(String[] args) {
        AdkWebServer.start(LlmAgent.builder()
            .name("news-search-agent")
            .description("A news search agent")
            .instruction("""
                You are a news search agent.
                Use the `google_search` tool
                when asked to search for recent events and information.
                Today is \
                """ + LocalDate.now())
            .model("gemini-2.5-flash")
            .tools(new GoogleSearchTool())
            .build());
    }
}

שימו לב איך העברנו מופע של כלי החיפוש עם: tools(new GoogleSearchTool()). כך הסוכן שלנו יכול להתעדכן במידע העדכני ביותר שזמין באינטרנט. בנוסף, בהנחיה צוין התאריך של היום, כי זה יכול לעזור למודל ה-LLM להבין מתי השאלות מתייחסות למידע מהעבר ומתי צריך לחפש מידע עדכני יותר.

לאחר מכן מריצים את המחלקה באמצעות הפקודה הבאה:

mvn compile exec:java -Dexec.mainClass=com.example.agent.LatestNews

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

שלב קוד: סוכן החיפוש ככלי

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

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

// src/main/java/com/example/agent/SearchAgentAsTool.java
package com.example.agent;

import java.time.LocalDate;

import com.google.adk.agents.LlmAgent;
import com.google.adk.tools.AgentTool;
import com.google.adk.tools.GoogleSearchTool;
import com.google.adk.web.AdkWebServer;

public class SearchAgentAsTool {
    public static void main(String[] args) {
        // 1. Define the specialized Search Agent
        LlmAgent searchAgent = LlmAgent.builder()
            .name("news-search-agent-tool")
            .description("Searches for recent events and provides a concise summary.")
            .instruction("""
                You are a concise information retrieval specialist.
                Use the `google_search` tool to find information.
                Always provide the answer as a short,
                direct summary, without commentary.
                Today is \
                """ + LocalDate.now())
            .model("gemini-2.5-flash")
            .tools(new GoogleSearchTool()) // This agent uses the Google Search Tool
            .build();

        // 2. Wrap the Search Agent as a Tool
        AgentTool searchTool = AgentTool.create(searchAgent);

        // 3. Define the Main Agent that uses the Search Agent Tool
        AdkWebServer.start(LlmAgent.builder()
            .name("main-researcher")
            .description("Main agent for answering complex, up-to-date questions.")
            .instruction("""
                You are a sophisticated research assistant.
                When the user asks a question that requires up-to-date or external information,
                you MUST use the `news-search-agent-tool` to get the facts before answering.
                After the tool returns the result, synthesize the final answer for the user.
                """)
            .model("gemini-2.5-flash")
            .tools(searchTool) // This agent uses the Search Agent as a tool
            .build()
       );
    }
}

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

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

mvn compile exec:java -Dexec.mainClass=com.example.agent.SearchAgentAsTool

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

6. איך משתמשים בתהליכי עבודה מבוססי-נציגים

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

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

  • סוכן רגיל עם subAgent() להאצלת משימות,
  • SequentialAgent, כדי לבצע משימות ברצף,
  • ParallelAgent, כדי להריץ סוכנים במקביל,
  • LoopAgent, בדרך כלל כדי לעבור תהליך שיפור כמה פעמים שצריך.

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

תהליך עבודה

ADK Class

תרחיש לדוגמה

יתרונות

חסרונות

סוכני משנה

LlmAgent

משימות גמישות שמבוססות על משתמשים, שבהן לא תמיד ידוע מה השלב הבא.

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

פחות צפוי, מסתמך על נימוקים של LLM לשליטה בתהליך.

לפי הסדר

SequentialAgent

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

צפוי, אמין, קל לניפוי באגים, מבטיח סדר.

לא גמיש, יכול להיות איטי יותר אם אפשר להריץ את המשימות במקביל.

מקביל

ParallelAgent

איסוף נתונים ממספר מקורות או הפעלת משימות עצמאיות.

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

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

לופ

LoopAgent

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

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

עלול להוביל ללולאות אינסופיות אם לא מעצבים אותו בקפידה (תמיד צריך להשתמש ב-maxIterations!).

7. תהליכי עבודה מבוססי-סוכנים – העברה של משימות לסוכני משנה

3b3074b840f57c1c.png

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

// src/main/java/com/example/agent/SupportAgent.java
package com.example.agent;

import com.google.adk.agents.LlmAgent;
import com.google.adk.web.AdkWebServer;

public class SupportAgent {
    public static void main(String[] args) {
                LlmAgent topicSearchAgent = LlmAgent.builder()
            .name("order-agent")
            .description("Order agent")
            .instruction("""
                Your role is to help our customers
                with all the questions they may have about their orders.
                Always respond that the order has been received, prepared,
                and is now out for delivery.
                """)
            .model("gemini-2.5-flash")
            .build();

        LlmAgent socialMediaAgent = LlmAgent.builder()
            .name("after-sale-agent")
            .description("After sale agent")
            .instruction("""
                You are an after sale agent,
                helping customers with the product they received.
                When a customer has a problem,
                suggest the person to switch the product off and on again.
                """)
            .model("gemini-2.5-flash")
            .build();

        AdkWebServer.start(LlmAgent.builder()
            .name("support-agent")
            .description("Customer support agent")
            .instruction("""
                Your role is help our customers.
                Call the `order-agent` for all questions related to order status.
                Call the `after-sale-agent` for inquiries about the received product.
                """)
            .model("gemini-2.5-flash")
            .subAgents(socialMediaAgent, topicSearchAgent)
            .build()
        );
    }
}

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

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

mvn compile exec:java -Dexec.mainClass=com.example.agent.SupportAgent

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

8. תהליכי עבודה מבוססי-סוכן – פס הייצור

108f8601cd36b559.png

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

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

// src/main/java/com/example/agent/PoetAndTranslator.java
package com.example.agent;

import com.google.adk.agents.LlmAgent;
import com.google.adk.agents.SequentialAgent;
import com.google.adk.web.AdkWebServer;

public class PoetAndTranslator {
    public static void main(String[] args) {
        LlmAgent poet = LlmAgent.builder()
            .name("poet-agent")
            .description("Poet writing poems")
            .model("gemini-2.5-flash")
            .instruction("""
                You are a talented poet,
                who writes short and beautiful poems.
                """)
            .outputKey("poem")
            .build();

        LlmAgent translator = LlmAgent.builder()
            .name("translator-agent")
            .description("English to French translator")
            .model("gemini-2.5-flash")
            .instruction("""
                As an expert English-French translator,
                your role is to translate the following poem into French,
                ensuring the poem still rhymes even after translation:

                {poem}
                """)
            .outputKey("translated-poem")
            .build();

        AdkWebServer.start(SequentialAgent.builder()
            .name("poet-and-translator")
            .subAgents(poet, translator)
            .build());
    }
}

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

mvn compile exec:java -Dexec.mainClass=com.example.agent.PoetAndTranslator

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

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

9. תהליכי עבודה מבוססי-סוכן – עבודה במקביל

4ba95f71e0189ae7.png

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

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

  • הפרופיל של החברה (מנכ"ל, משרדים ראשיים, מוטו וכו')
  • החדשות האחרונות על החברה.
  • פרטים על המצב הפיננסי של החברה.
// src/main/java/com/example/agent/CompanyDetective.java 
package com.example.agent;

import com.google.adk.agents.LlmAgent;
import com.google.adk.agents.ParallelAgent;
import com.google.adk.agents.SequentialAgent;
import com.google.adk.tools.GoogleSearchTool;
import com.google.adk.web.AdkWebServer;

public class CompanyDetective {
    public static void main(String[] args) {
        var companyProfiler = LlmAgent.builder()
            .name("company-profiler")
            .description("Provides a general overview of a company.")
            .instruction("""
                Your role is to provide a brief overview of the given company.
                Include its mission, headquarters, and current CEO.
                Use the Google Search Tool to find this information.
                """)
            .model("gemini-2.5-flash")
            .tools(new GoogleSearchTool())
            .outputKey("profile")
            .build();

        var newsFinder = LlmAgent.builder()
            .name("news-finder")
            .description("Finds the latest news about a company.")
            .instruction("""
                Your role is to find the top 3-4 recent news headlines for the given company.
                Use the Google Search Tool.
                Present the results as a simple bulleted list.
                """)
            .model("gemini-2.5-flash")
            .tools(new GoogleSearchTool())
            .outputKey("news")
            .build();

        var financialAnalyst = LlmAgent.builder()
            .name("financial-analyst")
            .description("Analyzes the financial performance of a company.")
            .instruction("""
                Your role is to provide a snapshot of the given company's recent financial performance.
                Focus on stock trends or recent earnings reports.
                Use the Google Search Tool.
                """)
            .model("gemini-2.5-flash")
            .tools(new GoogleSearchTool())
            .outputKey("financials")
            .build();

        var marketResearcher = ParallelAgent.builder()
            .name("market-researcher")
            .description("Performs comprehensive market research on a company.")
            .subAgents(
                companyProfiler,
                newsFinder,
                financialAnalyst
            )
            .build();

        var reportCompiler = LlmAgent.builder()
            .name("report-compiler")
            .description("Compiles a final market research report.")
            .instruction("""
                Your role is to synthesize the provided information into a coherent market research report.
                Combine the company profile, latest news, and financial analysis into a single, well-formatted report.

                ## Company Profile
                {profile}

                ## Latest News
                {news}

                ## Financial Snapshot
                {financials}
                """)
            .model("gemini-2.5-flash")
            .build();

        AdkWebServer.start(SequentialAgent.builder()
            .name("company-detective")
            .description("Collects various information about a company.")
            .subAgents(
                marketResearcher,
                reportCompiler
            ).build());
    }
}

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

mvn compile exec:java -Dexec.mainClass=com.example.agent.CompanyDetective

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

10. תהליכי עבודה אג'נטיים – שיפור איטרטיבי

ea37b0ab05aa5b28.png

למשימות שדורשות מחזור של 'יצירה → בדיקה → שיפור', משתמשים ב-LoopAgent. השיטה הזו מאפשרת לבצע שיפורים חוזרים ונשנים באופן אוטומטי עד להשגת היעד. בדומה ל-SequentialAgent, סוכן המשנה LoopAgent יתקשר לסוכני המשנה ברצף, אבל הוא יחזור להתחלה. זהו ה-LLM שבו הסוכן משתמש באופן פנימי כדי להחליט אם לבקש הפעלה של כלי מיוחד, הכלי המובנה exit_loop, כדי לעצור את הביצוע של הלולאה.

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

בואו נסתכל על הקוד:

// src/main/java/com/example/agent/CodeRefiner.java
package com.example.agent;

import com.google.adk.agents.LlmAgent;
import com.google.adk.agents.LoopAgent;
import com.google.adk.agents.SequentialAgent;
import com.google.adk.tools.ExitLoopTool;
import com.google.adk.web.AdkWebServer;

public class CodeRefiner {
    public static void main(String[] args) {
        var codeGenerator = LlmAgent.builder()
            .name("code-generator")
            .description("Writes and refines code based on a request and feedback.")
            .instruction("""
                Your role is to write a Python function based on the user's request.
                In the first turn, write the initial version of the code.
                In subsequent turns, you will receive feedback on your code.
                Your task is to refine the code based on this feedback.

                Previous feedback (if any):
                {feedback?}
                """)
            .model("gemini-2.5-flash")
            .outputKey("generated_code")
            .build();

        var codeReviewer = LlmAgent.builder()
            .name("code-reviewer")
            .description("Reviews code and decides if it's complete or needs more work.")
            .instruction("""
                Your role is to act as a senior code reviewer.
                Analyze the provided Python code for correctness, style, and potential bugs.

                Code to review:
                {generated_code}

                If the code is perfect and meets the user's request,
                you MUST call the `exit_loop` tool.

                Otherwise, provide constructive feedback for the `code-generator to improve the code.
                """)
            .model("gemini-2.5-flash")
            .outputKey("feedback")
            .tools(ExitLoopTool.INSTANCE)
            .build();

        var codeRefinerLoop = LoopAgent.builder()
            .name("code-refiner-loop")
            .description("Iteratively generates and reviews code until it is correct.")
            .subAgents(
                codeGenerator,
                codeReviewer
            )
            .maxIterations(3) // Safety net to prevent infinite loops
            .build();

        var finalPresenter = LlmAgent.builder()
            .name("final-presenter")
            .description("Presents the final, accepted code to the user.")
            .instruction("""
                The code has been successfully generated and reviewed.
                Present the final version of the code to the user in a clear format.

                Final Code:
                {generated_code}
                """)
            .model("gemini-2.5-flash")
            .build();

        AdkWebServer.start(SequentialAgent.builder()
            .name("code-refiner-assistant")
            .description("Manages the full code generation and refinement process.")
            .subAgents(
                codeRefinerLoop,
                finalPresenter)
            .build());
    }
}

מריצים את הסוכן באמצעות הפקודה הבאה:

mvn compile exec:java -Dexec.mainClass=com.example.agent.CodeRefiner

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

‫11. מעולה!

337a2e319008d004.png

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

מה השלב הבא?