Phase 16 - Lesson 03
Protocolos de Comunicación
Los agentes que no pueden hablar el mismo idioma no son un equipo. Son extraños gritando al vacío.
Tipo: Build Lenguajes: TypeScript Prerrequisitos: Phase 14 (Agent Engineering), Lesson 16.01 (Why Multi-Agent) Tiempo: ~120 minutos
Objetivos de Aprendizaje
- Implementar el descubrimiento e invocación de herramientas de MCP para que los agentes puedan utilizar herramientas expuestas por servidores externos
- Construir un Agent Card y un endpoint de tarea de A2A que permita a un agente delegar trabajo a otro a través de HTTP
- Comparar MCP (acceso a herramientas), A2A (agente a agente), ACP (auditoría empresarial) y ANP (confianza descentralizada) y explicar qué protocolo resuelve cada problema
- Conectar múltiples protocolos en un solo sistema donde los agentes descubren herramientas a través de MCP y delegan tareas a través de A2A
El Problema
Dividiste tu sistema en múltiples agentes. Un investigador, un programador, un revisor. Son excelentes en sus trabajos individuales. Pero ahora necesitas que realmente hablen entre ellos.
Tu primer intento es obvio: pasar strings de un lado a otro. El investigador devuelve un bloque de texto, el programador lo analiza como puede. Funciona hasta que el programador interpreta mal un resumen de investigación, o dos agentes entran en deadlock esperando al otro, o necesitas que colaboren agentes construidos por diferentes equipos. De repente, "simplemente pasar strings" se desmorona.
Este es el problema del protocolo de comunicación. Sin un contrato compartido sobre cómo los agentes intercambian información, los sistemas multiagente son frágiles, no auditables e imposibles de escalar más allá de un puñado de agentes que tú mismo escribiste.
El ecosistema de IA ha respondido con cuatro protocolos, cada uno de los cuales resuelve una parte diferente del problema:
- MCP para el acceso a herramientas
- A2A para la colaboración entre agentes (agente a agente)
- ACP para la auditabilidad empresarial
- ANP para la identidad descentralizada y la confianza
Esta lección profundiza en el tema. Leerás formatos de mensajes reales de cada especificación, construirás implementaciones funcionales y conectarás los cuatro en un sistema unificado.
El Concepto
El Panorama de los Protocolos
Piensa en estos cuatro protocolos como capas, cada una de las cuales aborda una pregunta diferente:
block-beta
columns 1
block:ANP["ANP — ¿Cómo confían los agentes en extraños?\nIdentidad descentralizada (DID), E2EE, meta-protocolo"]
end
block:A2A["A2A — ¿Cómo colaboran los agentes en objetivos?\nAgent Cards, ciclo de vida de tareas, streaming, negociación"]
end
block:ACP["ACP — ¿Cómo hablan los agentes en sistemas auditables?\nEjecuciones, metadados de trayectoria, continuidad de sesión"]
end
block:MCP["MCP — ¿Cómo usa un agente una herramienta?\nDescubrimiento de herramientas, ejecución, uso compartido de contexto"]
end
style ANP fill:#f3e8ff,stroke:#7c3aed
style A2A fill:#dbeafe,stroke:#2563eb
style ACP fill:#fef3c7,stroke:#d97706
style MCP fill:#d1fae5,stroke:#059669
No son competidores. Resuelven diferentes problemas a diferentes niveles.
MCP (Recapitulación)
MCP se cubre en detalle en la Fase 13. Recapitulación rápida: MCP estandariza cómo se conecta un LLM a herramientas externas y fuentes de datos. Es un protocolo cliente-servidor donde el agente (cliente) descubre y llama a las herramientas expuestas por un servidor.
sequenceDiagram
participant Agent as Agente (cliente)
participant MCP1 as Servidor MCP<br/>(base de datos, API, archivos)
Agent->>MCP1: listar herramientas
MCP1-->>Agent: definiciones de herramientas
Agent->>MCP1: llamar herramienta X
MCP1-->>Agent: resultado
MCP es comunicación de agente a herramienta (agent-to-tool). No ayuda a los agentes a hablar entre sí.
A2A (Protocolo Agent2Agent)
Creado por: Google (ahora bajo la Linux Foundation como lf.a2a.v1)
Versión de la especificación: 1.0.0
Problema: ¿Cómo colaboran, negocian y delegan tareas entre sí los agentes autónomos?
A2A es el protocolo para la colaboración entre agentes punto a punto (peer-to-peer). Mientras que MCP conecta un agente a las herramientas, A2A conecta un agente a otros agentes. Cada agente publica un Agent Card en una URL conocida (well-known), y otros agentes lo descubren, negocian con él y le delegan tareas.
Cómo Funciona A2A
sequenceDiagram
participant Client as Agente Cliente
participant Remote as Agente Remoto
Client->>Remote: GET /.well-known/agent-card.json
Remote-->>Client: Agent Card (habilidades, modos, seguridad)
Client->>Remote: POST /message:send
Remote-->>Client: Tarea (enviada/en ejecución)
alt Polling
Client->>Remote: GET /tasks/{id}
Remote-->>Client: Estado de la tarea + artefactos
else Streaming
Client->>Remote: POST /message:stream
Remote-->>Client: SSE: statusUpdate
Remote-->>Client: SSE: artifactUpdate
Remote-->>Client: SSE: completed
end
El Agent Card Real
Así es como se ve un A2A Agent Card real en producción. Servido en GET /.well-known/agent-card.json:
{
"name": "Research Agent",
"description": "Searches documentation and summarizes findings",
"version": "1.0.0",
"supportedInterfaces": [
{
"url": "https://research-agent.example.com/a2a/v1",
"protocolBinding": "JSONRPC",
"protocolVersion": "1.0"
},
{
"url": "https://research-agent.example.com/a2a/rest",
"protocolBinding": "HTTP+JSON",
"protocolVersion": "1.0"
}
],
"provider": {
"organization": "Your Company",
"url": "https://example.com"
},
"capabilities": {
"streaming": true,
"pushNotifications": false
},
"defaultInputModes": ["text/plain", "application/json"],
"defaultOutputModes": ["text/plain", "application/json"],
"skills": [
{
"id": "web-research",
"name": "Web Research",
"description": "Searches the web and synthesizes findings",
"tags": ["research", "search", "summarization"],
"examples": ["Research the latest changes in React 19"]
},
{
"id": "doc-analysis",
"name": "Documentation Analysis",
"description": "Reads and analyzes technical documentation",
"tags": ["docs", "analysis"],
"inputModes": ["text/plain", "application/pdf"],
"outputModes": ["application/json"]
}
],
"securitySchemes": {
"bearer": {
"httpAuthSecurityScheme": {
"scheme": "Bearer",
"bearerFormat": "JWT"
}
}
},
"security": [{ "bearer": [] }]
}
Detalles clave a tener en cuenta:
- Las habilidades (skills) son lo que un agente puede hacer. Cada una tiene un ID, etiquetas y tipos MIME de entrada/salida soportados. Así es como un agente cliente decide si este agente remoto puede manejar su solicitud.
- supportedInterfaces enumera múltiples vinculaciones de protocolo. Un solo agente puede hablar JSON-RPC, REST y gRPC simultáneamente.
- La seguridad (security) está integrada en el card. El cliente sabe qué autenticación necesita antes de realizar una sola solicitud.
Ciclo de Vida de la Tarea
Las tareas son la unidad central de trabajo en A2A. Se mueven a través de estados definidos:
stateDiagram-v2
[*] --> submitted
submitted --> working
working --> input_required: requiere más información
input_required --> working: cliente envía datos
working --> completed: éxito
working --> failed: error
working --> canceled: el cliente cancela
submitted --> rejected: el agente rechaza
completed --> [*]
failed --> [*]
canceled --> [*]
rejected --> [*]
note right of completed: Los estados terminales son inmutables.\nLos seguimientos crean nuevas tareas\ndentro del mismo contextId.
Los 8 estados (la especificación también define UNSPECIFIED como un centinela, omitido aquí):
| Estado | ¿Terminal? | Significado |
|---|---|---|
TASK_STATE_SUBMITTED |
No | Reconocida, pero aún no en proceso |
TASK_STATE_WORKING |
No | Procesándose activamente |
TASK_STATE_INPUT_REQUIRED |
No | El agente necesita más información del cliente |
TASK_STATE_AUTH_REQUIRED |
No | Se requiere autenticación |
TASK_STATE_COMPLETED |
Sí | Finalizada con éxito |
TASK_STATE_FAILED |
Sí | Finalizada con error |
TASK_STATE_CANCELED |
Sí | Cancelada antes de su finalización |
TASK_STATE_REJECTED |
Sí | El agente rechazó la tarea |
Una vez que una tarea alcanza un estado terminal, es inmutable. No se permiten más mensajes. Los seguimientos crean una nueva tarea dentro del mismo contextId.
Formato del Mensaje (Wire Format)
A2A utiliza JSON-RPC 2.0. Así es como se ve un intercambio de mensajes real:
El cliente envía una tarea:
{
"jsonrpc": "2.0",
"id": 1,
"method": "SendMessage",
"params": {
"message": {
"messageId": "msg-001",
"role": "ROLE_USER",
"parts": [{ "text": "Research React 19 compiler features" }]
},
"configuration": {
"acceptedOutputModes": ["text/plain", "application/json"],
"historyLength": 10
}
}
}
El agente responde con una tarea:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"task": {
"id": "task-abc-123",
"contextId": "ctx-xyz-789",
"status": {
"state": "TASK_STATE_COMPLETED",
"timestamp": "2026-03-27T10:30:00Z"
},
"artifacts": [
{
"artifactId": "art-001",
"name": "research-results",
"parts": [{
"data": {
"findings": [
"React 19 compiler auto-memoizes components",
"No more manual useMemo/useCallback needed",
"Compiler runs at build time, not runtime"
]
},
"mediaType": "application/json"
}]
}
]
}
}
}
Streaming a través de SSE:
POST /message:stream HTTP/1.1
Content-Type: application/json
A2A-Version: 1.0
data: {"task":{"id":"task-123","status":{"state":"TASK_STATE_WORKING"}}}
data: {"statusUpdate":{"taskId":"task-123","status":{"state":"TASK_STATE_WORKING","message":{"role":"ROLE_AGENT","parts":[{"text":"Searching documentation..."}]}}}}
data: {"artifactUpdate":{"taskId":"task-123","artifact":{"artifactId":"art-1","parts":[{"text":"partial findings..."}]},"append":true,"lastChunk":false}}
data: {"statusUpdate":{"taskId":"task-123","status":{"state":"TASK_STATE_COMPLETED"}}}
ACP (Agent Communication Protocol)
Creado por: IBM / BeeAI Versión de la especificación: 0.2.0 (OpenAPI 3.1.1) Estado: Fusionándose con A2A bajo la Linux Foundation Problema: ¿Cómo se comunican los agentes con total auditabilidad, continuidad de sesión y seguimiento de trayectoria?
ACP es el protocolo empresarial. A diferencia de lo que afirman muchos resúmenes, ACP no utiliza JSON-LD. Es una API REST/JSON directa definida mediante OpenAPI. Lo que lo hace especial es TrajectoryMetadata: cada respuesta del agente puede llevar un registro detallado de los pasos de razonamiento y las llamadas a herramientas que la produjeron.
sequenceDiagram
participant Client
participant ACP as Agente ACP
participant Audit as Registro de Auditoría
Client->>ACP: POST /runs (modo: sync)
ACP->>ACP: Procesar solicitud...
ACP->>Audit: Registrar trayectoria:<br/>razonamiento + llamadas a herramientas
ACP-->>Client: Respuesta + TrajectoryMetadata
Note over Audit: Cada paso registrado:<br/>tool_name, tool_input,<br/>tool_output, razonamiento
Descubrimiento de Agentes en ACP
ACP define cuatro métodos de descubrimiento:
graph LR
A[Descubrimiento de Agentes] --> B["Runtime<br/>GET /agents"]
A --> C["Abierto<br/>.well-known/agent.yml"]
A --> D["Registro<br/>Catálogo centralizado"]
A --> E["Incrustado<br/>Etiquetas de contenedor"]
style B fill:#dbeafe,stroke:#2563eb
style C fill:#d1fae5,stroke:#059669
style D fill:#fef3c7,stroke:#d97706
style E fill:#f3e8ff,stroke:#7c3aed
El AgentManifest es más simple que el Agent Card de A2A:
{
"name": "summarizer",
"description": "Summarizes documents with source citations",
"input_content_types": ["text/plain", "application/pdf"],
"output_content_types": ["text/plain", "application/json"],
"metadata": {
"tags": ["summarization", "RAG"],
"framework": "BeeAI",
"capabilities": [
{
"name": "Document Summarization",
"description": "Condenses long documents into key points"
}
],
"recommended_models": ["llama3.3:70b-instruct-fp16"],
"license": "Apache-2.0",
"programming_language": "Python"
}
}
Ciclo de Vida de la Ejecución (Run)
ACP utiliza "Runs" (Ejecuciones) en lugar de "Tasks" (Tareas). Un Run es una ejecución del agente con tres modos:
| Modo | Comportamiento |
|---|---|
sync |
Bloqueante. La respuesta contiene el resultado completo. |
async |
Devuelve 202 inmediatamente. Realiza polling en GET /runs/{id} para obtener el estado. |
stream |
Stream SSE. Los eventos se disparan a medida que el agente trabaja. |
stateDiagram-v2
[*] --> created
created --> in_progress
in_progress --> completed: éxito
in_progress --> failed: error
in_progress --> awaiting: requiere entrada
awaiting --> in_progress: el cliente reanuda
in_progress --> cancelling: cancelar solicitud
cancelling --> cancelled
completed --> [*]
failed --> [*]
cancelled --> [*]
TrajectoryMetadata (La Trilha de Auditoría)
Este es el diferenciador clave de ACP. Cada parte del mensaje puede incluir metadados que muestran exactamente lo que hizo el agente:
{
"role": "agent/researcher",
"parts": [
{
"content_type": "text/plain",
"content": "The weather in San Francisco is 72F and sunny.",
"metadata": {
"kind": "trajectory",
"message": "I need to check the weather for this location",
"tool_name": "weather_api",
"tool_input": { "location": "San Francisco, CA" },
"tool_output": { "temperature": 72, "condition": "sunny" }
}
}
]
}
Para las industrias reguladas, esto vale oro. Cada respuesta viene con una cadena de razonamiento demostrable: qué herramientas se llamaron, qué entradas se utilizaron y qué salidas se recibieron. Sin caja negra.
ACP también admite CitationMetadata para la atribución de fuentes:
{
"kind": "citation",
"start_index": 0,
"end_index": 47,
"url": "https://weather.gov/sf",
"title": "NWS San Francisco Forecast"
}
ANP (Agent Network Protocol)
Creado por: Comunidad de código abierto (fundado por GaoWei Chang) Repositorio: github.com/agent-network-protocol/AgentNetworkProtocol Problema: ¿Cómo confían entre sí los agentes de diferentes organizaciones sin una autoridad central?
ANP es el protocolo de identidad descentralizada. Construye la confianza utilizando W3C Decentralized Identifiers (DIDs) y cifrado de extremo a extremo (E2EE). A diferencia de A2A, donde descubres agentes a través de endpoints conocidos, ANP permite a los agentes probar su identidad criptográficamente.
ANP tiene tres capas:
graph TB
subgraph Layer3["Capa 3: Protocolo de Aplicación"]
AD[Documentos de Descripción de Agentes]
DISC[Endpoints de descubrimiento]
end
subgraph Layer2["Capa 2: Meta-Protocolo"]
NEG[Negociación de protocolo basada en IA]
CODE[Generación dinámica de código]
end
subgraph Layer1["Capa 1: Identidad y Comunicación Segura"]
DID["did:wba (W3C DID)"]
HPKE[HPKE E2EE - RFC 9180]
SIG[Verificación de firma]
end
Layer3 --> Layer2
Layer2 --> Layer1
style Layer1 fill:#d1fae5,stroke:#059669
style Layer2 fill:#dbeafe,stroke:#2563eb
style Layer3 fill:#f3e8ff,stroke:#7c3aed
Documentos DID (Estructura Real)
ANP utiliza un método DID personalizado llamado did:wba (Web-Based Agent). El DID did:wba:example.com:user:alice se resuelve en https://example.com/user/alice/did.json:
{
"@context": [
"https://www.w3.org/ns/did/v1",
"https://w3id.org/security/suites/jws-2020/v1",
"https://w3id.org/security/suites/secp256k1-2019/v1"
],
"id": "did:wba:example.com:user:alice",
"verificationMethod": [
{
"id": "did:wba:example.com:user:alice#key-1",
"type": "EcdsaSecp256k1VerificationKey2019",
"controller": "did:wba:example.com:user:alice",
"publicKeyJwk": {
"crv": "secp256k1",
"x": "NtngWpJUr-rlNNbs0u-Aa8e16OwSJu6UiFf0Rdo1oJ4",
"y": "qN1jKupJlFsPFc1UkWinqljv4YE0mq_Ickwnjgasvmo",
"kty": "EC"
}
},
{
"id": "did:wba:example.com:user:alice#key-x25519-1",
"type": "X25519KeyAgreementKey2019",
"controller": "did:wba:example.com:user:alice",
"publicKeyMultibase": "z9hFgmPVfmBZwRvFEyniQDBkz9LmV7gDEqytWyGZLmDXE"
}
],
"authentication": [
"did:wba:example.com:user:alice#key-1"
],
"keyAgreement": [
"did:wba:example.com:user:alice#key-x25519-1"
],
"humanAuthorization": [
"did:wba:example.com:user:alice#key-1"
],
"service": [
{
"id": "did:wba:example.com:user:alice#agent-description",
"type": "AgentDescription",
"serviceEndpoint": "https://example.com/agents/alice/ad.json"
}
]
}
Detalles clave a tener en cuenta:
- Se impone la separación de llaves. Las llaves de firma (secp256k1) están separadas de las llaves de cifrado (X25519).
humanAuthorizationes exclusivo de ANP. Estas llaves requieren una aprobación humana explícita (biométrica, contraseña, HSM) antes de su uso. Las operaciones de alto riesgo, como las transferencias de fondos, pasan por esta ruta.- Las llaves de
keyAgreementse utilizan para el cifrado de extremo a extremo HPKE (RFC 9180). - La sección service apunta al documento de Descripción del Agente.
Cómo Funciona la Confianza en ANP
ANP no utiliza una red de confianza (web-of-trust) ni un gráfico de respaldo. La confianza es bilateral y se verifica por interacción:
sequenceDiagram
participant A as Agente A
participant Domain as Dominio del Agente A
participant B as Agente B
A->>B: Solicitud HTTP + DID + firma
B->>Domain: Obtener documento DID (HTTPS)
Domain-->>B: Documento DID + llave pública
B->>B: Verificar firma con llave pública
B-->>A: Emitir token de acceso
A->>B: Solicitudes posteriores usan el token
Note over A,B: Confianza = Verificación de dominio TLS<br/>+ Verificación de firma DID<br/>+ Principio de menor confianza
La confianza proviene de tres fuentes:
- TLS a nivel de dominio verifica el host del documento DID
- Firmas criptográficas de DID verifican la identidad del agente
- Principio de menor confianza otorga sólo los permisos mínimos
No hay propagación de confianza basada en chismes ni puntuación de PageRank. Verificas a cada agente directamente a través de su DID.
Negociación de Meta-Protocolo
Esta es la característica más novedosa de ANP. Cuando dos agentes de diferentes ecosistemas se encuentran, no necesitan formatos de datos acordados previamente. Negocian en lenguaje natural:
{
"action": "protocolNegotiation",
"sequenceId": 0,
"candidateProtocols": "I can communicate using:\n1. JSON-RPC with hotel booking schema\n2. REST with OpenAPI 3.1 spec\n3. Natural language over HTTP",
"modificationSummary": "Initial proposal",
"status": "negotiating"
}
sequenceDiagram
participant A as Agente A
participant B as Agente B
A->>B: protocolNegotiation (candidateProtocols)
B->>A: protocolNegotiation (contrapropuesta)
A->>B: protocolNegotiation (aceptado)
Note over A,B: Los agentes generan código dinámicamente<br/>para manejar el formato acordado.<br/>Máximo 10 rondas, luego timeout.
Los agentes van y vienen (máximo 10 rondas) hasta que acuerdan un formato, luego generan código dinámicamente para manejarlo. Valores de estado: negotiating, rejected, accepted, timeout.
Esto significa que dos agentes que nunca se han visto antes pueden descubrir cómo comunicarse sin que nadie defina previamente un esquema compartido.
Comparación (Corregida)
| MCP | A2A | ACP | ANP | |
|---|---|---|---|---|
| Creado por | Anthropic | Google / Linux Foundation | IBM / BeeAI | Comunidad |
| Formato de especificación | JSON-RPC | JSON-RPC / REST / gRPC | OpenAPI 3.1 (REST) | JSON-RPC |
| Uso principal | Agente a Herramienta | Agente a Agente | Agente a Agente | Agente a Agente |
| Descubrimiento | Listado de herramientas | /.well-known/agent-card.json |
GET /agents, /.well-known/agent.yml |
/.well-known/agent-descriptions, endpoints de servicio DID |
| Identidad | Implícita (local) | Esquemas de seguridad (OAuth, mTLS) | Nivel de servidor | W3C DID (did:wba) con E2EE |
| Trayectoria de auditoría | N/A | Básica (historial de tareas) | TrajectoryMetadata (llamadas a herramientas, razonamiento) | En desarrollo (no especificado formalmente) |
| Máquina de estados | N/A | 9 estados de tarea | 7 estados de ejecución (run) | N/A |
| Streaming | N/A | SSE | SSE | Agnóstico del transporte |
| Característica exclusiva | Esquemas de herramientas | Agent Cards + Skills | Trayectoria de auditoría de trayectoria | Negociación de meta-protocolo |
| Mejor para | Herramientas y datos | Colaboración dinámica | Industrias reguladas | Confianza entre organizaciones |
| Estado | Estable | Estable (v1.0) | Fusionándose con A2A | Desarrollo activo |
Cómo Funcionan Juntos
Estos protocolos no se excluyen mutuamente. Un sistema corporativo realista utiliza múltiples:
graph TB
subgraph org["Tu Organización"]
RA[Research Agent] <-->|A2A| CA[Coding Agent]
RA -->|MCP| SS[Search Server]
CA -->|MCP| GS[GitHub Server]
AUDIT["Todas las respuestas de los agentes llevan<br/>ACP TrajectoryMetadata"]
end
subgraph ext["Externo (DID verificado a través de ANP)"]
EA[External Agent]
PA[Partner Agent]
end
RA <-->|ANP + A2A| EA
CA <-->|ANP + A2A| PA
style org fill:#f8fafc,stroke:#334155
style ext fill:#fef2f2,stroke:#991b1b
style AUDIT fill:#fef3c7,stroke:#d97706
- MCP conecta a cada agente con sus herramientas
- A2A maneja la colaboración entre agentes (internos y externos)
- ACP envuelve las respuestas en metadados de trayectoria para auditabilidad
- ANP proporciona verificación de identidad para agentes que no controlas
Build It
Paso 1: Tipos de Mensajes Principales
Todo sistema multiagente comienza con un formato de mensaje. Definimos tipos que se mapean con los que usan los protocolos reales:
import crypto from "node:crypto";
type MessageRole = "user" | "agent";
type MessagePart =
| { kind: "text"; text: string }
| { kind: "data"; data: unknown; mediaType: string }
| { kind: "file"; name: string; url: string; mediaType: string };
type TrajectoryEntry = {
reasoning: string;
toolName?: string;
toolInput?: unknown;
toolOutput?: unknown;
timestamp: number;
};
type AgentMessage = {
id: string;
role: MessageRole;
parts: MessagePart[];
trajectory?: TrajectoryEntry[];
replyTo?: string;
timestamp: number;
};
function createMessage(
role: MessageRole,
parts: MessagePart[],
replyTo?: string
): AgentMessage {
return {
id: crypto.randomUUID(),
role,
parts,
replyTo,
timestamp: Date.now(),
};
}
function textMessage(role: MessageRole, text: string): AgentMessage {
return createMessage(role, [{ kind: "text", text }]);
}
Nota: MessagePart es multimodal (texto, datos estructurados, archivos) al igual que las especificaciones reales de A2A y ACP. TrajectoryEntry captura la cadena de razonamiento, coincidiendo con TrajectoryMetadata de ACP.
Paso 2: A2A Agent Card y Registro
Construye un descubrimiento de agentes que coincida con la especificación real de A2A:
type Skill = {
id: string;
name: string;
description: string;
tags: string[];
inputModes: string[];
outputModes: string[];
};
type AgentCard = {
name: string;
description: string;
version: string;
url: string;
capabilities: {
streaming: boolean;
pushNotifications: boolean;
};
defaultInputModes: string[];
defaultOutputModes: string[];
skills: Skill[];
};
class AgentRegistry {
private cards: Map<string, AgentCard> = new Map();
register(card: AgentCard) {
this.cards.set(card.name, card);
}
discoverBySkillTag(tag: string): AgentCard[] {
return [...this.cards.values()].filter((card) =>
card.skills.some((skill) => skill.tags.includes(tag))
);
}
discoverByInputMode(mimeType: string): AgentCard[] {
return [...this.cards.values()].filter(
(card) =>
card.defaultInputModes.includes(mimeType) ||
card.skills.some((skill) => skill.inputModes.includes(mimeType))
);
}
resolve(name: string): AgentCard | undefined {
return this.cards.get(name);
}
listAll(): AgentCard[] {
return [...this.cards.values()];
}
}
Esto es sustancialmente más rico que un simple mapa de nombre a capacidad. Puedes descubrir agentes por etiquetas de habilidad, por tipos MIME de entrada o por nombre, tal como lo admite la especificación A2A real.
Paso 3: Ciclo de Vida de la Tarea A2A
Construye la máquina de estados de tarea completa:
type TaskState =
| "submitted"
| "working"
| "input-required"
| "auth-required"
| "completed"
| "failed"
| "canceled"
| "rejected";
const TERMINAL_STATES: TaskState[] = [
"completed",
"failed",
"canceled",
"rejected",
];
type TaskStatus = {
state: TaskState;
message?: AgentMessage;
timestamp: number;
};
type Artifact = {
id: string;
name: string;
parts: MessagePart[];
};
type Task = {
id: string;
contextId: string;
status: TaskStatus;
artifacts: Artifact[];
history: AgentMessage[];
};
type TaskEvent =
| { kind: "statusUpdate"; taskId: string; status: TaskStatus }
| {
kind: "artifactUpdate";
taskId: string;
artifact: Artifact;
append: boolean;
lastChunk: boolean;
};
type TaskHandler = (
task: Task,
message: AgentMessage
) => AsyncGenerator<TaskEvent>;
class TaskManager {
private tasks: Map<string, Task> = new Map();
private handlers: Map<string, TaskHandler> = new Map();
private listeners: Map<string, ((event: TaskEvent) => void)[]> = new Map();
registerHandler(agentName: string, handler: TaskHandler) {
this.handlers.set(agentName, handler);
}
subscribe(taskId: string, listener: (event: TaskEvent) => void) {
const existing = this.listeners.get(taskId) ?? [];
existing.push(listener);
this.listeners.set(taskId, existing);
}
async sendMessage(
agentName: string,
message: AgentMessage,
contextId?: string
): Promise<Task> {
const handler = this.handlers.get(agentName);
if (!handler) {
const task = this.createTask(contextId);
task.status = {
state: "rejected",
timestamp: Date.now(),
message: textMessage("agent", `No handler for ${agentName}`),
};
return task;
}
const task = this.createTask(contextId);
task.history.push(message);
task.status = { state: "submitted", timestamp: Date.now() };
this.processTask(task, handler, message).catch((err) => {
task.status = {
state: "failed",
timestamp: Date.now(),
message: textMessage("agent", String(err)),
};
});
return task;
}
getTask(taskId: string): Task | undefined {
return this.tasks.get(taskId);
}
cancelTask(taskId: string): boolean {
const task = this.tasks.get(taskId);
if (!task || TERMINAL_STATES.includes(task.status.state)) return false;
task.status = { state: "canceled", timestamp: Date.now() };
this.emit(taskId, {
kind: "statusUpdate",
taskId,
status: task.status,
});
return true;
}
private createTask(contextId?: string): Task {
const task: Task = {
id: crypto.randomUUID(),
contextId: contextId ?? crypto.randomUUID(),
status: { state: "submitted", timestamp: Date.now() },
artifacts: [],
history: [],
};
this.tasks.set(task.id, task);
return task;
}
private async processTask(
task: Task,
handler: TaskHandler,
message: AgentMessage
) {
task.status = { state: "working", timestamp: Date.now() };
this.emit(task.id, {
kind: "statusUpdate",
taskId: task.id,
status: task.status,
});
try {
for await (const event of handler(task, message)) {
if (TERMINAL_STATES.includes(task.status.state)) break;
if (event.kind === "statusUpdate") {
task.status = event.status;
}
if (event.kind === "artifactUpdate") {
const existing = task.artifacts.find(
(a) => a.id === event.artifact.id
);
if (existing && event.append) {
existing.parts.push(...event.artifact.parts);
} else {
task.artifacts.push(event.artifact);
}
}
this.emit(task.id, event);
}
} catch (err) {
task.status = {
state: "failed",
timestamp: Date.now(),
message: textMessage("agent", String(err)),
};
this.emit(task.id, {
kind: "statusUpdate",
taskId: task.id,
status: task.status,
});
}
}
private emit(taskId: string, event: TaskEvent) {
for (const listener of this.listeners.get(taskId) ?? []) {
listener(event);
}
}
}
Esto implementa el ciclo de vida real de la tarea A2A: enviada, en ejecución, entrada requerida, estados terminales. Los manejadores son generadores asíncronos que producen eventos (actualizaciones de estado y fragmentos de artefactos) que coinciden con el modelo de streaming SSE.
Paso 4: Trayectoria de Auditoría Estilo ACP
Envuelve la comunicación con el seguimiento de trayectoria:
type AuditEntry = {
runId: string;
agentName: string;
input: AgentMessage[];
output: AgentMessage[];
trajectory: TrajectoryEntry[];
status: "created" | "in-progress" | "completed" | "failed" | "awaiting";
startedAt: number;
completedAt?: number;
sessionId?: string;
};
class AuditableRunner {
private log: AuditEntry[] = [];
private handlers: Map<
string,
(input: AgentMessage[]) => Promise<{
output: AgentMessage[];
trajectory: TrajectoryEntry[];
}>
> = new Map();
registerAgent(
name: string,
handler: (input: AgentMessage[]) => Promise<{
output: AgentMessage[];
trajectory: TrajectoryEntry[];
}>
) {
this.handlers.set(name, handler);
}
async run(
agentName: string,
input: AgentMessage[],
sessionId?: string
): Promise<AuditEntry> {
const entry: AuditEntry = {
runId: crypto.randomUUID(),
agentName,
input: structuredClone(input),
output: [],
trajectory: [],
status: "created",
startedAt: Date.now(),
sessionId,
};
this.log.push(entry);
const handler = this.handlers.get(agentName);
if (!handler) {
entry.status = "failed";
return entry;
}
entry.status = "in-progress";
try {
const result = await handler(input);
entry.output = structuredClone(result.output);
entry.trajectory = structuredClone(result.trajectory);
entry.status = "completed";
entry.completedAt = Date.now();
} catch (err) {
entry.status = "failed";
entry.trajectory.push({
reasoning: `Error: ${String(err)}`,
timestamp: Date.now(),
});
entry.completedAt = Date.now();
}
return entry;
}
getFullAuditLog(): AuditEntry[] {
return structuredClone(this.log);
}
getAuditLogForAgent(agentName: string): AuditEntry[] {
return structuredClone(
this.log.filter((e) => e.agentName === agentName)
);
}
getAuditLogForSession(sessionId: string): AuditEntry[] {
return structuredClone(
this.log.filter((e) => e.sessionId === sessionId)
);
}
getTrajectoryForRun(runId: string): TrajectoryEntry[] {
const entry = this.log.find((e) => e.runId === runId);
return entry ? structuredClone(entry.trajectory) : [];
}
}
Cada ejecución del agente produce una entrada de auditoría completa: lo que ingresó, lo que salió y la trayectoria completa de llamadas a herramientas y pasos de razonamiento intermedios. Puedes consultar por agente, por sesión o por ejecución individual.
Paso 5: Verificación de Identidad Estilo ANP
Construye una identidad basada en DID y verificación:
type VerificationMethod = {
id: string;
type: string;
controller: string;
publicKeyDer: string;
};
type DIDDocument = {
id: string;
verificationMethod: VerificationMethod[];
authentication: string[];
keyAgreement: string[];
humanAuthorization: string[];
service: { id: string; type: string; serviceEndpoint: string }[];
};
type AgentIdentity = {
did: string;
document: DIDDocument;
privateKey: crypto.KeyObject;
publicKey: crypto.KeyObject;
};
class IdentityRegistry {
private documents: Map<string, DIDDocument> = new Map();
publish(doc: DIDDocument) {
this.documents.set(doc.id, doc);
}
resolve(did: string): DIDDocument | undefined {
return this.documents.get(did);
}
verify(did: string, signature: string, payload: string): boolean {
const doc = this.documents.get(did);
if (!doc) return false;
const authKeyIds = doc.authentication;
const authKeys = doc.verificationMethod.filter((vm) =>
authKeyIds.includes(vm.id)
);
for (const key of authKeys) {
const publicKey = crypto.createPublicKey({
key: Buffer.from(key.publicKeyDer, "base64"),
format: "der",
type: "spki",
});
const isValid = crypto.verify(
null,
Buffer.from(payload),
publicKey,
Buffer.from(signature, "hex")
);
if (isValid) return true;
}
return false;
}
requiresHumanAuth(did: string, operationKeyId: string): boolean {
const doc = this.documents.get(did);
if (!doc) return false;
return doc.humanAuthorization.includes(operationKeyId);
}
}
function createIdentity(domain: string, agentName: string): AgentIdentity {
const did = `did:wba:${domain}:agent:${agentName}`;
const { publicKey, privateKey } = crypto.generateKeyPairSync("ed25519");
const publicKeyDer = publicKey
.export({ format: "der", type: "spki" })
.toString("base64");
const keyId = `${did}#key-1`;
const encKeyId = `${did}#key-x25519-1`;
const document: DIDDocument = {
id: did,
verificationMethod: [
{
id: keyId,
type: "Ed25519VerificationKey2020",
controller: did,
publicKeyDer,
},
{
id: encKeyId,
type: "X25519KeyAgreementKey2019",
controller: did,
publicKeyDer,
},
],
authentication: [keyId],
keyAgreement: [encKeyId],
humanAuthorization: [],
service: [
{
id: `${did}#agent-description`,
type: "AgentDescription",
serviceEndpoint: `https://${domain}/agents/${agentName}/ad.json`,
},
],
};
return { did, document, privateKey, publicKey };
}
function signPayload(identity: AgentIdentity, payload: string): string {
return crypto
.sign(null, Buffer.from(payload), identity.privateKey)
.toString("hex");
}
Esto refleja el modelo de identidad real de ANP: los agentes tienen documentos DID con llaves de autenticación, acuerdo de llaves (key agreement) y autorización humana separadas. El IdentityRegistry simula la resolución de DID (en producción serían peticiones HTTP al dominio del agente).
Paso 6: Protocol Gateway
Conecta los cuatro protocolos en un sistema unificado:
graph LR
REQ[Solicitud Entrante] --> ANP_V{ANP: Verificar DID}
ANP_V -->|Válido| A2A_D{A2A: Descubrir Agente}
ANP_V -->|Inválido| REJECT[Rechazar]
A2A_D -->|Encontrado| ACP_A[ACP: Auditar Ejecución]
A2A_D -->|No Encontrado| REJECT
ACP_A --> A2A_T[A2A: Crear Tarea]
A2A_T --> RESULT[Tarea + Entrada de Auditoría]
style ANP_V fill:#d1fae5,stroke:#059669
style A2A_D fill:#dbeafe,stroke:#2563eb
style ACP_A fill:#fef3c7,stroke:#d97706
style A2A_T fill:#dbeafe,stroke:#2563eb
class ProtocolGateway {
private registry: AgentRegistry;
private taskManager: TaskManager;
private auditRunner: AuditableRunner;
private identityRegistry: IdentityRegistry;
constructor(
registry: AgentRegistry,
taskManager: TaskManager,
auditRunner: AuditableRunner,
identityRegistry: IdentityRegistry
) {
this.registry = registry;
this.taskManager = taskManager;
this.auditRunner = auditRunner;
this.identityRegistry = identityRegistry;
}
async delegateTask(
fromDid: string,
signature: string,
targetAgent: string,
message: AgentMessage,
sessionId?: string
): Promise<{ task: Task; audit: AuditEntry } | { error: string }> {
if (!this.identityRegistry.verify(fromDid, signature, message.id)) {
return { error: "Identity verification failed" };
}
const card = this.registry.resolve(targetAgent);
if (!card) {
return { error: `Agent ${targetAgent} not found in registry` };
}
const audit = await this.auditRunner.run(
targetAgent,
[message],
sessionId
);
const task = await this.taskManager.sendMessage(targetAgent, message);
return { task, audit };
}
discoverAndDelegate(
fromDid: string,
signature: string,
skillTag: string,
message: AgentMessage
): Promise<{ task: Task; audit: AuditEntry } | { error: string }> {
const candidates = this.registry.discoverBySkillTag(skillTag);
if (candidates.length === 0) {
return Promise.resolve({
error: `No agents found with skill tag: ${skillTag}`,
});
}
return this.delegateTask(
fromDid,
signature,
candidates[0].name,
message
);
}
}
El gateway realiza cuatro cosas en una sola llamada:
- ANP: Verifica la identidad del remitente a través de la firma DID
- A2A: Descubre el agente de destino y verifica sus capacidades
- ACP: Envuelve la ejecución en una trayectoria de auditoría
- A2A: Crea una tarea con seguimiento completo del ciclo de vida
Paso 7: Conectando Todo
async function protocolDemo() {
const registry = new AgentRegistry();
registry.register({
name: "researcher",
description: "Searches and summarizes findings",
version: "1.0.0",
url: "https://researcher.local/a2a/v1",
capabilities: { streaming: true, pushNotifications: false },
defaultInputModes: ["text/plain"],
defaultOutputModes: ["text/plain", "application/json"],
skills: [
{
id: "web-research",
name: "Web Research",
description: "Searches the web",
tags: ["research", "search", "summarization"],
inputModes: ["text/plain"],
outputModes: ["application/json"],
},
],
});
registry.register({
name: "coder",
description: "Writes code from specs",
version: "1.0.0",
url: "https://coder.local/a2a/v1",
capabilities: { streaming: false, pushNotifications: false },
defaultInputModes: ["text/plain", "application/json"],
defaultOutputModes: ["text/plain"],
skills: [
{
id: "code-gen",
name: "Code Generation",
description: "Generates code",
tags: ["coding", "generation"],
inputModes: ["text/plain", "application/json"],
outputModes: ["text/plain"],
},
],
});
const taskManager = new TaskManager();
const auditRunner = new AuditableRunner();
const researchTrajectory: TrajectoryEntry[] = [];
taskManager.registerHandler(
"researcher",
async function* (task, message) {
yield {
kind: "statusUpdate" as const,
taskId: task.id,
status: { state: "working" as const, timestamp: Date.now() },
};
researchTrajectory.push({
reasoning: "Searching for React 19 documentation",
toolName: "web_search",
toolInput: { query: "React 19 compiler features" },
toolOutput: {
results: ["react.dev/blog/react-19", "github.com/react/react"],
},
timestamp: Date.now(),
});
researchTrajectory.push({
reasoning: "Extracting key findings from search results",
toolName: "doc_analysis",
toolInput: { url: "react.dev/blog/react-19" },
toolOutput: {
summary:
"React 19 compiler auto-memoizes, no manual useMemo needed",
},
timestamp: Date.now(),
});
yield {
kind: "artifactUpdate" as const,
taskId: task.id,
artifact: {
id: crypto.randomUUID(),
name: "research-results",
parts: [
{
kind: "data" as const,
data: {
findings: [
"React 19 compiler auto-memoizes components",
"No more manual useMemo/useCallback needed",
"Compiler runs at build time, not runtime",
],
sources: ["react.dev/blog/react-19"],
},
mediaType: "application/json",
},
],
},
append: false,
lastChunk: true,
};
yield {
kind: "statusUpdate" as const,
taskId: task.id,
status: { state: "completed" as const, timestamp: Date.now() },
};
}
);
auditRunner.registerAgent("researcher", async () => ({
output: [
textMessage("agent", "React 19 compiler auto-memoizes components"),
],
trajectory: researchTrajectory,
}));
const identityRegistry = new IdentityRegistry();
const coderIdentity = createIdentity("coder.local", "coder");
const researcherIdentity = createIdentity("researcher.local", "researcher");
identityRegistry.publish(coderIdentity.document);
identityRegistry.publish(researcherIdentity.document);
const gateway = new ProtocolGateway(
registry,
taskManager,
auditRunner,
identityRegistry
);
console.log("=== Protocol Demo ===\n");
console.log("1. Agent Discovery (A2A)");
const researchAgents = registry.discoverBySkillTag("research");
console.log(
` Found ${researchAgents.length} agent(s):`,
researchAgents.map((a) => a.name)
);
console.log("\n2. Identity Verification (ANP)");
const message = textMessage("user", "Research React 19 compiler features");
const signature = signPayload(coderIdentity, message.id);
const verified = identityRegistry.verify(
coderIdentity.did,
signature,
message.id
);
console.log(` Coder DID: ${coderIdentity.did}`);
console.log(` Signature verified: ${verified}`);
console.log("\n3. Task Delegation (A2A + ACP + ANP)");
const result = await gateway.delegateTask(
coderIdentity.did,
signature,
"researcher",
message,
"session-001"
);
if ("error" in result) {
console.log(` Error: ${result.error}`);
return;
}
console.log(` Task ID: ${result.task.id}`);
console.log(` Task state: ${result.task.status.state}`);
console.log(` Artifacts: ${result.task.artifacts.length}`);
console.log("\n4. Audit Trail (ACP)");
console.log(` Run ID: ${result.audit.runId}`);
console.log(` Status: ${result.audit.status}`);
console.log(` Trajectory steps: ${result.audit.trajectory.length}`);
for (const step of result.audit.trajectory) {
console.log(` - ${step.reasoning}`);
if (step.toolName) {
console.log(` Tool: ${step.toolName}`);
}
}
console.log("\n5. Full Audit Log");
const fullLog = auditRunner.getFullAuditLog();
console.log(` Total runs: ${fullLog.length}`);
for (const entry of fullLog) {
const duration = entry.completedAt
? `${entry.completedAt - entry.startedAt}ms`
: "in-progress";
console.log(` ${entry.agentName}: ${entry.status} (${duration})`);
}
}
protocolDemo().catch((err) => {
console.error("Protocol demo failed:", err);
process.exitCode = 1;
});
Qué Puede Salir Mal
Los protocolos resuelven el camino feliz (happy path). Esto es lo que se rompe en producción:
Desviación de esquema (Schema drift). El agente A publica un Agent Card anunciando la salida application/json. Pero el esquema JSON cambia entre versiones. El agente B analiza el formato antiguo y obtiene basura. Solución: versiona tus habilidades y esquemas de salida. La especificación A2A admite version en los Agent Cards por este motivo.
Violaciones de la máquina de estados. Un manejador de agente produce un evento completed e intenta producir más artefactos. La tarea es inmutable. Tu código descarta silenciosamente las actualizaciones o genera un error. Solución: verifica el estado terminal antes de producir nuevos eventos. El TaskManager de arriba impone esto con la instrucción break después de los estados terminales.
Fallos en la resolución de confianza. El agente A intenta verificar el DID del agente B, pero el dominio del agente B está caído. El documento DID no se puede obtener. ¿Permites el acceso (aceptas agentes no verificados) o bloqueas (rechazas todo)? ANP recomienda bloquear el acceso (fail closed) con el principio de menor confianza.
Infiltración de trayectoria (Trajectory bloat). El registro de trayectoria de ACP es potente pero costoso. Un agente complejo que realiza 200 llamadas a herramientas por ejecución produce entradas de auditoría masivas. Solución: registra la trayectoria en niveles de detalle configurables. Registra los nombres de las herramientas y las entradas/salidas para cumplimiento normativo, y omite los pasos de razonamiento para cargas de trabajo no reguladas.
Avalancha de descubrimiento (Thundering herd). 50 agentes consultan GET /agents simultáneamente al inicio. Solución: almacena en caché los Agent Cards con TTL, escala los intervalos de descubrimiento o utiliza registro basado en push en lugar de polling.
Use It
Implementaciones Reales
A2A es el más maduro. La especificación oficial de Google es de código abierto bajo la Linux Foundation. Cuenta con SDKs para Python y TypeScript. Si tus agentes necesitan descubrimiento dinámico y colaboración, comienza aquí.
ACP se está fusionando con A2A. El proyecto BeeAI de IBM creó ACP como una alternativa enfocada primero en REST, pero el concepto de metadados de trayectoria está siendo absorbido por el ecosistema A2A. Utiliza patrones de ACP (registro de trayectoria, ciclo de vida de ejecución) incluso si utilizas A2A como transporte.
ANP es el más experimental. El repositorio de la comunidad tiene un SDK de Python (AgentConnect). El concepto de negociación de meta-protocolo es genuinamente novedoso. Vale la pena seguirlo para implementaciones de agentes entre organizaciones.
MCP ya se cubrió en la Fase 13. Si deseas que los agentes utilicen herramientas, MCP es el estándar.
Elegir el Protocolo Correcto
graph TD
START{¿Los agentes necesitan<br/>usar herramientas?}
START -->|Sí| MCP_R[Usa MCP]
START -->|No| TALK{¿Los agentes necesitan<br/>hablar entre sí?}
TALK -->|No| NONE[No necesitas<br/>un protocolo]
TALK -->|Sí| AUDIT{¿Necesitas trayectorias de<br/>auditoría para cumplimiento?}
AUDIT -->|Sí| ACP_R[A2A + ACP<br/>patrones de trayectoria]
AUDIT -->|No| ORG{¿Todos los agentes<br/>están en tu org?}
ORG -->|Sí| A2A_R[A2A<br/>Agent Cards + Tareas]
ORG -->|No| INFRA{¿Comparten<br/>infraestructura?}
INFRA -->|Sí| BROKER[A2A + message broker]
INFRA -->|No| ANP_R[ANP + A2A<br/>verificación de DID]
style MCP_R fill:#d1fae5,stroke:#059669
style A2A_R fill:#dbeafe,stroke:#2563eb
style ACP_R fill:#fef3c7,stroke:#d97706
style ANP_R fill:#f3e8ff,stroke:#7c3aed
style BROKER fill:#e0e7ff,stroke:#4338ca
Ship It
Esta lección produce:
code/main.ts-- implementación completa de los cuatro patrones de protocolooutputs/prompt-protocol-selector.md-- un prompt que te ayuda a elegir protocolos para tu sistema
Ejercicios
Delegación de tareas de múltiples saltos (Multi-hop). Extiende el
TaskManagerpara que el manejador de un agente pueda delegar subtareas a otros agentes. El investigador recibe una tarea, delega las subtareas "buscar" y "resumir" a dos agentes especialistas, espera a que ambos terminen y luego fusiona los resultados en sus propios artefactos.Trayectoria de auditoría en tiempo real (Streaming). Modifica el
AuditableRunnerpara admitir el modo streaming. En lugar de esperar el resultado completo, produce actualizaciones deAuditEntryen tiempo real a medida que se agregan entradas de trayectoria. Utiliza un generador asíncrono que produzca snapshots de auditoría.Rotación de DID. Agrega rotación de llaves a
IdentityRegistry. Un agente debe poder publicar un nuevo documento DID con llaves actualizadas, manteniendo una referencia apreviousDid. Los verificadores deben aceptar firmas tanto de la llave actual como de la anterior durante un período de gracia.Negociación de protocolo. Implementa el concepto de meta-protocolo de ANP. Dos agentes intercambian mensajes
protocolNegotiationcon formatos candidatos (por ejemplo, "Puedo hablar JSON-RPC" frente a "Prefiero REST"). Después de un máximo de 3 rondas, acuerdan un formato o se produce un timeout. El formato acordado determina quéTaskManageroAuditableRunnerutilizan.Descubrimiento con límite de tasa (Rate-limiting). Agrega un wrapper
RateLimitedRegistryque almacene en caché las consultas de Agent Cards con un TTL configurable y limite las consultas de descubrimiento por agente por segundo. Simula una avalancha (thundering herd) de 100 agentes que se descubren entre sí al inicio y mide la diferencia.
Términos Clave
| Término | Qué dice la gente | Qué significa realmente |
|---|---|---|
| MCP | "El protocolo para herramientas de IA" | Un protocolo cliente-servidor para que los agentes descubran y utilicen herramientas. De agente a herramienta, no de agente a agente. |
| A2A | "El protocolo de agentes de Google" | Un protocolo punto a punto para la colaboración de agentes bajo la Linux Foundation. Descubrimiento a través de Agent Cards, ciclo de vida de tareas de 9 estados, streaming a través de SSE. Admite vinculaciones JSON-RPC, REST y gRPC. |
| ACP | "Mensajería empresarial para agentes" | API REST de IBM/BeeAI para ejecuciones (runs) de agentes con TrajectoryMetadata: cada respuesta lleva la cadena completa de razonamiento y llamadas a herramientas. Fusionándose con A2A. |
| ANP | "Identidad descentralizada de agentes" | Un protocolo comunitario que utiliza did:wba (DID) para identidad criptográfica, HPKE para E2EE y negociación de meta-protocolo basada en IA para agentes que nunca se han visto antes. |
| Agent Card | "La tarjeta de presentación del agente" | Un documento JSON en /.well-known/agent-card.json que describe habilidades, tipos MIME admitidos, esquemas de seguridad y vinculaciones de protocolo. |
| DID | "ID descentralizado" | Estándar de W3C para identidades criptográficamente verificables alojadas en el propio dominio del agente. ANP utiliza el método did:wba. |
| TrajectoryMetadata | "El recibo de auditoría" | Mecanismo de ACP para adjuntar pasos de razonamiento, llamadas a herramientas y sus entradas/salidas a cada respuesta del agente. |
| Meta-protocolo | "Agentes negociando cómo hablar" | Enfoque de ANP donde los agentes utilizan lenguaje natural para acordar dinámicamente formatos de datos y luego generan código para manejarlos. |
| Tarea | "Una unidad de trabajo" | El objeto con estado de A2A que rastea el trabajo desde su envío hasta su finalización. Inmutable una vez terminal. |
Lecturas Adicionales
- Especificación de Google A2A -- especificación oficial y SDKs (v1.0.0, Linux Foundation)
- Especificación de IBM/BeeAI ACP -- especificación OpenAPI 3.1 para ejecuciones de agentes y metadados de trayectoria
- Agent Network Protocol -- identidad basada en DID, E2EE, negociación de meta-protocolo
- Documentación de Model Context Protocol -- especificación de MCP de Anthropic (cubierto en la Fase 13)
- W3C Decentralized Identifiers -- el estándar de identidad que fundamenta ANP
- RFC 9180 (HPKE) -- el esquema de cifrado que utiliza ANP para E2EE
- FIPA Agent Communication Language -- el precursor académico de los protocolos modernos de agentes