«`html
Target Audience Analysis
The target audience for the guide «Building an A2A-Compliant Random Number Agent» consists primarily of software developers, data scientists, and business decision-makers interested in AI and its applications in business management. This audience has a strong foundation in programming, particularly in Python, and is keen on exploring new methodologies for building interoperable AI systems.
Pain Points
- Difficulty in integrating AI agents developed on different frameworks due to lack of standard protocols.
- Challenges in implementing scalable multi-agent systems effectively.
- Need for clear and practical guidance on leveraging modern protocols like A2A.
Goals
- To develop and deploy AI agents capable of seamless communication.
- To learn about the implementation of the A2A protocol, enhancing their technical skills in AI.
- To create scalable solutions that can be used in diverse business contexts.
Interests
- Exploring innovative techniques for AI development and implementation.
- Understanding standards and protocols that enhance AI interoperability.
- Networking with peers and industry leaders in AI and software development.
Communication Preferences
- Technical language that accurately describes coding practices and protocols.
- Clear, step-by-step instructions for implementation.
- Visual aids and code examples to facilitate understanding.
Building an A2A-Compliant Random Number Agent
The Agent-to-Agent (A2A) protocol is a new standard by Google that enables AI agents—regardless of their underlying framework or developer—to communicate and collaborate seamlessly. It uses standardized messages, agent cards (which describe what an agent can do), and task-based execution, allowing agents to interact via HTTP without custom integration logic. A2A simplifies the process of building scalable, interoperable multi-agent systems by abstracting away the complexities of communication. This tutorial will guide you through implementing a simple demo agent that returns a random number, helping you understand the core structure and flow of the A2A protocol through hands-on code.
Setting Up the Dependencies
We will first set up our environment by installing the uv
package manager. For Mac or Linux:
curl -LsSf https://astral.sh/uv/install.sh | sh
For Windows (PowerShell):
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
Next, create a new project directory and initialize it with uv
:
uv init a2a-demo cd a2a-demo
We can now create and activate a virtual environment. For Mac or Linux:
uv venv source .venv/bin/activate
For Windows:
uv venv .venv\Scripts\activate
Now, install the required dependencies:
uv add a2a-sdk python-a2a uvicorn
Implementing the Core Building Blocks
Agent Executor (agent_executor.py
)
In this step, we implement the core logic of our agent by creating an Agent Executor, responsible for handling incoming requests and returning responses in the A2A format. The RandomNumberAgentExecutor
wraps a simple RandomNumberAgent
that generates a random number between 1 and 100. When a request comes in, the execute
method calls the agent’s logic, pushing the result into the event queue as a standardized A2A message. This setup forms the backend logic for A2A clients to interact with.
import random from a2a.server.agent_execution import AgentExecutor from a2a.server.agent_execution.context import RequestContext from a2a.server.events.event_queue import EventQueue from a2a.utils import new_agent_text_message from pydantic import BaseModel class RandomNumberAgent(BaseModel): """Generates a random number between 1 and 100""" async def invoke(self) -> str: number = random.randint(1, 100) return f"Random number generated: {number}" class RandomNumberAgentExecutor(AgentExecutor): def __init__(self): self.agent = RandomNumberAgent() async def execute(self, context: RequestContext, event_queue: EventQueue): result = await self.agent.invoke() await event_queue.enqueue_event(new_agent_text_message(result)) async def cancel(self, context: RequestContext, event_queue: EventQueue): raise Exception("Cancel not supported")
Setting Up the A2A Server and Agent Card (main.py
)
Here, we define the metadata that describes what our agent can do — called the Agent Card. Think of it as the agent’s business card containing information like its name, description, available skills, input/output types, and version.
Once the metadata is ready, we configure the A2A server using A2AStarletteApplication
. We provide the agent card and connect it with our custom agent logic using a DefaultRequestHandler
, which uses the RandomNumberAgentExecutor
implemented earlier. Finally, we run the server using uvicorn
, allowing the agent to start listening for incoming A2A messages on port 9999.
import uvicorn from a2a.server.apps import A2AStarletteApplication from a2a.server.request_handlers import DefaultRequestHandler from a2a.server.tasks import InMemoryTaskStore from a2a.types import AgentCapabilities, AgentCard, AgentSkill from agent_executor import RandomNumberAgentExecutor def main(): # Define the skill metadata skill = AgentSkill( id="random_number", name="Random Number Generator", description="Generates a random number between 1 and 100", tags=["random", "number", "utility"], examples=["Give me a random number", "Roll a number", "Random"], ) # Define the agent metadata agent_card = AgentCard( name="Random Number Agent", description="An agent that returns a random number between 1 and 100", url="http://localhost:9999/", defaultInputModes=["text"], defaultOutputModes=["text"], skills=[skill], version="1.0.0", capabilities=AgentCapabilities(), ) # Configure the request handler with our custom agent executor request_handler = DefaultRequestHandler( agent_executor=RandomNumberAgentExecutor(), task_store=InMemoryTaskStore(), ) # Create the A2A app server server = A2AStarletteApplication( http_handler=request_handler, agent_card=agent_card, ) # Run the server uvicorn.run(server.build(), host="0.0.0.0", port=9999) if __name__ == "__main__": main()
Interacting with the Agent Using A2AClient
(client.py
)
Next, we will create the client that interacts with our A2A agent. This client script performs three main tasks:
- Fetch the Agent Card: We start by resolving the agent’s public metadata using
A2ACardResolver
. This fetches the agent.json file from the/.well-known
endpoint, which contains essential details like the agent’s name, description, skills, and communication capabilities. - Initialize the A2A Client: Using the fetched AgentCard, we set up an
A2AClient
, which handles the communication protocol. - Send a Message and Receive a Response: We construct a message and send it as part of a
SendMessageRequest
. The agent processes this message and responds with a generated random number.
import uuid import httpx from a2a.client import A2ACardResolver, A2AClient from a2a.types import ( AgentCard, Message, MessageSendParams, Part, Role, SendMessageRequest, TextPart, ) PUBLIC_AGENT_CARD_PATH = "/.well-known/agent.json" BASE_URL = "http://localhost:9999" async def main() -> None: async with httpx.AsyncClient() as httpx_client: # Fetch the agent card resolver = A2ACardResolver(httpx_client=httpx_client, base_url=BASE_URL) try: print(f"Fetching public agent card from: {BASE_URL}{PUBLIC_AGENT_CARD_PATH}") agent_card: AgentCard = await resolver.get_agent_card() print("Agent card fetched successfully:") print(agent_card.model_dump_json(indent=2)) except Exception as e: print(f"Error fetching public agent card: {e}") return # Initialize A2A client with the agent card client = A2AClient(httpx_client=httpx_client, agent_card=agent_card) # Build message message_payload = Message( role=Role.user, messageId=str(uuid.uuid4()), parts=[Part(root=TextPart(text="Give me a random number"))], ) request = SendMessageRequest( id=str(uuid.uuid4()), params=MessageSendParams(message=message_payload), ) # Send message print("Sending message...") response = await client.send_message(request) # Print response print("Response:") print(response.model_dump_json(indent=2)) if __name__ == "__main__": import asyncio asyncio.run(main())
Running the Agent and Querying
To test our A2A setup, start by running the agent server using main.py
. This initializes the agent, exposes its agent card, and listens for incoming requests on port 9999:
uv run main.py
Next, run the client script, which will fetch the agent’s metadata, send a structured query using the A2A protocol, and receive a response (e.g., “Give me a random number”), with the agent returning a number between 1 and 100:
uv run client.py
For full code access, refer to the original sources credited in this post.
All credit for this research goes to the researchers of this project. For further information, consider following us on Twitter or subscribing to our newsletter.
«`