תהליך עבודה מבוסס-סוכן באמצעות ADK

1. מבוא

VibeStudio

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

התרחיש

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

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

תהליך העבודה שאתם יוצרים, מרעיון ועד לקליפ שפורסם

מה לומדים

סיכום ל-10 ימים

  • הבסיס להנדסת גרפים: ארכיטקטורות של סוכנים מרובי-שלבים דורשות זרימת בקרה מפורשת ונתיבי ביצוע מובנים. אתם יוצרים ADK‏ Workflow באמצעות טופלים של קצה, נקודת הכניסה START, ‏ JoinNode לצבירה מקבילית של fan-out, וצמתי נתב דטרמיניסטיים כדי לנהל את הביצוע על סמך מצב.
  • מצבי סוכן וקריאות חוזרות (callback) של מחזור החיים: משימות מיוחדות דורשות התנהגויות תפעוליות שונות וגבולות גזרה דטרמיניסטיים. מגדירים מופעי ADK Agent באמצעות מצבי chat, single_turn ו-task שמופעלים על ידי כלי כצמתים של תהליך עבודה, ומחילים מיירטים באמצעות before_model_callback ו-after_agent_callback.
  • תיזמור של אדם בתהליך: צינורות עיבוד נתונים מושהים בנקודות ביקורת יצירתיות קריטיות כדי לאפשר שיקול דעת אנושי. אתם מטמיעים את RequestInput כדי להשהות את הביצוע של תהליך העבודה, לאכוף סכימות של תגובות מובנות ולחדש את הביצוע בלי להשאיר תהליכי זמן ריצה במצב המתנה.
  • זיכרון היררכי של סוכנים: מערכות ייצור מפרידות בין מצב ביצוע זמני לבין הקשר עמיד. אתם מנהלים את מצב הסשן לטווח קצר באמצעות Event(state=...) וקישור פרמטרים, ומחברים את Memory Bank של GEAP כדי לחלץ, לאחד ולשמור את העדפות היוצרים בין הפעלות.
  • התבססות על בסיסי ידע ארגוניים: סוכנים אוטונומיים צריכים הקשר דינמי של הדומיין וסנטימנט הקהל. אתם מחברים קורפוס של GEAP RAG Engine כצומת אחזור ייעודי ב-fan-out המקבילי כדי לעגן סמנטית את הפלטים של הסוכן.
  • תהליכי עבודה ופריסה ממושכים: עיבוד וידאו מולטימודאלי פועל באופן אסינכרוני לאורך פרקי זמן ממושכים. מטמיעים את LongRunningFunctionTool עם אישורי שיחה בהמתנה כדי להשהות את תהליך העבודה ולחדש אותו לפי מזהה השיחה, ופורסים את צינור עיבוד הנתונים שהושלם באמצעות ADK‏ Runner ב-Cloud Run.

איך ה-Codelab הזה מאורגן

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

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

בסיום התרגילים בסביבת העבודה, תרכיבו צינור (pipeline) של סוכן מקצה לקצה ותפרוסו אפליקציית VibeStudio פעילה ב-Cloud Run כדי ליצור תוכן וידאו.

מה פועל איפה: VibeStudio Workbench, הקצה העורפי ושירותי Google Cloud

הסביבה מורכבת משלושה רכיבים עיקריים: VibeStudio Workbench (ממשק האינטרנט המקומי לעריכת קוד ולאימות בזמן ריצה), הקצה העורפי (ADK Workflow וארגזי חול של שלבים ב-agent/) ו-Google Cloud (מודלים של Gemini,‏ GEAP Memory Bank,‏ RAG Engine ויצירת סרטונים ב-Veo).

2. הגדרה

מימוש שוברי הפרסום לסדנאות

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

פתיחת Cloud Shell

‫Cloud Shell היא סביבת פיתוח מבוססת-דפדפן עם gcloud,‏ Python ו-git שמותקנים מראש.

כדי להפעיל את Cloud Shell:

  1. נכנסים למסוף Google Cloud.
  2. בכותרת הניווט העליונה, לוחצים על Activate Cloud Shell (הסמל של חלון הטרמינל).

Cloud Shell

סשן טרמינל ייפתח בחלק התחתון של חלון הדפדפן.

שכפול המאגר והפעלתו

כדי לשכפל את הפרויקט, מריצים את הפקודות הבאות במסוף Cloud Shell:

git clone https://github.com/gca-americas/vibetube-studio
cd ~/vibetube-studio

הנחיות להגדרה

במהלך ההגדרה תתבקשו לספק את הפרטים הבאים:

  • מזהה פרויקט בענן ב-Google Cloud: כשמוצגת בקשה מ-setup_project.sh, מקישים על Enter כדי ליצור פרויקט חדש באופן אוטומטי. אם אתם מעדיפים להשתמש בפרויקט קיים (למשל פרויקט שהוקצה מראש), מזינים את מזהה הפרויקט ומוודאים שהאיות נכון ושהחיוב פעיל.
  • קוד האירוע: מזינים את קוד החדר שקיבלתם מהמורה. אם לא קיבלתם כרטיס, כדאי לבדוק עם עוזר הוראה או עם שכן. אם אתם משלימים את שיעור ה-Lab הזה בבית, מקישים על Enter כדי לאשר את החדר sandbox שמוגדר כברירת מחדל.
  • השם המוצג של הערוץ: מזינים את השם או את הכינוי המועדף לערוץ כשמופיעה ההנחיה ב-setup_codelab.sh, או לוחצים על Enter כדי לאשר את ברירת המחדל שנוצרה מחשבון Google.

מריצים את שני סקריפטי ההגדרה לפי הסדר:

./setup_project.sh
./setup_codelab.sh
  • setup_project.sh: יוצר או משתמש מחדש בפרויקט בענן של Google עם חיוב פעיל, שומר את מזהה הפרויקט ב-~/project_id.txt ומגדיר את ההקשר הפעיל gcloud.
  • setup_codelab.sh: מתקין את uv ואת יחסי התלות של Python ב-.venv, מפעיל את Cloud APIs הנדרשים של Google Cloud, מגדיר את הגדרות הערוץ ב-.env, מאמת את הגישה למודל באמצעות Gemini, מקצה משאבים של Memory Bank ו-RAG, בונה את ממשק סביבת העבודה ומפעיל את VibeStudio Workbench.

הסקריפט מריץ את בדיקת הטרום-הפעלה ומתחיל את VibeStudio Workbench ברקע. בשורה האחרונה מוצג הקישור לפתיחה.

7 · Preflight
   python 3.12
   auth path A: Vertex via ADC (STUDIO_VERTEX=1)
   Google Cloud ADC (project <your-project>)
   stage0_prompt loads
  ...
   stage6_video loads (13 edges)
   aiplatform.googleapis.com enabled (Gemini, Veo, Memory Bank, RAG Engine)
   vectorsearch.googleapis.com enabled (the vector store a RAG corpus is built on)
   Memory Bank connected
   RAG corpus connected
   VibeStudio Workbench running on port 4600

PREFLIGHT GREEN

Setup finished. The VibeStudio Workbench is already running.

  Open this and start at step 1
      https://4600-<your cloud shell host>/step/story

  It runs in the background. You do not need to start anything else.
      log      runs/lab.log
      stop     kill $(cat runs/lab.pid)
      start    scripts/start.sh

לוחצים על הקישור. אותה כתובת זמינה גם בקטע תצוגה מקדימה באינטרנט → שינוי יציאה → 4600.

