ADK की मदद से एआई एजेंट बनाना: ग्राफ़ वर्कफ़्लो को व्यवस्थित करना

1. खास जानकारी

कई शर्तों वाले यात्रा अनुरोध को पूरा करने के लिए, स्ट्रक्चर्ड प्राथमिकताओं को निकालना, एक साथ कई जगहों की रिसर्च करना, आइटम के हिसाब से ड्राफ़्ट तैयार करना, और जवाब देने से पहले कुल लागत का ऑडिट करना ज़रूरी है. जैसे, क्योटो में 100 डॉलर में एक दिन की यात्रा की योजना बनाना, जिसमें ऐतिहासिक मंदिर, स्थानीय रामेन, और कोई भी मॉडर्न आर्ट म्यूज़ियम शामिल न हो. ADK 1.x में, इन चरणों को व्यवस्थित करने के लिए, SequentialAgent, ParallelAgent, और LoopAgent रैपर को नेस्ट करना पड़ता था. साथ ही, हर इंटरमीडिएट आउटपुट को सेशन स्टेट में सेव करना पड़ता था. इसके अलावा, रिफ़ाइनमेंट लूप से बाहर निकलने के लिए, एलएलएम टूल को अतिरिक्त कॉल करना पड़ता था.

इस कोडलैब में, Agent Development Kit (ADK) 2 के ग्राफ़ वर्कफ़्लो (Workflow) का इस्तेमाल करके, Agent Platform पर कई एजेंट वाला ट्रैवल प्लानर बनाया जाता है. तीन चरणों में, आपको खास Agent नोड (gemini-3.8-flash की मदद से काम करने वाले) को क्रम में चलने वाली एज चेन (node_input) में कनेक्ट करना होगा. साथ ही, JoinNode के साथ सिंक किए गए एक साथ काम करने वाले google_search रिसर्चर को फ़ैन आउट करना होगा. इसके अलावा, डिटरमिनिस्टिक Python राऊटर (Event(route=...)) की मदद से, बजट और बाहर रखने के नियमों को लागू करना होगा. यह राऊटर, शुरुआती ड्राफ़्ट के ऑडिट पास होने पर, उसमें सुधार करने की प्रोसेस को स्किप कर देता है.

ज़रूरी शर्तें

आपको ये सब सीखने को मिलेगा

  • Workflow एज टपल का इस्तेमाल करके, ADK 2 में मल्टी-एजेंट पाइपलाइन का एलान करने का तरीका
  • ADK 2, आउटपुट को सीधे किनारों पर node_input के तौर पर कैसे पास करता है और सेशन स्टेट की कुंजियों को फ़ंक्शन पैरामीटर के तौर पर कैसे बाइंड करता है
  • JoinNode की मदद से, एक साथ कई रिसर्च ब्रांच बनाने और उनके आउटपुट को मर्ज करने का तरीका
  • Event(route=...) की मदद से, Python में बाउंडेड मेकर-चेकर साइकल को डिटरमिनिस्टिक तरीके से रूट करने का तरीका
  • एडीके वेब यूज़र इंटरफ़ेस (यूआई) में, कंपाइल किए गए वर्कफ़्लो ग्राफ़, एक साथ चलने वाली टाइमलाइन, और स्टेट डेल्टा की जांच करने का तरीका

आपको इन चीज़ों की ज़रूरत पड़ेगी

  • एक Google खाता (अगर वर्कशॉप क्रेडिट पर दावा करना है, तो निजी @gmail.com खाते का इस्तेमाल करें)
  • आपके इंस्ट्रक्टर से मिला वर्कशॉप क्रेडिट लिंक (साथ ही, क्यूआर कोड स्कैन करने के लिए फ़ोन का कैमरा) या कोई चालू Cloud Billing खाता
  • Google Chrome (हमारा सुझाव है कि आप गुप्त विंडो का इस्तेमाल करें, ताकि निजी और ऑफ़िस वाले खाते आपस में मिक्स न हों)

2. Google Cloud क्रेडिट पर दावा करना (ज़रूरी नहीं)

अगर आपको किसी ऐसी वर्कशॉप में हिस्सा लेना है जिसके लिए इवेंट क्रेडिट लिंक दिया गया है, तो Google Developer Program क्रेडिट पर दावा करें. इससे आपको क्रेडिट कार्ड के बिना बिलिंग खाता सेट अप करने में मदद मिलेगी.

इवेंट क्रेडिट पर दावा करना

  1. वर्कशॉप क्रेडिट के लिंक को गुप्त विंडो में खोलें और अपने @gmail.com खाते से साइन इन करें.
  2. अपने फ़ायदे पर दावा करें पर जाकर, क्रेडिट पर दावा करें पर क्लिक करें:

Google Builders Hub के 'अपने फ़ायदे पर दावा करें' कार्ड में, 'क्रेडिट पर दावा करें' बटन दिख रहा है

  1. अगर आपको Google Developer Program प्रोफ़ाइल बनाने के लिए कहा जाए, तो फ़ॉर्म भरें.
  2. जब पुष्टि करने के लिए स्कैन करें कि आप इंसान हैं दिखे, तब अपने फ़ोन से क्यूआर कोड स्कैन करें और पुष्टि की प्रक्रिया पूरी करें. अगर क्यूआर कोड नहीं दिखता है, तो पेज को रीफ़्रेश करें और क्रेडिट पर दावा करें पर फिर से क्लिक करें:

Google Developer Program के लिए, 'यह पुष्टि करने के लिए स्कैन करें कि आप इंसान हैं' क्यूआर कोड का डायलॉग बॉक्स

  1. पुष्टि करें कि इवेंट के लिए क्रेडिट पर दावा कर लिया गया है! मैसेज दिख रहा है. इसके बाद, बिलिंग खाता खोलें पर क्लिक करें:

Google Builders Hub में, इवेंट के लिए क्रेडिट पर दावा करने की प्रोसेस पूरी होने की जानकारी और 'बिलिंग खाता खोलें' बटन दिख रहा है

🔍 अपने काम की जांच करें

बिलिंग खाते को सेटअप करने में तीन मिनट लग सकते हैं:

  1. Google Cloud Billing खोलें या यूआरएल को गुप्त विंडो में कॉपी करें. इसके बाद, आपके बिलिंग खाते को चुनें:
https://console.cloud.google.com/billing
  1. पेज को तब तक रीफ़्रेश करें, जब तक आपके GDP Credit खाते की स्थिति चालू है न दिखने लगे:

Google Cloud बिलिंग खाते के मैनेजमेंट पेज पर, जीडीपी क्रेडिट वाला एक चालू बिलिंग खाता दिख रहा है

3. Google Cloud प्रोजेक्ट बनाना (ज़रूरी नहीं)

एक खास Google Cloud प्रोजेक्ट बनाएं, ताकि इस कोडलैब के सभी संसाधन अलग रहें और उन्हें आसानी से मिटाया जा सके.

  1. प्रोजेक्ट बनाएं खोलें या यूआरएल को गुप्त विंडो में कॉपी करें:
https://console.cloud.google.com/projectcreate
  1. अपने प्रोजेक्ट की जानकारी डालें:
    • प्रोजेक्ट का नाम: उदाहरण के लिए, google-cloud-workshop
    • बिलिंग खाता (अगर दिख रहा है): GDP Credit (या अपना चालू बिलिंग खाता) चुनें
    • पैरंट रिसॉर्स: कोई संगठन नहीं (या ज़रूरत पड़ने पर अपना संगठन चुनें)
  2. प्रोजेक्ट के नाम के नीचे जनरेट किया गया प्रोजेक्ट आईडी नोट करें. इसके बाद, बनाएं पर क्लिक करें:

Google Cloud प्रोजेक्ट बनाने की स्क्रीन, जिसमें प्रोजेक्ट का नाम, प्रोजेक्ट आईडी, पैरंट रिसॉर्स, और 'बनाएं' बटन दिख रहा है

🔍 अपने काम की जांच करें

प्रोजेक्ट बन जाने के बाद, सबसे ऊपर दाएं टूलबार में मौजूद सूचनाएं पर क्लिक करें. इसके बाद, प्रोजेक्ट चुनें पर क्लिक करें:

Google Cloud Console के सूचना मेन्यू में, 'प्रोजेक्ट बनाएं' और 'प्रोजेक्ट चुनें' लिंक दिख रहा है

4. अपना एनवायरमेंट सेट अप करने का तरीका

Google Cloud Shell को कॉन्फ़िगर करें, Agent Platform API को चालू करें, और uv की मदद से एक आइसोलेटेड Python 3.12 वर्चुअल एनवायरमेंट बनाएं.

Cloud Shell को कॉन्फ़िगर करना

  1. Google Cloud console टूलबार में, Cloud Shell चालू करें (>_) पर क्लिक करें या सीधे Google Cloud Shell खोलें (या यूआरएल को अपनी गुप्त विंडो में कॉपी करें):
https://shell.cloud.google.com/?show=ide,terminal

Google Cloud console के टूलबार में मौजूद, Cloud Shell को चालू करने का बटन

  1. अगर कहा जाए, तो अनुमति दें पर क्लिक करें:

Cloud Shell को अनुमति देने वाला डायलॉग बॉक्स

  1. अपने टर्मिनल में नंबर वाले मेन्यू से, अपना ऐक्टिव Google Cloud प्रोजेक्ट आईडी चुनें:
unset CLOUDSDK_CORE_PROJECT
PS3="Select your project (e.g. 1): "
select PROJECT_ID in $(gcloud projects list --format="value(projectId)"); do
  [ -n "$PROJECT_ID" ] && gcloud config set project "$PROJECT_ID" && break
done

आपको इससे मिलता-जुलता आउटपुट दिखेगा (टैग नहीं किए गए प्रोजेक्ट के लिए, जानकारी देने वाली [environment: untagged] लाइन भी दिखती है):

