iT邦幫忙

2026 iThome 鐵人賽

DAY 19
0

Day 19 | 跨程序的握手:認識 A2A Protocol

主張:A2A 解決的不是「怎麼讓 agent 溝通」,而是「什麼時候真的需要讓 agent 跨網路溝通」——大多數人一開始都用錯了場景。
讀完能做到:判斷一個多 agent 需求該用 A2A 還是 local sub-agent,並說得出至少四個「不該用 A2A」的具體反例。

從「同一個程序」跳到「不同的服務」

這兩天你組過的 agent 團隊——委派、共享 session state、context 隔離——全部發生在同一個程序裡。coordinator 跟 subagent 之間的通訊快到你感覺不到延遲,因為那本質上就是記憶體內的函式呼叫。

但如果你想合作的對象是一個獨立的服務,跑在另一台機器、由另一個團隊維護,甚至是用 Java 寫的?這時候 local sub-agent 的模式就完全不適用了——你需要一個跨網路的通訊標準。Agent2Agent(A2A)Protocol(見 a2a-protocol.org)正是為了這個場景設計的開放標準,而且 ADK 已經把它拆成兩個角色,讓你不用自己重新發明一套跨程序的溝通機制。

發起呼叫的那一邊,你面對的是一個叫 RemoteA2aAgent 的代理人——寫法跟你平常接的 local sub-agent 幾乎一樣,內部卻是把每一次互動轉成一次網路請求。被呼叫的那一邊,你的 agent 會被包進一台 A2A Server,對外用標準協定接收請求,再轉交給底下真正在跑的 agent 程式碼。下面這張圖就是這兩個角色實際的關係:

https://ithelp.ithome.com.tw/upload/images/20260917/20183762MxNYN9mLih.png

