תחילת העבודה עם פרוטוקול Agent-to-Agent ‏ (A2A): אינטראקציות של סוכן קניות וסוכן מכירות מרחוק עם Gemini ב-Cloud Run וב-Agent Engine

1. מבוא

b013ad6b246401eb.png

פרוטוקול Agent-to-agent (A2A) נועד לתקנן את התקשורת בין סוכני AI, במיוחד בין סוכנים שנפרסים במערכות חיצוניות. בעבר, פרוטוקולים כאלה הוגדרו עבור כלים שנקראים Model Context Protocol (MCP). זהו תקן חדש לחיבור של מודלים גדולים של שפה (LLM) לנתונים ולמשאבים. ‫A2A מנסה להשלים את MCP. בעוד ש-MCP מתמקד בהפחתת המורכבות של חיבור סוכנים לכלים ולנתונים, A2A מתמקד בהפעלת שיתוף פעולה בין סוכנים בשיטות הטבעיות שלהם. הוא מאפשר לנציגים לתקשר כנציגים (או כמשתמשים) ולא ככלים. לדוגמה, הוא מאפשר תקשורת הלוך ושוב כשרוצים להזמין משהו.

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

83b1a03588b90b68.png

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

ב-A2A נעשה שימוש בעקרון הלקוח-שרת. זהו התהליך האופייני של A2A שצפוי בהדגמה הזו

aa6c8bc5b5df73f1.jpeg

  1. לקוח A2A יבצע קודם גילוי בכל כרטיס סוכן של שרת A2A שאפשר לגשת אליו, וישתמש במידע שלו כדי ליצור לקוח חיבור.
  2. כשנדרש, A2A Client ישלח הודעה אל A2A Server, והשרת יעריך את ההודעה הזו כמשימה שצריך להשלים. אם כתובת ה-URL של מקבל ההתראות בדחיפה מוגדרת בלקוח A2A ונתמכת על ידי שרת A2A, השרת יוכל גם לפרסם את מצב התקדמות המשימה בנקודת הקצה המקבלת בלקוח
  3. אחרי שהמשימה מסתיימת, שרת A2A שולח את ארטיפקט התגובה ללקוח A2A

במהלך ה-codelab, תשתמשו בגישה שלב אחר שלב באופן הבא:

  1. הכנת הפרויקט ב-Google Cloud והפעלת כל ממשקי ה-API הנדרשים בו
  2. הגדרת סביבת עבודה לסביבת הקידוד
  3. הכנת משתני סביבה לשירותי סוכנים של המבורגר ופיצה וניסיון מקומי
  4. פריסת סוכן המבורגר ופיצה ב-Cloud Run
  5. בדיקת הפרטים של אופן ההגדרה של שרת A2A
  6. הכנת משתני סביבה לקונסיירז' לרכישה וניסיון מקומי
  7. פריסת Concierge לרכישות ב-Agent Engine
  8. התחברות למנוע הסוכן דרך ממשק מקומי
  9. בדיקת הפרטים לגבי האופן שבו לקוח A2A נוצר והמודלים של הנתונים שלו
  10. בדיקת מטען הייעודי (payload) והאינטראקציה בין לקוח A2A לשרת

סקירה כללית של הארכיטקטורה

נפרוס את ארכיטקטורת השירות הבאה

9cfc4582f2d8b6f3.jpeg

נפרוס 2 שירותים שיפעלו כשרת A2A: סוכן המבורגר ( שמבוסס על מסגרת הסוכנים של CrewAI) וסוכן פיצה ( שמבוסס על מסגרת הסוכנים של Langgraph). המשתמש יקיים אינטראקציה ישירה רק עם עוזר הקניות, שיפעל באמצעות מסגרת Agent Development Kit (ADK) וישמש כלקוח A2A.

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

דרישות מוקדמות

  • נוח לעבוד עם Python
  • הבנה של ארכיטקטורת full-stack בסיסית באמצעות שירות HTTP

מה תלמדו

  • המבנה המרכזי של שרת A2A
  • המבנה המרכזי של לקוח A2A
  • פריסת שירות סוכן ב-Cloud Run
  • פריסת סוכן שירות ב-Agent Engine
  • איך לקוח A2A מתחבר לשרת A2A
  • מבנה הבקשה והתגובה בחיבור לא סטרימינג

מה צריך

  • דפדפן האינטרנט Chrome
  • חשבון Gmail
  • פרויקט ב-Cloud עם חיוב מופעל

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

‫2. לפני שמתחילים

בחירת פרויקט פעיל ב-Cloud Console

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

  1. ב-Google Cloud Console, בדף לבחירת הפרויקט, בוחרים או יוצרים פרויקט ב-Google Cloud.
  2. מוודאים שהחיוב מופעל בפרויקט ב-Cloud. כך בודקים אם החיוב מופעל בפרויקט

bc8d176ea42fbb7.png

הגדרת פרויקט בענן בטרמינל Cloud Shell

  1. תשתמשו ב-Cloud Shell, סביבת שורת פקודה שפועלת ב-Google Cloud ומגיעה עם bq שנטען מראש. לוחצים על 'הפעלת Cloud Shell' בחלק העליון של מסוף Google Cloud. אם מוצגת בקשה לאישור, לוחצים על אישור.

