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: