Visão geral
A comunicação agente para agente (A2A) permite que um agente descobrir e invocar outros agentes no mesmo projeto no tempo de execução. O Mecanismo de Orquestração (OE) intermediários cada chamada executando as seguintes ações:
Valida o acesso
Cria uma execução secundária
Despacha a solicitação para o agente de destino
Retorna o resultado
As garantias humanos no loop (HITL) se aplicam de ponta a ponta. Um agente de destino que suspende a revisão humana pausa a chamada em vez de retornar uma falha.
A2A segue esta sequência quando você a ativa:
Você adiciona um bloco
a2a:ao seu arquivoagent.yaml. Em sua primeira execução, a plataforma registra a configuração do agente com o OE, tornando o agente detectável e chamado por outros agentes no mesmo projeto.Quando a OE despacha uma execução para seu agente, ela injeta um token de curta duração no contexto de execução. Seu agente usa esse token de forma transparente ao ligar para outros agentes.
No código do agente , você expõe o A2A como ferramentas LLM com
app.a2a_tools()(recomendado) ou chama o cliente A2A diretamente por meio deapp._runtime.a2apara roteamento determinístico.A OE impõe o controle de acesso, vincula a execução do filho ao pai, despacha para o destino e retorna o resultado.
Habilite o A2A no .yaml do agente
Adicione uma seção a2a: ao seu arquivo agent.yaml para tornar seu agente detectável e chamável. Se você omitir essa seção ou definir a2a.enabled como false, seu agente permanecerá invisível para os chamadores A2A e será totalmente compatível com versões anteriores dos sistemas existentes.
Observação
Para testar o A2A localmente, defina a variável de ambiente A2A_JWT_SECRET com o mesmo valor no arquivo .env de cada agente que chama ou é chamado por meio do A2A.
O exemplo a seguir mostra uma seção a2a: totalmente configurada:
name: insurance-agent entrypoint: insurance_agent.main:app a2a: enabled: true allowed_callers: - "ws-router-002" skills: - name: policy-lookup description: Look up insurance policy details example_input: '{"policy_id": "POL-123"}' example_output: '{"status": "active", "premium": 240}' - name: get-quote description: Generate an insurance quote input_modes: - "text/plain" output_modes: - "text/plain"
Campos A2A
A tabela a seguir descreve os campos na seção a2a: do arquivo agent.yaml:
Campo | Tipo | Default | Descrição |
|---|---|---|---|
| bool |
| Torna o agente detectável e consultável por meio da A2A. Quando |
| list[str] |
| IDs de espaço de trabalho permitidas para invocar este agente. Uma lista vazia permite qualquer agente habilitado para A2A no mesmo projeto. Uma lista não vazia atua como uma lista de permissões. |
| list[object] |
| Qualificações que o agente anuncia para descoberta. Cada entrada exige um |
| list[str] |
| Tipos de extensões multifuncionais de correio da Internet (MIME) que o agente aceita, por exemplo |
| list[str] |
| Tipos de MIME que o agente produz. Usado como filtro de descoberta. |
Observação
Os name, skills, input_modes, output_modes e allowed_callers do agente são automaticamente enviados para o OE na inicialização. A descrição de texto livre e as marcações de recursos que aparecem nos resultados da descoberta vêm do AgentCard do espaço de trabalho que você configura por meio dos pontos de extremidade do espaço de trabalho do API Gateway, não do agent.yaml.
Ligar para outros agentes
Você pode chamar outros agentes a partir do código do agente de duas maneiras: expondo o A2A como ferramentas LLM (recomendadas) ou usando o cliente A2A diretamente. Ambos usam o mesmo cliente subjacente . Escolha sua abordagem com base no fato de desejar que o LLM conduza a decisão de roteamento ou se você precisa de um controle determinístico.
Use A2A como Ferramentas LLM
O método app.a2a_tools() retorna dois objetos LangChain StructuredTool: discover_available_agents e invoke_a2a_agent. Esses objetos permitem que o LLM descobrir e chamar outros agentes de forma autônoma. Vincule-os às suas ferramentas existentes para que o modelo possa rotear para outros agentes por conta própria.
O método app.a2a_tools() retorna uma lista vazia quando a2a.enabled está false em seu arquivo agent.yaml, portanto é seguro incluir em todos os agentes.
O exemplo a seguir vincula as ferramentas A2A às ferramentas existentes:
from agent_engine_sdk_langgraph import App from langchain_openai import ChatOpenAI from langgraph.graph import StateGraph from langgraph.prebuilt import ToolNode app = App(app_name="router-agent") def build_agent(): llm = app.llm(ChatOpenAI(model="gpt-5.4")) a2a = app.a2a_tools() llm_with_tools = llm.bind_tools( app.get_tool_schemas() + a2a ) tool_node = ToolNode(app.get_tools() + a2a) graph = StateGraph(...) return graph.compile(checkpointer=app.checkpointer())
Quando vinculado ao LLM, o modelo chama as duas ferramentas pelo nome. As ferramentas expõem as seguintes assinaturas de função:
discover_available_agents()retorna uma lista JSON de agentes visíveis para o agente de chamada. Cada entrada incluiagent_id,name,description,skillsecapabilities. O agente de chamada é filtrado a partir de seus próprios resultados.invoke_a2a_agent(agent_id, message, custom_headers="")invoca o agente de destino e retorna o resultado como JSON. Se você fornecercustom_headers, passe-a como uma string JSON, por exemplo'{"x-tenant": "acme"}'.
Ligar diretamente para o cliente A2A
When you want to use deterministic routing, you can access the AgentToAgent client directly through runtime.a2a, an attribute that exposes the client.
O exemplo a seguir descobre os agentes por habilidade e invoca a primeira correspondência:
agents = app._runtime.a2a.discover_agents( skills=["policy-lookup"], limit=10, ) if agents: resp = app._runtime.a2a.invoke_agent( agent_id=agents[0].agent_id, message="Look up policy POL-123", skill="policy-lookup", timeout=120, ) if resp.status == "completed": print(resp.result)
O atributo runtime.a2a retorna None quando A2A não está disponível. Para saber quando isso ocorre, consulte Considerações. Sempre verifique esse caso antes de chamar os métodos do cliente , como mostrado no exemplo a seguir:
a2a = app._runtime.a2a if a2a is None: ...
Observação
Acesse runtime.a2a no código do agente por meio de app._runtime.a2a. Não existe nenhum acessador app.a2a ou app.runtime público. Para a maioria dos casos de uso, app.a2a_tools() é a alternativa recomendada porque não depende de um atributo privado.
A2A Client SDK Reference
Nesta seção, você pode aprender sobre o SDK do cliente A2A, que fornece métodos para descobrir e invocar agentes programaticamente a partir do código do agente .
Para usar o SDK do cliente , importe os tipos A2A de agent_engine_runner_shared.a2a, conforme mostrado no exemplo a seguir:
from agent_engine_runner_shared.a2a import ( AgentToAgent, DiscoveredAgent, AgentSkill, AgentResponse, )
Classe AgentToAgent
AgentToAgent é um cliente HTTP que se comunica com os endpoints OE /a2a/discover e /a2a/invoke. No código do agente , use a instância pré-autenticada de runtime.a2a em vez de construir essa classe diretamente.
O exemplo a seguir mostra o construtor e seus parâmetros:
AgentToAgent(oe_url: str, auth_token: str = "")
A tabela a seguir descreve os parâmetros do construtor AgentToAgent:
Argument | Descrição |
|---|---|
| URL base HTTP do OE, por exemplo |
| A2A JSON Web Token (JWT) enviado como |
Método find_agents()
O método discover_agents() retorna agentes que o chamador tem permissão para ver. Quando você passa vários valores em um único parâmetro de filtro, um agente corresponde se satisfizer qualquer um desses valores. Por exemplo, skills=["a", "b"] retorna agentes anunciando qualquer habilidade. Quando você passa vários parâmetros de filtro, um agente deve satisfazer todos eles. Os agentes que não são habilitados para A2A e excluem o chamador por meio de allowed_callers nunca são retornados.
O exemplo a seguir mostra este método e seus parâmetros:
discover_agents( project_id: str = "", skills: list[str] | None = None, capabilities: list[str] | None = None, input_modes: list[str] | None = None, limit: int = 50, ) -> list[DiscoveredAgent]
A tabela seguinte descreve os parâmetros para o método discover_agents():
Parâmetro | Tipo | Descrição |
|---|---|---|
| str | Projeto para pesquisar. Uma string vazia usa o projeto do chamador. |
| list[str] | Filtrar por nome da habilidade. Retorna agentes que tenham qualquer uma das habilidades listadas. |
| list[str] | Filtrar por marcação de capacidade. Retorna agentes que têm qualquer um dos recursos listados. |
| list[str] | Filtre por tipo de MIME aceito. Retorna agentes que aceitam qualquer um dos tipos de MIME listados. |
| int | Número máximo de resultados a retornar. O padrão é |
Método invoke_agent()
O método invoke_agent() invoca um agente de destino e espera o resultado. O OE valida o token, verifica o controle de acesso, cria uma execução secundária e pesquisa até que o agente termine, suspenda para revisão humana ou expire.
O exemplo a seguir mostra este método e seus parâmetros:
invoke_agent( agent_id: str, message: str, skill: str = "", timeout: float | None = None, custom_headers: dict[str, str] | None = None, ) -> AgentResponse
A tabela seguinte descreve os parâmetros para o método invoke_agent():
Parâmetro | Tipo | Descrição |
|---|---|---|
| str | ID do espaço de trabalho de destino. Obtenha a partir de |
| str | Solicitação ou mensagem a ser enviada ao agente de destino. |
| str | Dica de habilidade opcional para agentes que anunciam muitas habilidades. |
| flutuar | none | Segundos para esperar antes de atingir o tempo limite. |
| dict[str, str] | None | Cabeçalhos de valor-chave encaminhados para o contexto de execução do agente de destino. As chaves não devem usar o prefixo |
Classes de dados de resposta
A tabela a seguir lista os tipos de resposta, que são todas classes de dados imutáveis:
classe | Campos |
|---|---|
|
|
|
|
|
|
Lidar com o status de resposta do agente
A tabela a seguir descreve cada valor possível para o campoAgentResponse.status :
Status | Significado | O que fazer |
|---|---|---|
| O agente de destino concluiu com sucesso. | Leia |
| O agente de destino retornou um erro. | Leia |
| O agente alvo suspenso para revisão humana. | Apresentar a pausa para seu usuário ou fluxo. Não trate isso como um fracasso. |
Erros de nível de rede e HTTP, como tempos limite ou 4xx e respostas 5xx do OE, elevam um httpx.HTTPError dos métodos discover_agents() e invoke_agent(). Um AgentResponse que tem um valor status de failed indica que a chamada atingiu o OE, mas o próprio agente de destino falhou. Para lidar com erros de rede e falhas de agente , envolva as chamadas em blocos try/except e verifique o valor status.
O exemplo a seguir lida com todos os três valores de status:
resp = app._runtime.a2a.invoke_agent( agent_id="ws-billing-001", message="Process the payment", ) if resp.status == "completed": handle(resp.result) elif resp.status == "input-required": notify_user( "The billing agent needs human approval before continuing." ) else: log.error("A2A call failed: %s", resp.error)
Encaminhar cabeçalhos personalizados
Você pode usar o A2A para anexar cabeçalhos de valor-chave arbitrários a uma invocação de agente para propagar o contexto do escopo da solicitação, como um ID de locatário, uma tag de rastreamento ou um token OAuth delegado.
Passe cabeçalhos para um agente chamado
Para anexar cabeçalhos a um agente chamado, passe um dict usando o parâmetro custom_headers de invoke_agent(). Ao utilizar a ferramenta LLM invoke_a2a_agent, passe cabeçalhos como uma string JSON.
O exemplo a seguir passa cabeçalhos personalizados usando o cliente direto :
app._runtime.a2a.invoke_agent( agent_id="ws-billing-001", message="Charge the customer", custom_headers={ "x-tenant": "acme", "oauth-token": "<delegated-token>", }, )
O exemplo a seguir passa cabeçalhos personalizados usando a ferramenta LLM:
invoke_a2a_agent( agent_id="ws-billing-001", message="Charge the customer", custom_headers='{"x-tenant": "acme"}', )
As chaves de cabeçalho não devem usar o prefixo reservado a2a- insensível a maiúsculas e minúsculas. A OE rejeita solicitações com uma resposta 400 Bad Request se alguma chave começar com a2a-. O prefixo a2a- é reservado para roteamento de plataforma e cabeçalhos de identidade como a2a-token e a2a-caller-workspace.
Ler cabeçalhos de um chamador
Para ler cabeçalhos personalizados encaminhados por um agente de chamada, use o método get_current_custom_headers() da classeagent_engine_runner_shared.context. O exemplo a seguir chama este método:
from agent_engine_runner_shared.context import get_current_custom_headers headers = get_current_custom_headers() tenant = headers.get("x-tenant")
get_current_custom_headers() retorna um dict[str, str]. O método remove todos os cabeçalhos de plataforma a2a- antes de retornar, para que o código do agente nunca veja tokens A2A ou metadados de roteamento. Se o chamador não enviar cabeçalhos personalizados, o método retornará um dicionário vazio.
Configurar controle de acesso
O campo a2a.allowed_callers em seu arquivo agent.yaml controla quais agentes podem descobrir seu por meio do seguinte comportamento:
Lista vazia (padrão): qualquer agente habilitado para A2A no mesmo projeto pode invocar seu agente e vê-lo nos resultados da descoberta.
Lista não vazia: somente os IDs de workspace listados podem invocar seu agente. Todos os outros agentes recebem uma resposta
403e não podem ver seu agente nos resultados da descoberta.
O OE verifica a identidade do agente de chamada em sua lista allowed_callers antes de enviar. Nenhuma alteração de código é necessária em seu agente. O exemplo a seguir restringe a invocação a um único agente roteador:
a2a: enabled: true allowed_callers: - "ws-router-002"
Visualizar chamadas A2A na interface do usuário
Cada chamada A2A aparece automaticamente no painel de rastreamento da sessão. Nenhuma instrumentação é necessária. A atividade do agente de destino aparece aninhada no mesmo rastreamento de sessão, para que você possa seguir a árvore de chamadas completa entre agentes em um só lugar.
Cada invocação é renderizada como um nó de subagente rotulado A2A: <target agent name>, retornando ao ID do espaço de trabalho do destino quando não tem nome de exibição. O nó mostra o status e a duração da chamada. O status começa como running e, em seguida, muda para done em caso de sucesso ou error em caso de falha. A seleção de um nó abre um painel de detalhes mostrando o nome, status, duração, descrição de despacho, saída transmitida e quaisquer erros.
As chamadas de ferramentas do agente de destino, as etapas do LLM e as operações de memória aparecem aninhadas no nó do subagente, reconstruindo a árvore de chamadas completa entre os agentes.
Considerações
Quando runtime.a2a não retorna nenhum
runtime.a2a retorna um cliente somente quando todas as seguintes condições forem verdadeiras:
O código do agente está sendo executado na sandbox do agente . A2A não está disponível para código executado na sandbox da ferramenta.
Uma URL OE está presente no contexto de execução.
Um
a2a-tokenestá presente nos cabeçalhos recebidos. O OE injeta esse token automaticamente em cada despacho quando o A2A é configurado.
Quando qualquer condição não é atendida, runtime.a2a retorna None. Ao usar app.a2a_tools(), as ferramentas retornam um JSON de erro estruturado em vez de gerar uma exceção.
Vida útil do token
Por padrão, o token A2A expira após 5 minutos. Para fluxos típicos de solicitação e resposta, isso é suficiente. Os agentes que funcionam por mais tempo do que a vida útil do token podem receber uma resposta 401 nas chamadas A2A. A atualização automática do token ainda não foi implementada. Trate uma resposta 401 de uma chamada de longa duração como uma falha transitória.
Filtro de autodescoberta
A ferramenta LLM discover_available_agents filtra o espaço de trabalho do próprio agente de chamada dos resultados para que o modelo não chame a si mesmo. Se você usar o método bruto do cliente discover_agents(), o OE poderá incluir seu próprio agente nos resultados. Filtre você mesmo, se necessário.
Escopo Intra-Projeto
Na versão atual, a descoberta e a invocação de A2A são limitadas aos agentes no mesmo projeto. O roteamento entre projetos ainda não foi implementado.
Observabilidade
A plataforma registra e vincula todas as chamadas A2A automaticamente. Você não precisa adicionar instrumentação. O destino é executado como uma execução filho vinculada ao chamador por meio de parent_execution_id. Um root_session_id compartilhado agrupa a árvore completa de chamadas entre agentes na sessão do usuário de origem.
Exemplo: Agente de roteador
O exemplo a seguir mostra um agente roteador que usa o LLM para localizar e delegar a um agente especialista. O arquivo agent.yaml habilita A2A e declara uma habilidade route:
name: router-agent entrypoint: router_agent.main:app a2a: enabled: true skills: - name: route description: Route a user request to the best specialist agent
O código do agente a seguir vincula as ferramentas A2A às suas próprias ferramentas e permite que o LLM cuide do roteamento:
from agent_engine_sdk_langgraph import App from langchain_openai import ChatOpenAI from langgraph.graph import StateGraph, MessagesState from langgraph.prebuilt import ToolNode app = App(app_name="router-agent") def build_agent(): llm = app.llm(ChatOpenAI(model="gpt-5.4")) a2a = app.a2a_tools() llm_with_tools = llm.bind_tools( app.get_tool_schemas() + a2a ) def call_model(state: MessagesState): return { "messages": [llm_with_tools.invoke(state["messages"])] } graph = StateGraph(MessagesState) graph.add_node("model", call_model) graph.add_node("tools", ToolNode(app.get_tools() + a2a)) graph.set_entry_point("model") graph.add_conditional_edges( "model", lambda s: ( "tools" if s["messages"][-1].tool_calls else "__end__" ), ) graph.add_edge("tools", "model") return graph.compile(checkpointer=app.checkpointer())
No tempo de execução, o LLM descobre um agente especialista que anuncia uma habilidade correspondente, chama-o por meio de invoke_a2a_agent, e o OE agencia a chamada enquanto impõe o controle de acesso e registra ambos os lados da invocação.
Para rotear deterministicamente em vez de depender do LLM, chame o cliente diretamente dentro de um nó do grafo:
def delegate(state): a2a = app._runtime.a2a if a2a is None: return {"messages": [("ai", "A2A is unavailable.")]} resp = a2a.invoke_agent( agent_id="ws-insurance-001", message=state["messages"][-1].content, skill="policy-lookup", custom_headers={"x-request-source": "router-agent"}, ) text = ( resp.result if resp.status == "completed" else f"({resp.status}) {resp.error}" ) return {"messages": [("ai", text)]}
Próximos passos
Depois de ativar a comunicação de agente para agente, você pode explorar os seguintes guias para saber mais sobre tarefas relacionadas:
Para monitorar o desempenho do seu agente e a integridade da sua implementação,consulte Monitorar o agente.
Para autenticar e invocar um agente implementado,consulte Invocar um agente.
Para testar seu agente e iterar em seu código,consulte Testar seu agente.