(圖:consuming 端的 RemoteA2aAgent 透過網路呼叫 exposing 端的 A2A Server,底下才是真正在跑的 agent 程式碼。概念取自 Introduction to A2A

先問一句:這件事真的需要跨網路嗎?

我看過有團隊把一個只做欄位驗證的 DataValidator 拆成獨立的 A2A 服務,理由是「微服務比較乾淨」。結果一次原本在記憶體裡、幾毫秒就跑完的函式呼叫,變成一次 HTTP round-trip 加兩次 JSON 序列化,還多了一個可能在半夜掛掉、需要你起床處理的服務。判斷該不該跨網路,比學會怎麼跨網路更重要。

Local Sub-Agents:在同一個應用程序內執行,像內部模組或函式庫,用來把程式碼組織成邏輯上可重用的元件。溝通直接發生在記憶體裡,沒有網路開銷。

Remote Agents(A2A):獨立執行的服務,透過網路溝通,A2A 定義了這層溝通的標準協定。

該用 A2A 的情境

  • 對方是一個獨立、standalone 的服務(例如一個專職的金融建模 agent)
  • agent 由不同團隊或組織維護
  • 需要連接用不同程式語言或不同 agent 框架寫的元件
  • 你想在系統元件之間強制一份正式、formal 的契約

官方給的具體例子涵蓋四種場景:

  • 串接第三方服務:主 agent 要拿即時股價,而資料商正好把它包成一支 A2A agent 對外開放。
  • 微服務架構:訂單處理、庫存管理、出貨各自是獨立服務,跨網路邊界溝通。
  • 跨語言整合:核心邏輯是 Python,但有個 Java 寫的舊系統元件要當 agent 接進來。
  • 強制正式契約:一個多團隊供稿的平台,需要嚴格的契約確保各團隊貢獻的 agent 相容穩定。

不該用 A2A 的情境(改用 local sub-agent)

官方文件點名了四種常見誤用,拆成 A2A 反而不划算:

  • 內部程式碼組織:把單一 agent 內部的複雜任務拆成更好管理的函式或模組(例如一個在處理前清理輸入資料的 DataValidator sub-agent)——這種情境用 local sub-agent 效能與簡潔度都更好。
  • 效能敏感的內部操作:高頻、低延遲、跟主 agent 執行緊密耦合的操作(例如處理即時資料流的 RealTimeAnalytics sub-agent)。
  • 需要共享記憶體/context:sub-agent 需要直接存取主 agent 的內部 state 或共享記憶體以求效率時,A2A 的網路開銷與序列化/反序列化成本反而會拖累系統。
  • 簡單的 helper 函式:不需要獨立部署或複雜狀態管理的小段可重用邏輯,一個函式或類別遠比獨立的 A2A agent 合適。

ADK 裡的 A2A 工作流程:簡化版

ADK 把 A2A 協定的實作細節抽象成兩個核心概念:

  1. Exposing(讓 agent 可被存取):把一個既有的 ADK agent「暴露」出來,變成一台 A2A Server——概念上就像替你的 agent 架一台 web server。實際落地時你呼叫的是 to_a2a(),它把 agent 包成一個 Starlette app,再交給 uvicorn 服務起來;「A2A Server」是官方文件用來稱呼這台伺服器的概念名詞,不是一個你能 import 的類別。
  2. Consuming(連接一個可被存取的 agent):在另一支 agent(可以在同一台機器,也可以在不同機器)裡,用一個叫 RemoteA2aAgent 的元件當 client,它知道怎麼跟你剛剛暴露出來的服務溝通,底層的網路通訊、認證、資料格式轉換全部被抽象掉。

從開發者的角度看,一旦連接建立好,跟遠端 agent 互動的體驗跟呼叫本地工具或函式幾乎沒有分別——ADK 把網路層藏起來了,讓分散式 agent 系統用起來跟本地系統一樣直覺。

那協定本身到底長什麼樣?

A2A 的核心其實是一份 JSON——Agent Card。它掛在一個固定路徑 /.well-known/agent-card.json 上,是一支 agent 對外的自我介紹:叫什麼名字、會什麼技能(skills)、吃什麼輸入格式(defaultInputModes)、支不支援串流(capabilities)、要透過哪個網址與哪種傳輸協定跟它說話(supportedInterfaces)。用 to_a2a() 暴露 agent 時,ADK 會自動從你的 agent 程式碼(工具、描述、模型設定)產生這份卡片,不用自己手刻。

Day 20 你會親手跑起官方那個會滾骰子、check 質數的 hello_world_agent,逐字節錄一份實際拿到的卡片:

{
    "name": "hello_world_agent",
    "description": "hello world agent that can roll a dice of 8 sides and check prime numbers.",
    "supportedInterfaces": [
        {
            "url": "http://localhost:8001",
            "protocolBinding": "JSONRPC",
            "protocolVersion": "1.0"
        }
    ],
    "version": "0.0.1",
    "capabilities": {
        "streaming": false,
        "pushNotifications": false
    },
    "defaultInputModes": ["text/plain"],
    "defaultOutputModes": ["text/plain"],
    "skills": [
        { "id": "hello_world_agent", "name": "model", "tags": ["llm"] },
        { "id": "hello_world_agent-roll_die", "name": "roll_die", "tags": ["llm", "tools"] },
        { "id": "hello_world_agent-check_prime", "name": "check_prime", "tags": ["llm", "tools"] }
    ]
}

Consuming 端的 RemoteA2aAgent 要接上一支遠端 agent,第一步就是去抓這份卡片——抓到之後才知道該怎麼發請求、對方能不能做你要它做的事。這就是為什麼 A2A 敢說自己是「正式契約」:能力清單是機器可讀的 JSON,不是寫在 README 裡靠人工對齊。

A2A 支援的三個核心能力

ADK 的 A2A 整合針對複雜的 agent 系統提供三項能力保證:

  • Reasoning:模型產生的推理/思考軌跡(例如 Gemini 的 thinking 內容),跨過 A2A 的網路邊界傳給下一支 agent 時不會被丟掉,對方看到的推理過程跟同一個程序裡一樣完整。
  • Long-Running Tools:追蹤那些跑得比一般回應還久的工具呼叫,讓一個要等好幾分鐘才回來的遠端工具,不會因為看起來卡住太久就被系統誤判成執行失敗。
  • Artifacts:在 agent 之間傳遞檔案類型的 artifact(例如生成的檔案)。

一個具體案例:客服 agent 查產品目錄

假設你有一個 Customer Service Agent,需要向一個獨立的 Product Catalog Agent 查詢產品資訊。

在還沒導入 A2A 之前,如果 Product Catalog Agent 是一個由別的團隊維護的獨立服務,Customer Service Agent 沒有一個標準、直接的方式去查詢它。導入 A2A 之後,Product Catalog Agent 透過 A2A Server 把自己的能力暴露成一個服務,Customer Service Agent 則透過 RemoteA2aAgent 消費這個服務——用起來就像呼叫一個工具一樣自然,ADK 把底層通訊全部處理掉。這種設計讓職責分離清楚,也讓專職 agent 的整合變得容易。

Exposing 與 Consuming 的兩條路

進到實作層,ADK 把 A2A 拆成兩條獨立的學習路徑:

  • Exposing(把你的 agent 暴露出去):讓別人可以透過網路呼叫到你的 agent。
  • Consuming(讓你的 agent 使用別人暴露出來的遠端 agent):另外還有一份 A2A Extension,用改版過的 A2aAgentExecutor 修掉 A2A 與 ADK 同時開串流時的三個老問題:使用者訊息在 task history 裡重複、遠端 agent 的輸出被誤判成 thought、巢狀 sub-agent 的輸出遺失。踩到這三個症狀再回頭查它就好。

這兩條路徑不是二選一,大多數真實系統兩者都會用到:你可能同時暴露自己的某個 agent 給別人用,又消費另一個團隊暴露出來的服務。

銜接

理論講完了,Day 20 直接動手:先把一支 agent 用 to_a2a() 暴露成 A2A 服務,再從另一支 agent 用 RemoteA2aAgent 把它接起來——包含官方範例裡那個會滾骰子、check 質數的完整雙 agent demo。動手前先說一句:A2A 的元件不在 google-adk 的預設安裝裡,第一步會是 pip install "google-adk[a2a]"


Google ADK 官方網站
GitHub - Agent Development Kit (ADK) 2.0

GitHub 開源實作:https://github.com/SeanLinH/adk_tutor


上一篇
Day 18 | 打造一支 Agent 團隊:Collaborative Workflows 與 Agent Modes
下一篇
Day 20 - 動手接上遠端 Agent:A2A Exposing 與 Consuming
系列文
Google ADK Agent 教戰:30 天從原型到可上線的 AI Agent 系統21
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言