1) google-cloud-workshop-4210
2) my-cloud-project-8812
Select your project (e.g. 1): 1
Updated property [core/project].

पुष्टि करें कि चुना गया प्रोजेक्ट आईडी, Cloud Shell प्रॉम्प्ट में पीले रंग से हाइलाइट किया गया हो:

Cloud Shell टर्मिनल में, चुने गए प्रोजेक्ट आईडी को पीले रंग से हाइलाइट किया गया है

ज़रूरी एपीआई चालू करना

Agent Platform API चालू करें, ताकि आपके ADK एजेंट, Gemini मॉडल को कॉल कर सकें:

gcloud services enable aiplatform.googleapis.com

आपको इससे मिलता-जुलता आउटपुट दिखेगा:

Operation "operations/acat.p2-..." finished successfully.

Python वर्चुअल एनवायरमेंट बनाना और ADK इंस्टॉल करना

~/adk-graph-agents फ़ाइल फ़ोल्डर बनाएं और Python 3.12 वर्चुअल एनवायरमेंट चालू करें:

cd ~
mkdir -p adk-graph-agents && cd ~/adk-graph-agents

which uv >/dev/null 2>&1 || curl -LsSf https://astral.sh/uv/install.sh | sh
export PATH="$HOME/.local/bin:$PATH"

uv venv .venv --python 3.12
source .venv/bin/activate

पुष्टि करें कि आपके टर्मिनल प्रॉम्प्ट की शुरुआत में (.venv) प्रीफ़िक्स दिख रहा है:

Cloud Shell टर्मिनल, जिसमें Python के चालू वर्चुअल एनवायरमेंट पर .venv प्रीफ़िक्स दिख रहा है

चालू वर्चुअल एनवायरमेंट में ADK 2 इंस्टॉल करें:

uv pip install --no-cache "google-adk==2.14.0"

🔍 अपने काम की जांच करें

इंस्टॉल किए गए adk सीएलआई वर्शन की पुष्टि करें:

adk --version

आपको इससे मिलता-जुलता आउटपुट दिखेगा:

adk, version 2.14.0

5. ट्रैवल प्लानर का स्केफ़ोल्ड

adk create की मदद से स्टार्टर travel_planner पैकेज जनरेट करें और adk web में डिफ़ॉल्ट सिंगल-एजेंट बेसलाइन की जांच करें.

स्टार्टर पैकेज जनरेट करो

~/adk-graph-agents से adk create को एक्ज़ीक्यूट करें:

cd ~/adk-graph-agents
adk create travel_planner

जब आपसे कहा जाए, तब ये विकल्प चुनें (अगर पहली बार इस्तेमाल करने पर Enable telemetry? [Y/n]: का विकल्प दिखे, तो Enter दबाएं या n टाइप करें और Enter दबाएं):

  1. रूट एजेंट के लिए कोई मॉडल चुनें: gemini-3.8-flash को चुनने के लिए 2 टाइप करें (विकल्प 1, gemini-3.5-flash को चुनता है) और Enter दबाएं.
  2. कोई बैकएंड चुनें: Google Cloud Agent Platform बैकएंड (नीचे दिए गए सीएलआई मेन्यू में विकल्प 2) चुनने के लिए, 2 टाइप करें. इसके बाद, Enter दबाएं.
  3. Google Cloud प्रोजेक्ट आईडी डालें: ब्रैकेट में मौजूद प्रोजेक्ट आईडी को स्वीकार करने के लिए, Enter दबाएं.
  4. Google Cloud क्षेत्र डालें: global टाइप करें (ग्लोबल Gemini एंडपॉइंट का इस्तेमाल करने के लिए) और Enter दबाएं.

adk create, GOOGLE_GENAI_USE_ENTERPRISE=1, आपके प्रोजेक्ट आईडी, और GOOGLE_CLOUD_LOCATION=global को travel_planner/.env में लिखता है. इससे, बाद में agent.py को बदलने पर आपके क्रेडेंशियल सुरक्षित रहते हैं.

आपको इससे मिलता-जुलता आउटपुट दिखेगा:

Choose a model for the root agent:
1. gemini-3.5-flash
2. gemini-3.8-flash
3. Other models (fill later)
Choose model (1, 2, 3): 2
1. Google AI
2. Vertex AI
3. Login with Google
Choose a backend (1, 2, 3): 2

You need an existing Google Cloud account and project, check out this link for details:
https://google.github.io/adk-docs/get-started/quickstart/#gemini---google-cloud-vertex-ai

Enter Google Cloud project ID [google-cloud-workshop-4210]:
Enter Google Cloud region [us-central1]: global

Agent created in /home/your_username/adk-graph-agents/travel_planner:
- .env
- .gitignore
- __init__.py
- agent.py

⚠️  WARNING: Secrets (like GOOGLE_API_KEY) are stored in .env.

