ADK 2 Orchestration: Graph, Collaborative & Dynamic Workflows

1. סקירה כללית

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

מה תלמדו

  • תהליכי עבודה של גרפים (Pillar 1) – כשאתם יכולים לצייר את התהליך לפני שהקלט מגיע.
  • סוכנים שפועלים בשיתוף פעולה (עמוד 2) – כשאתם יודעים את הצוות אבל הבקשה בוחרת את קבוצת המשנה – וכל שלושת מצבי שיתוף הפעולה (chat / task / single_turn), כל אחד פועל בזמן אמת.
  • תהליכי עבודה דינמיים (עמוד 3) – כשהצורה של העבודה עצמה תלויה בקלט.
  • איך בוחרים – עץ החלטות עם שאלה אחת, והסבר על דפוסי ההתנהגות.

הקו המרכזי

מבנה מוכר ← צוות מוכר / קבוצת משנה של משתנים ← צורה לא מוכרת ← בחירת הצורה הנכונה

מפת הדרכים ללמידה

מה תפַתחו

אפליקציה אחת – Marathon Race Day Coach – הרכיבה שלב אחד שניתן להרצה בכל פעם. כל רמה היא מודול פשוט של Python שמריצים מהטרמינל. החל מרמה 5, כל החלקים שבהמשך הם שלכם.

התמונה נוצרת מהקוד הפועל: כל קו מלא נקרא מתוך Workflow.graph.edges. השיעור הראשון הוא שאת החלקים שאפשר לצייר מראש אפשר לראות בדיוק בעמודה 1, והחלקים שאי אפשר לצייר מראש הם הסיבה לקיומן של עמודות 2 ו-3.

האפליקציה כולה – ומה שתרשים לא יכול להראות לכם

הדרישות

  • חשבון Google (ל-Colab) – אין צורך בהגדרה מקומית.
  • ‫~50 דקות (שתי הרמות L4 הן הארוכות ביותר – כדאי להקצות להן זמן).
  • אחת משתי הדרכים להגיע למודל Gemini. בוחרים את הדרך – מריצים שלב הגדרה אחד ומדלגים על השאר:

🎓 סדנה

‫🏠 Take-home

מי

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

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

מה צריך

קישור למימוש ההטבה וחשבון Google שיש לו הרשאה ליצור פרויקט בענן

מפתח API של AI Studio בחינם

פועל במהירות

‫Vertex AI, בפרויקט שמתבצע בו חיוב על הקרדיט שקיבלתם לסדנה

Google AI Studio

עלות

השובר תקף למוצר הזה

תוכנית בחינם

שלב ההגדרה

הגדרת הסדנה (השלב הבא)

הגדרה בבית (השלב הבא)

החל מההקדמה ואילך, הכול זהה בשני המקרים – הנתיב רק קובע לאיזה נקודת קצה של מודל מחברת ה-Notebook מתקשרת.

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

כל שלב שמופיע בהמשך מתאים לתא אחד ב-נוטבוק של Colab ולתיקייה אחת במאגר GitHub. בוחרים באחת מהאפשרויות הבאות:

  • ▶ Colab (מומלץ): פתיחת הנוטבוק → הפעלת התאים מלמעלה למטה.
  • 💻 מקומי: git clone מאגר, ./setup_venv.sh, ואז מריצים כל רמה כמודול (python -m ...) או מעיינים בכל הרמות באמצעות ./run.sh (adk web).

2. הגדרת סדנה · מימוש הקרדיט ומעבר ל-Vertex AI

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

‫1 · מימוש הקרדיט (כדקה אחת)

  1. פותחים את הקישור למימוש ההטבה שהמורה שיתף או שיתפה. נראה שhttps://me.developers.google.com/benefits/claim/your-workshop-name.
  2. נכנסים לחשבון וממשיכים לדף כדי לאשר את הזיכוי.
  3. שימו לב באיזה חשבון Google השתמשתם. כל שלב שמופיע בהמשך צריך להתבצע באותו חשבון.

‫2 · פותחים את ה-notebook ומתקינים את ADK 2 (~1 min)

לוחצים על פתיחה ב-Colab‏ ▶ ואז מריצים את תא הקוד הראשון. הוא מצמיד את הגרסה המדויקת של ADK 2 שבה בוצע האימות של ה-Codelab הזה ומדפיס ✓ installed.

‫3. מריצים את התא 'Workshop setup' (כ-3 דקות)

זה התא עם הכותרת 🎓 נתיב א' · סדנה. מריצים את התא, ומערכת Colab תבקש מכם לאשר – בוחרים את אותו חשבון Google שדרכו מימשתם את הקרדיט ומאשרים את הגישה.

התא עושה ארבעה דברים: הוא יוצר פרויקט בשם adk-2-tutorial-XXXX על הקרדיט שלכם, מפעיל את Vertex AI API בפרויקט, מגדיר את ארבעת משתני הסביבה שכל תא מאוחר יותר קורא, ואז מבצע שיחת ניסיון ל-Vertex וממתין עד שהיא נענית – כך שההגדרה מסתיימת או שמקבלים הסבר למה היא לא הסתיימה, במקום שהיא תיכשל בהמשך בתוך רמה.

הפלט המצופה – השורה האחרונה היא החשובה:

Signed in as: you@example.com
...
Successfully created GCP project 'adk-2-tutorial-4817'.
Successfully linked 'adk-2-tutorial-4817' to billing account '01ABCD-...'.
   waiting for Vertex AI to come up on the new project... (10s)
   waiting for Vertex AI to come up on the new project... (20s)

 Vertex AI on adk-2-tutorial-4817 · us-central1 · gemini-2.5-flash  answered a test call

‫4. מדלגים על השלב 'הגדרה בבית'.

אל תפעילו את תא המפתח של AI Studio – הפעלה כזו תחזיר את המחברת ל-AI Studio ותבטל את מה שעשיתם. (התא מגן מפני זה ולא יפעל, אבל הפעולה הנכונה יותר היא פשוט לדלג עליו). אפשר לעבור מכאן ישירות לתא Shared building blocks.

‫5 · מריצים את התא 'אבני בניין משותפות'

