建立 Agent2Agent 代理程式

您可以使用 Agent Runtime,透過 Agent2Agent (A2A) 通訊協定開發及部署代理。A2A 是一項開放標準,旨在讓 AI 代理流暢地通訊及協作。

本文說明如何在本機開發及測試 A2A 代理程式,包括定義 AgentCardAgentExecutor 等元件。

如要進一步瞭解如何管理已部署的代理程式,請參閱「管理已部署的代理程式」。

核心工作流程包含下列步驟:

  1. 定義主要元件
  2. 建立本機代理程式
  3. 測試本機代理

定義代理程式元件

如要建立 A2A 代理,您需要定義下列元件:AgentCardAgentExecutor 和 ADK LlmAgent

  • AgentCard 包含中繼資料文件,說明代理程式的功能。AgentCard 就像商家名片,其他代理可以藉此探索你的代理能做什麼。詳情請參閱代理資訊卡規格
  • AgentExecutor 包含代理的核心邏輯,並定義代理處理工作的方式。您可以在這裡實作代理程式的行為。詳情請參閱 A2A 通訊協定規格
  • 選用:LlmAgent 定義 ADK 代理程式,包括系統指令、生成模型和工具。

定義 AgentCard

下列程式碼範例會為匯率代理程式定義 AgentCard

from a2a.types import AgentCard, AgentSkill
from vertexai.agent_engines.templates.a2a import create_agent_card

# Define the skill for the CurrencyAgent
currency_skill = AgentSkill(
    id='get_exchange_rate',
    name='Get Currency Exchange Rate',
    description='Retrieves the exchange rate between two currencies on a specified date.',
    tags=['Finance', 'Currency', 'Exchange Rate'],
    examples=[
        'What is the exchange rate from USD to EUR?',
        'How many Japanese Yen is 1 US dollar worth today?',
    ],
)

# Create the agent card using the utility function
agent_card = create_agent_card(
    agent_name='Currency Exchange Agent',
    description='An agent that can provide currency exchange rates',
    skills=[currency_skill]
)

定義 AgentExecutor

下列程式碼範例定義 AgentExecutor,該函式會傳回匯率。這個函式會採用 CurrencyAgent 例項,並初始化 ADK Runner 來執行要求。

import requests
from a2a.server.agent_execution.agent_executor import AgentExecutor
from a2a.server.agent_execution.context import RequestContext
from a2a.server.events.event_queue import EventQueue
from a2a.server.tasks import TaskUpdater
from a2a import types as a2a_types
from a2a.types import Part

from google.adk import Runner
from google.adk.agents import LlmAgent
from google.adk.artifacts.in_memory_artifact_service import InMemoryArtifactService
from google.adk.memory.in_memory_memory_service import InMemoryMemoryService
from google.adk.sessions.in_memory_session_service import InMemorySessionService
from google.genai import types as genai_types

class CurrencyAgentExecutorWithRunner(AgentExecutor):
    """Executor that takes an LlmAgent instance and initializes the ADK Runner internally."""

    def __init__(self, agent: LlmAgent):
        self.agent = agent
        self.runner = None

    def _init_adk(self):
        if not self.runner:
            self.runner = Runner(
                app_name=self.agent.name,
                agent=self.agent,
                artifact_service=InMemoryArtifactService(),
                session_service=InMemorySessionService(),
                memory_service=InMemoryMemoryService(),
            )

    async def cancel(self, context: RequestContext, event_queue: EventQueue) -> None:
        task_id = context.task_id
        updater = TaskUpdater(
            event_queue=event_queue,
            task_id=task_id or "",
            context_id=context.context_id or "",
        )
        await updater.cancel()

    async def execute(
        self,
        context: RequestContext,
        event_queue: EventQueue,
    ) -> None:
        self._init_adk() # Initialize on first execute call

        if not context.message:
            return

        user_id = context.message.metadata.get('user_id') if context.message and context.message.metadata else 'a2a_user'

        updater = TaskUpdater(event_queue, context.task_id, context.context_id)
        
        task = a2a_types.Task(
            id=context.task_id,
            context_id=context.context_id,
            status=a2a_types.TaskStatus(state=a2a_types.TaskState.TASK_STATE_SUBMITTED),
            history=[context.message] if context.message else [],
        )
        await event_queue.enqueue_event(task)

        await updater.start_work()

        query = context.get_user_input()
        content = genai_types.Content(role='user', parts=[genai_types.Part.from_text(text=query)])

        try:
            session = await self.runner.session_service.get_session(
                app_name=self.runner.app_name,
                user_id=user_id,
                session_id=context.context_id,
            ) or await self.runner.session_service.create_session(
                app_name=self.runner.app_name,
                user_id=user_id,
                session_id=context.context_id,
            )

            final_event = None
            async for event in self.runner.run_async(
                session_id=session.id,
                user_id=user_id,
                new_message=content
            ):
                if event.is_final_response():
                    final_event = event

            if final_event and final_event.content and final_event.content.parts:
                response_text = "".join(
                    part.text for part in final_event.content.parts if hasattr(part, 'text') and part.text
                )
                if response_text:
                    await updater.add_artifact(
                        [Part(text=response_text)],
                        name='result',
                        last_chunk=True,
                    )
                    await updater.complete()
                    return

            await updater.update_status(
                a2a_types.TaskState.TASK_STATE_FAILED,
                message=updater.new_agent_message([Part(text='Failed to generate a final response with text content.')]),
            )

        except Exception as e:
            await updater.update_status(
                a2a_types.TaskState.TASK_STATE_FAILED,
                message=updater.new_agent_message([Part(text=f"An error occurred: {str(e)}")]),
            )