1829c3759227c19b.png

  1. אחרי שמתחברים ל-Cloud Shell, בודקים שכבר בוצע אימות ושהפרויקט מוגדר למזהה הפרויקט שלכם באמצעות הפקודה הבאה:
gcloud auth list
  1. מריצים את הפקודה הבאה ב-Cloud Shell כדי לוודא שפקודת gcloud מכירה את הפרויקט.
gcloud config list project
  1. אם הפרויקט לא מוגדר, משתמשים בפקודה הבאה כדי להגדיר אותו:
gcloud config set project <YOUR_PROJECT_ID>

אפשר גם לראות את המזהה במסוף:PROJECT_ID

4032c45803813f30.jpeg

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

8dc17eb4271de6b5.jpeg

  1. מפעילים את ממשקי ה-API הנדרשים באמצעות הפקודה שמוצגת למטה. זה יימשך כמה דקות, אז כדאי לחכות בסבלנות.
gcloud services enable aiplatform.googleapis.com \
                       run.googleapis.com \
                       cloudbuild.googleapis.com \
                       cloudresourcemanager.googleapis.com

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

Operation "operations/..." finished successfully.

אפשר גם לחפש כל מוצר במסוף או להשתמש בקישור הזה במקום בפקודת gcloud.

אם פספסתם API כלשהו, תמיד תוכלו להפעיל אותו במהלך ההטמעה.

אפשר לעיין במאמרי העזרה בנושא פקודות gcloud ושימוש בהן.

כניסה אל Cloud Shell Editor והגדרת ספריית עבודה של האפליקציה

עכשיו אפשר להגדיר את עורך הקוד כדי לבצע פעולות שקשורות לקוד. נשתמש ב-Cloud Shell Editor לצורך הזה

  1. לוחצים על הלחצן Open Editor (פתיחת העורך) כדי לפתוח את Cloud Shell Editor. כאן אפשר לכתוב את הקוד b16d56e4979ec951.png
  2. מוודאים שהפרויקט ב-Cloud Code מוגדר בפינה הימנית התחתונה (סרגל הסטטוס) של עורך Cloud Shell, כפי שמודגש בתמונה שלמטה, ושהוא מוגדר לפרויקט הפעיל ב-Google Cloud שבו מופעל החיוב. אם מתבקשים, לוחצים על אישור. אם כבר ביצעתם את הפקודה הקודמת, יכול להיות שהלחצן יפנה ישירות לפרויקט שהפעלתם במקום ללחצן הכניסה.

f5003b9c38b43262.png

  1. בשלב הבא, משכפלים את ספריית העבודה של התבנית בשביל ה-codelab הזה מ-GitHub, ומריצים את הפקודה הבאה. היא תיצור את ספריית העבודה בספרייה purchasing-concierge-a2a
git clone https://github.com/alphinside/purchasing-concierge-intro-a2a-codelab-starter.git purchasing-concierge-a2a
  1. אחרי זה, עוברים לקטע העליון של Cloud Shell Editor ולוחצים על File->Open Folder (קובץ > פתיחת תיקייה), מוצאים את ספריית שם המשתמש ואת הספרייה purchasing-concierge-a2a ואז לוחצים על הלחצן OK. הספרייה שנבחרה תוגדר כספריית העבודה הראשית. בדוגמה הזו, שם המשתמש הוא alvinprayuda, ולכן נתיב הספרייה מוצג למטה

2c53696f81d805cc.png

253b472fa1bd752e.png

עכשיו Cloud Shell Editor אמור להיראות כך

aedd0725db87717e.png

הגדרת הסביבה

השלב הבא הוא הכנת סביבת הפיתוח. הטרמינל הפעיל הנוכחי צריך להיות בתוך ספריית העבודה purchasing-concierge-a2a. ב-codelab הזה נשתמש ב-Python 3.12 וב-uv python project manager כדי לפשט את הצורך ביצירה ובניהול של גרסת Python וסביבה וירטואלית.

  1. אם עדיין לא פתחתם את הטרמינל, פותחים אותו על ידי לחיצה על Terminal (טרמינל) -> New Terminal (טרמינל חדש), או על ידי הקשה על Ctrl + Shift + C. חלון הטרמינל ייפתח בחלק התחתון של הדפדפן.

f8457daf0bed059e.jpeg

  1. עכשיו נאחל את הסביבה הווירטואלית של עוזר הקניות באמצעות uv (כבר מותקן מראש בטרמינל בענן). מריצים את הפקודה הזו
uv sync --frozen

תיקיית .venv תיצור ותתקין את יחסי התלות. תצוגה מקדימה מהירה של pyproject.toml תיתן לכם מידע על התלות שמוצגת כך

dependencies = [
    "a2a-sdk>=0.2.16",
    "google-adk>=1.8.0",
    "gradio>=5.38.2",
]
  1. כדי לבדוק את הסביבה הווירטואלית, יוצרים קובץ חדש בשם main.py ומעתיקים את הקוד הבא
def main():
   print("Hello from purchasing-concierge-a2a!")

if __name__ == "__main__":
   main()
  1. לאחר מכן, מריצים את הפקודה הבאה
uv run main.py

יוצג פלט כמו בדוגמה הבאה

Using CPython 3.12
Creating virtual environment at: .venv
Hello from purchasing-concierge-a2a!

ההודעה הזו מציינת שהגדרת פרויקט Python מתבצעת בצורה תקינה.

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