מפעילים פתרונות חכמים פעם אחת. הוא מגדיר את סכימות Pydantic + תרחישי מרתון מוכנים מראש שכל רמה מ-L2 ואילך עושה בהם שימוש חוזר. יוצג לכם ✓ schemas + scenarios ready.

אחרי הסדנה

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

כדי לנקות את החשבון מוקדם יותר: פותחים את מסוף Cloud, בוחרים את adk-2-tutorial-XXXX ומוחקים אותו. שום דבר אחר ב-codelab הזה לא יוצר משאבים שניתנים לחיוב.

3. הגדרה בבית · מפתח API של AI Studio

כל מה שמופיע בנתיב הזה מופעל באמצעות מפתח API חינמי של Google AI Studio – בלי פרויקט בענן ב-Google Cloud, בלי חיוב ובלי התקנה מקומית. השלב הזה נמשך כ-3 דקות.

‫1 · פותחים את הנוטבוק

לוחצים על פתיחה ב-Colab‏ ▶. תועברו אל ה-notebook – מבוא בפורמט Markdown, ואז תא אחד שניתן להרצה לכל רמה. מריצים תאים מלמעלה למטה, וכל אחד מהם מדפיס את הפלט שלו מיד מתחתיו.

‫2 · התקנת ADK 2 (~1 min)

מריצים את תא הקוד הראשון. הוא מצמיד את הגרסה המדויקת שבה בוצע האימות של ה-Codelab:

%pip install -q "google-adk==2.3.0" python-dotenv pydantic nest_asyncio

מחכים עד שהפעולה תסתיים – יופיע ✓ installed. (ההתקנה אורכת כ-30 עד 60 שניות בפעם הראשונה, ולאחר מכן היא נשמרת במטמון).

‫3 · קבלת מפתח Gemini API מ-AI Studio (~דקה)

  1. פותחים את aistudio.google.com/app/apikey בכרטיסייה חדשה בדפדפן.
  2. נכנסים באמצעות חשבון Google.
  3. לוחצים על Create API key (יצירת מפתח API) בפינה השמאלית העליונה.
  4. בוחרים פרויקט קיים ב-Google או מאפשרים למערכת ליצור פרויקט חדש.
  5. מעתיקים את המפתח – הוא מתחיל ב-AIza... ומכיל כ-40 תווים.

‫4 · הוספת המפתח ל-Colab (~1 min)

אפשרות א' – Colab Secrets (מומלצת; המפתח נשאר מוסתר):

  1. לוחצים על סמל המפתח 🔑 בסרגל הצד השמאלי של Colab.
  2. לוחצים על + הוספת סוד חדש.
  3. מגדירים את Name בדיוק לערך GOOGLE_API_KEY.
  4. מדביקים את המפתח בשדה Value (ערך).
  5. מעבירים את המתג גישה ל-Notebook למצב מופעל.

אפשרות ב' – הדבקה כשמופיעה בקשה (מהירה): מדלגים על הסוד. כשמריצים את התא הבא, מופיעה בקשה מוסתרת 🔑 Enter your Google AI Studio API key: – מדביקים ולוחצים על Enter.

‫5 · הרצת תא המפתח

הוא קורא את הסוד (או חוזר להנחיה להדבקה), ואז מפנה את ADK אל AI Studio (לא אל Vertex AI):

import os

# 🏠 TAKE-HOME ONLY — if you ran the Workshop setup cell, skip this one.
if os.environ.get("GOOGLE_GENAI_USE_VERTEXAI") == "True":
    raise SystemExit("✋ You're set up on the workshop path (Vertex AI). Skip this cell.")

# Google AI Studio API key — add GOOGLE_API_KEY in the 🔑 Secrets panel (or paste when prompted).
try:
    from google.colab import userdata
    key = userdata.get("GOOGLE_API_KEY")
except Exception:
    import getpass
    key = getpass.getpass("Enter your Google AI Studio API key: ")

os.environ["GOOGLE_API_KEY"] = "".join(key.split())    # drop any stray whitespace/newlines
os.environ["GOOGLE_GENAI_USE_VERTEXAI"] = "False"      # use AI Studio, not Vertex AI
print("✅ API key set — using Google AI Studio.")

הפלט הצפוי: ✅ API key set — using Google AI Studio.

‫6 · מריצים את התא 'אבני בניין משותפות'

מריצים את התא Shared building blocks פעם אחת. הוא מגדיר את סכימות Pydantic + תרחישי מרתון מוכנים מראש שכל רמה מ-L2 ואילך עושה בהם שימוש חוזר. יוצג לכם ✓ schemas + scenarios ready.

ההגדרה הושלמה. 🎽 לפני L0 – הגרסה שכולם יוצרים קודם – יש עוד נושא אחד שחשוב להכיר.

4. הקדמה · למה לא הנחיה גדולה אחת?

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

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

מה שתראו: אסטרטגיה ספציפית, מנוסחת היטב ומשכנעת... שהמספרים שלה מומצאים. באחד הניסיונות, הוא פתח עם "I have pulled today's weather metrics" ודיווח על טמפרטורה של 11.1°C, רוח של 14.5 קמ"ש וניתוח של יומן אימונים שהוא מעולם לא ראה. אין כאן API של מזג אוויר, אין נתונים של קורסים, אין יומן – קריאה אטומה אחת למודל או שממציאה את הקלט שלה או שמגדרת אותו כך שהוא חסר תועלת.

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

  1. אי אפשר לסמוך עליו – הנתונים מומצאים, בצורה שוטפת.
  2. אי אפשר לבדוק את זה – הניתוב של שלב 4 נמצא בתוך הטקסט, ואין if שאפשר לבדוק.
  3. אי אפשר להחליף שלב – אין נקודת חיבור שבה אפשר לחבר API אמיתי של מזג אוויר.
  4. אתם משלמים על כל דבר, בכל פעם – חמישה שלבים, קריאה אחת גדולה, ללא שמירה במטמון של חלק דטרמיניסטי.

