使用 ADK 建構 AI 代理:自動調度管理圖表工作流程

1. 總覽

如要滿足多重限制的旅遊要求 (例如規劃京都一日遊,預算 $100 美元,行程包含歷史悠久的寺廟、當地拉麵,且不含現代藝術博物館),代理必須擷取結構化偏好設定、同時研究景點、彙整分項草稿,並在回覆前稽核總費用。在 ADK 1.x 中,如要自動調度這些階段,必須巢狀結構化 SequentialAgent、ParallelAgent 和 LoopAgent 包裝函式,將每個中繼輸出內容儲存至工作階段狀態,並叫用額外的 LLM 工具呼叫,才能退出修正迴圈。

在本程式碼實驗室中,您將使用 Agent Development Kit (ADK) 2 圖表工作流程 (Workflow),在 Agent Platform 上建構多代理旅遊規劃工具。在三個漸進階段中,您將在依序邊緣鏈 (node_input) 中連結專業 Agent 節點 (由 gemini-3.8-flash 提供技術支援)、展開與 JoinNode 同步的並行 google_search 研究人員,並使用確定性 Python 路由器 (Event(route=...)) 強制執行預算和排除規則,只要初始草稿通過稽核,就會略過修正。

必要條件

課程內容

  • 如何使用 Workflow 邊緣元組在 ADK 2 中宣告多代理管道
  • ADK 2 如何將輸出內容直接沿著邊緣傳遞為 node_input,並將工作階段狀態索引鍵繫結為函式參數
  • 如何使用 JoinNode 展開並行研究分支,並合併輸出內容
  • 如何使用 Event(route=...) 在 Python 中確定性地轉送有界限的製作者-檢查者循環
  • 如何在 ADK 網頁 UI 中檢查已編譯的工作流程圖、平行執行時間軸和狀態差異

需求條件

  • Google 帳戶 (如要申請工作坊抵免額,請使用個人 @gmail.com 帳戶)
  • 講師提供的研討會抵免額連結 (以及用於掃描 QR code 的手機相機),或有效的 Cloud Billing 帳戶
  • Google Chrome (建議使用無痕視窗,避免混用個人和公司帳戶)

2. 領取 Google Cloud 抵免額 (選用)

如果您參加的工作坊提供活動抵免額連結,請領取 Google Developer Program 抵免額,即可在不使用信用卡的情況下佈建帳單帳戶。

領取活動抵免額

  1. 在無痕視窗中開啟工作坊抵免額連結,並使用 @gmail.com 帳戶登入。
  2. 在「Claim Your Benefit」頁面,點選「Claim Credit」:

Google 開發人員中心「領取福利」卡片,顯示「領取抵免額」按鈕

  1. 如果系統提示您建立 Google Developer Program 設定檔,請填寫表單。
  2. 看到「掃描以驗證您是真人」時,請用手機掃描 QR code 並完成驗證 (如果沒有看到 QR code,請重新整理並再次點選「領取抵免額」):

Google Developer Program「掃描完成真人驗證」QR code 對話方塊

  1. 確認畫面顯示「Successfully claimed your event credit!」,然後點選「Open Billing Account」:

Google 開發人員中心顯示「已成功領取活動抵免額」和「開啟帳單帳戶」按鈕

🔍 檢查成果

帳單帳戶設定最多需要 3 分鐘:

  1. 開啟 Google Cloud Billing (或將網址複製到無痕視窗),然後選取「您的帳單帳戶」:
https://console.cloud.google.com/billing
  1. 重新整理,直到 GDP Credit 帳戶顯示「Active」狀態:

Google Cloud Billing 帳戶管理頁面,顯示有效的 GDP 抵免額帳單帳戶

3. 建立 Google Cloud 專案 (選用)

建立專屬的 Google Cloud 雲端專案,讓這個程式碼實驗室的所有資源保持獨立,方便清理。

  1. 開啟「建立專案」頁面 (或將網址複製到無痕視窗):