3. פריסת סוכן מוכר מרוחק – שרת A2A ב-Cloud Run

בשלב הזה נבצע פריסה של שני סוכני המכירות מרחוק שמסומנים בתיבה האדומה. הסוכן להזמנת המבורגר יופעל על ידי מסגרת הסוכנים CrewAI, והסוכן להזמנת פיצה יופעל על ידי סוכן Langgraph. שניהם יופעלו על ידי מודל Gemini Flash 2.0

e91777eecfbae4f7.png

פריסת סוכן המבורגר מרוחק

קוד המקור של סוכן ההמבורגר נמצא בספרייה remote_seller_agents/burger_agent. אפשר לבדוק את האתחול של הנציג בסקריפט agent.py. זהו קטע הקוד של הסוכן שאותחל

from crewai import Agent, Crew, LLM, Task, Process
from crewai.tools import tool

...

       model = LLM(
            model="vertex_ai/gemini-2.5-flash-lite",  # Use base model name without provider prefix
        )
        burger_agent = Agent(
            role="Burger Seller Agent",
            goal=(
                "Help user to understand what is available on burger menu and price also handle order creation."
            ),
            backstory=("You are an expert and helpful burger seller agent."),
            verbose=False,
            allow_delegation=False,
            tools=[create_burger_order],
            llm=model,
        )

        agent_task = Task(
            description=self.TaskInstruction,
            agent=burger_agent,
            expected_output="Response to the user in friendly and helpful manner",
        )

        crew = Crew(
            tasks=[agent_task],
            agents=[burger_agent],
            verbose=False,
            process=Process.sequential,
        )

        inputs = {"user_prompt": query, "session_id": sessionId}
        response = crew.kickoff(inputs)
        return response

...

כל הקבצים שנמצאים בספרייה remote_seller_agents/burger_agent כבר מספיקים לפריסת הסוכן שלנו ב-Cloud Run, כדי שאפשר יהיה לגשת אליו כשירות. נרחיב על הנושא הזה בהמשך. מריצים את הפקודה הבאה כדי לפרוס אותו

gcloud run deploy burger-agent \
    --source remote_seller_agents/burger_agent \
    --port=8080 \
    --allow-unauthenticated \
    --min 1 \
    --region us-central1 \
    --update-env-vars GOOGLE_CLOUD_LOCATION=us-central1 \
    --update-env-vars GOOGLE_CLOUD_PROJECT={your-project-id}

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

Service [burger-agent] revision [burger-agent-xxxxx-xxx] has been deployed and is serving 100 percent of traffic.
Service URL: https://burger-agent-xxxxxxxxx.us-central1.run.app

החלק xxxx יהיה מזהה ייחודי כשנפרוס את השירות. עכשיו ננסה להשתמש בדפדפן כדי להגיע לhttps://burger-agent-xxxxxxxxx.us-central1.run.app/.well-known/agent.json שירותי הסוכן של המבורגרים שנפרסו. זו כתובת ה-URL לגישה לכרטיס של סוכן השרת A2A שנפרס.

אם הפריסה בוצעה בהצלחה, התגובה שמוצגת בדפדפן כשניגשים אל https://burger-agent-xxxxxxxxx.us-central1.run.app/.well-known/agent.json תיראה כך :

72fdf3f52b5e8313.png

אלה פרטי הכרטיס של סוכן ההמבורגרים שאמורים להיות זמינים למטרות גילוי. נרחיב על הנושא הזה בהמשך. שימו לב שהערך url עדיין מוגדר ל-http://0.0.0.0:8080/. הערך הזה של url אמור להיות המידע העיקרי שנדרש ללקוח A2A כדי לשלוח הודעות מהעולם החיצוני, אבל הוא לא מוגדר בצורה נכונה. בדמו הזה, נצטרך לעדכן את הערך הזה לכתובת ה-URL של שירות הסוכן שלנו להזמנת המבורגרים על ידי הוספת משתנה סביבה נוסף HOST_OVERRIDE.

עדכון הערך של כתובת ה-URL של סוכן המבורגר בכרטיס הסוכן באמצעות משתנה סביבה

כדי להוסיף את HOST_OVERRIDE לשירות של סוכן המבורגרים, פועלים לפי השלבים הבאים

  1. מחפשים את Cloud Run בסרגל החיפוש בחלק העליון של מסוף הענן.

1adde569bb345b48.png

  1. לוחצים על שירות Cloud Run שהופעל קודם, burger-agent.

9091c12526fb7f41.png

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

2701da8b124793b9.png

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