שירים שיעוררו בכם רגש. ב-9 הרמות הבאות, השלבים האלה מוצאים מההנחיה, אחד בכל פעם: פונקציות אחזור (L1–L2a), ניתוב של הצהרת if (L2b), חלוקת העבודה בין מומחים (L3a–L3b) וקביעת גבולות הצורה באמצעות קוד (L4a–L4b).

מאמן ההנחיות המגה – בטוח בעצמו, בלי שום דבר מאחורי התרשים

💻 מקומי: python -m shared.prologue

5. רמה 0 · הסוכן הראשון שלכם ב-ADK 2

תוכנית הפעולה – אתם כאן: רמה 0

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

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

הרעיון האחד – שלושה חלקים:

  • Agent – הגורם שמבצע את ההיגיון (מודל Gemini + הוראה).
  • Runner – הרכיב שמריץ סוכן בתוך סשן ומזרים אירועים.
  • כלי – פונקציית Python פשוטה (pace_splits) שהמודל מחליט להפעיל. ה-ADK קורא את החתימה + docstring ומעביר למודל הצהרה; אין כתיבת סכימה.

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

‫▶ Colab: מריצים את התא L0 · 📁 GitHub: L0_first_agent/ · 💻 Local: python -m L0_first_agent.agent

L0 flow

def pace_splits(target_finish: str) -> dict:
    """Convert a goal time like '3:30:00' into exact per-mile / per-km paces."""
    ...                                  # deterministic Python — no LLM

pace_coach = Agent(
    name="pace_coach", model=MODEL,
    tools=[pace_splits],                 # the model may call it; ADK reads the signature
    instruction="You are a friendly, concise marathon coach. ... If the runner "
                "mentions a goal time, call pace_splits — never do arithmetic yourself.",
)
runner = Runner(node=pace_coach, session_service=InMemorySessionService(), auto_create_session=True)
async for event in runner.run_async(user_id="u1", session_id="s1", new_message=msg):
    ...  # events carry the model's text

‫🔍 הסמנים: Agent(...) · tools=[pace_splits] · Runner(...). ובפלט, השורה 🔧 – זה המודל שמחליט, באמצע התשובה, לקרוא לקוד שלכם.

מה יוצג לכם:

   🔧 model called tool  pace_splits({'target_finish': '3:30:00'})
   🔧 tool returned      {'per_mile': '8:00', 'per_km': '4:58', ...}
🧠 Coach: To finish in 3:30:00, you need an average pace of 8:00 per mile...

השורות עם הסמל 🔧 הן השיעור: באמצע התשובה, המודל בחר לקרוא לפונקציה שלכם, והערך המדויק 8:00/mile בתשובה שלו הגיע מהקוד שלכם – לא מסטטיסטיקות של טוקנים.

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

‫👀 קריאה: pace_splits (פונקציה פשוטה) והשורה tools=[pace_splits]. · לוחצים על ▶ הפעלה. ‫· ✏️ שינוי: שואלים את השאלה הכללית (ללא זמן יעד) – שימו לב שהקווים 🔧 נעלמים: המודל מחליט מתי כדאי להפעיל כלי. אחר כך כותבים מחדש את instruction ומריצים מחדש – ההוראה היא שאר התוכנית.

6. רמה 1 · תהליך העבודה הראשון שלכם

תוכנית הפעולה – אתם כאן: רמה 1

⚡ בקצרה: פונקציה רגילה וסוכן LLM הם אותו סוג של צומת. עבודה צפויה → פונקציה (0 LLM, דטרמיניסטית); חשיבה רציונלית → סוכן.

השאלה: איך אפשר לשלב קוד רגיל ו-LLM בתהליך אחד, בלי לשלם על קריאה למודל בחלקים שהם רק קוד?

הרעיון המרכזי: ב-Workflow, פונקציית Python רגילה וסוכן LLM הם רק צמתים באותה רשימת edges.

START ──► fetch_conditions (function, 0 LLM) ──► advise (agent, 1 LLM)

‫▶ Colab: מריצים את התא L1 · 📁 GitHub: L1_graph_basics/ · 💻 Local: python -m L1_graph_basics.workflow

L1 flow

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

def fetch_conditions(node_input):                # function node — 0 LLM
    return Event(output=Conditions(temp_f=78, wind_mph=12, conditions="sunny").model_dump())

advise = Agent(name="advise", model=MODEL, mode="single_turn",
               input_schema=Conditions, instruction="...give pacing + gear advice...")

workflow = Workflow(edges=[(START, fetch_conditions, advise)])

🔍 הסמנים: טופל קצה אחד – (START, fetch_conditions, advise) – עם פונקציית Python פשוטה באמצע, ו-input_schema= מאמת את ההעברה.

מה חדש לעומת L0: Workflow(edges=[...]), START (כשהקלט נכנס), צומת פונקציה שמחזיר Event(output=...) ו-input_schema=Conditions, כך שהפלט של הפונקציה מאומת מול הסכימה הזו לפני שהסוכן רואה אותו (כטקסט JSON – input_schema מאמת את הגבול, הוא לא מעביר לסוכן אובייקט Python).

אולי תרצו לדעת: האם הסדר הנדרש הוא קודם פונקציה ואז סוכן? לא – כל הזמנה, כל שילוב, כל כמות. ‫advise פועל שני רק כי הוא צריך נתונים של fetch_conditions. השיעור הוא דרגת האצולה, ולא הרצף.

‫👀 קריאה: fetch_conditions מחזירה נתונים ללא קריאה למודל; ל-advise יש input_schema=Conditions. · לוחצים על ▶ הפעלה. ‫· ✏️ שינוי: מגדירים את temp_f=30 בפונקציה ומריצים אותה מחדש – ההמלצה משתנה והפונקציה עדיין לא כרוכה בעלויות של קריאות ל-LLM.

7. ‫L2a · Parallel fan-out + JoinNode (Pillar 1a)

מפת הדרכים – אתם כאן: L2a

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

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

הצורה:

START ──► fetch_weather ──┐
START ──► analyze_course ─┼─► JoinNode ─► strategy (1 agent)
START ──► pull_fitness ───┘   (bundles)

‫▶ Colab: מריצים את התא L2a · 📁 GitHub: L2a_parallel_join/ · 💻 Local: python -m L2a_parallel_join.workflow