Next steps:
- Chat with your agent in the terminal: adk run travel_planner
- Or in the browser: adk web .
- Have your coding agent evaluate and deploy it: uvx google-agents-cli setup
  (other install options: https://adk.dev/get-started/agents-cli/)

Cloud Shell के कोड एडिटर में ~/adk-graph-agents खोलें:

which cloudshell >/dev/null 2>&1 && cloudshell workspace ~/adk-graph-agents || true

फिर से लोड किए गए टर्मिनल में अपने वर्चुअल एनवायरमेंट को फिर से चालू करें:

cd ~/adk-graph-agents
source .venv/bin/activate

स्टार्टर सिंगल एजेंट को टेस्ट करना

एडीके वेब यूज़र इंटरफ़ेस (यूआई) को शुरू करें, ताकि यह देखा जा सके कि बिना किसी ऑर्केस्ट्रेशन के काम करने वाला स्टार्टर एजेंट, यात्रा से जुड़े प्रॉम्प्ट का जवाब कैसे देता है:

cd ~/adk-graph-agents
adk web --allow_origins "*"

सबसे ऊपर दाईं ओर मौजूद Cloud Shell टूलबार में, वेब की झलक आइकॉन पर क्लिक करें. इसके बाद, पोर्ट बदलें को चुनें, 8000 डालें, और बदलें और झलक देखें पर क्लिक करें:

Cloud Shell के वेब की झलक वाले पोर्ट कॉन्फ़िगरेशन डायलॉग को 8000 पर सेट किया गया

🔍 अपने काम की जांच करें

adk web के सबसे ऊपर बाईं ओर मौजूद ड्रॉपडाउन में, travel_planner को चुनें और "Help me plan a trip to Japan" भेजें:

एडीके डेवलपमेंट वेब यूज़र इंटरफ़ेस (यूआई), जिसमें एक एजेंट के साथ की गई बुनियादी बातचीत दिखाई गई है

आपको चैट पैनल में, बातचीत के अंदाज़ में एक जवाब दिखेगा. इसमें इवेंट में root_agent को एक बार कॉल किया गया होगा:

root_agent -> responds in one pass without structured preference extraction, live search, or budget verification

अगले चरण में फ़ाइलों में बदलाव करने से पहले, adk web को रोकने के लिए अपने Cloud Shell टर्मिनल में Ctrl + C दबाएं.

6. लीनियर ग्राफ़ वर्कफ़्लो बनाना

अपना पहला ADK 2 Workflow बनाएं: रिज़र्व किए गए "START" एंट्री पॉइंट को user_profiler_agent और itinerary_synthesizer_agent से जोड़ने वाली एक क्रम में चलने वाली चेन (कोई भी ऐसा नोड जिसमें कोई आउटगोइंग एज नहीं है, वह टर्मिनल नोड के तौर पर काम करता है). हर एज (a, b), आइसोलेटेड single_turn मोड में Agent (LlmAgent का दूसरा नाम) नोड को अपने-आप चलाता है. साथ ही, नोड a के आउटपुट को node_input (उपयोगकर्ता का इनकमिंग मैसेज) के तौर पर नोड b पर डिलीवर करता है. वहीं, निर्देश स्ट्रिंग में मौजूद {user_preferences}, output_key के ज़रिए सेव की गई मैचिंग सेशन स्टेट कुंजी को इंटरपोलेट करता है.

आर्किटेक्चर डायग्राम में, travel_workflow (वर्कफ़्लो) को START से user_profiler_agent और itinerary_synthesizer_agent से कनेक्ट करते हुए दिखाया गया है

शुरुआती स्पेशलिस्ट बनाना

पक्का करें कि adk web बंद हो (Ctrl + C). इसके बाद, travel_planner/specialists.py नाम की एक नई फ़ाइल खोलें:

cloudshell edit ~/adk-graph-agents/travel_planner/specialists.py

user_profiler_agent और itinerary_synthesizer_agent को तय करने के लिए, travel_planner/specialists.py में यह कोड चिपकाएं (अगले चरण के लिए, google_search को पहले ही इंपोर्ट करें):

from google.adk import Agent
from google.adk.tools import google_search

MODEL = "gemini-3.8-flash"

# --- Specialist 1: The User Profiler ---
user_profiler_agent = Agent(
    model=MODEL,
    name="user_profiler_agent",
    description="Extracts travel preferences and constraints from user input.",
    instruction="""
        Extract destination, duration (default 1 day), budget, interests, and exclusions.
        Return a single JSON object with keys: "destination", "duration", "budget", "interests", "exclusions".
    """,
    output_key="user_preferences",  # Saved for multi-hop constraints downstream
)

# --- Specialist 2: The Synthesizer ---
itinerary_synthesizer_agent = Agent(
    model=MODEL,
    name="itinerary_synthesizer_agent",
    description="Compiles preferences and research into a final Markdown itinerary.",
    instruction="""
        You are an elite Travel Concierge. Using the incoming input and
        User Preferences ({user_preferences}), write a Markdown itinerary
        with an itemized USD and local currency cost breakdown.
    """,
    output_key="final_itinerary",
)

क्रम में चलने वाली एज चेन तय करना

travel_planner/workflows.py नाम की नई फ़ाइल खोलें:

cloudshell edit ~/adk-graph-agents/travel_planner/workflows.py

Workflow की यह परिभाषा चिपकाएं (जहां तीन या उससे ज़्यादा नोड का ट्यूपल, "START" से शुरू होने वाली क्रम से चलने वाली चेन में कंपाइल होता है):

from google.adk import Workflow
from .specialists import (
    itinerary_synthesizer_agent,
    user_profiler_agent,
)

# 1. SEQUENTIAL GRAPH: START -> user_profiler_agent -> itinerary_synthesizer_agent
travel_workflow = Workflow(
    name="travel_workflow",
    description="Orchestrates the end-to-end travel planning process as a graph workflow.",
    edges=[
        ("START", user_profiler_agent, itinerary_synthesizer_agent),
    ],
)

वर्कफ़्लो को root_agent के तौर पर एक्सपोज़ करें

travel_planner/agent.py खोलें:

cloudshell edit ~/adk-graph-agents/travel_planner/agent.py

travel_planner/agent.py के पूरे कॉन्टेंट को इन दो लाइनों से बदलें:

from .workflows import travel_workflow

root_agent = travel_workflow

🔍 अपने काम की जांच करें

~/adk-graph-agents से adk web शुरू करें:

cd ~/adk-graph-agents
adk web --allow_origins "*"

पोर्ट 8000 पर वेब की झलक खोलें, + नया सेशन पर क्लिक करें, और यह भेजें:

Help me plan a trip to Kyoto

इवेंट व्यू में, आपको इवेंट का क्रम कुछ ऐसा दिखना चाहिए:

1. user_profiler_agent -> outputs JSON {"destination": "Kyoto", "duration": "1 day", ...} and writes state["user_preferences"]
2. itinerary_synthesizer_agent -> receives preferences via node_input and outputs the Markdown itinerary

ADK 2.14.0 के वेब यूज़र इंटरफ़ेस (यूआई) में, START से user_profiler_agent और itinerary_synthesizer_agent ग्राफ़ के एक्ज़ीक्यूशन और क्योटो की जनरेट की गई यात्रा की योजना को दिखाया गया है

इवेंट में जाकर किसी इवेंट पर क्लिक करें. इसके बाद, कंपाइल की गई START -> user_profiler_agent -> itinerary_synthesizer_agent टोपोलॉजी की जांच करने के लिए, ग्राफ़ आइकॉन (इवेंट की जानकारी वाले टूलबार में account_tree) पर क्लिक करें. इसके बाद, स्टेट टैब पर जाकर पुष्टि करें कि user_preferences और final_itinerary की जानकारी भरी गई है.

जारी रखने से पहले, adk web को रोकने के लिए अपने टर्मिनल में Ctrl + C दबाएं.

7. JoinNode की मदद से, फ़ैन आउट करना और शामिल होना

यात्रा की योजना को वेब पर मौजूद लाइव डेटा के आधार पर बनाने के लिए, एक साथ दो google_search स्पेशलिस्ट (activity_finder_agent और restaurant_finder_agent) जोड़ें. साथ ही, उन्हें JoinNode के साथ सिंक करें:

  • फ़ैन-आउट टपल: एज टपल के अंदर (activity_finder_agent, restaurant_finder_agent) को रखने से, user_profiler_agent का आउटपुट node_input के तौर पर दोनों रिसर्चर को एक साथ मिलता है.
  • फ़ैन-इन बैरियर (JoinNode): ट्यूपल के ठीक बाद JoinNode(name="research_join") को रखने पर, यह तब तक इंतज़ार करता है, जब तक कि आने वाली हर ब्रांच पूरी नहीं हो जाती. इसके बाद, यह दो बार सिंथेसिस को ट्रिगर करने के बजाय, मर्ज किए गए एक dict ({"activity_finder_agent": ..., "restaurant_finder_agent": ...}) को node_input के तौर पर itinerary_synthesizer_agent को पास करता है.

आर्किटेक्चर डायग्राम में दिखाया गया है कि travel_workflow, user_profiler_agent से node_input के ज़रिए activity_finder_agent और restaurant_finder_agent में एक साथ फ़ैन आउट हो रहा है. इसके बाद, यह research_join (JoinNode) पर मर्ज हो रहा है और node_input (dict) को itinerary_synthesizer_agent को पास कर रहा है

रिसर्च स्पेशलिस्ट को जोड़ना

पुष्टि करें कि adk web बंद है (Ctrl + C). इसके बाद, travel_planner/specialists.py खोलें:

cloudshell edit ~/adk-graph-agents/travel_planner/specialists.py

travel_planner/specialists.py के सबसे नीचे activity_finder_agent और restaurant_finder_agent जोड़ें (किसी को भी output_key की ज़रूरत नहीं है, क्योंकि research_join दोनों आउटपुट को सीधे node_input के ज़रिए फ़ॉरवर्ड करता है):

# --- Specialist 3: The Activity Finder ---
activity_finder_agent = Agent(
    model=MODEL,
    name="activity_finder_agent",
    description="Finds 3 attractions matching the user's destination and interests.",
    instruction="""
        Using the travel preferences in the input, call google_search to find 3 specific attractions
        (including budget-friendly options).
        Return a JSON list with keys: "attraction_name", "estimated_cost_local", "estimated_cost_usd", "notes".
    """,
    tools=[google_search],
)

# --- Specialist 4: The Restaurant Finder ---
restaurant_finder_agent = Agent(
    model=MODEL,
    name="restaurant_finder_agent",
    description="Finds 3 restaurants matching the destination and budget.",
    instruction="""
        Using the travel preferences in the input, call google_search to find 3 highly-rated restaurants
        across price points.
        Return a JSON list with keys: "restaurant_name", "cuisine", "estimated_cost_local", "estimated_cost_usd".
    """,
    tools=[google_search],
)

workflows.py में फ़ैन-आउट और JoinNode जोड़ें

travel_planner/workflows.py खोलें:

cloudshell edit ~/adk-graph-agents/travel_planner/workflows.py

travel_planner/workflows.py के पूरे कॉन्टेंट को फ़ैन-आउट और JoinNode ग्राफ़ से बदलें:

from google.adk import Workflow
from google.adk.workflow import JoinNode
from .specialists import (
    activity_finder_agent,
    itinerary_synthesizer_agent,
    restaurant_finder_agent,
    user_profiler_agent,
)

research_join = JoinNode(name="research_join")

# 2. FAN-OUT / FAN-IN GRAPH: Profile -> (Activities || Restaurants) -> Join -> Synthesize
travel_workflow = Workflow(
    name="travel_workflow",
    description="Orchestrates the end-to-end travel planning process as a graph workflow.",
    edges=[
        (
            "START",
            user_profiler_agent,
            (activity_finder_agent, restaurant_finder_agent),
            research_join,
            itinerary_synthesizer_agent,
        ),
    ],
)

🔍 अपने काम की जांच करें

एक साथ खोज करने और JoinNode को सिंक करने की सुविधा को टेस्ट करने के लिए, adk web को चालू करें:

cd ~/adk-graph-agents
adk web --allow_origins "*"

पोर्ट 8000 पर वेब की झलक खोलें, + नया सेशन पर क्लिक करें, और यह भेजें:

Help me plan a trip to Kyoto focused on historic temples and ramen

इवेंट व्यू में, आपको इवेंट का क्रम कुछ ऐसा दिखना चाहिए:

1. user_profiler_agent -> emits JSON preferences via node_input
2. activity_finder_agent & restaurant_finder_agent -> invoke google_search concurrently
3. research_join -> merges both researcher outputs into one dict node_input
4. itinerary_synthesizer_agent -> drafts final_itinerary from the merged dict + {user_preferences}

जारी रखने से पहले, adk web को रोकने के लिए अपने टर्मिनल में Ctrl + C दबाएं.

8. रास्ते में बदलाव करने की साइकल

बजट और नेगेटिव कंस्ट्रेंट लागू करने के लिए, एक itinerary_critic_agent, एक itinerary_refiner_agent, और दो डिटरमिनिस्टिक Python फ़ंक्शन नोड (route_critique और finalize_itinerary) जोड़ें. ये नोड, ADK Event ऑब्जेक्ट दिखाते हैं:

  • route: यह डिक्शनरी एज ({"revise": ..., "approved": ...}) में मौजूद, नाम वाली ब्रांच की उस कुंजी को चुनता है जिसे आगे चलाना है.
  • output: यह चैट मैसेज को रेंडर किए बिना, टाइप की गई Python वैल्यू या स्ट्रिंग को node_input के तौर पर अगले नोड पर भेजता है.
  • message और state: message, चैट ट्रांसक्रिप्ट में उपयोगकर्ता को दिखने वाला टेक्स्ट दिखाता है. वहीं, state, सेशन की स्थिति में मुख्य-वैल्यू के अपडेट (stateDelta) लागू करता है.

आर्किटेक्चर डायग्राम में travel_workflow दिखाया गया है. इसमें itinerary_critic_agent, route_critique को फ़ीड करता है. यह itinerary_refiner_agent को 'revise' (itinerary_critic_agent पर वापस लूप करना) या finalize_itinerary को 'approved' रूट करता है

Append the critic and refiner specialists

पुष्टि करें कि adk web बंद है (Ctrl + C). इसके बाद, travel_planner/specialists.py खोलें:

cloudshell edit ~/adk-graph-agents/travel_planner/specialists.py

travel_planner/specialists.py के सबसे नीचे itinerary_critic_agent और itinerary_refiner_agent जोड़ें (रिफ़ाइनर को किसी google_search या exit_loop टूल की ज़रूरत नहीं होती, क्योंकि यह {final_itinerary} में पहले से इकट्ठा किए गए आइटम को स्वैप करता है और सिर्फ़ तब चलता है, जब route_critique, "revise" को चुनता है):

# --- Specialist 5: The Budget & Constraint Critic ---
itinerary_critic_agent = Agent(
    model=MODEL,
    name="itinerary_critic_agent",
    description="Audits the itinerary draft against budget and negative constraints.",
    instruction="""
        Audit the itinerary in the input against User Preferences ({user_preferences}).
        Verify that the USD total stays within the user's budget and no excluded venue types appear.
        If all checks pass, respond EXACTLY with the single word "APPROVED".
        Otherwise, output 1-2 concise revision instructions explaining what to swap or adjust.
    """,
)

# --- Specialist 6: The Itinerary Refiner ---
itinerary_refiner_agent = Agent(
    model=MODEL,
    name="itinerary_refiner_agent",
    description="Revises the itinerary draft using the critic's feedback.",
    instruction="""
        Revise Current Itinerary ({final_itinerary}) using the auditor critique in the input
        so all USD budget and exclusion rules in {user_preferences} pass.
        Output ONLY the updated Markdown itinerary.
    """,
    output_key="final_itinerary",
)

route_critique और साइकल जोड़ें

travel_planner/workflows.py खोलें:

cloudshell edit ~/adk-graph-agents/travel_planner/workflows.py

travel_planner/workflows.py के पूरे कॉन्टेंट को पूरे ग्राफ़ वर्कफ़्लो से बदलें:

from google.adk import Event, Workflow
from google.adk.workflow import JoinNode
from .specialists import (
    activity_finder_agent, itinerary_critic_agent, itinerary_refiner_agent,
    itinerary_synthesizer_agent, restaurant_finder_agent, user_profiler_agent,
)

MAX_REVISIONS = 2
research_join = JoinNode(name="research_join")


def route_critique(node_input: str, revision_count: int = 0):
    """Routes to 'approved' when constraints pass, or 'revise' up to MAX_REVISIONS."""
    approved = node_input.strip().rstrip(".").upper() == "APPROVED"
    if approved or revision_count >= MAX_REVISIONS:
        return Event(route="approved", output=approved)
    return Event(
        route="revise", output=node_input, state={"revision_count": revision_count + 1}
    )


def finalize_itinerary(node_input: bool, final_itinerary: str, revision_count: int = 0):
    """Emits the verified itinerary and resets revision_count for the next turn."""
    status = (
        f"Itinerary approved after {revision_count} revision(s)."
        if node_input
        else f"Max revisions ({revision_count}) reached; returning best-effort draft."
    )
    return Event(
        message=f"{status}\n\n{final_itinerary}",
        output=final_itinerary,
        state={"revision_count": 0},
    )


travel_workflow = Workflow(
    name="travel_workflow",
    description="Orchestrates the end-to-end travel planning process as a graph workflow.",
    edges=[
        (
            "START",
            user_profiler_agent,
            (activity_finder_agent, restaurant_finder_agent),
            research_join,
            itinerary_synthesizer_agent,
            itinerary_critic_agent,
            route_critique,
        ),
        (route_critique, {"revise": itinerary_refiner_agent, "approved": finalize_itinerary}),
        (itinerary_refiner_agent, itinerary_critic_agent),
    ],
)

🔍 अपने काम की जांच करें

पुष्टि करें कि travel_planner/workflows.py सही तरीके से कंपाइल हो और डायरेक्टेड ग्राफ़ एज प्रिंट करे:

cd ~/adk-graph-agents && source .venv/bin/activate
python3 -c "from travel_planner.agent import root_agent; print('\n'.join(f'{e.from_node.name} -> {e.to_node.name} (route={e.route})' for e in root_agent.graph.edges))"

आपको इससे मिलता-जुलता आउटपुट दिखेगा:

__START__ -> user_profiler_agent (route=None)
user_profiler_agent -> activity_finder_agent (route=None)
user_profiler_agent -> restaurant_finder_agent (route=None)
activity_finder_agent -> research_join (route=None)
restaurant_finder_agent -> research_join (route=None)
research_join -> itinerary_synthesizer_agent (route=None)
itinerary_synthesizer_agent -> itinerary_critic_agent (route=None)
itinerary_critic_agent -> route_critique (route=None)
route_critique -> itinerary_refiner_agent (route=revise)
route_critique -> finalize_itinerary (route=approved)
itinerary_refiner_agent -> itinerary_critic_agent (route=None)

इसके बाद, तुरंत मिलने वाली मंज़ूरी और बजट के हिसाब से बदलाव करने की सुविधा को टेस्ट करने के लिए, adk web को शुरू करें:

adk web --allow_origins "*"

पोर्ट 8000 पर वेब की झलक खोलें. इसके बाद, + नया सेशन पर क्लिक करें और क्योटो के लिए कई शर्तों वाला प्रॉम्प्ट भेजें:

Design a full-day itinerary for Kyoto with a total budget of $100. My main interests are historical landmarks, temples, and authentic local cuisine. Please do not include any modern art museums or galleries.

route="revise" लूप को काम करते हुए देखने के लिए, फ़ॉलो-अप प्रॉम्प्ट भेजें. इसमें 15 डॉलर का बजट तय करें:

Now design a full-day Kyoto itinerary with paid temple admissions, lunch, and dinner on a strict total budget of $15 USD.

इवेंट व्यू में, आपको इस तरह का एक्ज़ीक्यूशन क्रम दिखेगा:

1. user_profiler_agent -> writes JSON constraints to state["user_preferences"]
2. (activity_finder_agent, restaurant_finder_agent) -> run google_search concurrently -> research_join
3. itinerary_synthesizer_agent -> writes initial draft to state["final_itinerary"]
4. itinerary_critic_agent -> outputs "APPROVED" (or revision instructions when over budget)
5. route_critique -> emits route="approved" (skipping refiner) or route="revise" -> itinerary_refiner_agent -> itinerary_critic_agent
6. finalize_itinerary -> emits "Itinerary approved after N revision(s)." (or max-revisions notice) and resets revision_count=0

adk web को रोकने के लिए, अपने टर्मिनल में Ctrl + C दबाएं.

9. समस्या का हल

adk web में एजेंट ड्रॉपडाउन खाली है

  • वजह: adk web को पैरंट ~/adk-graph-agents डायरेक्ट्री के बजाय, travel_planner/ में शुरू किया गया था या Cloud Shell सेशन को रीस्टार्ट किया गया था.
  • ठीक करने का तरीका: Ctrl + C दबाएं, पैरंट डायरेक्ट्री पर वापस जाएं, और cd ~/adk-graph-agents && source .venv/bin/activate && adk web --allow_origins "*" दबाकर फिर से शुरू करें.

404 NOT_FOUND मॉडल से जुड़ी गड़बड़ी

  • वजह: आपने adk create के दौरान, global टाइप करने के बजाय, डिफ़ॉल्ट us-central1 क्षेत्र को स्वीकार करने के लिए Enter दबाया.
  • ठीक करें: ~/adk-graph-agents/travel_planner/.env खोलें और पक्का करें कि GOOGLE_CLOUD_LOCATION=global सेट हो.

ERROR: [Errno 98] address already in use, पोर्ट 8000 पर

  • वजह: Cloud Shell में पिछला adk web सर्वर अब भी चल रहा है.
  • ठीक करने का तरीका: चालू टर्मिनल में Ctrl + C दबाएं या adk web को फिर से शुरू करने से पहले, pkill -f "adk web" की मदद से मौजूदा प्रोसेस को बंद करें.

10. व्यवस्थित करें

लोकल वर्कस्पेस को हटाएं. इसके अलावा, एपीआई को बंद करें या वर्कशॉप प्रोजेक्ट को मिटाएं.

लोकल प्रोजेक्ट की फ़ाइलें मिटाना

~/adk-graph-agents डायरेक्ट्री और वर्चुअल एनवायरमेंट को हटाएं:

cd ~
rm -rf ~/adk-graph-agents

Agent Platform API को बंद करना (ज़रूरी नहीं)

अगर आपको इस प्रोजेक्ट में Agent Platform API की ज़रूरत नहीं है, तो इसे बंद करें:

gcloud services disable aiplatform.googleapis.com --quiet

🔍 अपने काम की जांच करें

पुष्टि करें कि ~/adk-graph-agents को हटा दिया गया है:

test ! -d ~/adk-graph-agents && echo "Workspace ~/adk-graph-agents removed"

आपको इससे मिलता-जुलता आउटपुट दिखेगा:

Workspace ~/adk-graph-agents removed

11. बधाई हो

आपने ADK 2 Workflow का इस्तेमाल करके, मल्टी-एजेंट ट्रैवल प्लानर बनाया. इसमें आपने क्रम में चलने वाली चेन, JoinNode के साथ एक साथ google_search फ़ैन-आउट, और शर्त के साथ मेकर-चेकर साइकल को एक ही डिक्लेरेटिव एज ग्राफ़ में शामिल किया.

आपने क्या-क्या हासिल किया

  • ADK 2 Workflow(edges=[...]) का इस्तेमाल करके, मल्टी-एजेंट पाइपलाइन तय की गई
  • node_input और स्टेट-बाउंड पैरामीटर के ज़रिए, Agent, JoinNode, और Python फ़ंक्शन नोड के बीच सीधे डेटा पास किया गया. साथ ही, सेशन स्टेट को कम से कम रखा गया
  • एक साथ कई google_search ब्रांच बनाईं और उन्हें JoinNode के साथ सिंक किया
  • LoopAgent और टूल पर आधारित लूप एस्केलेशन को डिटरमिनिस्टिक Event(route=...) राऊटर से बदल दिया गया है. इससे मंज़ूर किए गए ड्राफ़्ट पर, एलएलएम को बेवजह कॉल नहीं किया जाता

अगले चरण