כדי לבדוק מחדש את הסביבה בכל שלב, מריצים את הפקודה python scripts/preflight.py. כדי להפעיל מחדש את סביבת העבודה, מריצים את הפקודה scripts/restart.sh. כדי להגדיר שוב, מריצים את ./setup_codelab.sh. ההגדרות וההתקדמות יישמרו.

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

כל חלק מעשי ב-VibeStudio Workbench מסתיים בחלונית אימות שקוראת את הארטיפקטים האמיתיים: הקובץ בדיסק והסשנים שנכתבו על ידי ההרצות.

פריסת מאגר

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

vibe-studio-lab/
├── agent/                  # Core ADK workflow, graph definition, and platform services
   ├── graph.py            # Workflow graph definition, node functions, and routers
   ├── desk.py             # Video render desk using LongRunningFunctionTool
   ├── schemas.py          # Pydantic schemas for directions, gates, and scripts
   ├── trends.py           # Trend generation and sampling utilities
   ├── backlog.txt         # Creator video ideas backlog
   ├── comments.md         # Audience comments for RAG Engine corpus seeding
   ├── policy_words.txt    # Blocked subject words for deterministic policy checks
   └── platform/           # Google Cloud service clients (Memory Bank, RAG, Veo)
       ├── config.py       # Environment variables, locations, and model configurations
       ├── memory.py       # GEAP Memory Bank callbacks and context injection
       ├── rag.py          # GEAP RAG Engine corpus creation and semantic retrieval
       └── videogen.py     # Veo video generation and operation polling
├── stage0_prompt/          # Step sandboxes: isolated agent.py files runnable in adk web
   └── ...                 # stage1_fanout through stage6_video for incremental steps
├── server/ & web/          # VibeStudio Workbench (FastAPI backend and React frontend)
├── vibestudio/             # Complete production application deployed to Cloud Run
   ├── server/             # FastAPI production server and event runner
   ├── web/                # End-user React web application
  • agent/: מכיל את הגרף המרכזי של תהליך העבודה. תערכו קבצים בספרייה הזו כדי להטמיע צמתים מקבילים של fan-out, ניתוב מדיניות דטרמיניסטי, קריאות חוזרות לזיכרון וכלי ליצירת סרטונים.
  • agent/platform/: ממשקי משתמש עם שירותי Google Cloud, כולל מודלים של Gemini,‏ GEAP Memory Bank,‏ GEAP RAG Engine וסינתזת וידאו של Veo.
  • stage0_prompt/ עד stage6_video/: סביבות ארגז חול עצמאיות. כל תיקייה מייצאת root_agent עצמאי, כך שאפשר להריץ ולבדוק כל שלב בנפרד באמצעות ממשק הפיתוח המוטמע של ADK.
  • server/ ו-web/: אפליקציית VibeStudio Workbench שפועלת באופן מקומי ביציאה 4600. הוא כולל את התיעוד של השלב, עורך קוד שמוטמע בדף, מאמתים של ראיות בזמן ריצה ותצוגה חזותית של הגרף.
  • vibestudio/: האפליקציה המלאה לייצור, שנארזת ונפרסת ב-Cloud Run בשלב האחרון. הוא מכיל עותק עצמאי משלו של תרשים זרימת העבודה שהושלם.

3. סוכן מונוליתי

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

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

ארכיטקטורה של סוכן ADK‏ (3A)

ב-VibeStudio Workbench, עוברים אל Step 3 · Monolithic agent ופותחים את ADK agent architecture (3A). בתצוגה הזו מוצגות שכבות הליבה של סוכן ADK‏ (LlmAgent):

03-3A

from google.adk.agents import LlmAgent
from google.adk.tools import mcp_toolset

root_agent = LlmAgent(
    model="gemini-3.5-flash",                 # model
    instruction=BRAND_INSTRUCTION,            # instruction
    skills=[load_skill("brand-audit")],       # skills
    tools=[mcp_toolset("mcp_brand_style")],   # tools
    output_schema=BrandStyleReport,           # structured output
    before_agent_callback=setup_ctx,          # interceptor
    before_model_callback=require_image,      # interceptor
    after_model_callback=schema_guard,        # interceptor
)

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

  • שכבת ההסקה (מודל): מודל השפה הבסיסי (כמו Gemini 3 Flash) שמבצע משימות קוגניטיביות, חשיבה רציונלית של פרומפטים ובחירה של כלים. כל שאר הרכיבים בארכיטקטורה מספקים מידע למודל הזה או מגבילים אותו.
  • שכבת ההקשר (הוראות ומיומנויות): הנחיות שמעצבות את ההיגיון של המודל. instruction מגדיר את ההנחיה הקבועה למערכת, את הדמות ואת כללי התפעול. skills מספקת הנחיות מפורטות עם מספור גרסאות (SKILL.md) לתהליכי עבודה שניתן לחזור עליהם.
  • שכבת שיתוף הפעולה והפעולה (כלים, סוכני משנה, תהליך עבודה, סכימת פלט): ממשקים שמאפשרים לסוכן לפעול במערכות חיצוניות ולפלוט נתונים מוקלדים. tools לספק פונקציות Python שניתן להפעיל או נקודות קצה של Model Context Protocol‏ (MCP). subagents לבצע משימות שהוקצו לבעלי הרשאה משנית. workflow קואורדינטות של גרפים של כמה סוכנים. ‫output_schema משתמש במודלים של Pydantic כדי להבטיח שצרכנים במורד הזרם יקבלו JSON מאומת במקום טקסט לא מובנה.
  • שכבת חסימה (Lifecycle Callbacks): אמצעי הגנה דטרמיניסטיים שמבצעים קוד מותאם אישית לפני ואחרי הפעלה של סוכן (before_agent/after_agent), תורות של מודלים ספציפיים (before_model/after_model) וקריאות לכלים (before_tool/after_tool). אמצעי החסימה אוכפים כללי מדיניות בלי להסתמך על התאמה של המודל.
  • מצב חיצוני (סשן וזיכרון): שמירה של מצב נפרד מהלוגיקה של הסוכן. ‫Session שומר את הזיכרון הזמני של המשימה ואת נתוני המעקב אחר האירועים של השרשור הנוכחי. ‫Memory שומר על עובדות והעדפות עמידות בין סשנים באמצעות שירותים מנוהלים כמו GEAP Memory Bank.

הסוכן המונוליטי בשלב הזה מיישם רק שלושה מהפרימיטיבים האלה: model,‏ instruction ו-tools. בשלבים הבאים נסביר על תהליכי עבודה של גרפים, סכימות מובנות, interceptors ושירותי זיכרון קבוע.

מפרט של סוכן מונוליטי (3B)

בסביבת העבודה, עוברים אל Monolithic agent specification (3B). פותחים את stage0_prompt/agent.py כדי לבדוק את ההגדרה של הסוכן הראשוני:

  • הנחיה אחת: הנחיית המערכת מתמצתת חמש משימות הפקה שונות לטקסט רציף: גילוי מגמות בפלטפורמה, בדיקת רעיונות לתוכן, הצעת קונספטים יצירתיים, אכיפת מדיניות בנושאים אסורים וטיוטת רשימת שוטים.
  • מקורות נתונים בסיסיים: הסוכן מתייחס לשני מקורות שמוגדרים לצד הגרף:
    • agent/trends.py: המערכת בוחרת מדגם של עשר מגמות פעילות של פורמטים וסגנונות מתוך מאגר של 250 מגמות עם ציוני חום דינמיים.
    • agent/backlog.txt: קורא את הערות הרעיון הגולמיות של היוצר שורה אחר שורה.

