Overview
La comunicación entre agentes (A2A) permite que un agente descubra e invoque a otros agentes en el mismo proyecto en tiempo de ejecución. El motor de orquestación (OE) gestiona cada llamada realizando las siguientes acciones:
Valida el acceso
Crea una ejecución secundaria
Envía la solicitud al agente de destino.
Devuelve el resultado
Las garantías de intervención humana (HITL, por sus siglas en inglés) se aplican de extremo a extremo. Un agente de destino que suspende la llamada para su revisión humana la pausa en lugar de devolver un error.
A2A sigue esta secuencia cuando lo habilitas:
Se añade un bloque
a2a:al archivoagent.yaml. En su primera ejecución, la plataforma registra la configuración del agente en el OE, lo que permite que otros agentes del mismo proyecto puedan descubrir y llamar al agente.Cuando el OE envía una ejecución a su agente, inyecta un token de corta duración en el contexto de ejecución. Su agente utiliza ese token de forma transparente al llamar a otros agentes.
Desde el código de su agente, expone A2A como herramientas LLM con
app.a2a_tools()(recomendado) o llama al cliente A2A directamente a través deapp._runtime.a2apara un enrutamiento determinista.El OE aplica el control de acceso, vincula la ejecución secundaria con la principal, la envía al destino y devuelve el resultado.
Habilitar A2A en agent.yaml
Agregue una sección a2a: a su archivo agent.yaml para que su agente sea detectable y se pueda invocar. Si omite esta sección o establece a2a.enabled en false, su agente permanecerá invisible para las llamadas A2A y será totalmente compatible con versiones anteriores de las implementaciones existentes.
Nota
Para probar A2A localmente, establezca la variable de entorno A2A_JWT_SECRET con el mismo valor en el archivo .env de cada agente que llame o sea llamado a través de A2A.
El siguiente ejemplo muestra una sección a2a: completamente 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
La siguiente tabla describe los campos de la sección a2a: del archivo agent.yaml:
Campo | Tipo | predeterminado | Descripción |
|---|---|---|---|
| bool |
| Hace que el agente sea detectable y llamable a través de A2A. Cuando |
| list[str] |
| Los ID de espacio de trabajo tienen permiso para invocar a este agente. Una lista vacía permite cualquier agente habilitado para A2A en el mismo proyecto. Una lista no vacía actúa como una lista de permitidos. |
| list[object] |
| Habilidades que el agente anuncia para su descubrimiento. Cada entrada requiere un campo |
| list[str] |
| Tipos de extensiones de correo de Internet multipropósito (MIME) que acepta el agente, por ejemplo |
| list[str] |
| Tipos MIME que produce el agente. Se utiliza como filtro de detección. |
Nota
Los identificadores name, skills, input_modes, output_modes y allowed_callers del agente se envían automáticamente al OE al iniciar. La descripción de texto libre y las etiquetas de capacidades que aparecen en los resultados de detección provienen de la tarjeta de agente del espacio de trabajo que se configura a través de los puntos finales del espacio de trabajo de API Gateway, no de agent.yaml.
Llama a otros agentes
Puedes llamar a otros agentes desde tu código de agente de dos maneras: exponiendo A2A como herramientas LLM (recomendado) o usando el cliente A2A directamente. Ambos métodos utilizan el mismo cliente subyacente. Elige el enfoque que prefieras según si deseas que LLM gestione la decisión de enrutamiento o si necesitas un control determinista.
Utilice A2A como herramienta LLM
El método app.a2a_tools() devuelve dos objetos LangChain StructuredTool: discover_available_agents y invoke_a2a_agent. Estos objetos permiten que el LLM descubra y llame a otros agentes de forma autónoma. Intégrelos con sus herramientas existentes para que el modelo pueda enrutar a otros agentes por sí mismo.
El método app.a2a_tools() devuelve una lista vacía cuando a2a.enabled es false en su archivo agent.yaml, por lo que es seguro incluirlo en todos los agentes.
El siguiente ejemplo vincula las herramientas A2A con las herramientas 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())
Cuando se vincula al LLM, el modelo llama a las dos herramientas por su nombre. Las herramientas exponen las siguientes firmas de función:
discover_available_agents()Devuelve una lista JSON de agentes visibles para el agente que realiza la llamada. Cada entrada incluyeagent_id,name,description,skillsycapabilities. El agente que realiza la llamada se filtra a partir de sus propios resultados.invoke_a2a_agent(agent_id, message, custom_headers="")Invoca al agente de destino y devuelve el resultado como JSON. Si proporcionacustom_headers, páselo como una cadena JSON, por ejemplo'{"x-tenant": "acme"}'.
Llame directamente al cliente de A2A
Cuando quieras utilizar el enrutamiento determinista, puedes acceder al cliente AgentToAgent directamente a través de runtime.a2a, un atributo que expone el cliente.
El siguiente ejemplo descubre agentes por habilidad e invoca la primera coincidencia:
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)
El atributo runtime.a2a devuelve None cuando A2A no está disponible. Para saber cuándo ocurre esto, consulte Consideraciones. Compruebe siempre este caso antes de llamar a los métodos del cliente, como se muestra en el siguiente ejemplo:
a2a = app._runtime.a2a if a2a is None: ...
Nota
Acceda a runtime.a2a en el código del agente a través de app._runtime.a2a. No existe ningún accesor público app.a2a ni app.runtime. Para la mayoría de los casos de uso, app.a2a_tools() es la alternativa recomendada, ya que no depende de un atributo privado.
Referencia del SDK del cliente A2A
En esta sección, podrá obtener información sobre el SDK del cliente A2A, que proporciona métodos para descubrir e invocar agentes mediante programación desde el código de su agente.
Para utilizar el SDK del cliente, importe los tipos A2A desde agent_engine_runner_shared.a2a, como se muestra en el siguiente ejemplo:
from agent_engine_runner_shared.a2a import ( AgentToAgent, DiscoveredAgent, AgentSkill, AgentResponse, )
Clase Agente a Agente
AgentToAgent Es un cliente HTTP que se comunica con los puntos finales /a2a/discover y /a2a/invoke de OE. En el código del agente, utilice la instancia preautenticada de runtime.a2a en lugar de construir esta clase directamente.
El siguiente ejemplo muestra el constructor y sus parámetros:
AgentToAgent(oe_url: str, auth_token: str = "")
La siguiente tabla describe los parámetros del constructor AgentToAgent:
Argument | Descripción |
|---|---|
| URL base HTTP de OE, por ejemplo |
| A2A JSON Web Token (JWT) sent as |
Método discover_agents()
El método discover_agents() devuelve los agentes que el usuario tiene permiso para ver. Al pasar varios valores en un único parámetro de filtro, un agente coincide si cumple con alguno de esos valores. Por ejemplo, skills=["a", "b"] devuelve los agentes que anuncian cualquiera de las habilidades. Al pasar varios parámetros de filtro, un agente debe cumplir con todos ellos. Los agentes que no tienen la función A2A habilitada y excluyen al usuario mediante allowed_callers nunca se devuelven.
El siguiente ejemplo muestra este método y sus 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]
La siguiente tabla describe los parámetros para el método discover_agents():
Parameter | Tipo | Descripción |
|---|---|---|
| str | Proyecto a buscar. Una cadena vacía utiliza el proyecto del llamador. |
| list[str] | Filtrar por nombre de habilidad. Devuelve los agentes que poseen alguna de las habilidades listadas. |
| list[str] | Filtrar por etiqueta de capacidad. Devuelve los agentes que tienen alguna de las capacidades enumeradas. |
| list[str] | Filtra por tipo MIME aceptado. Devuelve los agentes que aceptan cualquiera de los tipos MIME listados. |
| Int | Número máximo de resultados a devolver. El valor predeterminado es |
Método invoke_agent()
El método invoke_agent() invoca a un agente objetivo y espera el resultado. El OE valida el token, comprueba el control de acceso, crea una ejecución secundaria y realiza sondeos hasta que el agente finaliza, se suspende para revisión humana o se agota el tiempo de espera.
El siguiente ejemplo muestra este método y sus parámetros:
invoke_agent( agent_id: str, message: str, skill: str = "", timeout: float | None = None, custom_headers: dict[str, str] | None = None, ) -> AgentResponse
La siguiente tabla describe los parámetros para el método invoke_agent():
Parameter | Tipo | Descripción |
|---|---|---|
| str | ID del espacio de trabajo de destino. Obtener de |
| str | Mensaje o indicación que se enviará al agente de destino. |
| str | Sugerencia de habilidad opcional para agentes que anuncian muchas habilidades. |
| flotar | Ninguno | Segundos de espera antes de que se agote el tiempo de espera. |
| dict[str, str] | None | Encabezados clave-valor reenviados al contexto de ejecución del agente de destino. Las claves no deben usar el prefijo reservado |
Clases de datos de respuesta
La siguiente tabla enumera los tipos de respuesta, que son todos clases de datos inmutables:
clase | Campos |
|---|---|
|
|
|
|
|
|
Gestionar el estado de respuesta del agente
La siguiente tabla describe cada valor posible para el campo AgentResponse.status:
Estado | Significado | ¿Qué hacer? |
|---|---|---|
| El agente objetivo finalizó con éxito. | Leer |
| El agente de destino devolvió un error. | Leer |
| El agente objetivo ha sido suspendido para su revisión humana. | Muestra la pausa a tu usuario o flujo. No la consideres un fallo. |
Los errores de red y de nivel HTTP, como los tiempos de espera o las respuestas 4xx y 5xx del OE, generan un httpx.HTTPError desde los métodos discover_agents() y invoke_agent(). Un AgentResponse con un valor status de failed indica que la llamada llegó al OE, pero el agente de destino falló. Para gestionar tanto los errores de red como los fallos del agente, envuelva las llamadas en bloques try/except y compruebe el valor status.
El siguiente ejemplo maneja los tres valores de estado:
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)
Encabezados personalizados de reenvío
Puede utilizar A2A para adjuntar encabezados de clave-valor arbitrarios a una invocación de agente para propagar el contexto del ámbito de la solicitud, como un ID de inquilino, una etiqueta de seguimiento o un token OAuth delegado.
Pasar encabezados a un agente llamado
Para adjuntar encabezados a un agente llamado, pase un dict usando el parámetro custom_headers de invoke_agent(). Cuando use la herramienta LLM invoke_a2a_agent, pase los encabezados como una cadena JSON.
El siguiente ejemplo pasa encabezados personalizados mediante el uso del cliente directo:
app._runtime.a2a.invoke_agent( agent_id="ws-billing-001", message="Charge the customer", custom_headers={ "x-tenant": "acme", "oauth-token": "<delegated-token>", }, )
El siguiente ejemplo pasa encabezados personalizados mediante la herramienta LLM:
invoke_a2a_agent( agent_id="ws-billing-001", message="Charge the customer", custom_headers='{"x-tenant": "acme"}', )
Las claves de encabezado no deben usar el prefijo reservado a2a- que no distingue entre mayúsculas y minúsculas. El OE rechaza las solicitudes con una respuesta 400 Bad Request si alguna clave comienza con a2a-. El prefijo a2a- está reservado para el enrutamiento de la plataforma y los encabezados de identidad, como a2a-token y a2a-caller-workspace.
Leer los encabezados de una persona que realiza la llamada
Para leer los encabezados personalizados reenviados por un agente que realiza la llamada, utilice el método get_current_custom_headers() de la clase agent_engine_runner_shared.context. El siguiente ejemplo llama a 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() Devuelve un dict[str, str]. El método elimina todos los encabezados de plataforma a2a- antes de devolverlo, por lo que el código de su agente nunca ve tokens A2A ni metadatos de enrutamiento. Si quien realiza la llamada no envía encabezados personalizados, el método devuelve un diccionario vacío.
Configura el control de acceso
El campo a2a.allowed_callers en su archivo agent.yaml controla qué agentes pueden descubrir el suyo a través del siguiente comportamiento:
Lista vacía (predeterminado): Cualquier agente habilitado para A2A en el mismo proyecto puede invocar a su agente y verlo en los resultados de detección.
Lista no vacía: Solo los ID de espacio de trabajo listados pueden invocar a su agente. Todos los demás agentes reciben una respuesta
403y no pueden ver a su agente en los resultados de detección.
El OE verifica la identidad del agente que realiza la llamada con respecto a su lista allowed_callers antes de realizar el envío. No se requieren cambios de código en su agente. El siguiente ejemplo restringe la invocación a un único agente de enrutador:
a2a: enabled: true allowed_callers: - "ws-router-002"
Visualice las llamadas A2A en la interfaz de usuario.
Cada llamada A2A aparece automáticamente en el panel de seguimiento de la sesión. No se requiere instrumentación. La actividad del agente de destino aparece integrada en el mismo seguimiento de la sesión, lo que permite seguir todo el árbol de llamadas entre agentes en un solo lugar.
Cada invocación se representa como un nodo de subagente etiquetado como A2A: <target agent name>, utilizando el ID del espacio de trabajo del objetivo cuando no tiene nombre para mostrar. El nodo muestra el estado y la duración de la llamada. El estado comienza como running y cambia a done si la operación es exitosa o a error si falla. Al seleccionar un nodo, se abre un panel de detalles que muestra el nombre, el estado, la duración, la descripción de la operación, la salida transmitida y cualquier error.
Las llamadas a herramientas, los pasos LLM y las operaciones de memoria del propio agente objetivo aparecen anidados bajo el nodo del subagente, reconstruyendo el árbol de llamadas completo a través de los agentes.
Considerations
Cuando runtime.a2a devuelve None
runtime.a2a Devuelve un cliente solo cuando se cumplen todas las siguientes condiciones:
El código del agente se está ejecutando en el entorno aislado del agente. A2A no está disponible desde el código que se ejecuta en el entorno aislado de la herramienta.
En el contexto de ejecución hay una URL de OE presente.
En los encabezados entrantes se encuentra presente un
a2a-token. El OE inyecta este token automáticamente en cada envío cuando se configura A2A.
Cuando no se cumple alguna condición, runtime.a2a devuelve None. Al usar app.a2a_tools(), las herramientas devuelven un JSON de error estructurado en lugar de generar una excepción.
Duración del token
El token A2A caduca después de 5 minutos por defecto. Para flujos típicos de solicitud y respuesta, esto es suficiente. Los agentes que se ejecutan durante más tiempo que la vida útil del token pueden recibir una respuesta 401 en llamadas A2A. La actualización automática del token aún no está implementada. Considere una respuesta 401 de una llamada de larga duración como un fallo transitorio.
Filtro de autodescubrimiento
La herramienta LLM discover_available_agents filtra el espacio de trabajo del agente que realiza la llamada para evitar que el modelo se llame a sí mismo. Si utiliza el método de cliente discover_agents() sin procesar, el OE podría incluir su propio agente en los resultados. Filtre este espacio manualmente si es necesario.
Alcance dentro del proyecto
En la versión actual, el descubrimiento y la invocación de A2A están limitados a agentes del mismo proyecto. El enrutamiento entre proyectos aún no está implementado.
Observabilidad
La plataforma registra y vincula automáticamente cada llamada A2A. No es necesario añadir instrumentación. El objetivo se ejecuta como una ejecución secundaria vinculada al emisor a través de parent_execution_id. Un root_session_id compartido agrupa todo el árbol de llamadas entre agentes bajo la sesión del usuario de origen.
Ejemplo: Agente de enrutador
El siguiente ejemplo muestra un agente de enrutador que utiliza LLM para encontrar y delegar en un agente especializado. El archivo agent.yaml habilita A2A y declara una habilidad 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
El siguiente código de agente vincula las herramientas A2A junto con sus propias herramientas y permite que el LLM gestione el enrutamiento:
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())
En tiempo de ejecución, el LLM descubre un agente especializado que anuncia una habilidad coincidente, lo llama a través de invoke_a2a_agent y el OE intermedia la llamada al tiempo que aplica el control de acceso y registra ambos lados de la invocación.
Para enrutar de forma determinista en lugar de depender del LLM, llame al cliente directamente dentro de un nodo del 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 pasos
Tras habilitar la comunicación entre agentes, puede consultar las siguientes guías para obtener más información sobre las tareas relacionadas:
Para supervisar el rendimiento de su agente y el estado de su implementación, consulte la sección "Supervisar el agente".
Para autenticar e invocar un agente desplegado, consulte la sección "Invocar un agente".
Para probar tu agente y mejorar tu código, consulta la sección "Prueba tu agente".