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, ואז תיצרו את הסוכן הפשוט הראשון שלכם ותוסיפו בהדרגה יכולות מתקדמות יותר כמו כלים בהתאמה אישית, חיפוש באינטרנט ותזמור של כמה סוכנים.

629b7f6b771833f4.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 לסדנה

492905b93ced4f9d.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. תחילת העבודה: הסוכן הראשון

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

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

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

59e72cf609c13c7c.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 מוגדר באמצעות ה-method 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. ממשק המשתמש אמור להיראות כמו בצילום המסך שלמטה. אפשר לשאול את הסוכן שאלות שקשורות למדע.

6ff1cc994bf640bd.png

4. מעניקים לסוכנים כלים שימושיים

94fc3dfedbfa313c.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 הבסיסי כדי להבין מתי ואיך הוא צריך להפעיל שיטה נתונה.

חשוב לא פחות לתת למודל שפה גדול הוראות ברורות לגבי אופן השימוש בכלי ומתי להשתמש בו. יכול להיות שהמודל יצליח להבין את זה בעצמו, אבל אם תספקו הסברים מפורשים ב-method 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 לקבלת מידע עדכני

551964bb78b5a987.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

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

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

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

Loop

LoopAgent

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

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

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

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

90497ab15401bfc8.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 orderAgent = 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 afterSaleAgent = 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 to 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(afterSaleAgent, orderAgent)
            .build()
        );
    }
}

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

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

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

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

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

9075ee47f70e0d51.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. תהליכי עבודה מבוססי-סוכן – עבודה במקביל

8971b81128aee9fc.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. תהליכי עבודה אג'נטיים – שיפור איטרטיבי

1e5b5df3fd45f698.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. מעולה!

8797fc7eb90c750d.png

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

מה השלב הבא?

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