כלים בסוכן (3C)

בסביבת העבודה, עוברים אל Tools in Agent (3C) (כלים בסוכן (3C)).

מה זה כלי לסוכן?

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

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

03-3C

הפעלת כלים מתבצעת לפי פרוטוקול מפורש בן חמישה שלבים בין המודל לבין זמן הריצה של ADK:

  1. הצהרת סכימה: המפתח מספק פונקציות Python לסוכן. ה-ADK בודק את השם, הערות הסוג והמחרוזות של כל פונקציה כדי ליצור הצהרת סכימת JSON שתואמת ל-OpenAPI ומתארת את הפרמטרים והמטרה שלה.
  2. חשיבה רציונלית של המודל: במהלך היקש, המודל מעריך אם הפרומפט של המשתמש דורש נתונים חיצוניים. במקרה הצורך, המודל פולט אירוע מובנה function_call שמכיל את שם פונקציית היעד ומילון הארגומנטים שתואם לסכימה.
  3. הרצת זמן ריצה: המודל עצמו לא מריץ קוד. זמן הריצה של ADK מיירט את function_call, מפעיל את פונקציית ה-Python המקומית בפועל באמצעות הארגומנטים שסופקו ומתעד את הערך המוחזר.
  4. החדרה מחדש של ההקשר: חבילות ה-ADK בזמן הריצה אורזות את הערך שמוחזר מהפונקציה לאירוע function_response ומצרפות אותו להיסטוריית הסשן הפעיל.
  5. סינתזה סופית: המודל מעבד את הפלט של הכלי שמופיע עכשיו בחלון ההקשר שלו ומשלים את התשובה.

ב-stage0_prompt/agent.py, שני כלי המחקר מוגדרים כפונקציות Python רגילות:

def check_trends() -> dict:
    """Ten formats trending on the platform right now, with a heat score each."""
    from agent.trends import sample_trends
    return {"trends": sample_trends()}


def read_backlog() -> dict:
    """The creator's backlog: ideas they noted down to make someday."""
    from agent.graph import backlog_notes
    return {"backlog": backlog_notes()}

עריכה והרצה מעשיות

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

    tools=[check_trends, read_backlog],

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

לוחצים על Open adk web (פתיחת ADK באינטרנט) כדי להפעיל את ממשק הפיתוח המוטמע של ADK. שולחים את ההצעה לפרומפט:

tonight's idea: a tiny robot doing laundry at midnight

מה צפוי ולמה

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

  • שני אירועים של הפעלת כלי מופיעים לפני התשובה: מופיעים אירועים function_call ו-function_response עבור check_trends ו-read_backlog.
    • למה: Gemini העריך את ההנחיה בהנחיית המערכת ("בדוק מה פופולרי. עיין ברשימת הרעיונות שלך"), זיהה שחסרים לו משקלים של מגמות בפלטפורמה והערות בערוץ, והפעיל את שתי הפונקציות כדי להרחיב את ההקשר.
  • הסוכן מציע כיוון ועוצר כדי לקבל אישור: בתשובה מוצע כיוון לסרטון שמסכם את המגמות ואת רשימת המשימות, ומתבקש אישור.
    • למה: בהנחיה, המודל התבקש להסכים על הכיוון עם היוצר לפני יצירת התסריט.
  • דילוג על אישור בתור הבא: שולחים הודעה שנייה: skip the questions, just describe the video. הסוכן מדלג מיד על האישור ומנסח את שם הסרטון ואת הצילומים.
    • למה: ההנחיות בהנחיה הן המלצות ולא חסמים מוחלטים. בסוכן מונוליטי, הוראות המשתמש יכולות לבטל כללים קיימים של הנחיית המערכת, כי אין תהליך עבודה חיצוני ששולט בזרימת הביצוע.

מגבלות ארכיטקטוניות של הנחיה מונוליטית

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

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

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

4. היסודות של תהליכי עבודה אג'נטיים

ב-VibeStudio Workbench, עוברים אל Step 4 · Agentic workflow fundamentals (שלב 4 – יסודות של תהליך עבודה אקטיבי), חלקים 4A עד 4D.

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

ארכיטקטורת גרף ושרשראות ביצוע (4A)

בסביבת העבודה, פותחים את Graph architecture and execution chains (4A).

ב-ADK‏ Workflow, הרצת הסוכן בנויה כגרף מכוון שמוגדר על ידי רשימת קשתות:

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

04-4A

סוגי צמתים ב-ADK

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

אב-טיפוס של צומת

הטמעה

תפקיד בפייפליין

צומת פונקציה

פונקציית Python שמחזירה Event

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

צומת של הצטרפות

מופע מובנה של JoinNode

מסנכרנת ענפים מקבילים למילון מצטבר.

צומת של סוכן

Agent פועל במצב single_turn

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

צומת נתב

פונקציה שמחזירה Event עם תג route

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

צומת קלט אנושי

פונקציה שמחזירה ערך RequestInput

הפעולה מושהית עד שתגיע תגובה ממשתמש חיצוני.

root_agent = Workflow(
    name="stage1_fanout",
    description="2 real readers -> join -> one research dict",
    edges=[...])

במקרה הזה, root_agent הוא מופע של Workflow ולא Agent עצמאי. ב-ADK, תהליכי עבודה נחשבים לסוכנים ברמה גבוהה, ולכן אפשר לטעון, להציג ולבדוק גרף שלם כאפליקציה מאוחדת. name רושם את האפליקציה ב-ADK Web, ואילו רשימת edges מגדירה את טופולוגיית הביצוע שלה.

הסתעפות מחקר מקבילית (4B)

ב-Workbench, עוברים אל Parallel research fan-out (4B). פותחים את stage1_fanout/agent.py.

04-4B

צמתים של פונקציות ומחסומי סנכרון

בשלב המחקר נעשה שימוש בשני צמתי פונקציות שיובאו מ-agent/graph.py:

  • scan_trends: מחזירה את Event(output={"trends": [...]}) שמכיל עשר מגמות בפלטפורמה עם ניקוד.
  • read_backlog: מחזירה Event(output={"backlog": [...], "idea": "..."}) שמכילה חמישה עשר רעיונות לתוכן בערוץ, לצד ההנחיה הראשונית.

כל פונקציה מקבלת node_input (הפלט של הצומת הקודם) ומחזירה Event.

צומת JoinNode משמש כמחסום סנכרון: הוא מושהה עד שכל שרשרת נכנסת מעבירה אירוע, ואז הוא מצבר את כל התוצאות של הענפים למילון עם מפתחות לפי שם הצומת ({"scan_trends": {...}, "read_backlog": {...}}).

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

ב-stage1_fanout/agent.py, יוצרים מופע של JoinNode ומקשרים את שני השרשורים המקבילים שמתחילים ב-START:

join_research = JoinNode(name="join_research")
    edges=[(START, scan_trends, join_research),
           (START, read_backlog, join_research)])

שומרים את השינויים. המאמת של סביבת העבודה מוודא שהצמתים והקצוות מחוברים. מריצים את השלב באמצעות Run Stage 1 (הרצת שלב 1) או דרך ממשק האינטרנט המוטמע של ADK.

מה צפוי ולמה

  • הפעלה בו-זמנית של קוראים: בתרשים ההפעלה, הצמתים scan_trends ו-read_backlog מופעלים בו-זמנית.
    • למה: שתי השרשראות מתחילות ב-START. מנוע ה-ADK מתזמן ענפים עצמאיים במקביל.
  • פלט מילון מצטבר: תהליך העבודה מסתיים ב-join_research, ומופק מילון עם ערכים לשני הקוראים.
    • למה: הצומת JoinNode מבטיח שכל הנתונים יתועדו לפני שמאפשרים לצמתים הבאים לפעול.

צמתים של סוכנים (4C)

ב-Workbench, עוברים אל Agent nodes (4C) (צמתי סוכן (4C)). פותחים את stage2_direction/agent.py.

04-4C

מצבי הפעלה וסכימות מובנות

כשמטמיעים Agent בתוך Workflow, הוא פועל במצב single_turn כברירת מחדל:

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

ההקצאה של output_schema=Directions גורמת לסוכן לאכוף אימות Pydantic על פלט המודל. התרשים במורד הזרם מקבל אובייקטים מוקלדים במקום פרוזה לא מובנית:

class Direction(BaseModel):
    title: str           # <=60 chars, filmable, characterful
    angle: str           # the twist, one line
    hook: str = ""       # 2-4 words, the video's sticker line
    evidence: list[Evidence]


class Directions(BaseModel):
    candidates: list[Direction]   # exactly 4

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

עריכה מעשית: הגדרת צומת הסוכן ושרשור ההצטרפות

ב-stage2_direction/agent.py, מגדירים את propose_directions ומרחיבים את קצוות תהליך העבודה:

propose_directions = Agent(
    name="propose_directions",
    model=config.MODEL,
    instruction=PROPOSE_INSTRUCTION,
    output_schema=Directions)
    edges=[(START, scan_trends, join_research),
           (START, read_backlog, join_research),
           (join_research, propose_directions, direction_gate)])

מה צפוי ולמה

  • צריכה ישירה של מילון: propose_directions צורך את מטען ה-JSON הייעודי שמופק על ידי join_research ללא עיצוב ידני.
  • פלט מועמד עם סוג: הסוכן פולט אובייקט מאומת Directions שמכיל ארבעה מועמדים נפרדים. צמתים במורד הזרם קוראים שדות לפי שם המאפיין (candidate.title) בלי ניתוח מחרוזות.

האדם שבתהליך (4D)

ב-Workbench, עוברים אל Human-in-the-loop (האדם שבתהליך) (4D). פותחים את agent/graph.py.

04-4D

הוראות בפרומפט לעומת השעיה דטרמיניסטית

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

  • הפקודה Yielding RequestInput משעה את הביצוע של תהליך העבודה באופן מיידי.
  • ה-ADK מתעד קריאה פתוחה להפרעה בחנות הסשנים ומנפיק interrupt_id ייחודי.
  • תהליך ההפעלה נעצר בלי לצרוך אסימונים או שרשורים של השרת.
  • הביצוע של הגרף יתחדש רק כשיישלח ערך תקין של function_response שתואם לסכימה ולמזהה ההפרעה.

עריכה מעשית: השהיית הביצוע באמצעות RequestInput

ב-agent/graph.py, מטמיעים את קריאת ההשעיה בתוך direction_gate:

    yield RequestInput(
        message="Pick tonight's direction: 1, 2, 3 or 4.",
        response_schema={
            "type": "object",
            "properties": {
                "pick": {"type": "string", "enum": ["1", "2", "3", "4"]}}},
        payload={"candidates": cands})

RequestInput מגדיר שלושה מאפיינים:

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

מה צפוי ולמה

  • תהליך העבודה נעצר ב-direction_gate: ב-ADK Web או בממשק של סביבת העבודה, ההרצה מושהית ומוצג טופס אינטראקטיבי לבחירת מועמדים.
    • הסיבה: המנוע נתקל ב-RequestInput שהוחזר והמשיך את מצב הביצוע ל-runs/sessions.db.
  • המשך השיחה דורש קלט מובנה: שליחת טקסט שרירותי בצ'אט לא מקדם את הגרף. בחירה באחת מהאפשרויות (1, 2, 3 או 4) שולחת את הערך המוקלד function_response שעומד בתנאי response_schema וממשיכה את ההרצה.

5. מדינה ונתב

ב-VibeStudio Workbench, עוברים אל Step 5 · State and Router, חלקים (5A) עד (5C).

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

מצב תהליך העבודה (5A)

ב-Workbench, עוברים אל Workflow State (5A) (סטטוס תהליך העבודה (5A)).

05-5A

מצב הסשן לעומת פלט הצומת

בתהליך עבודה של ADK, הנתונים עוברים בגרף באמצעות שני מנגנונים שונים:

  • פלט של הצומת (Event(output=...)): נתונים שמופנים אך ורק לצרכני נתונים במורד הזרם המיידי, שמוגדרים ברשימת הקצה.
  • מצב הסשן (Event(state=...)): מילון משותף של זוגות מפתח/ערך שנגיש לכל צומת עוקב במחזור החיים של הביצוע.

05-5A

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

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

    yield Event(state={"direction": chosen["title"], "angle": chosen.get("angle", ""),
                       "hook": hook, "user:prefs": {"last_direction": chosen["title"]}})

הפונקציה מחזירה את הערך Event ומעבירה את השליטה לזמן הריצה Workflow, ששומר את הערכים החדשים ביומן הסשן ב-runs/sessions.db.

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

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

def persist_direction(node_input, candidates: list = []):
    ni = node_input if isinstance(node_input, dict) else {}
    raw = ni.get("pick")
    pick = str(raw).strip() if raw is not None else ""
    if candidates:
        i = int(pick) - 1 if pick.isdigit() else 0
        chosen = candidates[max(0, min(len(candidates) - 1, i))]
    else:
        chosen = {"title": "untitled", "angle": "", "evidence": []}
    hook = chosen.get("hook") or " ".join(chosen["title"].split()[:4])

במקרה הזה, הערך candidates נכתב למצב הסשן על ידי direction_gate. ה-ADK מאגד אותו ישירות ל-persist_direction(node_input, candidates: list = []) בלי לדרוש חיפושים מפורשים במילון.

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

עריכה מעשית: שמירת המצב וחיבור הצומת

  1. ב-agent/graph.py, בתוך persist_direction, מחליפים את השורה TODO: PERSIST_STATE בתפוקת אירוע המצב:
    yield Event(state={"direction": chosen["title"], "angle": chosen.get("angle", ""),
                       "hook": hook, "user:prefs": {"last_direction": chosen["title"]}})
  1. ב-stage3_router/agent.py, מוסיפים את persist_direction לשרשרת השלישית ברשימה edges:
           (join_research, propose_directions, direction_gate,
            persist_direction)

שומרים את הקבצים. בסביבת העבודה, מוודאים שגם state write in place וגם persist_direction in the chain מציגים סימני וי ירוקים.

צומת הנתב (5B)

ב-Workbench, עוברים אל צומת הנתב (5B).

05-5B

ניתוב דטרמיניסטי על סמך מדיניות

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

נתב מחזיר Event שמציין תג route:

def length_check(node_input):
    too_long = len(node_input.get("title", "")) > 60
    return Event(output=node_input, route="TRIM" if too_long else "PASS")

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

    (length_check, {"TRIM": shorten, "PASS": scripter}),

הנתב של תהליך העבודה policy_check קורא את הביטויים האסורים מ-agent/policy_words.txt ומבצע התאמה של מילים שלמות לשם ולזווית של הכיוון שנבחר:

    return Event(output=node_input, route="BLOCK" if bad else "OK")

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

יעדים: Scripter ו-Quarantine

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

  • scripter: צומת של סוכן single_turn שממיר את ההנחיות שאושרו לסקריפט הפקה מובנה בהתאם לסכימת Script Pydantic:
scripter = Agent(
    name="scripter",
    model=config.MODEL,
    instruction=SCRIPT_INSTRUCTION,
    output_schema=Script)
  • quarantine: בתחילה, פונקציית placeholder שמשהה הוראות שסומנו. בחלק הבא היא מוחלפת בסוכן תיקון אוטונומי.

עריכה מעשית: ניתוב של בדיקת המדיניות

  1. בקטע agent/graph.py, בתוך policy_check, משלימים את פקודת החזרה:
    return Event(output=node_input, route="BLOCK" if bad else "OK")
  1. ב-stage3_router/agent.py, מעדכנים את edges כדי להפנות ל-policy_check ומצטרפים מחדש לענף ההסגר ב-scripter:
           (join_research, propose_directions, direction_gate,
            persist_direction, policy_check),
           (policy_check, {"OK": scripter, "BLOCK": quarantine}),
           (quarantine, scripter)])

שומרים את הקבצים. בסביבת העבודה, מוודאים שמיפויי הנתבים של Edge אומתו.

מצבי סוכן וצומת המשימה (5C)

ב-Workbench, עוברים אל Agent modes and the task node (5C) (מצבי סוכן וצומת המשימה (5C)).

05-5C

מצבי הרצה של סוכנים

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

מצב

מחזור החיים של הביצוע

תפקיד בפייפליין

chat

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

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

single_turn

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

טרנספורמציות רציפות של גרפים (propose_directions, scripter).

task

Autonomous loop with tool execution. הנציג חוזר על הפעולה עד שהוא מתקשר לכלי המובנה finish_task.

תיקון ובדיקה רב-שלביים (quarantine).

תיקון אוטונומי של מדיניות

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

שני הכלים מוגדרים ב-agent/cleanup_tools.py עם חתימות מוקלדות ומחרוזות תיעוד:

def find_policy_hits(text: str) -> dict:
    """Which refused words appear in `text`. Matches whole words and phrases
    from agent/policy_words.txt, case-insensitive.

    Returns {"hits": [...], "clean": bool}. clean is true when hits is empty.
    """


def suggest_replacement(word: str) -> dict:
    """The channel's approved stand-in for a refused word, read from
    agent/policy_replacements.txt.

    Returns {"word", "replacement", "listed"}. When the word has no entry,
    listed is false and replacement is a hint to pick a gentle synonym.
    """

עריכה מעשית: הרכבת סוכן המשימות של ההסגר

ב-stage3_router/agent.py, מחליפים את פונקציית ה-placeholder quarantine בהגדרה של סוכן המשימות:

quarantine = Agent(
    name="quarantine",
    model=config.MODEL,
    instruction=QUARANTINE_INSTRUCTION,
    mode="task",
    tools=[find_policy_hits, suggest_replacement],
    output_schema=CleanedDirection,
)

במצב משימה, הסוכן מצויד בכלים וההרצה מסתיימת על ידי קריאה ל-finish_task. כשמגדירים את mode="task", ‏ ADK מספק אוטומטית את finish_task וגוזר את הפרמטרים שלו מ-output_schema, וכך מוודא שהצומת יפיק אובייקט מוקלד CleanedDirection שתואם לסכימת הקלט של צומת הסקריפט.

05-5C

מה צפוי ולמה

בודקים את שני נתיבי הביצוע ב-ADK Web או ב-VibeStudio Workbench:

  • מסלול מאושר (מועמד 1, 2 או 3):
    • בחירת מועמד שאושר מעבירה את המסלול מpolicy_check ישירות אל scripter (route="OK").
    • התסריטאי יוצר תסריט הפקה של 3 שוטים בהתאם לסכימה Script.
  • Quarantine remediation route (Candidate 4):
    • אלמנט החיפוש מספר 4 מכיל אוצר מילים מסומן ("קליקבייט", "פריצה ויראלית").
    • policy_check מסלולים אל quarantine (route="BLOCK").
    • במעקב אחר הסשן, אפשר לראות את quarantine קורא ל-find_policy_hits, קורא ל-suggest_replacement לכל הפרה, משכתב את הכותרת וקורא ל-finish_task.
    • ההרצה חוזרת ל-scripter, ויוצרת תסריט מההנחיה שעברה ניקוי.

6. Memory Bank

בסביבת העבודה של VibeStudio, עוברים אל שלב 6 – מאגר זיכרון, חלקים (6א) ו-(6ב).

כרגע, תהליך העבודה פועל ללא זיכרון בין הפעלות. כל הפעלה מתחילה מאפס, בלי לדעת מה היוצר בחר קודם או אילו ז'אנרים הוא מעדיף. בשלב הזה, מחברים את Vertex AI Agent Engine Memory Bank כדי לאחסן את ההעדפות של היוצרים ולאחזר אותן בין הפעלות.

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

Memory Bank (6A)

בסביבת העבודה, עוברים אל Memory Bank (6A) (מאגר הזיכרון).

06-6A

זיכרון נפרד לכל משתמש מנוהל

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

SCOPE = {"app_name": config.APP, "user_id": config.USER}
TOPICS = {
    "CREATOR_TASTE": "Which video directions this creator picks and passes on, "
                     "and how that preference changes over time.",
    "CHANNEL_RULES": "Standing instructions the creator states for every video "
                     "(style, subjects to avoid, format rules).",
}

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

  • חילוץ נושאים: כששולחים טקסט חדש לשיחה באמצעות memories.generate, השירות מפעיל מודל חילוץ על כל תיאור של נושא. אם הטקסט לא תואם לנושא, לא נוצרים זיכרונות.
  • איחוד והסרת כפילויות: השירות ממיר עובדות חדשות שחולצו להטבעות ומשווה אותן לזיכרונות קיימים בהיקף. כשתצפית תואמת לזיכרון קיים, השירות מעדכן את הזיכרון הזה. אם המידע חדש, השירות יוצר רשומה חדשה. תהליך האיחוד הזה מבטיח שמידע מכמה סשנים בנושא מסוים יאוחד לסיכום קוהרנטי, במקום ליצור רשומות מיותרות.
  • אחזור: קריאה ל-memories.retrieve עם היקף המשתמש מחזירה עובדות מאוחסנות, בסדר מהישן לחדש.

שתי הפעולות מיושמות ב-agent/platform/memory.py. שם משאב הבנק שהוקצה נשמר במטמון באופן מקומי ב-runs/memorybank.json.

הגדרת בנק הזיכרונות

משתמשים באמצעי הבקרה של סביבת העבודה או מריצים את פקודות ה-CLI בטרמינל:

  1. חיבור הבנק והקצאת הרשאות:
    python -m agent.platform.bank
    
    יצירת מופע של Agent Engine והגדרת הנושאים CREATOR_TASTE ו-CHANNEL_RULES.
  2. הוספת נתונים היסטוריים של סשנים:
    python -m agent.platform.bank load
    
    טוען ארבעה סשנים היסטוריים של יוצרים (שני עיצובים של בעלי חיים עם מגבלות סגנון, עיצוב אחד של גאדג'ט ועיצוב פנטזיה אחד מהזמן האחרון).
  3. בדיקת עובדות מאוחדות:
    python -m agent.platform.bank list
    
    בודקים את הפלט. שימו לב איך תמלילים נרטיביים הומרו להצהרות עובדתיות מובנות ומאוחדות.

קריאות חוזרות (6B)

ב-Workbench, עוברים אל Callbacks (6B). פותחים את stage4_memory/agent.py.

06-6A

קריאות חוזרות במחזור החיים של סוכן ADK

פונקציית קריאה חוזרת היא פונקציה שמועברת כארגומנט לפונקציה Agent. ערכת ה-ADK מפעילה קריאות חוזרות (callbacks) ברגעים מוגדרים מראש במחזור החיים, ומעבירה את ההקשר הפעיל. החזרת None ממשיכה את הביצוע הרגיל, והחזרת אובייקט חלופי מבטלת את הפעולה או מיירטת אותה.

06-6A

ערכת ה-ADK מספקת שלושה זוגות של פונקציות קריאה חוזרת:

זוג של קריאה חוזרת

נקודת הפעלה

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

התנהגות הערך המוחזר

before_agent_callback
after_agent_callback

מסביב לכל תור הסוכן

CallbackContext (state, session, invocation)

הקשה על מקש Content מחליפה את תשובת הנציג, והקשה על מקש None ממשיכה כרגיל.

before_model_callback
after_model_callback

מסביב לכל קריאה להסקת מסקנות של מודל שפה גדול

LlmRequest או LlmResponse

החזרת LlmResponse מיירטת את הקריאה למודל או מדלגת עליה; None ממשיך.

before_tool_callback
after_tool_callback

מסביב לכל הרצה של כלי

הגדרת הכלי, הארגומנטים והתוצאה

החזרת מילון מבטלת את פלט הכלי, None ממשיך.

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

עריכה מעשית: החזרת שיחות חוזרות וזכירת שיחות חוזרות

  1. ב-stage4_memory/agent.py, מעדכנים את propose_directions כדי לצרף את before_model_callback=recall_taste:
    output_schema=Directions,
    before_model_callback=recall_taste)

recall_taste מופעל מיד לפני ש-Gemini יוצר הצעות לניסוח. הכלי מאחזר את ההיסטוריה של היוצר מ-Memory Bank, מעצב את הזיכרונות מהישן לחדש ומצרף אותם לLlmRequest היוצא. ההנחיה מכוונת את המודל להטות את המועמדים 1 עד 3 לכיוון הטעם הנוכחי של היוצר, תוך התייחסות לכללי הערוץ כמגבלות מחמירות.

  1. ב-stage4_memory/agent.py, מעדכנים את scripter כדי לצרף את after_agent_callback=remember_pick:
    output_schema=Script,
    after_agent_callback=remember_pick)

הפעולה remember_pick מתבצעת אחרי ש-scripter משלים את התור שלו. הוא קורא את הכיוון שנבחר ממצב הסשן, מסנתז הצהרה תמציתית שמסכמת את ההחלטה של היוצר ומפעיל את memories.generate כדי לעדכן את Memory Bank.

מה צפוי ולמה

בודקים את תהליך העבודה המשופר באמצעות קריאה חוזרת (callback) בכלי הפיתוח או ב-ADK Web:

  1. מריצים פרומפט ריק:
    • במעקב אחר הסשן, בודקים את LlmRequest עבור propose_directions. שימו לב להקשר הזיכרון שנוסף, שבו מפורטת ההעדפה של היוצר לנושאי פנטזיה ולקצב תמציתי.
    • שימו לב להצעות: מועמדים 1 עד 3 תואמים להעדפות ההיסטוריות של היוצר, גם כשהמגמות מדגישות נושאים אחרים.
  2. בוחרים מועמד בכתובת direction_gate.
  3. אחרי שscripter מסתיים, בודקים את הרשומות ב-Memory Bank:
    python -m agent.platform.bank list
    
    המערכת תעדכן את הבחירה האחרונה בנתוני הבנק ותאחד אותה עם רשומות הטעם הקודמות.

7. RAG Engine

ב-VibeStudio Workbench, עוברים אל Step 7 · RAG Engine, חלקים (7A) ו-(7B).

07-7A

סרטונים שפורסמו צוברים משוב מתמשך מהצופים. מערכת AI אוספת 30 תגובות מייצגות ב-agent/comments.md, שכוללות שבחים מהצופים, ביקורת על קצב הצגת התוכן הממומן והעדפות לגבי אודיו. בשלב הזה, יוצרים אינדקס לתגובות האלה באמצעות Vertex AI RAG Engine ומחברים את השליפה הסמנטית ל-fan-out של המחקר.

אחזור מידע ממסמכים (7A)

ב-Workbench, עוברים אל RAG Engine (7A).

Memory Bank לעומת RAG Engine

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

מאפיין

Memory Bank

RAG Engine

תרחיש שימוש ראשי

העדפות משתמשים לטווח ארוך וכללים תפעוליים

אחזור סמנטי של אוספים גדולים של מסמכים

היקף

ההיקף מוגבל למזהי משתמשים ספציפיים ולשמות אפליקציות

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

עיבוד נתונים

חילוץ, הטמעה ואיחוד סמנטי בזמן אמת

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

שילוב של גרפים

קריאות חוזרות במחזור החיים של הסוכן (before_model_callback, after_agent_callback)

צומת פונקציה ייעודית ב-fan-out של מחקר (read_feedback)

07-7A

חלוקה לקטעים והטמעה של מסמכים

מנוע RAG מוסיף מסמכים לאינדקס על ידי חלוקת הטקסט לקטעים סמנטיים ושמירת הווקטורים שלהם במסד נתונים מנוהל:

corpus = rag.create_corpus(
    display_name="vibestudio-feedback",
    description="Vibe Studio: what the audience wrote under the channel's past videos.",
    backend_config=rag.RagVectorDbConfig(
        rag_embedding_model_config=rag.RagEmbeddingModelConfig(
            vertex_prediction_endpoint=rag.VertexPredictionEndpoint(
                publisher_model="publishers/google/models/text-embedding-005"))))

rag.upload_file(
    corpus_name=corpus.name, path="agent/comments.md", display_name="comments.md",
    transformation_config=rag.TransformationConfig(
        chunking_config=rag.ChunkingConfig(chunk_size=120, chunk_overlap=20)))
  • גודל המקטע: מוגדר ל-120 טוקנים עם 20 טוקנים של חפיפה. כך נאספות שתיים או שלוש תגובות לכל פסקה, כדי להבטיח שכל וקטור ייצג סנטימנט מגובש בלי לפגוע במשמעות של משוב לא קשור.
  • מודל הטמעה: text-embedding-005 ממיר טקסט לווקטורים עם הרבה ממדים. כששולחים שאילתה, המודל ממיר את השאילתה לווקטור ומוצא את ההתאמות הקרובות ביותר על סמך מרחק סמנטי. תגובה על דרקון קטן ששומר על גרביים תואמת להנחיה על יצורים קסומים, בלי שנדרש חפיפה מדויקת של מילות מפתח.

הגדרת מאגר המידע של RAG

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

  1. יוצרים את מאגר הטקסטים:
    python -m agent.platform.rag
    
    מבצע הקצאה של מסד נתונים וקטורי מנוהל ומתעד את מזהה המשאב ב-runs/ragcorpus.json.
  2. העלאה והוספה לאינדקס של תגובות: מעלה את agent/comments.md עם הגדרת חלוקה לחלקים וממתין לסיום ההוספה לאינדקס.
  3. שאילתות על הקורפוס: בודקים את אחזור הדמיון באמצעות שאילתות שלא כוללות מילים זהות לתגובות (לדוגמה, שאילתה 'יצורים קסומים קטנים' כדי לאחזר תגובות על דרקונים).

צומת האחזור (7B)

ב-Workbench, מנווטים אל The third reader (7B). פותחים את stage5_rag/agent.py.

07-7B

אחזור כצומת בתרשים

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

07-7B

def read_feedback(node_input):
    """The third reader (step 7): what the audience wrote under past videos,
    the passages nearest to tonight's idea. Retrieval, not a model call."""
    from .platform import rag
    idea = idea_text(node_input)
    query = idea or "what viewers liked and what they complained about"
    try:
        hits = rag.retrieve(query)
    except Exception as e:
        print(f"  [rag] feedback unavailable ({str(e)[:80]})")
        return Event(output={"query": query, "feedback": [],
                             "note": "no corpus connected - run: python -m agent.platform.rag"})
    return Event(output={"query": query, "feedback": [h["text"] for h in hits]})

read_feedback מחלץ את הרעיון הראשוני של המשתמש ומריץ שאילתת וקטורים מול מאגר המידע של מנוע ה-RAG. היא מחזירה את התגובות שאוחזרו במטען ייעודי (payload) מסוג Event(output=...).

עריכה מעשית: חיבור הקורא השלישי ל-fan-out

ב-stage5_rag/agent.py, מעדכנים את edges כדי להוסיף את read_feedback כענף מקביל שלישי שנכנס ל-join_research:

           (START, read_backlog, join_research),
           (START, read_feedback, join_research),

מכיוון ש-join_research הוא JoinNode, הוא מסנכרן את כל הענפים הנכנסים וממתין עד ש-scan_trends,‏ read_backlog ו-read_feedback ישדרו את כל האירועים לפני שהוא מעביר את חבילת האירועים המצטברת במורד הזרם.

מה צפוי ולמה

מריצים את תהליך העבודה ב-Workbench:

  1. שולחים הנחיית רעיון (לדוגמה, "דרקון מיניאטורי ששומר על משטח במטבח").
  2. במעקב הביצוע, מוודאים שכל שלושת צמתי הקריאה מופעלים בו-זמנית.
  3. שימו לב ל-join_research: מילון הפלט שלו מכיל עכשיו את trends,‏ backlog ו-feedback.
  4. בודקים את המועמדים שנוצרו מ-propose_directions: המודל משלב את התגובות של הצופים בהצעות שלו, ומפנה לרגשות של הקהל בשדות הראיות.
  5. שימו לב שאחזור RAG הוא דטרמיניסטי (שאילתות זהות מחזירות קטעי תגובות זהים), בעוד שצומת ההצעה הגנרטיבית יוצר וריאציות קריאייטיביות.

8. יצירת סרטונים אסינכרונית באמצעות Veo

ב-VibeStudio Workbench, עוברים אל Step 8 · The video, חלקים (8A) ו-(8B).

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

כלים לטווח ארוך (8A)

בסביבת העבודה, עוברים אל כלי שפועל לאורך זמן (8A). פותחים את stage6_video/agent.py ואת agent/deliver.py.

08-8A

כלים סינכרוניים לעומת כלים שפועלים לאורך זמן

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

אי אפשר להשלים את עיבוד הסרטון במהלך תור אחד. במקום זאת, render_submit מתחיל את משימת היצירה ומחזיר מיד קבלה תפעולית עם הסטטוס "pending":

def render_submit(prompt: str) -> dict:
    """Submit one Veo render of `prompt`. Returns at once with a pending
    receipt; the clip is delivered later, to this call, by id."""
    receipt = videogen.start(f"{prompt} {videogen.NO_TEXT}")
    return {"status": "pending", "operation": receipt["operation"], "prompt": receipt["prompt"]}

כשעוטפים את הסטטוס "pending" ב-ADK, הוא מיירט את הסטטוס "pending".LongRunningFunctionTool תור הסוכן מסתיים, תהליך העבודה מושהה בצומת, ומטא-הנתונים של השיחה בהמתנה (כולל מזהה השיחה והקבלה) נרשמים ב-runs/sessions.db. תהליך ההפעלה מסתיים בצורה נקייה בלי לשמור על חיבורי רשת פעילים או על שרשורי עובדים.

עריכה מעשית: עטיפת כלי העיבוד

ב-stage6_video/agent.py, מעדכנים את render_desk כך שיכלול את render_submit ב-LongRunningFunctionTool:

    tools=[LongRunningFunctionTool(render_submit)])

חידוש השיחה באמצעות מזהה השיחה

דפוס ההמשך האוניברסלי

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

טריגר השעיה

הפעלת Construct

מצב השעיה מאוחסן

אירוע המשך

החלטה אנושית

yield RequestInput(...)

פתיחת פרומפט קלט בחנות הסשנים

FunctionResponse כולל את מזהה השיחה של ההשעיה

כלי ממושך

LongRunningFunctionTool(...) חוזרים pending

פתיחת קריאה לכלי בחנות הסשנים

FunctionResponse כולל את מזהה השיחה של ההשעיה

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

עריכה מעשית: השלמת תשובת המשלוח

ב-agent/deliver.py, יוצרים את החלק של ההמשך FunctionResponse:

    part = Part(function_response=FunctionResponse(
        id=row["call_id"], name=row["name"], response=response))

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

הגדרה של STUDIO_REAL_VIDEO=0 ב-.env מאפשרת עיבוד מדומה: הפונקציה start מחזירה קבלה מיידית של בדיקה, והפונקציה check מדמה השלמה תוך חמש שניות בלי לבצע קריאות ל-Veo API שניתנות לחיוב.

שילוב צינורות עיבוד נתונים (8B)

בסביבת העבודה, עוברים אל render_desk בתרשים (8B). פותחים את stage6_video/agent.py.

צומת הטרמינל בצינור הוא store_video. הוא קורא את פרטי הרינדור שהושלם מ-runs/state.json (המקום שבו תהליך ההעברה תועד) ומבצע קומיט של כתובת ה-URL של הסרטון וסטטוס היצירה למצב סשן משותף.

08-8B

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

ב-stage6_video/agent.py, מעדכנים את edges כדי לצרף את render_desk ואת store_video:

           (quarantine, scripter),
           (scripter, render_desk, store_video)])

מה צפוי ולמה

בדיקת תהליך היצירה האסינכרוני בסביבת העבודה:

  1. מריצים את תהליך העבודה באמצעות בחירת מועמד ויצירת תסריט.
  2. ב-render_desk, מתבוננים בהפעלת הסוכן render_submit.
  3. תהליך העבודה מושהה באופן מיידי. בסביבת העבודה או ב-ADK Web, בודקים את הסטטוס 'בהמתנה': הסשן מכיל את מזהה השיחה הפתוחה, ולא מתבצעים תהליכים ברקע שצורכים משאבים.
  4. מריצים את דמון המסירה באמצעות מסוף סביבת העבודה או במסוף:
    python -m agent.deliver
    
    תהליך המסירה עוקב אחרי Veo עד שהסרטון מוכן, ואז שולח את אירוע ההמשך.
  5. ב-ADK Web, מרעננים את הסשן: ההרצה ממשיכה ב-store_video, כתובת ה-URL של הסרטון נשמרת במצב הסשן, ותהליך העבודה מסתיים.

9. פריסה ב-Cloud Run

ב-VibeStudio Workbench, עוברים אל Step 9 · Deploy (שלב 9 – פריסה).

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

09-9A

ה-ADK Runner

בפיתוח, adk web תזמן את הגרף. בסביבת הייצור, האפליקציה מארחת את תהליך העבודה באמצעות המחלקה Runner של ADK:

self._svc = DatabaseSessionService(db_url=config.DB_URL)
self._runner = Runner(app_name=config.APP, agent=wf, session_service=self._svc)

async for ev in self._runner.run_async(user_id=config.USER, session_id=run_id, new_message=message):
    self._absorb(ev)    # fold the ADK event into the run state, publish one app event

# the gate's answer and the render's delivery are the same call, with a function_response part
part = Part(function_response=FunctionResponse(id=call_id, name=name, response=response))
  • run_async: מפעיל את תהליך העבודה, מפיק אירועים ברצף בזמן שהצמתים מופעלים ושומר את העדכונים בשירות הסשן.
  • המשך פעולה מאוחד: גם החלטות המשתמש ב-direction_gate וגם מסירות הווידאו שהושלמו מ-Veo ממשיכות את הביצוע באמצעות אובייקטים זהים של FunctionResponse שנשלחים אל run_async.

ארכיטקטורת האפליקציה בסביבת הייצור

אפליקציית הייצור ב-vibestudio/ משלבת את צינור הנתונים המלא:

vibestudio/
  server/
    main.py                 FastAPI: application server, REST routes, static assets
    api.py                  REST API endpoints: run, pick, publish, backlog, profile, history
    runner.py               Runner orchestration over the workflow, background render poller
    platform/               Event bus (SSE stream), file storage, publishing, telemetry
    agent/                  Production agent package, verified by checks/verify_app.py
      graph.py              The complete workflow graph and node definitions
      desk.py               render_desk and render_submit wrapped with LongRunningFunctionTool
      schemas.py            Pydantic schemas: Directions, CleanedDirection, Script
      cleanup_tools.py      Deterministic policy tools: find_policy_hits, suggest_replacement
      platform/             Memory Bank, RAG Engine, and Veo integrations
  web/                      Production React user interface
  Dockerfile · deploy.py · run.sh
  • זרם אירועים יחיד: קצה העורף של FastAPI מפרסם אירועים בזרם יחיד של אירועים שנשלחים מהשרת (SSE). ממשק הקצה של React מציג את התקדמות הגרף בזמן אמת ומטפל בחיבורים מאוחרים בלי לאבד את המצב.
  • ביצוע מנותק: האפליקציה מנהלת את לולאת האירועים. תרשים זרימת העבודה מתמקד אך ורק בלוגיקת הביצוע, בלי להתייחס לממשק הקצה.

רשימת הקצוות המלאה של זרימת העבודה ב-agent/graph.py משלבת כל תבנית ארכיטקטונית שנבנתה במהלך ה-codelab הזה:

        (START, scan_trends, join_research),
        (START, read_backlog, join_research),
        (START, read_feedback, join_research),
        (join_research, propose_directions, direction_gate,
         persist_direction, policy_check),
        (policy_check, {"OK": scripter, "BLOCK": quarantine}),
        (quarantine, scripter),
        (scripter, render_desk, store_video),

פריסה ב-Cloud Run

‫Google Cloud Run מספק אירוח ללא שרת עם התאמה אוטומטית לעומס, ניתוב בקשות וגרסאות build משולבות של קונטיינרים:

gcloud run deploy vibestudio --source vibestudio \
  --project $GOOGLE_CLOUD_PROJECT --region us-central1 \
  --labels dev-tutorial-codelab=vibetube --allow-unauthenticated \
  --memory 2Gi --cpu 2 --timeout 3600 --concurrency 40 \
  --max-instances 1 --min-instances 1 --session-affinity \
  --set-env-vars GOOGLE_CLOUD_PROJECT=...,STUDIO_VERTEX=1,STUDIO_MEMORY_BANK=...,STUDIO_RAG_CORPUS=...,VIBETUBE_URL=...,VIBETUBE_EVENT=...,VIBETUBE_NAME=...,VIBETUBE_PROJECT=...
  • יצירת קונטיינר: הפקודה gcloud run deploy --source אורזת את הספרייה vibestudio/, יוצרת את קובץ האימג' של הקונטיינר באמצעות Cloud Build ומפריסה את השירות בפעולה אחת.
  • זיקה לסשן (session affinity): מפנה בקשות מאותו משתמש לאותו מופע של קונטיינר, ושומר על מצב הסשן המקומי לאורך שלבים חוזרים.
  • יכולת צפייה (Observability): שילוב של Cloud Trace מתעד טווחים מבוזרים לכל צומת, קריאה ל-LLM והפעלה של כלי, שאפשר לגשת אליהם ב-Trace Explorer במסוף Google Cloud.

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

אפליקציה

10. סיכום

ב-VibeStudio Workbench, עוברים אל שלב 10 · סיכום כדי לבדוק את הארכיטקטורה שהושלמה.

סיכום ל-10 ימים

שלב

ארכיטקטורה ומושגים

תבנית הטמעה

פרומפט אחד

פרומפט יחיד, כלים לפונקציות, לולאת צ'אט רציפה

Agent(tools=[...]), function_call / function_response

היסודות של תהליכי עבודה אג'נטיים

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

Workflow,‏ START,‏ JoinNode,‏ output_schema,‏ RequestInput

מדינה ונתב

מצב סשן משותף, קישור פרמטרים, ניתוב דטרמיניסטי, סוכן משימות

Event(state=...), Event(route=...), mode="task", finish_task

Memory Bank

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

memories.generate / retrieve, before_model_callback, after_agent_callback

RAG Engine

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

rag.create_corpus, RagEmbeddingModelConfig, צומת read_feedback

יצירת סרטונים אסינכרונית באמצעות Veo

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

LongRunningFunctionTool, חידוש של FunctionResponse(id=...)

פריסה ב-Cloud Run

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

Runner(agent=wf), ‏ run_async, ‏ Cloud Run deployment

עקרונות ארכיטקטוניים מרכזיים

  1. השעיה במקום המתנה: תהליכי העבודה מושהים בצורה נקייה כדי לאפשר קלט אנושי (RequestInput) או פעולות ממושכות (LongRunningFunctionTool). התהליכים לא ממתינים בלי פעילות ב-Threads או בשקעי רשת.
  2. הפעלה מחדש אוניברסלית: כל ההשעיות מופעלות מחדש באמצעות מנגנון זהה: הודעת function_response יחידה שכוללת את מזהה השיחה של הצומת שהושעה.
  3. ניהול מצב מנותק: הצמתים משתפים נתונים באמצעות מפתחות בעלי שם של מצב הסשן וקישור פרמטרים, במקום מטען ייעודי (payload) מפורט ומקושר באופן הדוק.
  4. ניתוב דטרמיניסטי לפני עלות גנרטיבית: מסנני ביטויים רגולריים ונתבים מבוססי-כללים בודקים את המדיניות ללא עלות של טוקנים לפני הפעלת מודלים גנרטיביים.
  5. הפרדה בין נושאים: הקשר שספציפי לסוכן מסוים שייך לקריאות חוזרות (callback) של מחזור החיים, בעוד שתלות בנתונים משותפים שייכת

10-output