Lesson 6 — Tutoriels pas à pas

06. Un agent qui appelle un autre agent (créé sur Vertex AI / Agent Runtime)

4 min read

This content is not translated yet. Showing the original version.

Pour qui : développeurs d'agents, architectes.


1. Trois modèles

A. Appel par l'API Agent Runtime (outil)B. A2A via Agent Registry (RemoteA2aAgent)C. Sous-agent dans le même code
PrincipeL'orchestrateur a un outil qui interroge l'agent distant (runtimes.get(...).async_stream_query)L'orchestrateur charge l'agent distant comme sous-agent à partir de sa carte A2A trouvée dans le registreLes deux agents sont dans le même déploiement
L'agent appelé doit…être un agent Agent Runtime (n'importe quel AdkApp)parler A2A (Agent Runtime A2A, ou Cloud Run to_a2a)être dans le même dépôt
Cross-projectOuiOuiNon
Droitsroles/aiplatform.viewer sur l'agent appeléSelon l'hébergement (run.invoker, ou fournisseur d'authentification lié dans le registre)Aucun
Qui garde la mainL'orchestrateur (l'agent distant est un outil)L'agent distant prend la conversation pendant sa tâche—
Quand l'utiliserPar défaut entre équipes : simple, marche avec tout agent Agent Runtime existantAgents d'éditeurs différents, autres frameworks, interopérabilitéMême équipe, même cycle de vie

Ce projet implémente A (avec découverte par le registre), car c'est celui qui fonctionne avec les agents ADK déjà déployés par les équipes, sans leur demander de changer de protocole.


2. Modèle A, le code (agents/orchestrator/orchestrator_agent/agent.py)

2.1 Trouver l'agent dans le registre de l'autre équipe

@functools.cache                          # une seule recherche par démarrage
def stock_agent_name() -> str:
    if os.getenv("STOCK_AGENT"):          # repli : nom passé en dur par Terraform
        return os.environ["STOCK_AGENT"]
    agents = AgentRegistry(project_id=AGENTS_PROJECT, location=REGISTRY_LOCATION).list_agents().get("agents", [])
    match = next((a for a in agents if a.get("displayName") == "stock-agent"), None)
    return urn_to_resource(match["agentId"])

Un agent Agent Runtime s'enregistre automatiquement dans le registre de son projet. Son identifiant est un URN :

urn:agent:projects-123:projects:123:locations:europe-west1:reasoningEngines:456
                                  ↓ urn_to_resource()
projects/123/locations/europe-west1/reasoningEngines/456

Pourquoi passer par le registre : l'équipe Supply peut redéployer ou recréer son agent (nouvel ID) sans que l'équipe Magasins change une ligne. Et un agent absent du registre n'est pas utilisable : la gouvernance est appliquée par construction.

2.2 L'appeler

async def ask_stock_agent(question: str, tool_context: ToolContext) -> dict:
    name = stock_agent_name()
    _, project, _, location, *_ = name.split("/")
    remote = agentplatform.Client(project=project, location=location).runtimes.get(name=name)
    session = await remote.async_create_session(user_id=tool_context.user_id)
    async for event in remote.async_stream_query(user_id=tool_context.user_id, session_id=session["id"], message=question):
        ...                                   # on garde le texte des réponses
    return {"status": "success", "answer": ...}
  • L'appel part avec l'identité de l'orchestrateur (son compte de service sur Agent Runtime).
  • L'utilisateur final est transmis (user_id) : l'agent appelé le voit dans ses journaux (traçabilité), mais il n'agit pas avec ses droits. Si l'agent appelé doit accéder à des données au nom de l'utilisateur, il faut OAuth (ressource d'autorisation Gemini Enterprise), pas ce modèle.
  • Erreur (droit manquant, agent supprimé) → l'outil renvoie status: error, l'orchestrateur l'explique au lieu de planter.

2.3 Les droits (terraform/20_orchestrator.tf)

# Posé SUR l'agent stock, dans le projet agents : appeler CET agent, et seulement lui.
resource "google_vertex_ai_reasoning_engine_iam_member" "orchestrator_calls_stock" {
  provider         = google.agents
  reasoning_engine = module.stock_agent.name
  role             = "roles/aiplatform.viewer"     # contient reasoningEngines.get + .query
  member           = module.orchestrator.service_account
}

# Lire le registre du projet agents (pour la découverte).
resource "google_project_iam_member" "orchestrator_reads_registry" {
  provider = google.agents
  role     = "roles/agentregistry.viewer"
  member   = module.orchestrator.service_account
}

Pourquoi un droit sur l'agent et pas sur le projet : aiplatform.user sur le projet permettrait d'appeler (et de modifier, supprimer, créer) tous les agents de l'équipe Supply. Ici, l'orchestrateur ne peut qu'interroger stock-agent.

2.4 Le prouver

python scripts/test_agents.py                          # hors ligne : découverte, URN, appel, échec propre
python scripts/query.py orchestrator "Il reste des tentes 8771234 à Lyon ?"
#   -> outil : ask_stock_agent(...)
# Preuve négative : retirer le droit sur l'agent (en recette) -> l'orchestrateur répond « Agent stock indisponible : 403 »

3. Modèle B, A2A par le registre (variante)

Si l'agent appelé parle A2A et que sa carte est dans le registre, ADK le transforme en sous-agent en une ligne :

from google.adk.integrations.agent_registry import AgentRegistry

registry = AgentRegistry(project_id="acme-agents-supply", location="europe-west1")
stock = registry.get_remote_a2a_agent("projects/…/locations/europe-west1/agents/stock-agent")   # -> RemoteA2aAgent
root_agent = Agent(name="assistant_magasin", ..., sub_agents=[stock])
  • L'authentification est résolue depuis les liaisons (bindings) du registre, qui associent un agent source, une cible, et un fournisseur d'authentification (google_agent_registry_binding en Terraform).
  • Rendre un agent ADK compatible A2A : sur Cloud Run, google.adk.a2a.utils.agent_to_a2a.to_a2a(root_agent) ; sur Agent Runtime, le modèle agentplatform.frameworks.a2a.A2aAgent.
  • À choisir quand les agents viennent d'éditeurs ou de frameworks différents.

4. Bonnes pratiques entre agents

PratiquePourquoi
Une identité par agent, droits sur l'agent appelé uniquementUn orchestrateur compromis ne compromet pas tout le projet d'en face
Model Armor dans chaque agentLa réponse d'un agent est une entrée pour l'autre : une injection peut se propager
L'agent appelé reste spécialisé et en lectureLes actions qui modifient restent avec confirmation humaine (require_confirmation)
Descriptions précisesLe modèle choisit l'outil ou l'agent sur la description
Délai et erreurs gérés dans l'outilUn agent lent ou absent ne doit pas bloquer la conversation
Sortie par Agent Gateway quand elle est en placeLes appels inter-agents sont journalisés et soumis aux politiques
Éviter les cycles (A appelle B qui appelle A)Coûts et boucles infinies
Next