
A2A (Agent2Agent) .NET SDK는 Google의 A2A Protocol v0.2.1을 구현하는 .NET 라이브러리로, .NET 애플리케이션에서 에이전트 간 통신을 가능하게 합니다. 이 SDK는 ASP.NET Core 애플리케이션과 함께 작동하도록 설계되어 에이전트에 A2A 지원을 추가하는 간단한 방법을 제공합니다.
라이브러리는 프로토콜 기능의 대부분을 구현했지만, 일부 시나리오는 여전히 불완전할 수 있습니다. 가장 큰 누락 기능은 푸시 알림을 사용한 클라이언트 콜백입니다.
src/
├── A2A/ # 핵심 A2A 프로토콜 구현
│ ├── Client/ # 클라이언트 구성 요소
│ ├── JsonRpc/ # JSON-RPC 구현
│ ├── Models/ # 데이터 모델
│ ├── Server/ # 서버 측 구성 요소
│ └── openapi.yaml # API 사양
└── A2A.AspNetCore/ # ASP.NET Core 통합
A2AClient 클래스는 에이전트와 통신하기 위한 주요 클라이언트 인터페이스로, IA2AClient 인터페이스를 구현합니다.
public class A2AClient : IA2AClient
{
// 메시지 전송
public Task<A2AResponse> SendMessageAsync(MessageSendParams taskSendParams)
// 작업 가져오기
public Task<AgentTask> GetTaskAsync(string taskId)
// 작업 취소
public Task<AgentTask> CancelTaskAsync(TaskIdParams taskIdParams)
// 스트림 메시지 전송
public async IAsyncEnumerable<SseItem<A2AEvent>> SendMessageStreamAsync(MessageSendParams taskSendParams)
// 작업 재구독
public async IAsyncEnumerable<SseItem<A2AEvent>> ResubscribeToTaskAsync(string taskId)
}
JSON-RPC 요청을 위한 전용 HTTP 콘텐츠 클래스:
public class JsonRpcContent : HttpContent
{
public JsonRpcContent(JsonRpcRequest request)
{
_request = request;
Headers.ContentType = new MediaTypeHeaderValue("application/json");
}
}
에이전트 카드 정보 파싱 및 검색에 사용:
public class A2ACardResolver
{
public async Task<AgentCard> GetAgentCardAsync(Uri agentUri)
public async Task<AgentCard> GetAgentCardAsync(string agentUrl)
}
TaskManager는 작업 생명주기 관리를 담당하는 핵심 서버 측 구성 요소입니다.
ITaskStore 인터페이스를 통한 작업 지속성public class TaskManager : ITaskManager
{
// 이벤트 핸들러
public Func<MessageSendParams, Task<Message>>? OnMessageReceived { get; set; }
public Func<AgentTask, Task> OnTaskCreated { get; set; }
public Func<AgentTask, Task> OnTaskCancelled { get; set; }
public Func<AgentTask, Task> OnTaskUpdated { get; set; }
public Func<string, AgentCard> OnAgentCardQuery { get; set; }
// 핵심 작업
public async Task<AgentTask> CreateTaskAsync(string? contextId = null)
public async Task<AgentTask?> CancelTaskAsync(TaskIdParams? taskIdParams)
public async Task<A2AResponse?> SendMessageAsync(MessageSendParams messageSendParams)
public async Task<IAsyncEnumerable<A2AEvent>> SendMessageStreamAsync(MessageSendParams messageSendParams)
public async Task UpdateStatusAsync(string taskId, TaskState status, Message? message = null, bool final = false)
public async Task ReturnArtifactAsync(string taskId, Artifact artifact)
}
작업 저장을 위한 추상 인터페이스:
public interface ITaskStore
{
Task<AgentTask?> GetTaskAsync(string taskId);
Task SetTaskAsync(AgentTask task);
Task UpdateStatusAsync(string taskId, TaskState status);
}
A2A.AspNetCore 라이브러리에서 제공하는 확장 메서드를 통해:
// JSON-RPC A2A 지원 추가
app.MapA2A(taskManager, "/echo");
// HTTP A2A 지원 추가
app.MapHttpA2A(taskManager, "/echo");
public class AgentCard
{
public string Name { get; set; } // 에이전트 이름
public string Description { get; set; } // 에이전트 설명
public string Url { get; set; } // 에이전트 URL
public AgentProvider? Provider { get; set; } // 제공자 정보
public string Version { get; set; } // 버전 정보
public AgentCapabilities Capabilities { get; set; } // 에이전트 기능
public List<AgentSkill> Skills { get; set; } // 에이전트 기술
public List<string> DefaultInputModes { get; set; } // 기본 입력 모드
public List<string> DefaultOutputModes { get; set; }// 기본 출력 모드
}
public class AgentTask : A2AResponse
{
public string Id { get; set; } // 작업 ID
public string? ContextId { get; set; } // 컨텍스트 ID
public AgentTaskStatus Status { get; set; } // 작업 상태
public List<Artifact>? Artifacts { get; set; } // 작업 아티팩트
public List<Message>? History { get; set; } // 메시지 기록
public Dictionary<string, JsonElement>? Metadata { get; set; } // 메타데이터
}
public class Message : A2AResponse
{
public MessageRole Role { get; set; } // 메시지 역할 (User/Agent)
public List<Part> Parts { get; set; } // 메시지 부분
public string? MessageId { get; set; } // 메시지 ID
public string? TaskId { get; set; } // 연관된 작업 ID
public string? ContextId { get; set; } // 컨텍스트 ID
public Dictionary<string, JsonElement>? Metadata { get; set; } // 메타데이터
}
public enum TaskState
{
Submitted, // 제출됨
Working, // 작업 중
InputRequired, // 입력 필요
Completed, // 완료됨
Canceled, // 취소됨
Failed, // 실패
Rejected // 거부됨
}
여러 메시지 부분 유형 지원:
TextPart: 텍스트 콘텐츠FilePart: 파일 콘텐츠DataPart: 데이터 콘텐츠AgentServer 예제는 다음을 포함하여 여러 다른 유형의 에이전트를 생성하고 배포하는 방법을 보여줍니다:
public class EchoAgent
{
private ITaskManager? _taskManager;
public void Attach(TaskManager taskManager)
{
_taskManager = taskManager;
taskManager.OnMessageReceived = ProcessMessage;
taskManager.OnAgentCardQuery = GetAgentCard;
}
public Task<Message> ProcessMessage(MessageSendParams messageSendParams)
{
var messageText = messageSendParams.Message.Parts.OfType<TextPart>().First().Text;
var message = new Message()
{
Role = MessageRole.Agent,
MessageId = Guid.NewGuid().ToString(),
ContextId = messageSendParams.Message.ContextId,
Parts = [new TextPart() { Text = $"Echo: {messageText}" }]
};
return Task.FromResult(message);
}
}
ResearcherAgent는 더 복잡한 상태 머신 구현을 보여줍니다:
public class ResearcherAgent
{
private enum AgentState
{
Planning, // 계획 단계
WaitingForFeedbackOnPlan, // 계획 피드백 대기
Researching // 연구 단계
}
public async Task Invoke(string taskId, string message)
{
switch (_agentStates[taskId])
{
case AgentState.Planning:
await DoPlanning(taskId, message);
break;
case AgentState.WaitingForFeedbackOnPlan:
if (message == "go ahead")
await DoResearch(taskId, message);
else
await DoPlanning(taskId, message);
break;
case AgentState.Researching:
await DoResearch(taskId, message);
break;
}
}
}
프로젝트 복제:
git clone https://github.com/a2aproject/a2a-dotnet
cd a2a-dotnet
예제 디렉토리로 이동:
cd samples/AgentServer
프로젝트 실행:
dotnet run
서비스 실행 확인:
서비스는 다음 포트에서 시작됩니다:
/echo - 간단한 에코 에이전트/echotasks - 작업 지원이 있는 에코 에이전트/hostedclient - 호스팅된 클라이언트 에이전트/researcher - 연구자 에이전트curl -X GET http://localhost:5048/echo/.well-known/agent.json
curl -X POST http://localhost:5048/echo \
-H "Content-Type: application/json" \
-d '{
"id": "1",
"jsonrpc": "2.0",
"method": "message/send",
"params": {
"message": {
"messageId": "12345",
"role": "user",
"parts": [
{
"kind": "text",
"text": "Hello, world!"
}
]
}
}
}'
curl -X POST http://localhost:5048/researcher \
-H "Content-Type: application/json" \
-d '{
"id": "1",
"jsonrpc": "2.0",
"method": "message/send",
"params": {
"message": {
"messageId": "research-1",
"role": "user",
"parts": [
{
"kind": "text",
"text": "Research the current price of butter"
}
]
}
}
}'
sequenceDiagram
participant Client as Client
participant Server as A2A Server
participant Agent as Agent Implementation
participant TaskMgr as TaskManager
Client->>Server: POST /echo (JSON-RPC message/send)
Server->>TaskMgr: SendMessageAsync()
TaskMgr->>Agent: OnMessageReceived()
Agent->>Agent: ProcessMessage()
Agent-->>TaskMgr: Return Message
TaskMgr-->>Server: Return A2AResponse
Server-->>Client: JSON-RPC Response
sequenceDiagram
participant Client as Client
participant Server as A2A Server
participant TaskMgr as TaskManager
participant Agent as Agent Implementation
participant Store as TaskStore
Client->>Server: POST /researcher (message/send)
Server->>TaskMgr: SendMessageAsync()
TaskMgr->>TaskMgr: CreateTaskAsync()
TaskMgr->>Store: SetTaskAsync()
TaskMgr->>Agent: OnTaskCreated()
Agent->>Agent: State Change (Planning)
Agent->>TaskMgr: UpdateStatusAsync(Working)
TaskMgr->>Store: UpdateStatusAsync()
Agent->>TaskMgr: ReturnArtifactAsync()
Agent->>TaskMgr: UpdateStatusAsync(InputRequired)
TaskMgr-->>Server: Return AgentTask
Server-->>Client: JSON-RPC Response
Note over Client,Store: Client sends follow-up message
Client->>Server: POST /researcher (message/send, taskId)
Server->>TaskMgr: SendMessageAsync()
TaskMgr->>Store: GetTaskAsync()
TaskMgr->>Agent: OnTaskUpdated()
Agent->>Agent: State processing logic
Agent->>TaskMgr: UpdateStatusAsync(Completed)
TaskMgr-->>Server: Return AgentTask
Server-->>Client: JSON-RPC Response
sequenceDiagram
participant Client as Client
participant Server as A2A Server
participant TaskMgr as TaskManager
participant Agent as Agent Implementation
Client->>Server: POST /echo (JSON-RPC message/stream)
Server->>TaskMgr: SendMessageStreamAsync()
TaskMgr->>Agent: OnMessageReceived()
loop Streaming Response
Agent->>TaskMgr: Generate Event
TaskMgr->>Server: A2AEvent
Server->>Client: Server-Sent Event
end
Agent->>TaskMgr: Complete Processing
TaskMgr->>Server: End Stream
Server->>Client: Close Connection
TaskManager는 이벤트 기반 패턴을 사용합니다:
OnMessageReceived: 들어오는 메시지 처리OnTaskCreated: 작업 생성 이벤트OnTaskUpdated: 작업 업데이트 이벤트OnTaskCancelled: 작업 취소 이벤트다른 에이전트 구현은 다른 처리 전략을 가질 수 있습니다:
ITaskStore 인터페이스를 통한 작업 저장소 추상화:
InMemoryTaskStore: 메모리 내 저장소 구현통합된 OpenTelemetry 지원:
// OpenTelemetry 구성 예제
builder.Services.AddOpenTelemetry()
.WithTracing(tracing => tracing
.AddSource(TaskManager.ActivitySource.Name)
.AddSource(A2AJsonRpcProcessor.ActivitySource.Name)
.AddConsoleExporter()
.AddOtlpExporter());
A2A .NET SDK는 .NET 애플리케이션에서 에이전트 간 통신을 구현하기 위한 완전하고 프로덕션 준비된 솔루션을 제공합니다.
이 SDK는 .NET 개발자에게 차세대 지능형 에이전트 시스템을 구축하기 위한 강력하고 유연한 플랫폼을 제공합니다. 명확한 아키텍처와 풍부한 예제를 통해 개발자는 빠르게 시작하고 복잡한 에이전트 간 통신 애플리케이션을 구축할 수 있습니다.