https://console.cloud.google.com/projectcreate
  1. 輸入專案詳細資料:
    • 專案名稱:例如 google-cloud-workshop
    • 帳單帳戶(如有顯示):選取 GDP Credit (或有效的帳單帳戶)
    • 「Parent resource」(上層資源):「No organization」(無組織) (或視需要選取組織)
  2. 記下專案名稱下方產生的專案 ID,然後點選「建立」:

「建立 Google Cloud 雲端專案」畫面,顯示專案名稱、專案 ID、父項資源和「建立」按鈕

🔍 檢查成果

專案建立完成後,按一下右上角工具列中的「通知」,然後點選「選取專案」:

Google Cloud 控制台的「通知」選單,顯示「建立專案」和「選取專案」連結

4. 設定環境

設定 Google Cloud Shell、啟用 Agent Platform API,並使用 uv 建立獨立的 Python 3.12 虛擬環境。

設定 Cloud Shell

  1. 點選 Google Cloud 控制台工具列中的「啟用 Cloud Shell」圖示 >_,或直接開啟 Google Cloud Shell (或將網址複製到無痕視窗):
https://shell.cloud.google.com/?show=ide,terminal

Google Cloud 控制台工具列中的「啟用 Cloud Shell」按鈕

  1. 如果出現提示訊息,請點選「授權」:

「Authorize Cloud Shell」(授權 Cloud Shell) 對話方塊

  1. 在終端機的編號選單中,選取有效的 Google Cloud 專案 ID:
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].

確認所選專案 ID 在 Cloud Shell 提示中以黃色醒目顯示:

Cloud Shell 終端機,以黃色醒目顯示所選專案 ID

啟用必要的 API

啟用 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 終端機顯示 .venv 前置字串,代表 Python 虛擬環境已啟用

在啟動的虛擬環境中安裝 ADK 2:

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

🔍 檢查成果

驗證已安裝的 adk CLI 版本:

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. 選擇根代理的模型:輸入 2 選取 gemini-3.8-flash (選項 1 會選取 gemini-3.5-flash),然後按下 Enter 鍵。
  2. Choose a backend:輸入 2 選取 Google Cloud Agent Platform 後端 (下方 CLI 選單中的選項 2),然後按下 Enter 鍵。
  3. 輸入 Google Cloud 雲端專案 ID:按下 Enter 鍵,接受括號中偵測到的專案 ID。
  4. 輸入 Google Cloud 區域:輸入 global (使用全域 Gemini 端點),然後按下 Enter 鍵。

adk create 會將 GOOGLE_GENAI_USE_ENTERPRISE=1、專案 ID 和 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

測試新手單一代理

開啟 ADK 網頁 UI,觀察單一未經自動化調度的入門代理如何回答旅遊提示:

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

點選 Cloud Shell 工具列右上方的「網頁預覽」圖示,選取「變更通訊埠」,輸入 8000,然後點選「變更並預覽」:

Cloud Shell 網頁預覽通訊埠設定對話方塊,設為 8000

🔍 檢查成果

在 adk web 左上方的下拉式選單中選取「travel_planner」travel_planner,然後傳送 "Help me plan a trip to Japan":

ADK 開發網頁 UI 顯示基本單一代理對話

對話窗格中應會顯示單次對話回覆 (「事件」中只有一個 root_agent 叫用):

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

在 Cloud Shell 終端機中按下 Ctrl + C 停止 adk web,然後在下一個步驟編輯檔案。

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

將下列程式碼貼到 travel_planner/specialists.py 中,定義 user_profiler_agent 和 itinerary_synthesizer_agent (預先匯入 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 定義 (其中 3 個以上節點的元組會編譯成 "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 開啟網頁預覽,按一下「+ New Session」,然後傳送:

Help me plan a trip to Kyoto

在「Events」(事件) 檢視畫面中,您應該會看到類似以下的事件序列:

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 網頁 UI 顯示從 START 到 user_profiler_agent 再到 itinerary_synthesizer_agent 的圖表執行過程,以及生成的京都行程

在「事件」中點選任一事件,然後點選「圖表」圖示 (事件詳細資料工具列中的 account_tree),檢查編譯的 START -> user_profiler_agent -> itinerary_synthesizer_agent 拓撲,接著查看「狀態」分頁,確認 user_preferences 和 final_itinerary 已填入。

在終端機中按下 Ctrl + C 停止 adk web,然後再繼續。

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

將 activity_finder_agent 和 restaurant_finder_agent 附加到 travel_planner/specialists.py 的底部 (兩者都不需要 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],
)

將展開和 JoinNode 新增至 workflows.py

開啟 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,
        ),
    ],
)

