ai-agentsmulti-agenta2a-protocolevent-driventutorial

Agent-to-Agent Protocol: Building Semantic Event Buses

Agent-to-Agent Protocol Architecture: Specialized Agents Orchestrated via an Asynchronous Semantic Event Bus

The Problem: The Chaos of Unstructured Multi-Agent Chat

As artificial intelligence systems transition from monolithic single-agent loops to collaborative multi-agent swarms, software architects encounter a critical coordination barrier: unstructured agent interaction.

In early multi-agent frameworks, agents communicated primarily through conversational natural language prompts in shared chat contexts:

  • Conversational Ping-Pong and Hallucinatory Drift: When Agent A asks Agent B for assistance in open-ended natural language, agents frequently engage in endless conversational loops (“Thank you for the update. Is there anything else you need?”). Every back-and-forth round-trip burns thousands of input tokens and accumulates conversational noise.
  • Context Window Bloat: Passing an entire collaborative history to every participating subagent causes context saturation. Subagents lose track of their specific operational goals because their prompts are crowded with intermediate banter from other workers.
  • Lack of Backpressure and Asynchronous Scale: In synchronous multi-agent systems, if an extraction agent takes 45 seconds to harvest web data, the supervisor agent blocks and idles. Without persistent event streaming, systems cannot distribute workloads across multiple machines or handle network blips gracefully.

While the Model Context Protocol (MCP) standardized how an individual agent interfaces with local and remote tools, it left inter-agent coordination unaddressed.

The Solution: The Agent-to-Agent (A2A) Protocol Standard

To solve multi-agent coordination with the reliability of distributed systems engineering, the industry has standardized on the Agent-to-Agent (A2A) Protocol deployed over Semantic Event Buses.

Instead of treating other agents like human chat participants, the A2A specification treats agents as independent, event-driven microservices. Agents publish, consume, and acknowledge strictly typed Event Envelopes across high-throughput message brokers (such as NATS, Redis Streams, or Kafka).

A2A Architecture Topology: Event Broker Bus, Typed Delegation Envelopes, Consensus Voting, and State Delta Streaming

Contrasting Protocol Layers: MCP vs. A2A

Modern agentic stacks divide operational responsibilities between two complementary protocols:

DimensionModel Context Protocol (MCP)Agent-to-Agent Protocol (A2A)
Primary ScopeAgent-to-Tool / Host-to-ClientAgent-to-Agent / Swarm-to-Swarm
Communication PatternSynchronous Request / Response (RPC)Asynchronous Publish / Subscribe & Event Streaming
Transport LayerStdio, HTTP/SSE, WebSocketsNATS, Redis Streams, Kafka, MQTT
Payload SchemaTool definitions, parameters, tool resultsTyped task delegations, state diffs, consensus votes
State ResponsibilityStateless execution inside the tool serverDistributed state machines with durable checkpoints
Concurrency ModelSequential tool callsConcurrent, parallel subagent swarms

The Anatomy of an A2A Event Envelope

