news 2026/4/23 19:26:01

多Agent协作入门:基于A2A协议的Agent通信(下)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
多Agent协作入门:基于A2A协议的Agent通信(下)

Travel Planner介绍

本篇我们要开发的一个小助手暂且叫它为“Travel Planner”,顾名思义,旅行规划助手。它的场景工作流程如下图所示:

image

让我们通过上一篇的知识,区分一下他们的角色:

User 角色:用户

Client 角色:主助手

Remoet Agent 角色:航班智能体、酒店智能体、旅游智能体

在这个流程中,主助手Client负责接收用户发来的请求,并通过A2A协议从远端Agent获取必要的信息,最后整合这些信息发给大模型整理输出一个完整友好的旅游规划方案返回给用户。

那么,这也就意味着我们需要创建四个.NET项目,其中:

1个.NET控制台项目:主助手

3个ASP.NET Web项目:航班智能体、酒店智能体、旅游智能体

在VS解决方案中它们是这样子的:

image

那么,接下来我们就来一一实现,上干货!

注意:为了示例Code简单易懂,本案例并未涉及Task状态的相关设置。关于Task状态机相关案例,可以参考A2A .NET SDK的GitHub中的示例代码。

航班Agent实现

添加NuGet包,后续不再赘述:

A2A.AspNetCore "0.3.1-preview"

创建一个FlightAgent类,定义其能力 和 AgentCard:

复制代码

public class FlightAgent

{

public void Attach(ITaskManager taskManager)

{

taskManager.OnMessageReceived = QueryFlightsAsync;

taskManager.OnAgentCardQuery = GetAgentCardAsync;

}

private Task<A2AResponse> QueryFlightsAsync(MessageSendParams messageSendParams, CancellationToken cancellationToken)

{

if (cancellationToken.IsCancellationRequested)

{

return Task.FromCanceled<A2AResponse>(cancellationToken);

}

// Process the message

var messageText = messageSendParams.Message.Parts.OfType<TextPart>().First().Text;

// Create and return an artifact

var message = new AgentMessage()

{

Role = MessageRole.Agent,

MessageId = Guid.NewGuid().ToString(),

ContextId = messageSendParams.Message.ContextId,

Parts = [new TextPart() {

Text = $"Available Flilghts: CZ6250 - RMB380, MF1952 - RMB 390, GJ3156 - RMB410"

}]

};

return Task.FromResult<A2AResponse>(message);

}

private Task<AgentCard> GetAgentCardAsync(string agentUrl, CancellationToken cancellationToken)

{

if (cancellationToken.IsCancellationRequested)

{

return Task.FromCanceled<AgentCard>(cancellationToken);

}

var capabilities = new AgentCapabilities()

{

Streaming = true,

PushNotifications = false,

};

return Task.FromResult(new AgentCard()

{

Name = "Flight Agent",

Description = "Agent which will query and return available flights to user.",

Url = agentUrl,

Version = "1.0.0",

DefaultInputModes = ["text"],

DefaultOutputModes = ["text"],

Capabilities = capabilities,

Skills = [],

});

}

}

复制代码

这里说明一下,我这里为了方便是直接返回了一个固定的输出内容,但在实际应用中往往需要进行具体的业务逻辑处理 或 调用大模型进行处理。下面的几个Agent也是类似的情况,就不再赘述。

然后,在Program.cs中进行注册:

复制代码

using A2A;

using A2A.AspNetCore;

using FlightAgentServer;

var builder = WebApplication.CreateBuilder(args);

var app = builder.Build();

var taskManager = new TaskManager();

var agent = new FlightAgent();

agent.Attach(taskManager);

// Add JSON-RPC endpoint for A2A

app.MapA2A(taskManager, "/flights");

// Add well-known agent card endpoint for A2A

app.MapWellKnownAgentCard(taskManager, "/flights");

// Add HTTP endpoint for A2A

app.MapHttpA2A(taskManager, "/flights");

app.Run();

复制代码

这里需要注意的是:为了能够让Client端能够发现该Agent Card,请务必使用 MapWellKnownAgentCard 扩展方法进行注册。

酒店Agent实现

创建一个HotelAgent类,定义其能力 和 AgentCard:

复制代码

public class HotelAgent

{

public void Attach(ITaskManager taskManager)

{

taskManager.OnMessageReceived = QueryHotelsAsync;

taskManager.OnAgentCardQuery = GetAgentCardAsync;

}

private Task<A2AResponse> QueryHotelsAsync(MessageSendParams messageSendParams, CancellationToken cancellationToken)

{

if (cancellationToken.IsCancellationRequested)

{

return Task.FromCanceled<A2AResponse>(cancellationToken);

}

// Process the message

var messageText = messageSendParams.Message.Parts.OfType<TextPart>().First().Text;

// Create and return an artifact

var message = new AgentMessage()

{

Role = MessageRole.Agent,

MessageId = Guid.NewGuid().ToString(),

ContextId = messageSendParams.Message.ContextId,

Parts = [new TextPart() {

Text = $"Available Hotels: JI Hotel - RMB350, Holiday In - RMB 320, Hilton - RMB610"

}]

};

return Task.FromResult<A2AResponse>(message);

}

private Task<AgentCard> GetAgentCardAsync(string agentUrl, CancellationToken cancellationToken)

{

if (cancellationToken.IsCancellationRequested)

{

return Task.FromCanceled<AgentCard>(cancellationToken);

}

var capabilities = new AgentCapabilities()

{

Streaming = true,

PushNotifications = false,

};

return Task.FromResult(new AgentCard()

{

Name = "Hotel Agent",

Description = "Agent which will query and return available hotels to user.",

Url = agentUrl,

Version = "1.0.0",

DefaultInputModes = ["text"],

DefaultOutputModes = ["text"],

Capabilities = capabilities,

Skills = [],

});

}

}

复制代码

同样,请参考航班Agent完成Program.cs中的注册。

规划Agent实现

创建一个PlanAgent类,定义其能力 和 AgentCard:

复制代码

public class PlanAgent

{

public void Attach(ITaskManager taskManager)

{

taskManager.OnMessageReceived = QueryPlansAsync;

taskManager.OnAgentCardQuery = GetAgentCardAsync;

}

private Task<A2AResponse> QueryPlansAsync(MessageSendParams messageSendParams, CancellationToken cancellationToken)

{

if (cancellationToken.IsCancellationRequested)

{

return Task.FromCanceled<A2AResponse>(cancellationToken);

}

// Process the message

var messageText = messageSendParams.Message.Parts.OfType<TextPart>().First().Text;

// Create and return an artifact

var message = new AgentMessage()

{

Role = MessageRole.Agent,

MessageId = Guid.NewGuid().ToString(),

ContextId = messageSendParams.Message.ContextId,

Parts = [new TextPart() {

Text = $"Available Plans: The Muslim Quarter - 2 Hours, The Terracotta Warriors - 3 Hours, The Huaqing Palace - 2 Hours"

}]

};

return Task.FromResult<A2AResponse>(message);

}

private Task<AgentCard> GetAgentCardAsync(string agentUrl, CancellationToken cancellationToken)

{

if (cancellationToken.IsCancellationRequested)

{

return Task.FromCanceled<AgentCard>(cancellationToken);

}

var capabilities = new AgentCapabilities()

{

Streaming = true,

PushNotifications = false,

};

return Task.FromResult(new AgentCard()

{

Name = "Plan Agent",

Description = "Agent which will query and return travel plans to user.",

Url = agentUrl,

Version = "1.0.0",

DefaultInputModes = ["text"],

DefaultOutputModes = ["text"],

Capabilities = capabilities,

Skills = [],

});

}

}

复制代码

主助手实现

这里我们暂且命名为TravelPlannerClient,在该项目中我们需要用到Semantic Kernel进行与大模型API的集成,因此我们先安装一下相关NuGet包:

A2A 0.3.1-preivew