תהליך L2a

🔍 הסמנים: שלוש קצוות שמתחילים ב-START – זה ה-fan-out – וב-JoinNode, נקודת המפגש.

  • שלושת האחזורים הם פונקציות – הם פועלים במקביל, 0 קריאות ל-LLM.
  • JoinNode מחכה לשלושתם ומאגדת אותם למטען ייעודי (payload) אחד עם הקלדה (BundledRunData), שמקבל מפתח לפי שם הפונקציה.
  • סוכן strategy קורא את החבילה וכותב RaceStrategy.

מה שרואים: בכל אחזור מודפסת חותמת זמן של started / finished. שלושתם מתחילים ב-0.0s וה-fan-out מסתיים ב-2.0s – האחזור הכי איטי, ולא ב-4.5s שמתקבלים מסיכום משך הזמן שלהם. החפיפה הזו היא המקביליות. (הזמן הכולל שחלף מההתחלה ועד הסוף total שמוצג בסוף הוא בערך 8 שניות, כי הוא כולל גם את הקריאה ל-LLM של סוכן האסטרטגיה – צריך לקרוא את חותמות הזמן של האחזור של הטענה המקבילה, ולא את הסכום הכולל).

💡 הקדמה: הפרומפט הענק המציא את מזג האוויר. הטמפרטורה כאן מגיעה מפונקציה של אחזור נתונים – קוד אמיתי, נקודת חיבור אמיתית. מחליפים את הדיבור המוקלט ב-API של מזג אוויר אמיתי, ושום דבר אחר לא משתנה.

אולי תרצו לדעת: כמה

JoinNode

מה חשוב לדעת? משפט אחד: הפונקציה מחכה עד שכל ענף מקביל מסתיים, אורזת את התוצאות במילון אחד עם מפתח שהוא השם של הפונקציה במעלה הזרם, ולא מבצעת חישוב בעצמה. בגלל ההכתבה הזו, הנתב של L2b יכול לכתוב node_input["fetch_weather"]["temp_f"].

‫👀 קריאה: שלושה קצוות יוצאים מ-START;‏ JoinNode מאגד אותם לסוכן אחד. ‫· ▶ מריצים את הפקודה וקוראים את חותמות הזמן, לא את הסכום הכולל. ‫· ✏️ שינוי: גורמים להשהיית אחזור אחד 3.0 – קודם חוזים את שעת הסיום החדשה של ה-fan-out, ואז מאמתים.

8. ‫L2b · הוספת הנתב הדטרמיניסטי (Pillar 1b)

מפת דרכים – אתם כאן: L2b

⚡ סיכום: סוכן L2a ללא שינויים + סוכן if פשוט מחליטים איזה סוכן אחד יפעל. הסתעפות, בלי לשאול את המודל.

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

הצורה (L2a + נתב):

... JoinNode ─► route_by_weather ─► hot_strategy
               (if-statement)   ─► normal_strategy
                                ─► cold_strategy

‫▶ Colab: מריצים את התא L2b – אפשר לנסות run("NORMAL") / run("COLD") · 📁 GitHub: L2b_router/ · 💻 Local: python -m L2b_router.workflow COLD

תהליך L2b

def route_by_weather(node_input):                        # an if-statement, 0 LLM
    temp = node_input["fetch_weather"]["temp_f"]
    route = "HOT" if temp >= 70 else "COLD" if temp <= 40 else "NORMAL"
    return Event(output=node_input, route=route)

(route_by_weather, {"HOT": hot_strategy, "NORMAL": normal_strategy, "COLD": cold_strategy})

‫🔍 הסמנים: Event(output=..., route=...) – צומת פונקציה שנותן שם לנתיב – והקצה של המילון {"HOT": ..., "NORMAL": ..., "COLD": ...} שממפה שמות לצמתים.

המסר העיקרי – שלושה סוגי עבודה, שלושה בתים:

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

מה שיוצג: temp=78F -> route=HOT, ואז RaceStrategy מובנה. עלות נטו: קריאה אחת ל-LLM.

‫⚠️ אם מוסיפים ענף רביעי, צריך להוסיף גם רשומה של DEFAULT_ROUTE למילון הנתיבים. אם המסלול לא תואם למילון, זו לא שגיאה – הענף פשוט מסתיים, והתוכנית יוצאת 0 ללא פלט, וזהו מצב מבולבל לניפוי באגים.

אולי תהיתם: אז L2b הוא פשוט L2a בתוספת נתב? כן – הפעולות של שליפת הנתונים וההצטרפות לא משתנות, ועדיין מדובר בדיוק בקריאה אחת של LLM. מה השתנה: האפשרות 'תמיד אותו סוכן' הפכה ל'אחד משלושה, שנבחר על סמך נתונים'.

‫👀 קריאה: route_by_weather – הנתב הוא הצהרת if, לא סוכן. · ▶ Run run("COLD") גם כן. ‫· ✏️ שינוי: הוספת הסתעפות WINDY עם סוכן רביעי – וחשוב לקרוא את האזהרה DEFAULT_ROUTE למעלה לפני שעושים את זה.

9. L3a · Collaborative agents: one flag, two worlds — Pillar 2

מפת הדרכים – אתם כאן: L3a

⚡ אמ;לק: אותו צוות, flag אחד. ‫chat מעביר את כל השיחה למומחה אחד ולא חוזר; single_turn הופך כל מומחה לכלי – קבוצת משנה מקבילה, חזרה אוטומטית, סינתזה אחת.

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

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

‫▶ Colab: מריצים את התא L3a · 📁 GitHub: L3a_collaborative/ · 💻 Local: python -m L3a_collaborative.concierge --mode chat "What about fueling?"

תהליך L3a

🔍 הסמנים: mode="single_turn" במפעל – ובפלט, TRANSFER → (פעימה 1) לעומת רצף של DISPATCH → שורות עם חותמת זמן אחת (פעימה 2).

‫Beat 1 · מריצים את ברירת המחדל קודם – ורואים שהיא לא מצליחה לבצע את העבודה