Every interaction in an A2A ecosystem is packaged inside an immutable, cryptographically verifiable event envelope:

  • eventId & traceId: Globally unique identifiers enabling end-to-end distributed tracing across the swarm.
  • sender & recipient: Agent URI identifiers (e.g., agent://marketing/scraper to agent://marketing/enricher).
  • eventType: Standardized lifecycle verbs (task.delegate, task.progress, task.completed, consensus.request, state.patch).
  • schemaVersion: Ensures backward-compatible payload evolution.
  • payload: Validated JSON matching explicit domain schemas (e.g., via Zod or JSON Schema).

Implementation: Building a Distributed Multi-Agent Event Bus in TypeScript

Let us implement a complete A2A event-driven swarm architecture in TypeScript featuring typed event contracts, an in-memory semantic event broker, and specialized collaborating agents.

Step 1: Define Typed A2A Event Schemas with Zod

Establish strict typing for event envelopes and domain tasks:

import { z } from 'zod'; // Standardized A2A Event Envelope export const A2AEnvelopeSchema = z.object({ eventId: z.string().uuid(), traceId: z.string().uuid(), timestamp: z.number(), senderUri: z.string(), recipientUri: z.string().optional(), // Omitted for broadcast events eventType: z.enum([ 'task.delegate', 'task.progress', 'task.completed', 'task.failed', 'state.patch', ]), payload: z.record(z.any()), }); export type A2AEnvelope = z.infer<typeof A2AEnvelopeSchema>; // Typed task delegation payload for data harvesting export const ScrapeTaskPayloadSchema = z.object({ targetUrl: z.string().url(), extractionFields: z.array(z.string()), maxRetries: z.number().default(3), }); export type ScrapeTaskPayload = z.infer<typeof ScrapeTaskPayloadSchema>;

Step 2: Implement the Semantic Event Bus Broker

Create a decoupled message bus supporting topic subscriptions, agent routing, and dead-letter queueing:

import { EventEmitter } from 'events'; import { type A2AEnvelope } from './schemas'; export type EventHandler = (event: A2AEnvelope) => Promise<void>; export class SemanticEventBus { private emitter = new EventEmitter(); private deadLetterQueue: A2AEnvelope[] = []; constructor() { this.emitter.setMaxListeners(100); } public subscribe(eventType: string, handler: EventHandler): void { this.emitter.on(eventType, async (event: A2AEnvelope) => { try { await handler(event); } catch (error) { console.error(`[A2A Error] Failed processing event ${event.eventId}:`, error); this.deadLetterQueue.push(event); } }); } public async publish(event: A2AEnvelope): Promise<void> { console.log( `[A2A Bus] Event [${event.eventType}] from ${event.senderUri} -> ${ event.recipientUri || 'BROADCAST' }`, ); this.emitter.emit(event.eventType, event); } public getDeadLetters(): A2AEnvelope[] { return this.deadLetterQueue; } }

Step 3: Implement Specialized Worker Agents with Typed Handoffs

Implement a Supervisor Agent that delegates work and a Scraper Worker Agent that executes autonomously:

import { randomUUID } from 'crypto'; import { SemanticEventBus } from './bus'; import { type A2AEnvelope, ScrapeTaskPayloadSchema } from './schemas'; export class ScraperAgent { public uri = 'agent://data/scraper'; private bus: SemanticEventBus; constructor(bus: SemanticEventBus) { this.bus = bus; this.registerHandlers(); } private registerHandlers(): void { this.bus.subscribe('task.delegate', async (event: A2AEnvelope) => { if (event.recipientUri !== this.uri) return; console.log(`[ScraperAgent] Received delegated task for: ${event.payload.targetUrl}`); const task = ScrapeTaskPayloadSchema.parse(event.payload); // Simulate asynchronous extraction execution const extractedData = { title: 'Acme SaaS Pricing 2026', tier: 'Enterprise', priceMonthly: 499, }; // Publish task completion back to the supervisor await this.bus.publish({ eventId: randomUUID(), traceId: event.traceId, timestamp: Date.now(), senderUri: this.uri, recipientUri: event.senderUri, eventType: 'task.completed', payload: { originalTaskId: event.eventId, status: 'SUCCESS', data: extractedData, }, }); }); } } export class SupervisorAgent { public uri = 'agent://orchestrator/supervisor'; private bus: SemanticEventBus; constructor(bus: SemanticEventBus) { this.bus = bus; this.registerHandlers(); } private registerHandlers(): void { this.bus.subscribe('task.completed', async (event: A2AEnvelope) => { if (event.recipientUri !== this.uri) return; console.log(`[Supervisor] Received completed task output from ${event.senderUri}:`); console.log(JSON.stringify(event.payload.data, null, 2)); }); } public async delegateScrapeJob(targetUrl: string): Promise<void> { const traceId = randomUUID(); await this.bus.publish({ eventId: randomUUID(), traceId, timestamp: Date.now(), senderUri: this.uri, recipientUri: 'agent://data/scraper', eventType: 'task.delegate', payload: { targetUrl, extractionFields: ['title', 'tier', 'priceMonthly'], maxRetries: 3, }, }); } }

Production Best Practices for A2A Architectures

When orchestrating enterprise multi-agent networks:

  • Enforce Circuit Breakers and Timeouts: Never allow an agent to wait indefinitely for a peer response. Implement dead-letter queues and circuit breakers that fall back to secondary agents if an assigned agent fails to respond within an SLA window.
  • Implement Idempotent Event Handlers: In distributed message queues, network retries can deliver duplicate events. Ensure recipient agents record processed eventId tokens to prevent re-executing identical side effects.
  • Semantic State Delta Compression: When sharing state updates across a swarm, avoid publishing full workspace snapshots. Emit atomic state patches (JSON Patch RFC 6902) to keep network payloads lightweight and real-time.

Conclusion

The evolution of agentic AI mirrors the history of modern cloud software: moving from monolithic codebases and fragile conversational prompts to decoupled, event-driven architectures. By unifying multi-agent swarms through the Agent-to-Agent (A2A) protocol and semantic message buses, engineering teams build resilient, scalable systems that execute complex enterprise workflows without conversational drift.