31ea00e12134d74d.png

  1. אחר כך לוחצים על הוספת משתנה ומגדירים את HOST_OVERRIDE הערך לכתובת ה-URL של השירות ( הכתובת עם התבנית https://burger-agent-xxxxxxxxx.us-central1.run.app)

52b382da7cf33cd5.png

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

11464f4a51ffe54.png

עכשיו, כשתיגשו שוב לכרטיס של סוכן burger-agent בדפדפן https://burger-agent-xxxxxxxxx.us-central1.run.app/.well-known/agent.json , הערך url כבר יוגדר כמו שצריך

2ed7ebcb530f070a.png

פריסת סוכן הפיצה המרוחק

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

from langchain_google_vertexai import ChatVertexAI
from langgraph.prebuilt import create_react_agent

...

self.model = ChatVertexAI(
    model="gemini-2.5-flash-lite",
    location=os.getenv("GOOGLE_CLOUD_LOCATION"),
    project=os.getenv("GOOGLE_CLOUD_PROJECT"),
)
self.tools = [create_pizza_order]
self.graph = create_react_agent(
    self.model,
    tools=self.tools,
    checkpointer=memory,
    prompt=self.SYSTEM_INSTRUCTION,
)

...

בדומה לשלב הקודם של פריסת סוכן ה-burger, כל הקבצים שנמצאים בספרייה remote_seller_agents/pizza_agent כבר מספיקים לפריסת הסוכן שלנו ב-Cloud Run, כך שאפשר לגשת אליו כשירות. מריצים את הפקודה הבאה כדי לפרוס אותו

gcloud run deploy pizza-agent \
    --source remote_seller_agents/pizza_agent \
    --port=8080 \
    --allow-unauthenticated \
    --min 1 \
    --region us-central1 \
    --update-env-vars GOOGLE_CLOUD_LOCATION=us-central1 \
    --update-env-vars GOOGLE_CLOUD_PROJECT={your-project-id}

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

Service [pizza-agent] revision [pizza-agent-xxxxx-xxx] has been deployed and is serving 100 percent of traffic.
Service URL: https://pizza-agent-xxxxxxxxx.us-central1.run.app

החלק xxxx יהיה מזהה ייחודי כשנפרוס את השירות. המצב דומה עם סוכן ההמבורגרים. כשמנסים לעבור לhttps://pizza-agent-xxxxxxxxx.us-central1.run.app/.well-known/agent.json הנתיב של שירותי סוכן הפיצה שנפרסו דרך הדפדפן כדי לגשת לכרטיס סוכן השרת A2A, הערך של סוכן הפיצה url בכרטיס הסוכן שלו עדיין לא מוגדר כראוי. צריך גם להוסיף את HOST_OVERRIDE למשתנה הסביבה שלו

עדכון הערך של כתובת ה-URL של סוכן הפיצה בכרטיס הסוכן באמצעות משתנה סביבה

כדי להוסיף HOST_OVERRIDE לשירות של סוכן הפיצה, מבצעים את השלבים הבאים

  1. מחפשים את Cloud Run בסרגל החיפוש בחלק העליון של מסוף הענן.

1adde569bb345b48.png

  1. לוחצים על שירות Cloud Run pizza-agent שנפרס בעבר.

5743b0aa0555741f.png

  1. לוחצים על עריכה ופריסה של גרסה חדשה.

d60ba267410183be.png

  1. מעתיקים את כתובת ה-URL של שירות הפיצה ולוחצים על הקטע Variable & Secrets (משתנה וסודות).

618e9da2f94ed415.png

  1. אחר כך לוחצים על הוספת משתנה ומגדירים את HOST_OVERRIDE הערך לכתובת ה-URL של השירות ( הכתובת עם התבנית https://pizza-agent-xxxxxxxxx.us-central1.run.app)

214a6eb98f877e65.png

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

11464f4a51ffe54.png

עכשיו, כשתיגשו שוב לכרטיס של סוכן הפיצה בדפדפן https://pizza-agent-xxxxxxxxx.us-central1.run.app/.well-known/agent.json, הערך url כבר יוגדר כמו שצריך

c37b26ec80c821b6.png

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

4. רכיבי הליבה של שרת A2A

עכשיו נדון במושג הליבה וברכיבים של שרת A2A

כרטיס נציג

לכל שרת A2A צריך להיות כרטיס סוכן שאפשר לגשת אליו במשאב /.well-known/agent.json. המידע הזה נועד לתמוך בשלב הגילוי בלקוח A2A, שבו אמורים לקבל מידע מלא והקשרים לגבי הגישה לסוכן ולכל היכולות שלו. זה די דומה לתיעוד API מפורט באמצעות Swagger או Postman.

This is the content of our deployed burger agent agent card

{
  "capabilities": {
    "streaming": true
  },
  "defaultInputModes": [
    "text",
    "text/plain"
  ],
  "defaultOutputModes": [
    "text",
    "text/plain"
  ],
  "description": "Helps with creating burger orders",
  "name": "burger_seller_agent",
  "protocolVersion": "0.2.6",
  "skills": [
    {
      "description": "Helps with creating burger orders",
      "examples": [
        "I want to order 2 classic cheeseburgers"
      ],
      "id": "create_burger_order",
      "name": "Burger Order Creation Tool",
      "tags": [
        "burger order creation"
      ]
    }
  ],
  "url": "https://burger-agent-109790610330.us-central1.run.app",
  "version": "1.0.0"
}

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

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

בקוד שלנו, ההטמעה של כרטיס הנציג מתבצעת באמצעות A2A python sdk. אפשר לראות את ההטמעה בקטע הקוד remote_seller_agents/burger_agent/main.py שלמטה.

...

        capabilities = AgentCapabilities(streaming=True)
        skill = AgentSkill(
            id="create_burger_order",
            name="Burger Order Creation Tool",
            description="Helps with creating burger orders",
            tags=["burger order creation"],
            examples=["I want to order 2 classic cheeseburgers"],
        )
        agent_host_url = (
            os.getenv("HOST_OVERRIDE")
            if os.getenv("HOST_OVERRIDE")
            else f"http://{host}:{port}/"
        )
        agent_card = AgentCard(
            name="burger_seller_agent",
            description="Helps with creating burger orders",
            url=agent_host_url,
            version="1.0.0",
            defaultInputModes=BurgerSellerAgent.SUPPORTED_CONTENT_TYPES,
            defaultOutputModes=BurgerSellerAgent.SUPPORTED_CONTENT_TYPES,
            capabilities=capabilities,
            skills=[skill],
        )

...

אפשר לראות שם כמה שדות, כמו:

  1. AgentCapabilities : הצהרה על פונקציות אופציונליות נוספות שנתמכות על ידי סוכן השירות,כמו יכולת סטרימינג או תמיכה בהתראות פוש
  2. AgentSkill : כלים או פונקציות שהסוכן תומך בהם
  3. Input/OutputModes : סוג המודאליות של הקלט/פלט שנתמך
  4. Url : כתובת לתקשורת עם הסוכן

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

תור משימות וסוכן לביצוע

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

b9eb6b4025db4642.jpeg

לכן, כל שרת A2A צריך להיות מסוגל לעקוב אחרי משימות נכנסות ולאחסן מידע מתאים לגביהן. ערכת ה-SDK של A2A מספקת מודולים לטיפול בבעיה הזו בשרת A2A. קודם כול, אפשר ליצור מופע של לוגיקה לגבי אופן הטיפול בבקשה הנכנסת. על ידי ירושה של מחלקת AgentExecutor מופשטת, אנחנו יכולים לשלוט באופן שבו אנחנו רוצים לנהל את ביצוע המשימות והביטול שלהן. אפשר לבדוק את היישום לדוגמה הזה במודול remote_seller_agents/burger_agent/agent_executor.py ( נתיב דומה למקרה של מוכר הפיצה)

...

class BurgerSellerAgentExecutor(AgentExecutor):
    """Burger Seller AgentExecutor."""

    def __init__(self):
        self.agent = BurgerSellerAgent()

    async def execute(
        self,
        context: RequestContext,
        event_queue: EventQueue,
    ) -> None:
        query = context.get_user_input()
        try:
            result = self.agent.invoke(query, context.context_id)
            print(f"Final Result ===> {result}")

            parts = [Part(root=TextPart(text=str(result)))]
            await event_queue.enqueue_event(
                completed_task(
                    context.task_id,
                    context.context_id,
                    [new_artifact(parts, f"burger_{context.task_id}")],
                    [context.message],
                )
            )
        except Exception as e:
            print("Error invoking agent: %s", e)
            raise ServerError(error=ValueError(f"Error invoking agent: {e}")) from e

    async def cancel(
        self, request: RequestContext, event_queue: EventQueue
    ) -> Task | None:
        raise ServerError(error=UnsupportedOperationError())

...

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

אחרי שיוצרים את ה-executor, אפשר להשתמש ישירות ב-DefaultRequestHandler, InMemoryTaskStore וב-A2AStarletteApplication המובנים כדי להפעיל את שרת ה-HTTP. אפשר לבדוק את היישום הזה בremote_seller_agents/burger_agent/__main__.py

...

        request_handler = DefaultRequestHandler(
            agent_executor=BurgerSellerAgentExecutor(),
            task_store=InMemoryTaskStore(),
        )
        server = A2AStarletteApplication(
            agent_card=agent_card, http_handler=request_handler
        )

        uvicorn.run(server.build(), host=host, port=port)

...

המודול הזה יספק לכם הטמעה של מסלול /.well-known/agent.json לגישה לכרטיס הסוכן, וגם נקודת קצה של POST לתמיכה בפרוטוקול A2A

סיכום

בקיצור, עד עכשיו פרסנו שרת A2A באמצעות Python SDK, שיכול לתמוך בשתי הפונקציות הבאות:

  1. פרסום כרטיס הנציג במסלול /.well-known/agent.json
  2. טיפול בבקשת JSON-RPC באמצעות תור משימות בזיכרון

אפשר לבדוק את נקודת הכניסה להפעלת הפונקציות האלה בסקריפט __main__.py ( ב-remote_seller_agents/burger_agent או ב-remote_seller_agents/pizza_agent) .

5. פריסת Concierge לרכישה – לקוח A2A ב-Agent Engine

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

c4a8e7a3d18b1ef.png

קוד המקור של סוכן ה-Concierge לרכישות נמצא בספרייה purchasing_concierge. אפשר לבדוק את האתחול של הנציג בסקריפט purchasing_agent.py. זהו קטע הקוד של הסוכן שאותחל.

from google.adk import Agent

...

def create_agent(self) -> Agent:
        return Agent(
            model="gemini-2.5-flash-lite",
            name="purchasing_agent",
            instruction=self.root_instruction,
            before_model_callback=self.before_model_callback,
            before_agent_callback=self.before_agent_callback,
            description=(
                "This purchasing agent orchestrates the decomposition of the user purchase request into"
                " tasks that can be performed by the seller agents."
            ),
            tools=[
                self.send_task,
            ],
        )

...

אנחנו נפעיל את הסוכן הזה במנוע הסוכנים. ‫Vertex AI Agent Engine הוא קבוצה של שירותים שמאפשרים למפתחים לפרוס סוכני AI, לנהל אותם ולבצע להם התאמה לעומס (scaling) בסביבת ייצור. הוא מטפל בתשתית להרחבת סוכנים בסביבת ייצור, כדי שנוכל להתמקד ביצירת אפליקציות. מידע נוסף זמין במסמך הזה . אם בעבר היינו צריכים להכין קבצים שנדרשים לפריסת שירות הסוכן שלנו (כמו סקריפט השרת main ו-Dockerfile), במקרה הזה אנחנו יכולים לפרוס את הסוכן שלנו ישירות מסקריפט Python בלי לפתח שירות קצה עורפי משלנו, באמצעות שילוב של ADK ו-Agent Engine. כדי לפרוס את התוסף :

  1. קודם צריך ליצור את האחסון הזמני ב-Cloud Storage
gcloud storage buckets create gs://purchasing-concierge-{your-project-id} --location=us-central1
  1. עכשיו צריך להכין את המשתנה .env. לשם כך, מעתיקים את .env.example לקובץ .env.
cp .env.example .env
  1. עכשיו פותחים את הקובץ .env ורואים את התוכן הבא
GOOGLE_GENAI_USE_VERTEXAI=TRUE
GOOGLE_CLOUD_PROJECT={your-project-id}
GOOGLE_CLOUD_LOCATION=us-central1
STAGING_BUCKET=gs://purchasing-concierge-{your-project-id}
PIZZA_SELLER_AGENT_URL={your-pizza-agent-url}
BURGER_SELLER_AGENT_URL={your-burger-agent-url}
AGENT_ENGINE_RESOURCE_NAME={your-agent-engine-resource-name}

הסוכן הזה יתקשר עם הסוכן של המבורגר והסוכן של פיצה, ולכן צריך לספק את ההרשאות המתאימות לשניהם. נצטרך לעדכן את PIZZA_SELLER_AGENT_URL ואת BURGER_SELLER_AGENT_URL עם כתובת ה-URL של Cloud Run מהשלבים הקודמים.

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

1adde569bb345b48.png

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

179e55cc095723a8.png

כדי לראות את כתובת ה-URL הציבורית של השירותים האלה, לוחצים על אחד מהשירותים ותועברו לדף פרטי השירות. כתובת ה-URL מופיעה באזור העליון, ממש ליד פרטי האזור.

64c01403a92b1107.png

משתנה הסביבה הסופי אמור להיראות בערך כך

GOOGLE_GENAI_USE_VERTEXAI=TRUE
GOOGLE_CLOUD_PROJECT={your-project-id}
GOOGLE_CLOUD_LOCATION=us-central1
STAGING_BUCKET=gs://purchasing-concierge-{your-project-id}
PIZZA_SELLER_AGENT_URL=https://pizza-agent-xxxxx.us-central1.run.app
BURGER_SELLER_AGENT_URL=https://burger-agent-xxxxx.us-central1.run.app
AGENT_ENGINE_RESOURCE_NAME={your-agent-engine-resource-name}
  1. עכשיו אנחנו מוכנים לפרוס את סוכן שירותי הרכישה שלנו. בדמו הזה נבצע פריסה באמצעות הסקריפט deploy_to_agent_engine.py שמוצג בהמשך
"""
Copyright 2025 Google LLC

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    https://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""

import vertexai
from vertexai.preview import reasoning_engines
from vertexai import agent_engines
from dotenv import load_dotenv
import os
from purchasing_concierge.agent import root_agent

load_dotenv()

PROJECT_ID = os.getenv("GOOGLE_CLOUD_PROJECT")
LOCATION = os.getenv("GOOGLE_CLOUD_LOCATION")
STAGING_BUCKET = os.getenv("STAGING_BUCKET")

vertexai.init(
    project=PROJECT_ID,
    location=LOCATION,
    staging_bucket=STAGING_BUCKET,
)

adk_app = reasoning_engines.AdkApp(
    agent=root_agent,
)

remote_app = agent_engines.create(
    agent_engine=adk_app,
    display_name="purchasing-concierge",
    requirements=[
        "google-cloud-aiplatform[adk,agent_engines]",
        "a2a-sdk==0.2.16",
    ],
    extra_packages=[
        "./purchasing_concierge",
    ],
    env_vars={
        "GOOGLE_GENAI_USE_VERTEXAI": os.environ["GOOGLE_GENAI_USE_VERTEXAI"],
        "PIZZA_SELLER_AGENT_URL": os.environ["PIZZA_SELLER_AGENT_URL"],
        "BURGER_SELLER_AGENT_URL": os.environ["BURGER_SELLER_AGENT_URL"],
    },
)

print(f"Deployed remote app resource: {remote_app.resource_name}")

אלה השלבים שצריך לבצע כדי לפרוס את סוכן ה-ADK במנוע הסוכן. קודם כל, צריך ליצור אובייקט AdkApp מ-ADK root_agent. לאחר מכן אפשר להפעיל את השיטה agent_engines.create על ידי אספקת האובייקט adk_app, ציון הדרישות בשדה requirements, ציון הנתיב של ספריית הסוכן ב-extra_packages ואספקת משתני הסביבה הנדרשים.

כדי לפרוס אותו, מריצים את הסקריפט:

uv run deploy_to_agent_engine.py

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

AgentEngine created. Resource name: projects/xxxx/locations/us-central1/reasoningEngines/yyyy
To use this AgentEngine in another session:
agent_engine = vertexai.agent_engines.get('projects/xxxx/locations/us-central1/reasoningEngines/yyyy)
Deployed remote app resource: projects/xxxx/locations/us-central1/reasoningEngines/xxxx

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

29738fbf7e5f5ecc.png

בדיקת הסוכן שפרסתם ב-Agent Engine

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

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

curl \
-H "Authorization: Bearer $(gcloud auth print-access-token)" \
-H "Content-Type: application/json" \
https://us-central1-aiplatform.googleapis.com/v1/projects/{YOUR_PROJECT_ID}/locations/us-central1/reasoningEngines/{YOUR_AGENT_ENGINE_RESOURCE_ID}:streamQuery?alt=sse -d '{
  "class_method": "stream_query",
  "input": {
    "user_id": "user_123",
    "message": "List available burger menu please",
  }
}'

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

{
  "content": {
    "parts": [
      {
        "text": "Here is our burger menu:\n- Classic Cheeseburger: IDR 85K\n- Double Cheeseburger: IDR 110K\n- Spicy Chicken Burger: IDR 80K\n- Spicy Cajun Burger: IDR 85K"
      }
    ],
    "role": "model"
  },
  "usage_metadata": {
    "candidates_token_count": 51,
    "candidates_tokens_details": [
      {
        "modality": "TEXT",
        "token_count": 51
      }
    ],
    "prompt_token_count": 907,
    "prompt_tokens_details": [
      {
        "modality": "TEXT",
        "token_count": 907
      }
    ],
    "total_token_count": 958,
    "traffic_type": "ON_DEMAND"
  },
  "invocation_id": "e-14679918-af68-45f1-b942-cf014368a733",
  "author": "purchasing_agent",
  "actions": {
    "state_delta": {},
    "artifact_delta": {},
    "requested_auth_configs": {}
  },
  "id": "dbe7fc43-b82a-4f3e-82aa-dd97afa8f15b",
  "timestamp": 1754287348.941454
}

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

6. רכיבי הליבה של לקוח A2A

aa6c8bc5b5df73f1.jpeg

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

  1. הלקוח ינסה למצוא כרטיס של סוכן שפורסם בכתובת ה-URL של הסוכן המרוחק שצוינה במסלול /.well-known/agent.json
  2. לאחר מכן, כשצריך, הוא ישלח סוכנות הודעות לאותו סוכן עם ההודעה ופרמטרים של מטא-נתונים נדרשים ( למשל, מזהה סשן, הקשר היסטורי וכו'). השרת יתפוס את ההודעה הזו כמשימה שצריך להשלים.
  3. תהליך השרת A2A מעבד את הבקשה. אם השרת תומך בהתראות Push, הוא יוכל גם לפרסם התראות מסוימות במהלך עיבוד המשימה ( הפונקציונליות הזו לא נכללת ב-codelab הזה).
  4. אחרי שהפעולה מסתיימת, שרת A2A שולח את ארטיפקט התגובה בחזרה ללקוח

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

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

Card Discovery

כשמפעילים את שירות הלקוח A2A, התהליך הרגיל הוא לנסות לקבל את פרטי כרטיס הנציג ולאחסן אותם כדי שיהיה קל לגשת אליהם כשצריך. ב-codelab הזה אנחנו מטמיעים אותו ב-before_agent_callback, אפשר לראות את ההטמעה ב-purchasing_concierge/purchasing_agent.py בקטע הקוד שבהמשך

...

async def before_agent_callback(self, callback_context: CallbackContext):
        if not self.a2a_client_init_status:
            httpx_client = httpx.AsyncClient(timeout=httpx.Timeout(timeout=30))
            for address in self.remote_agent_addresses:
                card_resolver = A2ACardResolver(
                    base_url=address, httpx_client=httpx_client
                )
                try:
                    card = await card_resolver.get_agent_card()
                    remote_connection = RemoteAgentConnections(
                        agent_card=card, agent_url=card.url
                    )
                    self.remote_agent_connections[card.name] = remote_connection
                    self.cards[card.name] = card
                except httpx.ConnectError:
                    print(f"ERROR: Failed to get agent card from : {address}")
            agent_info = []
            for ra in self.list_remote_agents():
                agent_info.append(json.dumps(ra))
            self.agents = "\n".join(agent_info)

...

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

הכלי 'הנחיה ושליחת משימה'

זו ההנחיה והכלי שאנחנו מספקים לסוכן ADK כאן

...

def root_instruction(self, context: ReadonlyContext) -> str:
    current_agent = self.check_active_agent(context)
    return f"""You are an expert purchasing delegator that can delegate the user product inquiry and purchase request to the
appropriate seller remote agents.

Execution:
- For actionable tasks, you can use `send_task` to assign tasks to remote agents to perform.
- When the remote agent is repeatedly asking for user confirmation, assume that the remote agent doesn't have access to user's conversation context. 
So improve the task description to include all the necessary information related to that agent
- Never ask user permission when you want to connect with remote agents. If you need to make connection with multiple remote agents, directly
connect with them without asking user permission or asking user preference
- Always show the detailed response information from the seller agent and propagate it properly to the user. 
- If the remote seller is asking for confirmation, rely the confirmation question to the user if the user haven't do so. 
- If the user already confirmed the related order in the past conversation history, you can confirm on behalf of the user
- Do not give irrelevant context to remote seller agent. For example, ordered pizza item is not relevant for the burger seller agent
- Never ask order confirmation to the remote seller agent 

Please rely on tools to address the request, and don't make up the response. If you are not sure, please ask the user for more details.
Focus on the most recent parts of the conversation primarily.

If there is an active agent, send the request to that agent with the update task tool.

Agents:
{self.agents}

Current active seller agent: {current_agent["active_agent"]}
"""

...

async def send_task(self, agent_name: str, task: str, tool_context: ToolContext):
        """Sends a task to remote seller agent

        This will send a message to the remote agent named agent_name.

        Args:
            agent_name: The name of the agent to send the task to.
            task: The comprehensive conversation context summary
                and goal to be achieved regarding user inquiry and purchase request.
            tool_context: The tool context this method runs in.

        Yields:
            A dictionary of JSON data.
        """
        if agent_name not in self.remote_agent_connections:
            raise ValueError(f"Agent {agent_name} not found")
        state = tool_context.state
        state["active_agent"] = agent_name
        client = self.remote_agent_connections[agent_name]
        if not client:
            raise ValueError(f"Client not available for {agent_name}")
        session_id = state["session_id"]
        task: Task
        message_id = ""
        metadata = {}
        if "input_message_metadata" in state:
            metadata.update(**state["input_message_metadata"])
            if "message_id" in state["input_message_metadata"]:
                message_id = state["input_message_metadata"]["message_id"]
        if not message_id:
            message_id = str(uuid.uuid4())

        payload = {
            "message": {
                "role": "user",
                "parts": [
                    {"type": "text", "text": task}
                ],  # Use the 'task' argument here
                "messageId": message_id,
                "contextId": session_id,
            },
        }

        message_request = SendMessageRequest(
            id=message_id, params=MessageSendParams.model_validate(payload)
        )
        send_response: SendMessageResponse = await client.send_message(
            message_request=message_request
        )
        print(
            "send_response",
            send_response.model_dump_json(exclude_none=True, indent=2),
        )

        if not isinstance(send_response.root, SendMessageSuccessResponse):
            print("received non-success response. Aborting get task ")
            return None

        if not isinstance(send_response.root.result, Task):
            print("received non-task response. Aborting get task ")
            return None

        return send_response.root.result

...

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

פרוטוקולי התקשורת

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

65b8878a4854fd93.jpeg

9ddfae690d40cbbf.jpeg

ההחלפה הזו של הודעה -> משימה מיושמת באמצעות פורמט המטען הייעודי (payload) על בסיס תקן JSON-RPC, כמו בדוגמה הבאה של פרוטוקול message/send :

{
  # identifier for this request
  "id": "abc123",
  # version of JSON-RPC protocol
  "jsonrpc": "2.0",
  # method name
  "method": "message/send",
  # parameters/arguments of the method
  "params": {
    "message": "hi, what can you help me with?"
  }  
}

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

‫7. בדיקת שילוב ובדיקת מטען ייעודי (payload)

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

קודם צריך לעדכן את AGENT_ENGINE_RESOURCE_NAME ב- .קובץ env. חשוב לוודא שציינתם את שם המשאב הנכון של מנוע הסוכן. קובץ .env אמור להיראות כך:

GOOGLE_GENAI_USE_VERTEXAI=TRUE
GOOGLE_CLOUD_PROJECT={your-project-id}
GOOGLE_CLOUD_LOCATION=us-central1
STAGING_BUCKET=gs://purchasing-concierge-{your-project-id}
PIZZA_SELLER_AGENT_URL=https://pizza-agent-xxxxx.us-central1.run.app
BURGER_SELLER_AGENT_URL=https://burger-agent-xxxxx.us-central1.run.app
AGENT_ENGINE_RESOURCE_NAME=projects/xxxx/locations/us-central1/reasoningEngines/yyyy

לאחר מכן, מריצים את הפקודה הבאה כדי לפרוס אפליקציית Gradio

uv run purchasing_concierge_ui.py

אם הפעולה תצליח, הפלט הבא יוצג

* Running on local URL:  http://0.0.0.0:8080
* To create a public link, set `share=True` in `launch()`.

אחר כך, לוחצים על Ctrl + לחיצה על כתובת ה-URL http://0.0.0.0:8080 בטרמינל או לוחצים על לחצן התצוגה המקדימה של האינטרנט כדי לפתוח את ממשק המשתמש של האינטרנט.

b38b428d9e4582bc.png

אפשר לנסות לנהל שיחה כזו :

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

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

ff5f752965816b2b.png

6f65155c7a289964.png

b390f4b15f1c5a8c.png

ff44c54b50c36e1a.png

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

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

8. האתגר

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

‫9. הסרת המשאבים

כדי לא לצבור חיובים לחשבון Google Cloud על המשאבים שבהם השתמשתם ב-Code Lab הזה:

  1. במסוף Google Cloud, עוברים לדף Manage resources.
  2. ברשימת הפרויקטים, בוחרים את הפרויקט שרוצים למחוק ולוחצים על Delete.
  3. כדי למחוק את הפרויקט, כותבים את מזהה הפרויקט בתיבת הדו-שיח ולוחצים על Shut down.
  4. לחלופין, אפשר לעבור אל Cloud Run במסוף, לבחור את השירות שפרסתם ולמחוק אותו.