לא נכתב mode= → ברירת המחדל של סוכני המשנה היא chat. הפרטים שיוצגו הם:

TRANSFER  nutrition_specialist   (transfer_to_agent  the only tool chat subagents provide)
Final speaker: nutrition_specialist

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

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

‫Beat 2 · One flag, two worlds

ההבדל היחיד: mode="single_turn" אצל כל מומחה. אותה שאלה, הרצה חוזרת:

[t= 7.8s] DISPATCH  medical_specialist       same timestamp =
[t= 7.8s] DISPATCH  weather_specialist         one turn, many calls
[t=14.5s]    medical_specialist replied      replies land inside
[t=14.5s]    weather_specialist replied        one short window
🧠 Concierge (synthesized): <one answer>

עכשיו, ADK מחדיר כלי אחד להענקת הרשאות לכל מומחה – הכלי נקרא על שם הסוכן המשני, והוא מתואר על ידי description= (הטקסט הזה הוא מה שהמתאם קורא כשהוא בוחר את קבוצת המשנה; אם לא תציינו אותו, ההפניה תתבסס רק על השמות). המתאם שולח כמה קריאות בתור אחד, ה-ADK מריץ אותן במקביל, כל אחת מחזירה אוטומטית את התוצאה שלה, והמתאם מבצע סינתזה.

שאלה

מומחים שפוטרו

"What about fueling?"

תזונה בלבד

‫"My knee hurts at mile 18"

רפואי בלבד

"כדאי לי לרוץ היום?"

רפואי + מזג אוויר + קצב

"יש משהו שצריך להדאיג אותי?"

all 6

למה כל מומחה מקבל את התדרוך המלא: כל סוכן משנה single_turn פועל בענף סשן מבודד משלו – הוא לא יכול לראות את השיחה או את סוכני המשנה האחרים. שום דבר לא סביבתי: הרכיב המתאם חייב להעביר את כל SpecialistInput (שאלה + אסטרטגיה + נתונים של הרכיב שמריץ את האסטרטגיה) בנפרד לכל קריאה מקבילה.

💡 איפה ADK 2 נותן לזה בית ישיר: מודל שפה גדול בוחר קבוצת משנה לכל בקשה ומריץ אותה במקביל – מוצהר באמצעות sub_agents + mode="single_turn". בגרסה 1.x, אפשר להרכיב את אותה צורה סגורה על ידי הוספת התג AgentTool לכל מומחה. ההבדל הוא שעכשיו מדובר בהצהרה ולא בצינור. (ParallelAgent תמיד כולל את כל המידע ו-transfer_to_agent הוא סידורי).

⚠️ שתי הערות חשובות: (1) המודל בוחר את קבוצת המשנה, ולכן הוא פחות דטרמיניסטי מהנתב המקודד של L2 – קבוצת המשנה המדויקת יכולה להשתנות מהרצה להרצה. ‫(2) מדי פעם תראו שורה Error validating input: ... של מומחה אחד. כמעט אף פעם לא מדובר בפלט של המומחה – output_schema גורם ל-Gemini לאכוף את זה בצד השרת. זה הקלט: הרכיב המתאם צריך לשכפל את כל SpecialistInput המקונן מילה במילה לכל קריאה מקבילה, ולפעמים הוא טועה באחת מהן. ה-ADK מחזיר את השגיאה כתוצאה של הכלי הזה, הרכיב המתאם משחזר את הנתונים והסינתזה עדיין מתבצעת.

אולי אתם שואלים את עצמכם: מה זה

chat

רק סוג אחד של הענקת הרשאות בסגנון גרסה 1.x – סוכן אחד בכל פעם? כן, בעצם: זו התנהגות ברירת המחדל בגרסה 1.x, ועכשיו יש לה שם. הפער בין single_turn לבין כלים מומחים הוא תלת-ממדי: מה המתאם מחזיק (transfer_to_agent אחד לעומת כלי אחד לכל מומחה) · כמה יכולים לעבוד (אחד, בבעלות השיחה לעומת N במקביל) · אם השליטה חוזרת (לעולם לא לעומת אוטומטית, עם תוצאות). לגבי הקוד: הסתעפות if mode == של המפעל קיימת רק כדי שאפשר יהיה ליצור צוות אחד בשתי הדרכים לצורך השוואה – באפליקציה אמיתית, מצב אחד מוטמע בקוד ו-if נעלם.

‫👀 Read: the _specialist factory — the mode parameter is the whole level. ‫· ▶ Run (הפעלה) של שני המקצבים. ‫· ✏️ שינוי: שואלים "my knee hurts at mile 18" (הברך שלי כואבת בקילומטר ה-29) – קודם חוזים את קבוצת המשנה, ואז בודקים את שורות ה-DISPATCH.

# The factory's mode parameter is THE variable this level teaches:
def _specialist(name, domain, focus, mode):
    kwargs = {}
    if mode == "single_turn":   # the structured contract only makes sense for a TOOL
        kwargs = dict(mode="single_turn",
                      input_schema=SpecialistInput, output_schema=SpecialistResponse)
    return Agent(name=name, model=MODEL,
                 description=f"Marathon {domain} specialist. Consult for: {focus}.",
                 instruction=..., **kwargs)

race_concierge = Agent(name="race_concierge", model=MODEL,
                       sub_agents=[...six specialists...],   # NOTE: no `mode` on the coordinator
                       instruction="...DECIDE which specialists are relevant... call them IN PARALLEL... SYNTHESIZE...")

10. ‫L3b · מצב משימה: שיחה עם קו סיום – עמוד 2

תוכנית — אתם כאן: L3b

⚡ בקצרה: המצב האמצעי – מתקשרים עם המשתמש עד לאיסוף השדות, ואז חוזרים אוטומטית עם אובייקט מאומת.

השאלה: L3a left a gap. chat הוא הבעלים של כל השיחה, ו-single_turn אף פעם לא מדבר עם המשתמש. אבל יש עבודת קליטה אמיתית באמצע: "תדבר עם המשתמש עד שתאסוף X – ואז תחזור עם אובייקט מאומת". איזה מצב זה?

הצורה:

race_desk (coordinator)
  └─ gear_fitter (mode="task", output_schema=GearOrder)

‫▶ Colab: מריצים את התא L3b · 📁 GitHub: L3b_task_desk/ · 💻 Local: python -m L3b_task_desk.desk

L3b flow

gear_fitter holding the task open — a paused task, not a hang

🔍 הסמנים: mode="task" + output_schema= באותו סוכן — ובפלט, ההשהיה ⏸ והשיחה finish_task.

מה יוצג לכם:

━━ TURN 1 ━━  user: 'I need shoes for the marathon.'
  race_desk  delegate: gear_fitter
  gear_fitter: What is your shoe size?
    The run ENDED  but nothing failed. This is a PAUSED task.

━━ TURN 2 ━━  user: 'Size 9, wide.'   (same session  resumes the task)
  gear_fitter  finish_task   (payload validates as GearOrder)
  race_desk: Your order ... in size 9 Wide has been confirmed.

קרו שלושה דברים שלא יכולים לקרות במצב L3a:

  1. ההפעלה באמת נעצרה באמצע המשימה – משימה מושהית, לא קריסה ולא כשל. הסוכן שאל שאלת הבהרה והמשימה עדיין פתוחה. (ב-adk web פשוט מקלידים את התשובה, והסקריפטים של ה-harness מתייחסים אליה כהודעה שנייה באותה סשן).
  2. ההודעה הבאה הפעילה מחדש את אותו סוכן משימות – לא בוצעה הפניה מחדש ולא הוקצתה מחדש משימה. בסשן יודעים מי חיכה.
  3. finish_task הסתיים – כלי ADK הוזרק בגלל mode="task". הנציג חייב להתקשר אליו כדי לסיים את הפעולה, והמטען הייעודי (payload) שלו חייב לעבור אימות מול output_schema. שיחה עם קו סיום מוקלד – ואז השליטה חוזרת אוטומטית לתיאום, התוצאה מצורפת.

הכלל של שאלה אחת לבחירת מצב

💡 "האם המשתמש צריך לדבר עם ה-Agent – ועד מתי?" chat = indefinitely · task = until the fields are collected · single_turn = never.

מצב

האדם שבתהליך

מקביל?

חזרה להורה

chat (ברירת המחדל של סוכן משנה) – עוזר תמיכה, טייס משנה עם אפשרויות פתוחות

שיחה מלאה

לא

ידני (דרך העברה)

task — קבלת פניות, הזמנות, פתרון בעיות

שאלות הבהרה בלבד

לא

אוטומטי (דרך finish_task, עם אובייקט מאומת)

single_turn — סיווג · חילוץ · שיפוט · יצירה

אין

yes

אוטומטי (עם התוצאה שלו)

mode מופיע בסוכנים משניים בלבד – אף פעם לא בסוכן הראשי. צמתים בתהליך העבודה מוגדרים כברירת מחדל ל-single_turn (ולכן L1–L2b אף פעם לא כתבו אותו), ואילו סוכני משנה מוגדרים כברירת מחדל ל-chat (ולכן L3a היה צריך).

‫⚠️ שתי הערות לגבי הגרסה לפני שמתחילים לבנות על בסיס הגרסה הזו: (1) task

כצומת גרף סטטי תלוי בגרסה – בגרסאות 2.0.0b1 עד 2.3.0 (הגרסה שמוצמדת בשיעור Codelab הזה), Workflow(...) מופעל בזמן הבנייה. צריך להשתמש בדיוק במה שהרמה הזו עושה (מתאם צ'אט עם סוכני משנה של משימות) או לשלוח באמצעות ctx.run_node. הוסר בגרסה 2.5.0. ‫(2) "סוכני משימות חייבים להיות סוכני עלים" (ללא סוכני משנה משלהם) היא מגבלה מתועדת של ADK – אבל חוזה, לא אמצעי הגנה בזמן ריצה: לא גרסה 2.3.0 ולא גרסה 2.5.0 ימנעו מכם לעשות זאת. היעדר שגיאה לא אומר שיש הרשאה.

💡 מידע נוסף: taskסוכן שמוטמע בתהליך עבודה של גרף (הצורה 2.5.0+), עם ניתוב שיכול להחזיר את השיחה לניסיון חוזר: מאגר נלווה 22_agent_in_workflow · מדריך למצב מלא: docs/agent-modes.md.

יכול להיות שאתם שואלים את עצמכם: מה המשמעות של

task

לקנות לי את זה, ששני האחרים לא יכולים? שלושה דברים: חזרה אוטומטית (הצ'אט ממשיך את השיחה במקום) · קו סיום מוקלד (המטען הייעודי (payload) של finish_task צריך לעבור אימות מול הסכימה – מקבלים נתונים בחזרה, לא תמליל) · השהיה/הפעלה מחדש (הסמל ⏸ הוא משימה בהמתנה שממתינה לאדם, ולא ניתוק).

‫👀 קריאה: gear_fittermode="task" + output_schema הוא החוזה המלא. · לוחצים על ▶ הפעלה. ‫· ✏️ שינוי: run_desk("I need a hydration vest", "2 liters, medium") — שאלת ההבהרה משתנה, קו הסיום נשאר מוקלד.

11. ‫L4a · fan-out מקבילי בגודל זמן ריצה (Pillar 3a)

מפת דרכים – אתם כאן: רמה 4א

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

⚠️ חשוב לדעת: זה השלב הכי תלול בסולם. ברמה הקודמת היו 44 שורות, ברמה הזו יש בערך 120 שורות – שלושה סוכנים ושני צמתי תהליך עבודה, ואין שורות ריקות. כדאי להקדיש לזה כ-15 דקות, ולהסתמך על השורה Read/Run/Change בסוף: לא צריך להבין כל שורה בקריאה הראשונה.

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

הצורה (רמה אחת לעומק):

START ─► decompose ─► research_topic (parallel_worker) ─► synthesize
                                 
                             └──┴──┴─ (flat: no children yet)

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

‫▶ Colab: מריצים את התא L4a · 📁 GitHub: L4a_flat_research/ · 💻 Local: python -m L4a_flat_research.deep_research

L4a flow

🔍 הסמנים – אין

dynamic=True

החלפה. דינמי הוא דרך לכתוב, לא הגדרה. שני סמנים בלבד: @node(parallel_worker=True) (מקבל רשימה בגודל זמן ריצה, מפעיל worker אחד לכל פריט) ו-ctx.run_node(...) (מתזמן ישירות צמתי תזמון של קוד). אם רואים את אחת מהאפשרויות האלה → אתם משתמשים בשיטה הדינמית.

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

שני דגלים שחשוב להבין לגבי העובד:

  • rerun_on_resume=Trueחובה בכל צומת שמבצע קריאה ל-ctx.run_node – אם לא מציינים אותו, ADK מעלה ValueError. כשממשיכים את ההפעלה, הצומת של השליחה צריך להפעיל מחדש את השליחה כדי לבנות מחדש את הצמתים הצאצאים שהוא יצר, כי הם לא נמצאים בתרשים הסטטי.
  • retry_config= bounds how this FAILS. עובד מקביל מבטל כל אח ומעלה מחדש את המיידי אם אחד מהצאצאים נכשל – כך שבלי ניסיון חוזר, שגיאת 429 זמנית אחת מבטלת את כל הריצה, כולל כל שיחה שכבר שולמה. הניסיון החוזר מגיע לצומת הפנימי של כל פריט, כך שכל ענף מנסה שוב באופן עצמאי.

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

‫👀 קריאה: שני הדגלים ב-research_topicparallel_worker ו-rerun_on_resume. · לוחצים על ▶ הפעלה. ‫· ✏️ שינוי: החלפה בשאלה פתוחה משלכם – N משתנה כי הקלט קובע את הרוחב.

12. L4b · Add recursive spawning (Pillar 3b)

מפת דרכים – אתם כאן: רמה 4ב

⚡ תקציר: רקורסיה נכתבת, לא ניתנת – העובד קורא לעצמו דרך ctx.run_node, פייתון רגיל – ולכן צריך לכתוב גם את הבלם. זה אומר שנשארו MAX_DEPTH.

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

הצורה (עכשיו רקורסיבית):

START ─► decompose ─► research_topic (parallel_worker, recursive) ─► synthesize
                                 
                                 └─ research(q3) ─► maybe spawn children
                               └─── research(q2) ─► maybe spawn children
                             └────── research(q1) ─► maybe spawn children

‫▶ Colab: מריצים את התא L4b · 📁 GitHub: L4b_recursion/ · 💻 Local: python -m L4b_recursion.deep_research

תהליך L4b

@node(parallel_worker=True, rerun_on_resume=True)
async def research_topic(ctx, node_input):
    finding = coerce(await ctx.run_node(research_agent, node_input=...), ResearchFinding)
    if finding.needs_deeper and finding.deeper_questions and depth < MAX_DEPTH:   # boundary in CODE
        children = await ctx.run_node(research_topic, node_input=deeper)          # recursive fan-out
    yield Event(output={..., "children": children})

🔍 הסמנים: ctx.run_node(research_topic, ...) בתוך research_topic עצמו – ההפניה העצמית is היא הרקורסיה – והשומר depth < MAX_DEPTH בשורה אחת מעליו.

מה שרואים: צמתי מחקר שמדפיסים spawning N deeper – רקורסיה שמתרחשת בזמן אמת – ואז צורת עץ של זמן ריצה (למשל 5 top-level + 10 recursive children). העץ שונה בכל הפעלה.

‫⚠️ לפני שמגדילים את המכסה: המכסה גדלה במהירות – MAX_DEPTH=3 לוקחת את המקרה הגרוע מ-30 שיחות ל-93 שיחות. ובסוף הריצה, יכול להיות שתראו שורת יומן cancelling N leftover tasks: זהו ADK שמפרק את קבוצת המשימות המקבילות שלו אחרי שהתוצאה כבר הושלמה. לא מזיק – ואם ההגדרה של הרישום ביומן לא תואמת, יכול להיות שלא תראו אותו אף פעם.

אולי תשאלו: האם הגדרת הרימרקטינג הדינמי לא מופעלת כברירת מחדל? לא – L4a הוא דינמי לחלוטין עם אפס רקורסיה. הפונקציה הדינמית מספקת רק זרימת בקרה רגילה של Python. L4b בוחר לכתוב איתה רקורסיה. ומכיוון שאתם כתבתם את הרקורסיה, אתם צריכים לכתוב את הגבול שלה – כאן הסיסמה "תנו ל-LLM לעצב את העבודה, שמרו על הגבולות בקוד" מפסיקה להיות סיסמה.

‫👀 קריאה: השומר: if finding.needs_deeper and depth < MAX_DEPTH. · לוחצים על ▶ הפעלה. ‫· ✏️ שינוי: הגדרה של MAX_DEPTH = 1 והרצה מחדש – העץ משוטח (וההרצה הופכת לזולה יותר). הגבול הוא שלכם, בקוד.

13. ‫L5 · באיזה דפוס כדאי להשתמש?

תוכנית הפעולה – אתם כאן: רמה 5

⚡ סיכום: ציר אחד קובע הכול – מי בוחר את השלב הבא: התרשים שציירתם, ה-LLM או הקוד שלכם.

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

הציר: מי מחליט מה יפעל בהמשך?

עמוד

מי מחליט מה יפעל בהמשך

מובנה

1 · Graph

הגרף שציירתם

L2a / L2b

2 · שיתופי

ה-LLM

L3a / L3b

‫3 · דינמית

קוד Python, בזמן הריצה

L4a / L4b

שלב 0: האם בכלל צריך גרף?

חבילת ADK כוללת סוכני תהליכי עבודה מוכנים מראשSequentialAgent,‏ ParallelAgent ו-LoopAgent. בשרשרת פשוטה של סוכנים, אלה התשובות הנכונות הכי זולות ואין גרף להרכבה. אפשר להשתמש בהם כשצריך ניתוב מפורש (הנתב של L2b), צומת איחוד (JoinNode של L2a) או צמתים שהם לא סוכנים (פונקציה רגילה, ללא קריאות ל-LLM) – בדרך כלל זו הסיבה.

Would a prebuilt SequentialAgent / ParallelAgent / LoopAgent do?

├─ YES ──────────────────────────────► use it; stop here

└─ NO  I need routing, a join, or non-agent nodes
   
   Can you draw the workflow before the input arrives?
   
   ├─ YES ───────────────────────────► Pillar 1 · Graph workflow    (L2a/L2b)
   
   └─ NO
      ├─ Known team, request picks the subset? ─► Pillar 2 · Collaborative  (L3a/L3b)
      └─ Does the shape depend on the input?  ──► Pillar 3 · Dynamic        (L4a/L4b)

L5 · which pattern

המסגרת האמינה של 1.x לעומת 2

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

דוגמת קוד

העלות בגרסה 1.x

הדף הראשי של ADK 2

תרשים

‫4 קריאות ל-LLM בגרסה הנפוצה; ההפניה מוסתרת בהנחיה

פונקציה + צמתי סוכן כעמיתים → שיחה אחת, נתב של משפטי if

שיתופי

ניתן להגדרה באמצעות AgentTool plumbing; ParallelAgent תמיד כל, transfer_to_agent סדרתי

צוות מוצהר: sub_agents + mode="single_turn"

Dynamic

recursion drops you out of the framework

parallel_worker + recursive ctx.run_node בתוך המסגרת

האפליקציה כולה, ומה שלא רואים בגרף

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

עמוד

מה כלול ב-graph.edges

סיבה

1 · Graph (רמה 2b)

10 קצוות, מסלולים והכול

ציירתם אותו לפני שהגיעו נתונים

2 · שיתוף פעולה (L3a)

0 קצוות – רק sub_agents + mode

ה-LLM בוחר את קבוצת המשנה לכל בקשה

3 · דינמי (L4a/L4b)

3 קצוות – זהים בשני הצדדים

הרקרוסיה כתובה ב-Python, ולא מחוברת בתרשים

השורה האחרונה היא ההוכחה לתשובה לשאלה L4b: ל-L4a ול-L4b יש גרף זהה, ורק אחד מהם חוזר על עצמו.

מה אפשר לפתח עכשיו

כל דוגמה שהרצתם היא צורה של מוצר אמיתי:

תרגלת

בטבע,

התחל מ-

גרף + נתב (L2a/L2b)

document pipelines, ETL-with-LLM-steps, review/approval chains, eval harnesses

L2b של המאגר הזה

רכז + צוות single_turn (רמה 3א)

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

מצב marathon demo 2

task agents (L3b)

טפסים לקבלת פרטים, תהליכי הזמנה, צירוף משתמשים, אימות זהות (KYC) – כל תהליך של "איסוף ואז פעולה"

22_agent_in_workflow

רוחב/עומק דינמי (L4a/L4b)

סוכני מחקר, כלי ליצירת דוחות, סריקות ביקורת על קלט בגודל לא ידוע

מצב 3 של הדגמה של ריצת מרתון

הם יוצרים

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

האפליקציה כולה – ומה שתרשים לא יכול להראות לכם

💡 כדאי לנסות את זה בתהליך העבודה שלכם: הסקריפט ששימש ליצירת התמונה הזו הוא scripts/graph_dump.py. מצמידים אותו לכל Workflow והוא ידפיס את הקצוות האמיתיים – דיאגרמה מבנית חינמית של כל מה שתבנו.

14. מזל טוב

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

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

מה למדתם

  • הקדמה – הנחיה מגה שהמציאה מזג אוויר משלה: למה בכלל קיים מבנה.
  • L0–L1Agent, Runner, כלי אמיתי שהמודל בוחר להפעיל, ו-Workflow הראשון (צמתי פונקציות + צמתי סוכן כעמיתים).
  • L2a / L2b — graph workflows: parallel fan-out + JoinNode, then deterministic routing — one LLM call.
  • L3a – סוכנים שפועלים בשיתוף פעולה: אותו צוות פועל ב-chat (stranded) ואז ב-single_turn (parallel subset + synthesis) – דגל אחד, שני עולמות.
  • L3b — מצב task: שאלה להבהרה בהשהיה, חידוש מתוסרט, finish_task החזרת אובייקט מאומת.
  • L4a / L4b – Workflows דינמיים: רוחב זמן ריצה (fan-out), ואז עומק זמן ריצה (רקורסיה) עם גבולות בקוד.
  • L5 – עץ ההחלטה, והאופן שבו הדפוסים מורכבים.

שורות שכדאי לשמור

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

מאפשרים ל-LLM לעצב את העבודה, אבל שומרים על הגבולות בקוד.

מתאימים את התבנית לצורה של הבעיה.

השלבים הבאים

  • כדאי להריץ את האפליקציה המלאה שממנה נלקחו הרמות האלה – Marathon Race Day Coach, גרסת FastAPI + SSE עם ממשק משתמש בדפדפן שמציג את כל שלושת המצבים בשידור חי: github.com/cuppibla/adk-2-marathon-demo.
  • הרחבה: adk-workflows-compared – כל 23 הדוגמאות הרשמיות של תהליכי עבודה ב-ADK 2, כל אחת עם יציאה מגרסה 1.x והנחיות מתי להשתמש. מומלץ להתחיל עם docs/three-pillars.md, ואז לעבור לנושאים שלא נכללו ב-Codelab הזה: 07_loop, 17_request_input, 22_agent_in_workflow.
  • העברת בעיה משלכם: אילו חלקים הם בעלי מבנה מוכר (רמה 2), צוות מוכר (רמה 3א/רמה 3ב) וצורה לא מוכרת (רמה 4)?
  • אפשר לעיין בקוד בכתובת github.com/cuppibla/adk2-tutorial.
  • השתתפת בסדנה? הקרדיט שלכם – והפרויקט שנוצר בעזרתו – לא יישארו לתמיד. כדי להמשיך להריץ את הרמות האלה בחינם, צריך לבצע את השלב הגדרה לשימוש ביתי במקום: מפתח AI Studio בחינם, ללא פרויקט בענן וללא חיוב. השינוי היחיד הוא החלפת התא הזה.