.NET下的A2A协议

什么是 A2A 协议?

A2A,全称 Agent-to-Agent 协议,是 Google 在 2025 年推出的一个开放标准,主要解决 不同 AI 代理之间沟通不畅的问题。

你可以理解为一套“通用语言”,不管代理是用什么框架写的,都能通过 A2A 协议顺利交流。

它的核心点有几个:

Agent Card:相当于代理的身份证,上面写明这个代理会什么、支持哪些功能。
消息通信:适合短平快的交互,问一句答一句。
任务通信:专门处理那些需要花时间的工作,可以异步执行,还能实时查看进度。
流式支持 (SSE):代理能一边处理,一边把结果流式推送过来。
安全与兼容:支持 OAuth、mTLS 等安全机制,同时基于 HTTP、JSON-RPC,方便集成进现有系统。
简单说,A2A 就是把各个 AI 代理从“单打独斗”变成能协作的团队成员。

在 .NET 里,已经有了一个官方开源的 A2A .NET SDK,帮开发者更轻松地落地 A2A 协议。

它的亮点是:

高层接口:你可以用 A2AClient、TaskManager 等现成类快速实现一个代理,几乎不用操心底层细节。
底层控制:如果想自己掌握流程,也能直接操作任务存储、消息处理等细节。

ASP.NET Core 集成:通过内置扩展,把代理托管成一个 Web 服务只需要几行代码。
跨平台:支持 .NET Standard 2.0 和 .NET 8+,能跑在 Windows、Linux、macOS。
协议兼容:目前实现了 A2A 协议 v0.2.6 的主要功能,基本够用。
换句话说,A2A .NET SDK 让 .NET 程序员可以很快写出支持 A2A 的代理,无论是消息类应用还是长任务,都能搞定。

示例

下面预留三个示例代码的位置,你可以直接填充实现:

例子 1:A2AServer(示例一)

net10.0 enable enable

using A2A;
using A2A.AspNetCore;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Agents;
using Microsoft.SemanticKernel.Agents.A2A;
using System.ComponentModel;

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
var arr = File.ReadAllLines(“C:/gpt/azure_key.txt”);
string? apiKey = arr[2];
string? endpoint = arr[1];
string modelId = arr[0];
IEnumerable invoicePlugins = [KernelPluginFactory.CreateFromType()];
A2AHostAgent? shopHostAgent = CreateChatCompletionHostAgent(
modelId, endpoint, apiKey, “ShopAgent”,
“””
您专门处理与商店进销存的相关的请求。
“””, invoicePlugins);
app.MapA2A(shopHostAgent!.TaskManager!, “/”);
app.Run();
A2AHostAgent CreateChatCompletionHostAgent(string modelId, string endpoint, string apiKey, string name, string instructions, IEnumerable? plugins = null)
{
var builder = Kernel.CreateBuilder();
builder.AddAzureOpenAIChatCompletion(modelId, endpoint, apiKey);
if (plugins is not null)
{
foreach (var plugin in plugins)
{
builder.Plugins.Add(plugin);
}
}
var kernel = builder.Build();
var agent = new ChatCompletionAgent()
{
Kernel = kernel,
Name = name,
Instructions = instructions,
Arguments = new KernelArguments(new PromptExecutionSettings() { FunctionChoiceBehavior = FunctionChoiceBehavior.Auto() }),
};
var agentCard = GetShopAgentCard();
return new A2AHostAgent(agent, agentCard);
}
AgentCard GetShopAgentCard()
{
var capabilities = new AgentCapabilities()
{
Streaming = false,
PushNotifications = false,
};

var invoiceQuery = new AgentSkill()
{
    Id = "id_shop_agent",
    Name = "ShopAgent",
    Description = "处理与商店进销存的相关的请求。",
    Tags = ["水果", "semantic-kernel"],
    Examples =
    [
       "按照水果名称(Name)查询水果",
    ],
};

return new()
{
    Name = "ShopAgent",
    Description = "处理与商店进销存的相关的请求。",
    Version = "1.0.0",
    DefaultInputModes = ["text"],
    DefaultOutputModes = ["text"],
    Capabilities = capabilities,
    Skills = [invoiceQuery],
};

}

public class StoreSystemPlugin
{
public List GoodsList { get; set; } = new List
{
new Goods(“苹果”,5,100),
new Goods(“香蕉”,3,200),
new Goods(“橙子”,4,150),
new Goods(“桃子”,6,120),
new Goods(“梨”,5,100),
new Goods(“葡萄”,7,80),
new Goods(“西瓜”,8,60),
new Goods(“菠萝”,9,40),
new Goods(“芒果”,10,30),
new Goods(“草莓”,11,20),
new Goods(“柠檬”,4,100),
new Goods(“橘子”,3,100),
new Goods(“蓝莓”,6,100),
new Goods(“樱桃”,7,100),
new Goods(“葡萄柚”,8,100),
new Goods(“柚子”,9,100),
new Goods(“榴莲”,10,100),
new Goods(“火龙果”,11,100),
new Goods(“荔枝”,12,100),
new Goods(“椰子”,13,100),
new Goods(“桑葚”,5,100),
new Goods(“杨梅”,4,100),
new Goods(“树梅”,6,100),
new Goods(“莓子”,7,100),
new Goods(“石榴”,8,100),
new Goods(“蜜桃”,9,100),
};
public decimal Total { get; set; } = 0;
[KernelFunction]
[Description(“按照水果名称(Name)查询水果”)]
public string GetGoodsByName([Description(“水果名称”)] string name)
{
return GoodsList.FirstOrDefault(g => g.Name == name)?.ToString() ?? “未找到水果”;
}
[KernelFunction]
[Description(“查询单价(Price)少于等于参数的所有水果”)]
public string GetGoodsLessEqualsPrice([Description(“水果单价”)] decimal price)
{
var goodses = GoodsList.Where(g => g.Price <= price); if (goodses == null || goodses.Any() == false) { return “未找到水果”; } else { return string.Join(“\n”, goodses); } } [Description(“查询单价(Price)少于参数的所有水果”)] public string GetGoodsLessPrice([Description(“水果单价”)] decimal price) { var goodses = GoodsList.Where(g => g.Price < price); if (goodses == null || goodses.Any() == false) { return “未找到水果”; } else { return string.Join(“\n”, goodses); } } [KernelFunction] [Description(“查询单价(Price)大于等于参数的所有水果”)] public string GetGoodsGreaterEqualsPrice([Description(“水果单价”)] decimal price) { var goodses = GoodsList.Where(g => g.Price >= price);
if (goodses == null || goodses.Any() == false)
{
return “未找到水果”;
}
else
{
return string.Join(“\n”, goodses);
}
}
[KernelFunction]
[Description(“查询单价(Price)大于参数的所有水果”)]
public string GetGoodsGreaterPrice([Description(“水果单价”)] decimal price)
{
var goodses = GoodsList.Where(g => g.Price > price);
if (goodses == null || goodses.Any() == false)
{
return “未找到水果”;
}
else
{
return string.Join(“\n”, goodses);
}
}

[KernelFunction]
[Description("查询库存数量(Quantity)大于等于参数的所有水果")]
public string GetGoodsGreaterEqualsQuantity([Description("水果库存数量")] int quantity)
{
    var goodses = GoodsList.Where(g => g.Quantity >= quantity);
    if (goodses == null || goodses.Any() == false)
    {
        return "未找到水果";
    }
    else
    {
        return string.Join("\n", goodses);
    }
}

[KernelFunction]
[Description("查询库存数量(Quantity)大于参数的所有水果")]
public string GetGoodsGreaterQuantity([Description("水果库存数量")] int quantity)
{
    var goodses = GoodsList.Where(g => g.Quantity > quantity);
    if (goodses == null || goodses.Any() == false)
    {
        return "未找到水果";
    }
    else
    {
        return string.Join("\n", goodses);
    }
}
[KernelFunction]
[Description("查询库存数量(Quantity)少于等于参数的所有水果")]
public string GetGoodsLessEqualsQuantity([Description("水果数量")] int quantity)
{
    var goodses = GoodsList.Where(g => g.Quantity <= quantity);
    if (goodses == null || goodses.Any() == false)
    {
        return "未找到水果";
    }
    else
    {
        return string.Join("\n", goodses);
    }
}
[KernelFunction]
[Description("查询库存数量(Quantity)少于参数的所有水果")]
public string GetGoodsLessQuantity([Description("水果数量")] int quantity)
{
    var goodses = GoodsList.Where(g => g.Quantity < quantity);
    if (goodses == null || goodses.Any() == false)
    {
        return "未找到水果";
    }
    else
    {
        return string.Join("\n", goodses);
    }
}
[KernelFunction]
[Description("购买水果")]
public string BuyGoods([Description("水果名称")] string name, [Description("购买数量")] int quantity)
{
    var goods = GoodsList.FirstOrDefault(g => g.Name == name);
    if (goods != null)
    {
        var newQuantity = goods.Quantity - quantity;
        if (newQuantity < 0)
        {
            return "库存不足";
        }
        else
        {
            goods.Quantity = newQuantity;
            goods.BuyQuantity += quantity;
            Total += goods.Price * quantity;
            return "购买成功!";
        }
    }
    else
    {
        return "未找到水果";
    }
}

}
public class Goods
{
public Goods(string name, decimal price, int quantity)
{
Name = name;
Price = price;
Quantity = quantity;
}
public string Name { get; set; }
public decimal Price { get; set; }
public int Quantity { get; set; }
public int BuyQuantity { get; set; } = 0;

public override string ToString()
{
    return $"名称(Name):{Name},单价(Price):{Price},库存数量(Quantity):{Quantity},销售数量(BuyQuantity):{BuyQuantity}";
}

}
例子 2:A2AServer(示例二)

