Building Transport‑Agnostic AI Agents with Semantic Kernel, A2A Protocol and Agent Discovery Service
1. What is the Agent‑to‑Agent (A2A) protocol?
Originating from Google’s open specification, A2A defines a lean JSON‑RPC envelope that lets autonomous agents share intents, stream partial results and report progress in real time. By speaking A2A, you can mix and match agents written in any language or framework without inventing yet another bespoke API.
2. Why pair A2A with Microsoft Semantic Kernel?
Semantic Kernel (SK) gives C# developers a first‑class toolkit for wrapping LLM prompts as skills, composing them into pipelines and injecting traditional .NET code where it still shines. When you bolt A2A on top, each SK instance becomes a portable micro‑agent whose skills are instantly discoverable and callable by its peers.
3. Why discovery matters
The most important piece of the puzzle is not messaging or prompts — it's agent discovery.
The discovery service in this repository acts like a temporary memory of the agent ecosystem. Agents register their capabilities (like reverse
or upper
) with it, including the transport they support and how to reach them. Other agents then poll the /resolve
endpoint until the desired skill becomes available, and only then initiate contact.
This approach brings three major benefits:
- Loose coupling – Agents don’t need to know about each other ahead of time.
- Late binding – You can add, replace, or scale agents without changing consumers.
- Transport independence – The discovery layer is completely unaware of how agents talk.
Think of it as DNS for autonomous bots: discover first, communicate second.
4. What the repository contains
Agent2AgentProtocol.Discovery.Service
– a minimal ASP.NET Core API that registers capabilities and resolves them to endpoints.Semantic.Kernel.Agent2AgentProtocol.Example.Agent2
– the responder. It advertises two text‑processing skills:reverse
andupper
.Semantic.Kernel.Agent2AgentProtocol.Example.Agent1
– the initiator. It hunts for an agent that can handlereverse
then streams the outcome back to the console.Semantic.Kernel.Agent2AgentProtocol.Example.Core
– shared plumbing: the A2A message models, a pluggableIMessagingTransport
, and helper extensions for SK.
5. Cloning and building
git clone https://github.com/jordiag/semantic-kernel-agent-a2a-protocol-example.git
cd semantic-kernel-agent-a2a-protocol-example
dotnet build
6. Firing up the demo
# 1️⃣ Start the discovery service
dotnet run --project Agent2AgentProtocol.Discovery.Service
# 2️⃣ Launch the responder (Agent 2)
dotnet run --project Semantic.Kernel.Agent2AgentProtocol.Example.Agent2
# 3️⃣ Launch the initiator (Agent 1)
dotnet run --project Semantic.Kernel.Agent2AgentProtocol.Example.Agent1
Agent 2 registers its skills with the discovery service, including the chosen transport and address. Agent 1 polls /resolve
until it finds a match, then dispatches a JSON‑RPC request such as reverse:Hello A2A
. As the responder streams each chunk, the console shows something like: A|2|A | olleH
.
7. Switching transports – zero code changes
The demo defaults to a local NamedPipeTransport
for friction‑free testing. To see the exact same conversation traverse the cloud:
- Provision an Azure Service Bus namespace and queue.
- Set an environment variable called
SERVICEBUS_CONNECTIONSTRING
. - Flip
UseAzureServiceBus = true
in both agents’appsettings.json
.
Because both transports implement the same IMessagingTransport
interface, no other code changes are required. Feel free to add WebSockets, gRPC or MQTT – the agents will not notice.
8. Anatomy of a capability card
Agent 2 serialises a concise JSON card that lists each skill, its description and the transport payload needed to reach it. Here’s a trimmed example:
{
"capabilities": [
{
"name": "reverse",
"description": "Reverses input text",
"transport": {
"type": "NamedPipe",
"address": "sk-agent2"
}
}
]
}
9. Extending the sample
- Add skills: Drop a new Semantic Kernel function into
TextProcessingFunctions.cs
, register it, and voilà – it appears in the capability card. - Experiment with transports: Plug in RabbitMQ or Redis streams by implementing
IMessagingTransport
. - Scale out discovery: Swap the in‑memory dictionary for Consul, etcd or an Azure App Configuration store.
- Track long‑running tasks: Use the
TaskManager
helper ina2a-dotnet
to stream incremental progress.
10. Key takeaways
By separating discovery, transport and skill execution, this tiny solution proves that AI agents can stay loosely coupled yet perfectly interoperable. Clone it, tweak it and watch your own agents strike up a conversation—without caring where or how their messages travel.
Comments
Be the first to post a comment