多智能体微服务实战(2/4):三大开放协议让智能体互联互通
三大协议让智能体"说同一种语言" - MCP、A2A与Agent Framework深度解析
引言
在上一篇文章中,我们介绍了为什么企业需要多智能体协作系统。但一个关键问题还没有解答:
这么多智能体,它们之间怎么通信?如何确保不同团队开发的智能体能够互操作?
想象一下,如果没有HTTP协议,Web就不可能存在;如果没有TCP/IP协议,互联网就无从谈起。同样,要构建企业级的多智能体系统,我们需要标准化的通信协议。
在AgentFrameworkAspire项目中,我们使用了三大开放协议:
- MCP (Model Context Protocol) - 工具暴露标准
- A2A (Agent-to-Agent Protocol) - 智能体通信协议
- Agent Framework - 工作流编排框架
这三个协议各司其职,又相互配合,共同构建了一个强大而灵活的多智能体生态系统。
本文将深入这三大协议的技术细节,带你理解它们的设计理念和实际应用。
一、MCP协议:让AI模型发现和调用工具
1.1 为什么需要MCP?
在MCP出现之前,如果你想让AI模型调用你的业务系统功能,通常有两种做法:
方式1:Function Calling(函数调用)
{
"name": "validate_budget",
"description": "验证预算是否在可用范围内",
"parameters": {
"type": "object",
"properties": {
"amount": {"type": "number"},
"category": {"type": "string"}
}
}
}
问题:
- 每个AI提供商的格式略有不同
- 需要手工编写JSON描述
- 工具和实现代码分离,容易不一致
方式2:自然语言提示
你有以下工具可用:
- validate_budget: 验证预算,参数amount和category
请根据用户问题调用工具...
问题:
- 格式不标准,AI容易理解错误
- 参数类型不明确
- 无法自动发现工具
MCP的解决方案:
MCP (Model Context Protocol) 是一个开放标准,定义了:
- 工具如何描述自己(Tool Metadata)
- 工具如何被调用(Tool Invocation)
- 资源如何被访问(Resources)
- 提示词如何被复用(Prompts)
更重要的是,MCP提供了自动发现机制。AI模型可以:
- 查询服务有哪些工具(List Tools)
- 获取工具的详细描述
- 调用工具并获得结果
1.3 MCP调用流程图解
下面用序列图展示完整的MCP工具调用流程:
关键步骤解析:
- Handshake(握手):客户端连接到MCP服务器,协商协议版本
- ListTools(列出工具):客户端查询服务器有哪些可用工具
- CallTool(调用工具):客户端调用具体工具,传递参数
- 工具执行:服务器执行业务逻辑
- 返回结果:服务器返回标准化的
CallToolResult
1.4 MCP在AgentFrameworkAspire中的实现
让我们看一个实际的例子:Finance服务的MCP工具。
工具定义
// Finance/Program.cs
using ModelContextProtocol.Server;
using ModelContextProtocol.Protocol;
[McpServerToolType]
public class FinanceTools
{
/// <summary>
/// 验证预算是否在可用范围内
/// </summary>
[McpServerTool]
public Task<CallToolResult> ValidateBudget(
decimal amount,
string category,
string costCenter)
{
// 业务逻辑:检查预算
var isValid = amount > 0 && amount < 1000000;
var availableBudget = 500000m;
var result = new
{
IsValid = isValid && amount <= availableBudget,
RequestedAmount = amount,
AvailableBudget = availableBudget,
Category = category,
CostCenter = costCenter,
Message = isValid && amount <= availableBudget
? "Budget validation passed"
: $"Budget validation failed: requested {amount} " +
$"exceeds available {availableBudget}"
};
return Task.FromResult(new CallToolResult
{
Content = [new TextContentBlock {
Text = System.Text.Json.JsonSerializer.Serialize(result)
}]
});
}
/// <summary>
/// 查询历史项目成本数据
/// </summary>
[McpServerTool]
public Task<CallToolResult> GetHistoricalCosts(
string projectType,
string department)
{
// 模拟从数据库查询历史数据
var historicalData = new
{
ProjectType = projectType,
Department = department,
AverageCost = 250000m,
MinCost = 150000m,
MaxCost = 450000m,
ProjectCount = 15,
TimeframeMonths = 12
};
return Task.FromResult(new CallToolResult
{
Content = [new TextContentBlock {
Text = System.Text.Json.JsonSerializer.Serialize(historicalData)
}]
});
}
/// <summary>
/// 计算投资回报率(ROI)
/// </summary>
[McpServerTool]
public Task<CallToolResult> CalculateROI(
decimal investment,
string expectedReturnsJson)
{
var expectedReturns = System.Text.Json.JsonSerializer
.Deserialize<decimal[]>(expectedReturnsJson) ?? [];
var totalReturn = expectedReturns.Sum();
var roi = investment > 0 ? ((totalReturn - investment) / investment) * 100 : 0;
var result = new
{
Investment = investment,
TotalReturn = totalReturn,
NetProfit = totalReturn - investment,
ROI_Percentage = Math.Round(roi, 2),
PaybackPeriod = expectedReturns.Length > 0 ? "Estimated 18 months" : "N/A"
};
return Task.FromResult(new CallToolResult
{
Content = [new TextContentBlock {
Text = System.Text.Json.JsonSerializer.Serialize(result)
}]
});
}
}
关键点解析:
[McpServerToolType]特性:标记这个类包含MCP工具[McpServerTool]特性:标记每个公开的工具方法- 强类型参数:
decimal amount,string category- MCP会自动生成类型描述 - 返回
CallToolResult:标准化的返回格式,包含文本内容
注册MCP服务
// Finance/Program.cs
var builder = WebApplication.CreateBuilder(args);
// 注册MCP Server,支持HTTP传输
builder.Services.AddMcpServer()
.WithHttpTransport()
.WithTools<FinanceTools>() // 注册工具
.WithResources<BudgetResources>(); // 注册资源(可选)
var app = builder.Build();
// 暴露MCP端点
app.MapMcp("/finance/mcp");
app.Run();
工具发现流程
当AI模型或其他智能体想要使用Finance的工具时:
步骤1:发现工具
POST /finance/mcp
Content-Type: application/json
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/list"
}
MCP服务器响应:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"tools": [
{
"name": "ValidateBudget",
"description": "验证预算是否在可用范围内",
"inputSchema": {
"type": "object",
"properties": {
"amount": {"type": "number"},
"category": {"type": "string"},
"costCenter": {"type": "string"}
},
"required": ["amount", "category", "costCenter"]
}
},
{
"name": "GetHistoricalCosts",
"description": "查询历史项目成本数据",
"inputSchema": {
"type": "object",
"properties": {
"projectType": {"type": "string"},
"department": {"type": "string"}
}
}
},
{
"name": "CalculateROI",
"description": "计算投资回报率(ROI)",
"inputSchema": {
"type": "object",
"properties": {
"investment": {"type": "number"},
"expectedReturnsJson": {"type": "string"}
}
}
}
]
}
}
步骤2:调用工具
POST /finance/mcp
Content-Type: application/json
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "ValidateBudget",
"arguments": {
"amount": 300000,
"category": "Development",
"costCenter": "IT-001"
}
}
}
MCP服务器响应:
{
"jsonrpc": "2.0",
"id": 2,
"result": {
"content": [
{
"type": "text",
"text": "{\"IsValid\":true,\"RequestedAmount\":300000,\"AvailableBudget\":500000,...}"
}
]
}
}
1.3 MCP Resources:不止于工具
除了工具,MCP还支持Resources(资源),用于暴露静态或动态的数据源。
// Finance/Program.cs
[McpServerResourceType]
public class BudgetResources
{
/// <summary>
/// 获取标准预算分配模板
/// </summary>
[McpServerResource]
public Task<ReadResourceResult> GetBudgetTemplate()
{
var template = new
{
Category = "General",
StandardAllocations = new
{
Personnel = "60%",
Infrastructure = "20%",
Operations = "15%",
Contingency = "5%"
}
};
return Task.FromResult(new ReadResourceResult
{
Contents = [new TextResourceContents
{
Uri = "budget://templates/general",
MimeType = "application/json",
Text = System.Text.Json.JsonSerializer.Serialize(template)
}]
});
}
}
使用场景:
- 暴露配置文件、模板、规范文档
- 提供数据集、历史记录
- 分享知识库、FAQ
1.4 MCP的价值
对比传统的REST API:
| 特性 | REST API | MCP |
|---|---|---|
| 自动发现 | ❌ 需要文档 | ✅ 内置List API |
| 类型安全 | ⚠️ OpenAPI可选 | ✅ 强制类型定义 |
| AI友好 | ❌ 需要额外转换 | ✅ 原生设计 |
| 统一标准 | ❌ 各家不同 | ✅ 开放标准 |
| 工具和代码一致性 | ⚠️ 容易不同步 | ✅ 代码即文档 |
二、A2A协议:智能体之间的"标准对讲机"
2.1 智能体通信的挑战
假设Tech团队和Finance团队各自开发了自己的智能体,现在PMO团队想要调用它们。没有标准协议的情况下:
Tech团队的接口:
{
"query": "评估技术可行性",
"response_format": "json"
}
Finance团队的接口:
{
"question": "评估技术可行性",
"output_type": "structured"
}
问题:
- 参数名不一样(
queryvsquestion) - 格式不一样(
response_formatvsoutput_type) - 如何知道智能体能做什么?
- 如何处理流式响应?
- 如何传递上下文?
A2A协议的解决方案:
A2A (Agent-to-Agent Protocol) 是由 Google Cloud 主导的智能体间通信标准,它定义了:
- AgentCard:智能体的"名片",描述自己的能力
- 标准消息格式:
MessageSendParams和AgentMessage - 发现机制:
.well-known/agent-card.json约定 - 能力声明:支持的输入输出模式、是否支持流式等
2.2 A2A握手流程图解
下面展示ProjectManager如何通过A2A协议连接并调用Tech Agent:
三阶段详解:
- 服务发现阶段:通过标准化的
.well-known/agent-card.json端点获取Agent元数据 - 初始化阶段:使用AgentCard中的信息创建可复用的AIAgent客户端
- 消息传递阶段:使用标准化的消息格式进行双向通信
2.3 AgentCard:智能体的自我介绍
每个A2A智能体都需要暴露一个AgentCard,描述自己:
// Tech/Program.cs
public class TechAnalystAgent
{
// ... (其他代码)
private Task<AgentCard> GetAgentCardAsync(
string agentUrl,
CancellationToken cancellationToken)
{
return Task.FromResult(new AgentCard
{
Name = "Technical Requirements Analyst",
Description = "Technical analyst agent specializing in " +
"requirements analysis, architecture design, " +
"and technology stack validation",
Url = agentUrl,
Version = "1.0.0",
DefaultInputModes = ["text"], // 支持文本输入
DefaultOutputModes = ["text"], // 输出文本
Capabilities = new AgentCapabilities
{
Streaming = false, // 不支持流式
PushNotifications = false // 不支持推送
},
Skills = [] // 技能列表(可选)
});
}
}
AgentCard会自动暴露在标准端点:
https://localhost:7063/.well-known/agent-card.json
任何智能体或客户端都可以访问这个端点,了解Tech Agent的能力。
2.3 消息发送和接收
发送方(Web前端的ProjectManagerAgent)
// AgentFrameworkAspire.Web/Services/ProjectManagerAgent.cs
public class ProjectManagerAgent
{
private AIAgent? _techAgent;
/// <summary>
/// 使用A2ACardResolver初始化单个agent
/// </summary>
private async Task<AIAgent> InitializeAgentAsync(
string agentName,
string agentUrl,
CancellationToken ct)
{
_logger.LogInformation(
"Initializing {AgentName} agent from {Url}",
agentName, agentUrl);
// A2ACardResolver会自动在URL后追加 /.well-known/agent-card.json
// 例如: https://localhost:7063/.well-known/agent-card.json
var url = new Uri(agentUrl);
var agentCardResolver = new A2ACardResolver(url, _httpClient);
// 获取AgentCard并创建AIAgent客户端
var agent = await agentCardResolver.GetAIAgentAsync(
httpClient: _httpClient,
cancellationToken: ct);
_logger.LogInformation("{AgentName} agent initialized successfully", agentName);
return agent;
}
/// <summary>
/// 调用A2A Agent
/// </summary>
private async Task<AgentRunResponse> CallA2AAgentAsync(
string agentName,
AIAgent agent,
string userInput,
CancellationToken ct)
{
_logger.LogInformation("Calling {AgentName} agent via A2A...", agentName);
// 创建标准消息
var messages = new List<ChatMessage>
{
new(ChatRole.User, userInput)
};
// 调用远程智能体
var response = await agent.InvokeAsync(
messages,
cancellationToken: ct);
_logger.LogInformation(
"{AgentName} agent responded with {MessageCount} messages",
agentName,
response.Messages.Count);
return response;
}
}
关键步骤:
- A2ACardResolver:从URL解析AgentCard
- GetAIAgentAsync:创建AIAgent客户端代理
- InvokeAsync:调用远程智能体,发送消息并获取响应
接收方(Tech服务的TechAnalystAgent)
// Tech/Program.cs
public class TechAnalystAgent
{
private readonly IChatClient _chatClient;
public void Attach(ITaskManager taskManager)
{
// 注册消息处理器
taskManager.OnMessageReceived = ProcessMessageAsync;
// 注册AgentCard查询处理器
taskManager.OnAgentCardQuery = GetAgentCardAsync;
}
/// <summary>
/// 处理收到的A2A消息
/// </summary>
private async Task<A2AResponse> ProcessMessageAsync(
MessageSendParams messageSendParams,
CancellationToken cancellationToken)
{
// 提取消息文本
var messageText = messageSendParams.Message.Parts
.OfType<TextPart>()
.FirstOrDefault()?.Text ?? "";
// 构造提示词
var messages = new List<ChatMessage>
{
new(ChatRole.System,
"You are a technical requirements analyst specializing in " +
"software architecture and technical feasibility. You help " +
"analyze technical requirements, assess complexity, and " +
"recommend technology stacks."),
new(ChatRole.User, messageText)
};
// 调用底层的IChatClient(连接到OpenAI等)
var completion = await _chatClient.GetResponseAsync(
messages,
cancellationToken: cancellationToken);
// 返回标准的A2A响应
return new AgentMessage
{
Role = MessageRole.Agent,
MessageId = Guid.NewGuid().ToString(),
ContextId = messageSendParams.Message.ContextId,
Parts = [new TextPart { Text = completion.Text ?? "No response generated" }]
};
}
}
关键点:
- ITaskManager:A2A协议的任务管理器
- MessageSendParams:标准消息参数(包含消息ID、上下文ID、消息内容)
- AgentMessage:标准响应格式
暴露A2A端点
// Tech/Program.cs
var app = builder.Build();
// 创建TechAnalystAgent实例
var requirementAnalyst = new TechAnalystAgent(
AgentHelpers.CreateChatClient(app.Configuration)
);
// 创建TaskManager并绑定Agent
var techTaskManager = new TaskManager();
requirementAnalyst.Attach(techTaskManager);
// 暴露A2A端点
app.MapA2A(techTaskManager, "/tech/requirement-analyst");
// 暴露AgentCard端点
app.MapWellKnownAgentCard(techTaskManager, "/tech/requirement-analyst");
app.Run();
2.4 完整的A2A通信流程
让我们追踪一次完整的A2A调用:
2.5 A2A vs REST:为什么需要专门的协议?
你可能会问:A2A和普通的REST API有什么区别?
| 特性 | REST API | A2A |
|---|---|---|
| 目标 | 数据交换 | 智能体协作 |
| 消息格式 | 自定义JSON | 标准化MessageSendParams |
| 能力描述 | OpenAPI(可选) | AgentCard(强制) |
| 上下文传递 | ❌ 需要自己设计 | ✅ 内置ContextId |
| 流式支持 | ⚠️ SSE或WebSocket | ✅ 标准流式协议 |
| AI原生 | ❌ 为人类设计 | ✅ 为AI设计 |
核心差异:A2A协议是AI原生的,考虑了智能体协作的特殊需求。
三、Agent Framework:编排复杂的多阶段工作流
3.1 为什么需要工作流编排?
前面介绍的MCP和A2A解决了"单点调用"的问题。但在实际场景中,我们常常需要:
- 串行执行:先做A,再做B,最后做C
- 并行执行:同时做A、B、C、D
- 条件分支:如果X成立,做A;否则做B
- 状态传递:前一步的输出是后一步的输入
这就需要工作流编排(Workflow Orchestration)。
3.2 两种Agent:Simple Agent vs Workflow Agent
在AgentFrameworkAspire中,我们使用了两种类型的智能体:
Simple Agent(简单智能体)
适用于单次调用、无状态的场景:
// Finance/Program.cs - Simple Agent
public class FinanceAgent
{
private readonly IChatClient _chatClient;
private async Task<A2AResponse> ProcessMessageAsync(
MessageSendParams messageSendParams,
CancellationToken cancellationToken)
{
// 直接调用IChatClient,返回结果
var messages = new List<ChatMessage>
{
new(ChatRole.System, "You are a financial analyst..."),
new(ChatRole.User, messageText)
};
var completion = await _chatClient.GetResponseAsync(
messages,
cancellationToken: cancellationToken);
return new AgentMessage { ... };
}
}
特点:
- 单次输入输出
- 无内部状态
- 快速响应
Workflow Agent(工作流智能体)
适用于多阶段、有状态的复杂场景。以QA团队的RiskManagementExpert为例,展示典型的4阶段串行工作流:
Workflow Agent实现代码:
// QA/Program.cs - Workflow Agent
public class RiskManagementExpertAgent
{
private readonly Workflow _riskAnalysisWorkflow;
private readonly IChatClient _chatClient;
public RiskManagementExpertAgent(IChatClient chatClient)
{
_chatClient = chatClient;
// 定义4个阶段的Agent
var riskIdentifier = new ChatClientAgent(chatClient, new ChatClientAgentOptions
{
Instructions = "你是项目风险识别专家。请用简洁的中文列出项目的主要风险...",
Name = "Risk Identifier"
});
var impactAnalyzer = new ChatClientAgent(chatClient, new ChatClientAgentOptions
{
Instructions = "你是风险影响分析专家。请用中文分析每个风险的影响和概率...",
Name = "Impact Analyzer"
});
var mitigationPlanner = new ChatClientAgent(chatClient, new ChatClientAgentOptions
{
Instructions = "你是风险缓解策略专家。请为每个高优先级风险制定具体的缓解措施...",
Name = "Mitigation Planner"
});
var monitoringPlanner = new ChatClientAgent(chatClient, new ChatClientAgentOptions
{
Instructions = "你是风险监控计划专家。请制定风险监控和控制计划...",
Name = "Monitoring Planner"
});
// 使用WorkflowBuilder构建工作流
_riskAnalysisWorkflow = new WorkflowBuilder(riskIdentifier)
.AddEdge(riskIdentifier, impactAnalyzer)
.AddEdge(impactAnalyzer, mitigationPlanner)
.AddEdge(mitigationPlanner, monitoringPlanner)
.WithOutputFrom(monitoringPlanner)
.Build();
}
}
工作流关键特性:
- 串行执行:每个阶段按顺序执行,前一阶段输出作为后一阶段输入
- 状态传递:通过
AddEdge()定义数据流向 - 专业分工:每个Agent有明确的专业角色和职责
- 最终输出:
WithOutputFrom()指定最终输出来源
3.3 执行工作流并流式返回
执行工作流的代码实现:
// QA/Program.cs
private async Task<A2AResponse> ProcessMessageAsync(
MessageSendParams messageSendParams,
CancellationToken cancellationToken)
{
var messageText = messageSendParams.Message.Parts
.OfType<TextPart>()
.FirstOrDefault()?.Text ?? "";
_logger.LogInformation("=== QA Risk Management Expert - Message Received ===");
_logger.LogInformation("Input Message: {MessageText}", messageText);
// 使用workflow处理风险分析 - 使用StreamingRun
var env = InProcessExecution.Default;
var run = await env.StreamAsync(_riskAnalysisWorkflow, messageText);
await run.TrySendMessageAsync(new TurnToken(emitEvents: true));
var responseBuilder = new StringBuilder();
// 监听工作流的流式事件
await foreach (var evt in run.WatchStreamAsync().WithCancellation(cancellationToken))
{
if (evt is AgentRunUpdateEvent update)
{
var response = update.AsResponse();
foreach (var message in response.Messages)
{
foreach (var content in message.Contents)
{
if (content is TextContent textContent)
{
responseBuilder.Append(textContent.Text);
}
}
}
}
}
return new AgentMessage
{
Role = MessageRole.Agent,
MessageId = Guid.NewGuid().ToString(),
ContextId = messageSendParams.Message.ContextId,
Parts = [new TextPart { Text = responseBuilder.ToString() }]
};
}
关键API:
- InProcessExecution.Default:本地执行环境
- StreamAsync:启动流式工作流
- WatchStreamAsync:监听工作流事件
- AgentRunUpdateEvent:每个阶段的输出事件
3.4 Web前端的3阶段工作流编排
ProjectManagerAgent实现了一个更复杂的工作流:
// AgentFrameworkAspire.Web/Services/ProjectManagerAgent.cs
public async IAsyncEnumerable<ExecutorEvent> ExecuteWorkflowStreamAsync(
string userInput,
[EnumeratorCancellation] CancellationToken ct = default)
{
// 确保agents已初始化
await InitializeAgentsAsync(ct);
// ==================== Stage 1: 并行分析 ====================
yield return new WorkflowStageStartEvent("pm-workflow")
{
StageName = "并行分析阶段",
InvolvedAgents = ["Tech", "HR", "Finance", "QA"]
};
// 并行调用4个specialist agents (使用A2A)
var parallelTasks = new[]
{
CallA2AAgentAsync("Tech", _techAgent!, userInput, ct),
CallA2AAgentAsync("HR", _hrAgent!, userInput, ct),
CallA2AAgentAsync("Finance", _financeAgent!, userInput, ct),
CallA2AAgentAsync("QA", _qaAgent!, userInput, ct)
};
var analysisResults = new List<AgentRunResponse>();
// 使用 Task.WhenEach 实现真正的并行 + 流式返回
await foreach (var task in Task.WhenEach(parallelTasks))
{
var response = await task;
analysisResults.Add(response);
// 流式输出每个specialist的响应
foreach (var message in response.Messages)
{
var update = new AgentRunResponseUpdate
{
Role = message.Role,
Contents = message.Contents,
AgentId = response.AgentId,
CreatedAt = DateTimeOffset.Now,
MessageId = message.MessageId
};
yield return new AgentRunUpdateEvent("pm-workflow", update);
}
}
yield return new WorkflowStageCompleteEvent("pm-workflow")
{
StageName = "并行分析阶段"
};
// ==================== Stage 2: PMO规划 ====================
yield return new WorkflowStageStartEvent("pm-workflow")
{
StageName = "PMO规划阶段",
InvolvedAgents = ["PMO"]
};
// 整合Stage 1的结果
var consolidatedAnalysis = ConsolidateAnalysisResults(analysisResults);
// 调用PMO Agent
var pmoResponse = await CallA2AAgentAsync(
"PMO",
_pmoAgent!,
$"基于以下分析结果生成详细项目计划:\n\n{consolidatedAnalysis}",
ct);
foreach (var message in pmoResponse.Messages)
{
var update = new AgentRunResponseUpdate
{
Role = message.Role,
Contents = message.Contents,
AgentId = "PMO",
CreatedAt = DateTimeOffset.Now,
MessageId = message.MessageId
};
yield return new AgentRunUpdateEvent("pm-workflow", update);
}
yield return new WorkflowStageCompleteEvent("pm-workflow")
{
StageName = "PMO规划阶段"
};
// ==================== Stage 3: PM整合决策 ====================
yield return new WorkflowStageStartEvent("pm-workflow")
{
StageName = "整合决策阶段",
InvolvedAgents = ["ProjectManager"]
};
// PM使用IChatClient进行最终整合
var chatClient = AgentHelpers.CreateChatClient(_configuration);
var finalPrompt = BuildFinalIntegrationPrompt(analysisResults, pmoResponse);
var finalResponse = await chatClient.GetResponseAsync(
[new ChatMessage(ChatRole.User, finalPrompt)],
null,
ct);
var finalUpdate = new AgentRunResponseUpdate
{
Role = ChatRole.Assistant,
Contents = [new TextContent(finalResponse.Text ?? "")],
AgentId = "ProjectManager",
CreatedAt = DateTimeOffset.Now
};
yield return new AgentRunUpdateEvent("pm-workflow", finalUpdate);
yield return new WorkflowStageCompleteEvent("pm-workflow")
{
StageName = "整合决策阶段"
};
// 工作流完成
yield return new WorkflowOutputEvent("pm-workflow");
}
工作流特点:
- Stage 1并行:Tech、HR、Finance、QA同时执行,大幅缩短时间
- Stage 2依赖Stage 1:PMO基于前4个团队的分析结果进行规划
- Stage 3汇总:PM整合所有信息,生成最终报告
- 流式返回:使用
IAsyncEnumerable,每个Agent完成后立即返回,无需等待全部完成
3.5 WorkflowBuilder API详解
Agent Framework提供了流畅的API来构建工作流:
// 线性工作流(Sequential)
var workflow = new WorkflowBuilder(agent1)
.AddEdge(agent1, agent2)
.AddEdge(agent2, agent3)
.WithOutputFrom(agent3)
.Build();
// 并行工作流(Parallel)
var workflow = new WorkflowBuilder(agent1)
.AddEdge(agent1, agent2)
.AddEdge(agent1, agent3) // agent2和agent3并行执行
.AddEdge(agent1, agent4)
.WithOutputFrom([agent2, agent3, agent4]) // 收集所有输出
.Build();
// 条件分支(Conditional)
var workflow = new WorkflowBuilder(agent1)
.AddEdge(agent1, agent2, condition: ctx => ctx.SomeFlag)
.AddEdge(agent1, agent3, condition: ctx => !ctx.SomeFlag)
.Build();
四、三大协议如何协同工作?
现在我们理解了每个协议的作用,让我们看看它们如何配合:
4.1 分层架构
4.2 完整的调用链路
让我们追踪一次完整的用户提问:
调用链路详解:
- 用户提问 → PM Agent接收并启动Agent Framework工作流
- Stage 1(并行):PM通过A2A协议同时调用4个专家Agent
- MCP工具调用:Finance Agent调用MCP工具验证预算和查询历史数据
- 结果汇总:4个Agent的分析结果收集到一起
- Stage 2(串行):PMO Agent基于前4个结果进行项目规划
- Stage 3(整合):PM Agent整合所有信息生成最终报告
- 返回用户:完整的项目计划呈现给用户
关键点:
- Workflow编排:PM Agent决定调用哪些Agent、什么时候调用
- A2A通信:所有Agent间的调用使用标准A2A协议
- MCP工具:Agent在处理请求时,可以调用自己的MCP工具获取数据
五、代码实践:如何实现一个新的智能体?
理论讲完了,让我们动手实践。假设你要添加一个"Legal(法务)"智能体。
步骤1:创建ASP.NET Core项目
dotnet new webapi -n Legal
cd Legal
dotnet add package A2A.AspNetCore
dotnet add package ModelContextProtocol.Server
dotnet add package Microsoft.Extensions.AI.OpenAI
步骤2:定义MCP工具
// Legal/Program.cs
using ModelContextProtocol.Server;
using ModelContextProtocol.Protocol;
[McpServerToolType]
public class LegalTools
{
[McpServerTool]
public Task<CallToolResult> CheckCompliance(string projectType, string region)
{
// 模拟合规检查
var result = new
{
ProjectType = projectType,
Region = region,
ComplianceStatus = "Compliant",
RequiredLicenses = new[] { "Business License", "Data Processing Agreement" },
Risks = new[] { "GDPR compliance required for EU customers" }
};
return Task.FromResult(new CallToolResult
{
Content = [new TextContentBlock {
Text = System.Text.Json.JsonSerializer.Serialize(result)
}]
});
}
}
步骤3:实现A2A Agent
// Legal/Program.cs
using A2A;
using A2A.AspNetCore;
using Microsoft.Extensions.AI;
public class LegalAgent
{
private readonly IChatClient _chatClient;
public LegalAgent(IChatClient chatClient)
{
_chatClient = chatClient;
}
public void Attach(ITaskManager taskManager)
{
taskManager.OnMessageReceived = ProcessMessageAsync;
taskManager.OnAgentCardQuery = GetAgentCardAsync;
}
private async Task<A2AResponse> ProcessMessageAsync(
MessageSendParams messageSendParams,
CancellationToken cancellationToken)
{
var messageText = messageSendParams.Message.Parts
.OfType<TextPart>()
.FirstOrDefault()?.Text ?? "";
var messages = new List<ChatMessage>
{
new(ChatRole.System,
"You are a legal compliance expert. Analyze legal and compliance requirements."),
new(ChatRole.User, messageText)
};
var completion = await _chatClient.GetResponseAsync(
messages,
cancellationToken: cancellationToken);
return new AgentMessage
{
Role = MessageRole.Agent,
MessageId = Guid.NewGuid().ToString(),
ContextId = messageSendParams.Message.ContextId,
Parts = [new TextPart { Text = completion.Text ?? "" }]
};
}
private Task<AgentCard> GetAgentCardAsync(
string agentUrl,
CancellationToken cancellationToken)
{
return Task.FromResult(new AgentCard
{
Name = "Legal Compliance Expert",
Description = "Legal expert for compliance analysis and risk assessment",
Url = agentUrl,
Version = "1.0.0",
DefaultInputModes = ["text"],
DefaultOutputModes = ["text"],
Capabilities = new AgentCapabilities
{
Streaming = false,
PushNotifications = false
},
Skills = []
});
}
}
步骤4:启动服务
// Legal/Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.AddServiceDefaults();
// 注册MCP Server
builder.Services.AddMcpServer()
.WithHttpTransport()
.WithTools<LegalTools>();
var app = builder.Build();
app.MapDefaultEndpoints();
// 暴露MCP端点
app.MapMcp("/legal/mcp");
// 暴露A2A端点
var legalAgent = new LegalAgent(
AgentHelpers.CreateChatClient(app.Configuration)
);
var legalTaskManager = new TaskManager();
legalAgent.Attach(legalTaskManager);
app.MapA2A(legalTaskManager, "/legal/compliance-expert");
app.MapWellKnownAgentCard(legalTaskManager, "/legal/compliance-expert");
app.Run();
步骤5:在Aspire中注册
// AgentFrameworkAspire.AppHost/Program.cs
var legalService = builder.AddProject<Projects.Legal>("legal")
.WithEnvironment("OpenAI__ApiKey", openAiApiKey)
.WithEnvironment("OpenAI__DeploymentName", openAiDeployment);
builder.AddProject<Projects.AgentFrameworkAspire_Web>("webfrontend")
// ... 其他引用
.WithReference(legalService)
.WaitFor(legalService);
完成! 现在你有了一个完整的Legal智能体,支持MCP工具和A2A通信。
六、最佳实践与注意事项
6.1 MCP工具设计
✅ DO:
- 工具职责单一,每个工具只做一件事
- 参数类型明确,避免使用
object - 返回结构化数据,便于AI理解
- 添加详细的XML注释,会自动生成工具描述
❌ DON'T:
- 工具不要有副作用(如修改数据库)
- 避免长时间运行的操作(超过10秒)
- 不要返回大量数据(超过10KB)
6.2 A2A Agent实现
✅ DO:
- AgentCard要准确描述能力
- 正确处理ContextId,支持多轮对话
- 使用结构化日志,便于调试
- 返回有意义的错误消息
❌ DON'T:
- 不要在ProcessMessageAsync中做耗时操作
- 不要忽略CancellationToken
- 避免在Agent中保存状态(应该是无状态的)
6.3 Workflow设计
✅ DO:
- 合理划分阶段,每个阶段职责清晰
- 并行任务要真正独立,没有依赖
- 使用
IAsyncEnumerable实现流式返回 - 记录每个阶段的耗时,便于性能优化
❌ DON'T:
- 避免过深的嵌套(超过5层)
- 不要在循环中调用Agent(可能导致成本爆炸)
- 避免没有超时控制的等待
七、性能与成本优化
7.1 并行加速
// ❌ 串行执行(慢)
var techResult = await CallA2AAgentAsync("Tech", _techAgent, input, ct);
var hrResult = await CallA2AAgentAsync("HR", _hrAgent, input, ct);
var financeResult = await CallA2AAgentAsync("Finance", _financeAgent, input, ct);
// 总时间 = T_tech + T_hr + T_finance
// ✅ 并行执行(快)
var tasks = new[]
{
CallA2AAgentAsync("Tech", _techAgent, input, ct),
CallA2AAgentAsync("HR", _hrAgent, input, ct),
CallA2AAgentAsync("Finance", _financeAgent, input, ct)
};
var results = await Task.WhenAll(tasks);
// 总时间 = max(T_tech, T_hr, T_finance)
7.2 缓存AgentCard
private static readonly ConcurrentDictionary<string, AIAgent> _agentCache = new();
private async Task<AIAgent> GetOrCreateAgentAsync(string url)
{
if (_agentCache.TryGetValue(url, out var cachedAgent))
{
return cachedAgent;
}
var agent = await InitializeAgentAsync(url);
_agentCache.TryAdd(url, agent);
return agent;
}
7.3 控制AI调用成本
// 使用更便宜的模型进行初步分析
var cheapClient = AgentHelpers.CreateChatClient(config, "gpt-4o-mini");
// 只在关键决策时使用高级模型
var premiumClient = AgentHelpers.CreateChatClient(config, "gpt-4");
八、常见问题(FAQ)
Q1: 为什么不直接用HTTP/REST?
A: A2A和MCP是AI原生的协议,专门为智能体协作设计:
- AgentCard:AI可以自动理解智能体的能力
- 标准化消息格式:减少适配工作
- 上下文管理:内置支持多轮对话
- 社区生态:未来会有更多工具支持
Q2: 这些协议成熟吗?会不会频繁变化?
A:
- MCP:由 Anthropic 主导,已有不少项目使用
- A2A:由 Google Cloud 主导推出的开放标准,Microsoft 提供了 .NET SDK 实现
- 都是开放标准,社区驱动
- 建议:代码做好抽象层,协议变化时影响面小
Q3: 性能够吗?HTTP调用会不会太慢?
A:
- 并行执行可以大幅减少总时间(3-4倍加速)
- 对于AI应用,瓶颈通常是LLM响应(5-15秒),而非网络(100-300ms)
- 可以使用gRPC替代HTTP(更快,但生态较小)
Q4: 如何调试分布式的Agent系统?
A:
- 使用Aspire Dashboard查看所有服务日志
- 分布式追踪(OpenTelemetry)追踪请求链路
- 结构化日志记录每个Agent的输入输出
- 单独启动服务进行单元测试
九、结语
三大协议——MCP、A2A、Agent Framework——共同构建了一个强大的多智能体协作生态:
- MCP:让每个团队暴露专业工具,AI可以自动发现和调用
- A2A:让智能体之间"说同一种语言",标准化通信
- Agent Framework:编排复杂工作流,实现真正的智能协作
关键优势:
- 互操作性:基于开放标准,不同团队开发的Agent可以无缝协作
- 灵活性:可以单独使用MCP/A2A,也可以组合使用
- 可扩展性:添加新Agent只需几行代码
- 面向未来:这些协议会成为企业AI的基础设施
下一步:
- 克隆项目,运行Demo
- 尝试添加你自己的智能体
- 探索更复杂的工作流场景
在下一篇文章中,我们将深入 .NET Aspire,看看如何用它简化多智能体系统的开发和调试。
参考资料
- MCP规范:https://modelcontextprotocol.io/
- MCP C# SDK:https://github.com/microsoft/mcp-dotnet
- A2A协议(Google Cloud):https://cloud.google.com/agentspace/docs/agent-to-agent
- A2A .NET SDK(社区实现):https://github.com/microsoft/a2a-dotnet
- Agent Framework:https://github.com/microsoft/agents
- 本项目地址:https://github.com/MadLongTom/A2AMicroserviceSample
本文来自博客园,作者:MadLongTom,转载请注明原文链接:https://www.cnblogs.com/madtom/p/19140161
浙公网安备 33010602011771号