net10.0 enable enable

using A2A;
using A2A.AspNetCore;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Agents;
using Microsoft.SemanticKernel.Agents.A2A;
using System.ComponentModel;

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
var arr = File.ReadAllLines(“C:/gpt/azure_key.txt”);
string? apiKey = arr[2];
string? endpoint = arr[1];
string modelId = arr[0];

A2AHostAgent? translateHostAgent = CreateChatCompletionHostAgent(
modelId, endpoint, apiKey, “TranslateAgent”,
“””
你能把用户输入的准确的翻译成日语和英语。
“””, new KernelPlugin[0]);

app.MapA2A(translateHostAgent!.TaskManager!, “/”);
app.Run();
A2AHostAgent CreateChatCompletionHostAgent(string modelId, string endpoint, string apiKey, string name, string instructions, IEnumerable? plugins = null)
{
var builder = Kernel.CreateBuilder();
builder.AddAzureOpenAIChatCompletion(modelId, endpoint, apiKey);
if (plugins is not null)
{
foreach (var plugin in plugins)
{
builder.Plugins.Add(plugin);
}
}
var kernel = builder.Build();

var agent = new ChatCompletionAgent()
{
    Kernel = kernel,
    Name = name,
    Instructions = instructions,
    Arguments = new KernelArguments(new PromptExecutionSettings() { FunctionChoiceBehavior = FunctionChoiceBehavior.Auto() }),
};

var agentCard = GetTranslateAgentCard();
return new A2AHostAgent(agent, agentCard);

}
AgentCard GetTranslateAgentCard()
{
var capabilities = new AgentCapabilities()
{
Streaming = false,
PushNotifications = false,
};

var invoiceQuery = new AgentSkill()
{
    Id = "id_translate_agent",
    Name = "TranslateAgent",
    Description = "你能把用户输入的内容准确地翻译成日语和英语。",
    Tags = ["翻译", "semantic-kernel"],
    Examples =
    [
    ],
};
return new()
{
    Name = "TranslateAgent",
    Description = "你能把用户输入的准确的翻译成日语和英语。",
    Version = "1.0.0",
    DefaultInputModes = ["text"],
    DefaultOutputModes = ["text"],
    Capabilities = capabilities,
    Skills = [invoiceQuery],
};

}
例子 3:A2AClient(示例)

Exe net10.0 enable enable
using A2A;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Unicode;

while (true)
{
Console.WriteLine(“1、水果Agent 2、翻译Agent”);
switch (Console.ReadLine())
{

    case "1":
        await ShopAgentAsync();
        break;
    case "2":
        await TranslateAgentAsync();
        break;
    default:
        Console.WriteLine("输入错误,默认水果Agent");
        break;
}

}
async Task ShopAgentAsync()
{
var cardResolver = new A2ACardResolver(new Uri(“http://localhost:5000/”));
var agentCard = await cardResolver.GetAgentCardAsync();
var options = new JsonSerializerOptions
{
Encoder = JavaScriptEncoder.Create(UnicodeRanges.All)
};
Console.WriteLine(System.Text.Json.JsonSerializer.Serialize(agentCard, options));
var client = new A2AClient(new Uri(agentCard.Url));

var a2aResponse = await client.SendMessageAsync(new MessageSendParams
{
    Message = new Message
    {
        Role = MessageRole.User,
        Parts = [new TextPart { Text = "给出单价大于10块的水果!" }]
    }
});
if (a2aResponse is AgentTask)
{
    var message = a2aResponse as AgentTask;
    Console.WriteLine(System.Text.Json.JsonSerializer.Serialize(message, options));
}

}
async Task TranslateAgentAsync()
{
var cardResolver = new A2ACardResolver(new Uri(“http://localhost:6000/”));
var agentCard = await cardResolver.GetAgentCardAsync();
var options = new JsonSerializerOptions
{
Encoder = JavaScriptEncoder.Create(UnicodeRanges.All)
};
Console.WriteLine(System.Text.Json.JsonSerializer.Serialize(agentCard, options));
var client = new A2AClient(new Uri(agentCard.Url));
var a2aResponse = await client.SendMessageAsync(new MessageSendParams
{
Message = new Message
{
Role = MessageRole.User,
Parts = [new TextPart { Text = “把我输入的内容翻译成日语:明天是个好日子” }]
}
});
if (a2aResponse is AgentTask)
{
var message = a2aResponse as AgentTask;
Console.WriteLine(System.Text.Json.JsonSerializer.Serialize(message, options));
}
}
调用结果:

声明:来自硅基-桂迹,仅代表创作者观点。链接:https://eyangzhen.com/2691.html

硅基-桂迹的头像硅基-桂迹

相关推荐

关注我们
关注我们
购买服务
购买服务
返回顶部