🔍 檢查成果

啟動 adk web,測試並行搜尋和 JoinNode 同步處理:

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

在通訊埠 8000 開啟網頁預覽,按一下「+ New Session」,然後傳送:

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

在「Events」(事件) 檢視畫面中,您應該會看到類似以下的事件序列:

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}

在終端機中按下 Ctrl + C 停止 adk web,然後再繼續。

8. 路線修訂週期

新增 itinerary_critic_agent、itinerary_refiner_agent 和兩個會傳回 ADK Event 物件的確定性 Python 函式節點 (route_critique 和 finalize_itinerary),強制執行預算和負面限制:

  • route:選取 dict 邊緣 ({"revise": ..., "approved": ...}) 中接下來要執行的命名分支索引鍵。
  • output:將輸入的 Python 值或字串沿著邊緣傳遞至下一個節點,做為 node_input,不顯示即時通訊訊息。
  • message 和 state:message 會在對話轉錄稿中發出使用者可見的文字,而 state 則會將鍵/值更新 (stateDelta) 提交至工作階段狀態。

架構圖顯示 travel_workflow,其中 itinerary_critic_agent 會提供 route_critique,將「revise」轉送至 itinerary_refiner_agent (迴圈回到 itinerary_critic_agent),或將「approved」轉送至 finalize_itinerary

附加評論和修正專家

確認 adk web 已停止 (Ctrl + C),然後開啟 travel_planner/specialists.py:

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

將 itinerary_critic_agent 和 itinerary_refiner_agent 附加到 travel_planner/specialists.py 的底部 (精煉器不需要 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 開啟網頁預覽,點選「+ New Session」,然後傳送多重限制的京都提示:

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.

在「Events」(事件) 檢視畫面中,您應該會看到類似以下的執行序列:

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

在終端機中按下 Ctrl + C 即可停止 adk web。

9. 疑難排解

adk web 的代理下拉式選單是空的

  • 原因:adk web 是在 travel_planner/ 內啟動,而非上層 ~/adk-graph-agents 目錄,或是 Cloud Shell 工作階段重新啟動。
  • 修正方式:按下 Ctrl + C,返回上層目錄,然後使用 cd ~/adk-graph-agents && source .venv/bin/activate && adk web --allow_origins "*" 重新啟動。

404 NOT_FOUND 項模型錯誤

  • 原因:您在 adk create 期間按下 Enter 鍵接受預設的 us-central1 區域,而非輸入 global。
  • 修正:開啟 ~/adk-graph-agents/travel_planner/.env,並確認已設定 GOOGLE_CLOUD_LOCATION=global。

ERROR: [Errno 98] address already in use (通訊埠:8000)

  • 原因:先前的 adk web 伺服器仍在 Cloud Shell 中執行。
  • 修正方式:在執行中的終端機按下 Ctrl + C,或使用 pkill -f "adk web" 停止現有程序,然後再次啟動 adk web。

10. 清除

移除本機工作區,並視需要停用 API 或刪除專屬工作坊專案。

刪除本機專案檔案

移除 ~/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 建構多代理旅遊規劃工具,在單一宣告式邊緣圖中,整合循序鏈、並行 google_search 扇出與 JoinNode,以及條件式製作者-檢查者循環。

完成的目標

  • 使用 ADK 2 宣告多代理管道 Workflow(edges=[...])
  • 透過 node_input 和狀態繫結參數,直接在 Agent、JoinNode 和 Python 函式節點之間傳遞資料,同時盡可能減少工作階段狀態
  • 展開並行 google_search 分支,並與 JoinNode 同步
  • 將 LoopAgent 和工具型迴圈升級機制,替換為確定性 Event(route=...) 路由器,略過已核准草稿的不必要 LLM 呼叫

後續步驟