定義 LlmAgent

首先,請定義 LlmAgent 要使用的貨幣匯率工具:

def get_exchange_rate(
    currency_from: str = "USD",
    currency_to: str = "EUR",
    currency_date: str = "latest",
):
    """Retrieves the exchange rate between two currencies on a specified date.
    Uses the Frankfurter API (https://api.frankfurter.app/) to obtain
    exchange rate data.
    """
    try:
        response = requests.get(
            f"https://api.frankfurter.app/{currency_date}",
            params={"from": currency_from, "to": currency_to},
        )
        response.raise_for_status()
        return response.json()
    except requests.exceptions.RequestException as e:
        return {"error": str(e)}

接著,定義使用該工具的 ADK LlmAgent

my_llm_agent = LlmAgent(
    model='gemini-2.0-flash',
    name='currency_exchange_agent',
    description='An agent that can provide currency exchange rates.',
    instruction="""You are a helpful currency exchange assistant.
                   Use the get_exchange_rate tool to answer user questions.
                   If the tool returns an error, inform the user about the error.""",
    tools=[get_exchange_rate],
)

建立本機代理程式

定義代理程式的元件後,請建立 A2aAgent 類別的執行個體,使用 AgentCardAgentExecutorLlmAgent 開始進行本機測試。

from vertexai.agent_engines.templates.a2a import A2aAgent

a2a_agent = A2aAgent(
    agent_card=agent_card, # Assuming agent_card is defined
    agent_executor_builder=lambda: CurrencyAgentExecutorWithRunner(
        agent=my_llm_agent,
    )
)
a2a_agent.set_up()

A2A 代理範本可協助您建立符合 A2A 規範的服務。這項服務會做為包裝函式,將轉換層從您身上抽象化。

測試本機代理

匯率代理程式支援下列三種方法:

  • handle_authenticated_agent_card
  • on_message_send
  • on_get_task

測試 handle_authenticated_agent_card

下列程式碼會擷取代理程式的已驗證資訊卡,其中說明代理程式的功能。

# Test the `authenticated_agent_card` endpoint.
response_get_card = await a2a_agent.handle_authenticated_agent_card(request=None, context=None)
print(response_get_card)

測試 on_message_send

以下程式碼會模擬用戶端傳送新訊息給服務專員。A2aAgent 會建立新工作,並傳回工作 ID。

from a2a.types import SendMessageRequest, Message, Part
from a2a.server.context import ServerCallContext

# 1. Define the message
message = Message(
    role="ROLE_USER",
    message_id="local-test-message-id",
    parts=[Part(text="What is the exchange rate from USD to EUR today?")]
)

# 2. Construct the request
request = SendMessageRequest(message=message)

# 3. Construct context
context = ServerCallContext()

# 4. Call the agent
send_message_response = await a2a_agent.on_message_send(request=request, context=context)

print(send_message_response)

測試 on_get_task

下列程式碼會擷取工作狀態和結果。輸出內容會顯示工作已完成,並包含「Hello World」回應構件。

from a2a.types import GetTaskRequest

# 1. Provide the task_id from the previous step.
# In a real application, you would store and retrieve this ID.
task_id_to_get = send_message_response.id

# 2. Construct the request
request = GetTaskRequest(id=task_id_to_get)

# 3. Call the agent's handler to get the task status.
# Reusing the context constructed in the previous step
task_status_response = await a2a_agent.on_get_task(request=request, context=context)

print(f"Successfully retrieved status for Task ID: {task_id_to_get}")
print("\nFull task status response:")
print(task_status_response)

後續步驟

指南

瞭解如何根據開發需求,在 Agent Platform Runtime 部署代理程式。

指南

使用 Agent Platform Runtime 搭配 Agent2Agent 代理。

指南

建立及部署基本代理,並使用 Gen AI Evaluation Service 評估代理

疑難排解

瞭解如何解決建立自訂代理程式時的常見錯誤。

資源

尋找 Google Agent Platform 的相關資源和支援。

資源

在 GitHub 上探索 Python 的 Agent2Agent 範例。