Microsoft.SemanticKernel.Agents.Core 1.65.0

Microsoft.SemanticKernel.Agents.OpenAI 1.65.0-preview

然后,假设我们有一个配置文件appsettings.json定义了大模型供应商的信息:

复制代码

{

"LLM": {

"BASE_URL": "https://api.siliconflow.cn",

"API_KEY": "******************************",

"MODEL_ID": "Qwen/Qwen2.5-32B-Instruct"

}

}

复制代码

这里我们使用SiliconCloud提供的 Qwen2.5-32B-Instruct 模型,你可以通过这个URL注册账号:https://cloud.siliconflow.cn/i/DomqCefW 获取大量免费的Token来进行本次实验。

最后,就是完整的Client端代码实现,这里我分了几个主要的步骤写在注释中,方便你理解每个步骤是干什么的:

复制代码

using A2A;

using Microsoft.Extensions.Configuration;

using Microsoft.SemanticKernel;

using Microsoft.SemanticKernel.ChatCompletion;

using TravelPlannerClient.Infrastructure;

Console.WriteLine("[Step1] Now loading the configuration...");

var config = new ConfigurationBuilder()

.AddJsonFile($"appsettings.json", optional: false, reloadOnChange: true)

.Build();

Console.WriteLine("[Step2] Now loading the chat client...");

var chattingApiConfiguration = new OpenAiConfiguration(

config.GetSection("LLM:MODEL_ID").Value,

config.GetSection("LLM:BASE_URL").Value,

config.GetSection("LLM:API_KEY").Value);

var openAiChattingClient = new HttpClient(new OpenAiHttpHandler(chattingApiConfiguration.EndPoint));

var kernel = Kernel.CreateBuilder()

.AddOpenAIChatCompletion(chattingApiConfiguration.ModelId, chattingApiConfiguration.ApiKey, httpClient: openAiChattingClient)

.Build();

Console.WriteLine("[Step3] Now loading the remote agent clients...");

// Remote Agent 1 : Flight Agent

var cardResolver = new A2ACardResolver(new Uri("https://localhost:7011/"));

var agentCard = await cardResolver.GetAgentCardAsync();

var flightAgentClient = new A2AClient(new Uri(agentCard.Url));

// Remote Agent 2 : Hotel Agent

cardResolver = new A2ACardResolver(new Uri("https://localhost:7021/"));

agentCard = await cardResolver.GetAgentCardAsync();

var hotelAgentClient = new A2AClient(new Uri(agentCard.Url));

// Remote Agent 3 : Plan Agent

cardResolver = new A2ACardResolver(new Uri("https://localhost:7031/"));

agentCard = await cardResolver.GetAgentCardAsync();

var planAgentClient = new A2AClient(new Uri(agentCard.Url));

Console.WriteLine("[Step4] Welcome to use Travel Planner!\n");

var task = "Hello, I'd like to have a travelling in Xian, please give me one travel plan.";

Console.WriteLine($"# User Message: \n{task}\n");

var userMessage = new AgentMessage

{

Role = MessageRole.User,

Parts = [new TextPart { Text = task }]

};

Console.WriteLine("[Step5] Now getting A2A Response from Remote Agents...");

var agentResponse = (AgentMessage)await flightAgentClient.SendMessageAsync(new MessageSendParams

{

Message = userMessage

});

var flightOptions = ((TextPart)agentResponse.Parts[0]).Text;

Console.WriteLine($"Flight-Agent Response: {flightOptions}");

agentResponse = (AgentMessage)await hotelAgentClient.SendMessageAsync(new MessageSendParams

{

Message = userMessage

});

var hotelOptions = ((TextPart)agentResponse.Parts[0]).Text;

Console.WriteLine($"Hotel-Agent Response: {hotelOptions}");

agentResponse = (AgentMessage)await planAgentClient.SendMessageAsync(new MessageSendParams

{

Message = userMessage

});

var planOptions = ((TextPart)agentResponse.Parts[0]).Text;

Console.WriteLine($"Plan-Agent Response: {planOptions}\n");

Console.WriteLine("[Step6] Now sumarizing the final response to user...");

var prompt = $"""

You are a travel planner assistant.

You have received the flight options, hotel options and travel plan from different agents.

You need to summarize the information and give a final travel plan to user.

Flight Options: {flightOptions}

Hotel Options: {hotelOptions}

Travel Plan: {planOptions}

Please provide a concise and clear travel plan to the user.

""";

var chatCompletionService = kernel.GetRequiredService<IChatCompletionService>();

var response = await chatCompletionService.GetChatMessageContentAsync(prompt);

Console.WriteLine($"Your Plan: {response.Content}");

Console.ReadKey();

复制代码

在代码中,我直接给出了用户的请求:“I'd like to have a travelling in Xian, please give me one travel plan.”,即我想去西安旅游,给我一个旅游规划。

最终的执行结果如下所示,图中用红色矩形框标注的就是最终返回给用户的旅游规划:

image

可以看到,主助手通过A2A协议分别从多个远端Agent获取了部分信息,然后交由大模型进行了整合优化,一个用户友好的旅游规划方案就生成好了。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/23 8:32:05

22、Linux脚本编写与用户组管理全解析

Linux脚本编写与用户组管理全解析 1. 脚本创建与用户交互 在Linux系统中,脚本是自动化任务和简化操作的强大工具。例如,可以创建一个脚本来提示用户输入用户名,并尝试以此用户名创建一个账户。以下是示例代码: #!/bin/bash echo -n "Enter a username: " rea…

作者头像 李华
网站建设 2026/4/23 9:52:23

电商系统中事务回滚的典型场景解析

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 开发一个电商事务模拟器&#xff0c;模拟以下场景&#xff1a;1. 订单创建与库存更新的分布式事务 2. 支付超时导致的事务回滚 3. 优惠券并发使用冲突。要求&#xff1a;展示完整的…

作者头像 李华
网站建设 2026/4/23 11:18:51

论文 AI 查重工具全攻略:从选择到使用,守住学术诚信底线

导语 对于高校学生、期刊投稿者和学者来说&#xff0c;论文查重是学术创作的必经之路&#xff0c;而 AI 技术的普及让查重多了 “AI 生成检测” 的关键需求。一款靠谱的论文 AI 查重工具&#xff0c;不仅能精准识别文字重复率&#xff0c;还能揪出 AI 代写痕迹&#xff0c;既帮…

作者头像 李华
网站建设 2026/4/22 16:29:17

MultiWOZ数据集终极指南:快速构建专业级对话系统

MultiWOZ数据集终极指南&#xff1a;快速构建专业级对话系统 【免费下载链接】multiwoz Source code for end-to-end dialogue model from the MultiWOZ paper (Budzianowski et al. 2018, EMNLP) 项目地址: https://gitcode.com/gh_mirrors/mu/multiwoz MultiWOZ数据集…

作者头像 李华
网站建设 2026/4/23 9:55:42

2025年12月不锈钢高强度螺丝厂家推荐排行榜:精挑细选,品质至上

在当前工程建设、机械制造和电子设备行业&#xff0c;对高强度、不锈钢螺丝的需求持续增长。为帮助行业用户快速锁定具有稳定品质、可靠供货和完善技术服务的一线厂商&#xff0c;本文对市场上具有代表性的生产企业进行综合评估&#xff0c;给出推荐指数与关键理由&#xff0c;…

作者头像 李华
网站建设 2026/4/23 9:52:40

OpenSCA-cli:软件成分分析与安全检测实战指南

OpenSCA-cli是一款功能强大的开源软件成分分析工具&#xff0c;专注于扫描项目的第三方组件依赖、检测安全问题及分析许可证信息。无论你是开发新手还是安全工程师&#xff0c;都能通过本指南快速上手这款专业的供应链安全检测工具。 【免费下载链接】OpenSCA-cli OpenSCA 是一…

作者头像 李华