Skip to content

A2A (Agent-to-Agent) - Getting Started

Overview

LlmTornado A2A provides a framework for building distributed agent systems where multiple agents can communicate and collaborate across network boundaries. The A2A architecture enables scalable, modular agent systems with standardized communication protocols.

Introduction

Agent-to-Agent (A2A) communication enables:

  • Distributed Agent Systems: Deploy agents across multiple servers
  • Modular Architecture: Independent agent services that can be composed
  • Scalability: Scale individual agent services independently
  • Interoperability: Standardized communication between different agent types
  • Isolation: Agents run in isolated environments (containers)

Installation

Download the template to create A2A LlmTornado Agents!:

bash
From the Github Templates Folder download the Template Zip for A2A.AgentServer

Architecture Overview

Agent Runtime Configuration

Provides the necessary configuration and context for agents to operate within their environment. Defines how agents behave and interact.

csharp
public class MyA2ARuntime : BaseA2ATornadoRuntimeConfiguration
{
    public override AgentCard DescribeAgentCard(string agentUrl)
    {
        return new AgentCard
        {
            Name = "MyAgent",
            Description = "A specialized agent for specific tasks",
            Url = agentUrl,
            Capabilities = new[] { "task1", "task2" }
        };
    }
}

A2A Agent Server

Main A2A API that runs the Agent Runtime and manages agent interactions. This is where agents are built and executed. (Try out the tempalte!)

  • Hosts individual agents as services
  • Exposes agents via REST API
  • Handles incoming requests and routes to agents
  • Manages agent lifecycle
  • Ready to use Template

Quick Start

Creating Your First A2A Agent

csharp
using LlmTornado.A2A;
using LlmTornado.Agents;

// 1. Define your runtime configuration
public class A2ATornadoAgentSample 
{
    TornadoAgent Agent;
    TornadoApi Client;

    public A2ATornadoAgentSample()
    {
        Client = new TornadoApi(LlmTornado.Code.LLmProviders.OpenAi, Environment.GetEnvironmentVariable("OPENAI_API_KEY") ?? "");

        string instructions = @"
        You are an expert assistant designed to help users with a variety of tasks.
        You can perform tasks such as answering questions, providing recommendations, and assisting with problem-solving.
        You should always strive to provide accurate and helpful information to the user.
        ";

        Agent = new TornadoAgent(
            client: Client,
            model: ChatModel.OpenAi.Gpt5.V5,
            name: "Assistant",
            instructions: instructions,
            streaming: true);
    }

    public BaseA2ATornadoRuntimeConfiguration Build()
    {
        IRuntimeConfiguration runtimeConfig = new SingletonRuntimeConfiguration(Agent); //Add your Runtime Configuration here

        return new SampleRuntimeConfiguration(
            runtimeConfig: runtimeConfig,
            name: "LlmTornado.A2A.AgentServer",  //Name of your agent server
            version: "1.0.0" //Version of your agent server
            );
    }
}

/// <summary>
/// Define the Agent Capabilities here
/// </summary>
public class SampleRuntimeConfiguration : BaseA2ATornadoRuntimeConfiguration
{
    /// <summary>
    /// Initializes a new instance of the A2ATornadoRuntimeService
    /// </summary>
    public SampleRuntimeConfiguration(IRuntimeConfiguration runtimeConfig, string name, string version) : base(runtimeConfig, name, version) { }

    /// <summary>
    /// Defines a static Agent Card for the agent
    /// </summary>
    /// <returns></returns>
    public override AgentCard DescribeAgentCard(string agentUrl)
    {
        AgentCapabilities capabilities = new AgentCapabilities()
        {
            Streaming = true,
            PushNotifications = false,
        };

        AgentSkill chattingSkill = new AgentSkill()
        {
            Id = "chatting_skill",
            Name = "Chatting feature",
            Description = "Agent to chat with and search the web.",
            Tags = ["chat", "llm-tornado"],
            Examples =
            [
                "Hello, what's up?",
            ],
        };

        return new AgentCard()
        {
            Name = AgentName,
            Description = "Agent to chat with and search the web",
            Url = agentUrl, // Placeholder URL
            Version = AgentVersion,
            DefaultInputModes = ["text"],
            DefaultOutputModes = ["text"],
            Capabilities = capabilities,
            Skills = [chattingSkill],
        };
    }
}

Creating A2A Agents

BaseA2ATornadoRuntimeConfiguration

When creating a new A2A Runtime configuration, inherit from BaseA2ATornadoRuntimeConfiguration and implement the abstract method DescribeAgentCard(string agentUrl):

csharp
public class TaskAgentRuntime : BaseA2ATornadoRuntimeConfiguration
{
    public TaskAgentRuntime(IRuntimeConfiguration runtimeConfig, string name, string version) : base(runtimeConfig, name, version) { }

    public override AgentCard DescribeAgentCard(string agentUrl)
    {
        AgentCapabilities capabilities = new AgentCapabilities()
        {
            Streaming = true,
            PushNotifications = false,
        };

        AgentSkill chattingSkill = new AgentSkill()
        {
            Id = "chatting_skill",
            Name = "Chatting feature",
            Description = "Agent to chat with and search the web.",
            Tags = ["chat", "llm-tornado"],
            Examples =
            [
                "Hello, what's up?",
            ],
        };

        return new AgentCard()
        {
            Name = AgentName,
            Description = "Agent to chat with and search the web",
            Url = agentUrl, // Placeholder URL
            Version = AgentVersion,
            DefaultInputModes = ["text"],
            DefaultOutputModes = ["text"],
            Capabilities = capabilities,
            Skills = [chattingSkill],
        };
    }
}

Communication Protocol

A2A agents communicate using a standardized protocol:

csharp
// Request format
public class A2ARequest
{
    public string AgentId { get; set; }
    public string Message { get; set; }
    public Dictionary<string, object>? Context { get; set; }
}

// Response format
public class A2AResponse
{
    public string AgentId { get; set; }
    public string Response { get; set; }
    public Dictionary<string, object>? Metadata { get; set; }
}

Deployment

Docker Deployment

Each agent runs in its own Docker container:

dockerfile
# Dockerfile
FROM mcr.microsoft.com/dotnet/aspnet:8.0
WORKDIR /app
COPY . .
ENTRYPOINT ["dotnet", "MyAgent.dll"]
bash
# Build image
docker build -t my-weather-agent .

# Run with API key
docker run -p 5000:80 \
  -e OPENAI_API_KEY=your_api_key \
  my-weather-agent

Docker Compose

Orchestrate multiple agents:

yaml
version: '3.8'
services:
  weather-agent:
    image: weather-agent:latest
    ports:
      - "5001:80"
    environment:
      - OPENAI_API_KEY=${OPENAI_API_KEY}
      
  task-agent:
    image: task-agent:latest
    ports:
      - "5002:80"
    environment:
      - OPENAI_API_KEY=${OPENAI_API_KEY}

Best Practices

Agent Design

  • Keep agents focused on specific domains
  • Define clear capabilities in agent cards
  • Implement proper error handling
  • Use versioning for breaking changes

Communication

  • Use standardized message formats
  • Include context when needed
  • Handle timeouts gracefully
  • Implement retry logic

Security

  • Validate all incoming requests
  • Use authentication/authorization
  • Sanitize inputs and outputs
  • Encrypt sensitive data

Monitoring

  • Log all agent interactions
  • Track performance metrics
  • Monitor agent health
  • Implement alerting

Next Steps

Explore the detailed documentation for each component: