이를 통해 애플리케이션은 A2AAgentA2A(에이전트 간) 프로토콜을 통해 노출되는 원격 에이전트에 연결할 수 있습니다. A2A 규격 엔드포인트를 표준 AIAgent으로 래핑하므로 빌드된 프레임워크 또는 기술에 관계없이 익숙한 메서드를 RunAsyncRunStreamingAsync 사용하고 원격 에이전트와 상호 작용할 수 있습니다.
에이전트 프레임워크 에이전트를 A2A 서버로 노출하려면 A2A가 있는 호스트 에이전트를 참조하세요.
Getting Started
프로젝트에 필요한 NuGet 패키지를 추가합니다.
dotnet add package Microsoft.Agents.AI.A2A --prerelease
에이전트 검색
원격 A2A 에이전트와 통신하기 전에 이를 검색하고 인스턴스를 AIAgent 만들어야 합니다. A2A 프로토콜은 각각 에이전트 프레임워크에서 지원하는 세 가지 검색 전략을 정의합니다.
Well-Known URI
A2A 에이전트는 표준화된 경로에서 https://{domain}/.well-known/agent-card.json 검색할 수 있도록 할 수 있습니다. 카드를 A2ACardResolver 가져오고 한 번의 호출로 에이전트를 만들려면 다음을 사용합니다.
using A2A;
using Microsoft.Agents.AI;
// Initialize a resolver pointing at the remote agent's host.
A2ACardResolver resolver = new(new Uri("https://a2a-agent.example.com"));
// Resolve the agent card and create an AIAgent in one step.
AIAgent agent = await resolver.GetAIAgentAsync();
// Use the agent.
Console.WriteLine(await agent.RunAsync("Hello!"));
Catalog-Based 검색
엔터프라이즈 환경 또는 공용 마켓플레이스에서 에이전트 카드는 종종 중앙 레지스트리에서 관리됩니다. 이러한 레지스트리에서 이미 AgentCard 가져온 경우 다음으로 직접 AIAgent변환합니다.
using A2A;
using Microsoft.Agents.AI;
// Assume agentCard was retrieved from a registry or catalog.
AgentCard agentCard = await GetAgentCardFromRegistryAsync("travel-planner");
AIAgent agent = agentCard.AsAIAgent();
Console.WriteLine(await agent.RunAsync("Plan a trip to Paris."));
직접 구성
에이전트 엔드포인트가 미리 알려진 긴밀하게 결합된 시스템 또는 개발 시나리오의 경우 직접 만들고 A2AClient 다음으로 AIAgent변환합니다.
using A2A;
using Microsoft.Agents.AI;
// Create a client pointing at the known agent endpoint.
A2AClient a2aClient = new(new Uri("https://a2a-agent.example.com"));
AIAgent agent = a2aClient.AsAIAgent(name: "my-agent", description: "A helpful assistant.");
Console.WriteLine(await agent.RunAsync("What can you help me with?"));
프로토콜 선택
A2A 에이전트는 HTTP+JSON 및 JSON-RPC와 같은 여러 프로토콜 바인딩을 노출할 수 있습니다. 기본적으로 HTTP+JSON은 JSON-RPC보다 선호됩니다. 사용되는 프로토콜 바인딩을 명시적으로 제어하는 데 사용합니다 A2AClientOptions.PreferredBindings .
비고
선택한 프로토콜 바인딩을 지원하는 엔드포인트에서 원격 A2A 에이전트를 사용할 수 있어야 합니다.
using A2A;
using Microsoft.Agents.AI;
A2ACardResolver agentCardResolver = new(new Uri("https://a2a-agent.example.com"));
AgentCard agentCard = await agentCardResolver.GetAgentCardAsync();
// Prefer HTTP+JSON protocol binding. For JSON-RPC, set PreferredBindings = [ProtocolBindingNames.JsonRpc]
A2AClientOptions options = new()
{
PreferredBindings = [ProtocolBindingNames.HttpJson]
};
AIAgent agent = agentCard.AsAIAgent(options: options);
Console.WriteLine(await agent.RunAsync("Tell me a joke about a pirate."));
Streaming
A2A는 Server-Sent 이벤트를 통해 스트리밍 응답을 지원합니다. 원격 에이전트가 요청을 처리할 때 실시간으로 업데이트를 수신하는 데 사용합니다 RunStreamingAsync .
using A2A;
using Microsoft.Agents.AI;
A2ACardResolver resolver = new(new Uri("https://a2a-agent.example.com"));
AIAgent agent = await resolver.GetAIAgentAsync();
await foreach (var update in agent.RunStreamingAsync("Write a short story about a robot."))
{
if (!string.IsNullOrEmpty(update.Text))
{
Console.Write(update.Text);
}
}
백그라운드 응답
A2A 에이전트는 장기 실행 작업을 처리하기 위한 백그라운드 응답을 지원합니다. 원격 A2A 에이전트가 즉각적인 메시지 대신 작업을 반환하는 경우 에이전트 프레임워크는 결과를 폴링하거나 중단된 스트림에 다시 연결하는 데 사용할 수 있는 연속 토큰을 제공합니다.
작업 완료를 위한 폴링
비 스트리밍 시나리오의 경우 연속 토큰을 수신하고 태스크가 완료될 때까지 폴링하는 데 사용합니다 AllowBackgroundResponses .
using A2A;
using Microsoft.Agents.AI;
A2ACardResolver resolver = new(new Uri("https://a2a-agent.example.com"));
AIAgent agent = await resolver.GetAIAgentAsync();
AgentSession session = await agent.CreateSessionAsync();
// AllowBackgroundResponses must be true so the server returns immediately with a continuation token
// instead of blocking until the task is complete.
AgentRunOptions options = new() { AllowBackgroundResponses = true };
// Start the initial run with a long-running task.
AgentResponse response = await agent.RunAsync(
"Conduct a comprehensive analysis of quantum computing applications in cryptography.",
session,
options: options);
// Poll until the response is complete.
while (response.ContinuationToken is { } token)
{
// Wait before polling again.
await Task.Delay(TimeSpan.FromSeconds(2));
// Continue with the token.
response = await agent.RunAsync(session, options: new AgentRunOptions { ContinuationToken = token });
}
Console.WriteLine(response);
스트림 다시 연결
스트리밍 시나리오에서 각 업데이트에는 연속 토큰이 포함될 수 있습니다. 스트림이 중단되면 토큰을 사용하여 다시 연결하고 처음부터 응답 스트림을 가져옵니다.
using A2A;
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
A2ACardResolver resolver = new(new Uri("https://a2a-agent.example.com"));
AIAgent agent = await resolver.GetAIAgentAsync();
AgentSession session = await agent.CreateSessionAsync();
ResponseContinuationToken? continuationToken = null;
await foreach (var update in agent.RunStreamingAsync(
"Conduct a comprehensive analysis of quantum computing applications in cryptography.",
session))
{
// Save the continuation token to reconnect later if the stream is interrupted.
// Continuation tokens are only returned for long-running tasks. If the A2A agent
// returns a message instead of a task, the continuation token will not be initialized.
if (update.ContinuationToken is { } token)
{
continuationToken = token;
}
}
// If the stream was interrupted and a continuation token was captured,
// reconnect to the response stream using the saved continuation token.
if (continuationToken is not null)
{
await foreach (var update in agent.RunStreamingAsync(
session,
options: new() { ContinuationToken = continuationToken }))
{
if (!string.IsNullOrEmpty(update.Text))
{
Console.WriteLine(update.Text);
}
}
}
비고
A2A 에이전트는 스트림의 특정 지점에서 다시 시작을 스트림하는 것이 아니라 스트림 다시 연결을 지원합니다(처음부터 동일한 응답 스트림 가져오기).
Tools
A2AAgent 는 원격 A2A 에이전트를 둘러싼 전송 수준 래퍼입니다. 원격 에이전트가 사용하는 도구는 원격 쪽에서 라이브로 사용되며 코드에 표시되지 않습니다. 에이전트 프레임워크 도구 형식(함수 도구, 코드 인터프리터, 파일 검색, 호스트/로컬 MCP 등)은 자체에 A2AAgent 구성되지 않으므로 원격 에이전트의 기능을 확장하고 원격 에이전트의 구성을 변경합니다.
Getting Started
A2A 패키지를 설치합니다.
pip install agent-framework-a2a --pre
초기화
A2AAgent 는 원격 에이전트에 대해 미리 알고 있는 양에 따라 세 가지 방법으로 초기화할 수 있습니다.
직접 URL
엔드포인트가 알려진 개발 또는 긴밀하게 결합된 시스템의 경우:
from agent_framework.a2a import A2AAgent
async with A2AAgent(name="remote", url="https://a2a-agent.example.com") as agent:
response = await agent.run("Hello!")
print(response.messages[0].text)
URL만 제공되면 A2AAgent 내부적으로 최소 에이전트 카드를 만들고 JSON-RPC를 사용하여 연결합니다.
에이전트 카드
레지스트리 또는 카탈로그가 AgentCard 있는 경우 직접 전달합니다.
from agent_framework.a2a import A2AAgent
async with A2AAgent(agent_card=agent_card) as agent:
response = await agent.run("Plan a trip to Paris.")
print(response.messages[0].text)
제공 AgentCard 되면 A2AAgent 기본값 name 및 description 카드에서 가져옵니다. 카드로 전송을 협상합니다 supported_interfaces.
Well-Known URI(A2ACardResolver)
표준 잘 알려진 경로(A2ACardResolver)에서 원격 에이전트를 검색하려면 다음 a2a-sdk 을 사용합니다/.well-known/agent.json.
import httpx
from a2a.client import A2ACardResolver
from agent_framework.a2a import A2AAgent
async with httpx.AsyncClient(timeout=60.0) as http_client:
resolver = A2ACardResolver(httpx_client=http_client, base_url="https://a2a-agent.example.com")
agent_card = await resolver.get_agent_card()
async with A2AAgent(agent_card=agent_card) as agent:
response = await agent.run("What can you help me with?")
print(response.messages[0].text)
Streaming
원격 에이전트가 요청을 처리할 때 실시간으로 업데이트를 수신하는 데 사용합니다 stream=True .
from agent_framework.a2a import A2AAgent
async with A2AAgent(name="remote", url="https://a2a-agent.example.com") as agent:
stream = agent.run("Write a short story about a robot.", stream=True)
async for update in stream:
for content in update.contents:
if content.text:
print(content.text, end="", flush=True)
final = await stream.get_final_response()
print(f"\n({len(final.messages)} message(s))")
장시간 실행 작업
기본적으로 A2AAgent 원격 에이전트가 완료되기를 기다린 후 반환합니다. 장기 실행 작업의 경우 나중에 폴링하거나 구독하는 데 사용할 수 있는 연속 토큰을 표시하도록 설정합니다 background=True .
from agent_framework.a2a import A2AAgent
async with A2AAgent(name="worker", url="https://a2a-agent.example.com") as agent:
# Start a long-running task
response = await agent.run("Process this large dataset", background=True)
if response.continuation_token:
# Poll for completion later
result = await agent.poll_task(response.continuation_token)
print(result)
폴링 대신 SSE 스트림을 다시 구독할 수도 있습니다.
# Resubscribe to the task's event stream
response = await agent.run(continuation_token=response.continuation_token)
대화 식별자(context_id)
A2AAgent 는 지속성 프로토콜 상태를 AgentSession.service_session_id 매핑으로 A2AServiceSessionId 저장합니다.
| Field | Type | Purpose |
|---|---|---|
context_id |
str |
A2A 대화를 식별합니다. |
task_id |
str \| None |
응답에서 만든 가장 최근의 원격 작업을 추적합니다. |
task_state |
TaskState \| None |
다음 요청이 입력 필수 작업을 계속하거나 완료된 작업을 참조할 수 있도록 최신 작업 상태를 기록합니다. |
애플리케이션이 A2A 컨텍스트를 이미 알고 있는 경우 구조화된 상태로 세션을 만듭니다.
from agent_framework import AgentSession
from agent_framework.a2a import A2AAgent, A2AServiceSessionId
async with A2AAgent(name="remote", url="https://a2a-agent.example.com") as agent:
session = AgentSession(
service_session_id=A2AServiceSessionId(
context_id="my-conversation-1",
task_id=None,
task_state=None,
)
)
# The A2A message uses context_id="my-conversation-1".
response = await agent.run("Hello!", session=session)
# A2AAgent updates task_id and task_state from the response.
response = await agent.run("Follow-up question", session=session)
첫 번째 응답에서 구조화된 매핑으로 AgentSession()A2AAgent 시작하고 채울 수도 있습니다. 일반 세션을 session.to_dict() 유지하고 A2A 컨텍스트, 작업 ID 및 작업 상태를 함께 유지하여 복원 AgentSession.from_dict(...)합니다.
작업의 TASK_STATE_INPUT_REQUIRED경우 다음 메시지는 동일한 작업을 계속하도록 설정합니다 task_id . 다른 작업 상태의 경우 원격 에이전트가 이전 결과를 구체화하거나 계속할 수 있도록 이전 작업 ID가 전송 reference_task_ids 됩니다.
Authentication
A2A 보안 엔드포인트에 AuthInterceptor를 사용합니다.
from a2a.client.auth.interceptor import AuthInterceptor
from agent_framework.a2a import A2AAgent
class BearerAuth(AuthInterceptor):
def __init__(self, token: str):
self.token = token
async def intercept(self, request):
request.headers["Authorization"] = f"Bearer {self.token}"
return request
async with A2AAgent(
name="secure-agent",
url="https://secure-a2a-agent.example.com",
auth_interceptor=BearerAuth("your-token"),
) as agent:
response = await agent.run("Hello!")
시간 제한 구성
A2AAgent 는 timeout 요청 시간 제한을 제어하기 위한 매개 변수를 허용합니다.
import httpx
from agent_framework.a2a import A2AAgent
# Simple timeout (applies to all components)
async with A2AAgent(name="remote", url="https://example.com", timeout=120.0) as agent:
...
# Fine-grained timeout
async with A2AAgent(
name="remote",
url="https://example.com",
timeout=httpx.Timeout(connect=10.0, read=120.0, write=10.0, pool=5.0),
) as agent:
...
시간 제한을 지정하지 않으면 기본값은 10대 연결, 60년대 읽기, 10s 쓰기, 5s 풀입니다.
Tools
A2AAgent 는 원격 A2A 에이전트를 둘러싼 전송 수준 래퍼입니다. 원격 에이전트가 사용하는 도구는 원격 쪽에서 라이브로 사용되며 코드에 표시되지 않습니다. 에이전트 프레임워크 도구 형식(함수 도구, 코드 인터프리터, 파일 검색, 호스트/로컬 MCP 등)은 자체에 A2AAgent 구성되지 않으므로 원격 에이전트의 기능을 확장하고 원격 에이전트의 구성을 변경합니다.
Foundry 에이전트가 A2A 에이전트를 도구로 호출하도록 하려면 <
Go는 패키지를 통해 원격 A2A 에이전트를 provider/a2aprovider 지원합니다.
에이전트 프레임워크 및 A2A 패키지를 설치합니다.
go get github.com/microsoft/agent-framework-go
go get github.com/a2aproject/a2a-go/v2
원격 A2A 에이전트에 연결
원격 에이전트 카드를 해석하여 A2A 클라이언트를 생성하고, 이 클라이언트를 표준 Agent Framework 에이전트로 래핑합니다.
import (
"context"
"github.com/a2aproject/a2a-go/v2/a2aclient"
"github.com/a2aproject/a2a-go/v2/a2aclient/agentcard"
"github.com/microsoft/agent-framework-go/agent"
"github.com/microsoft/agent-framework-go/provider/a2aprovider"
)
ctx := context.Background()
card, err := agentcard.DefaultResolver.Resolve(ctx, "http://localhost:5000")
if err != nil {
panic(err)
}
client, err := a2aclient.NewFromCard(ctx, card)
if err != nil {
panic(err)
}
a := a2aprovider.NewAgent(
client,
a2aprovider.AgentConfig{
Config: agent.Config{
Name: card.Name,
Description: card.Description,
},
},
)
resp, err := a.RunText(ctx, "Hello!").Collect()
공급자는 에이전트 프레임워크 세션에 A2A context_id 및 작업 ID를 저장하므로 후속 메시지는 대화 연속성을 유지할 수 있습니다.
프로토콜 선택
원격 에이전트가 여러 전송 바인딩을 보급하는 경우 A2A 클라이언트를 만들 때 기본 전송을 구성합니다.
client, err := a2aclient.NewFromCard(
ctx,
card,
a2aclient.WithConfig(a2aclient.Config{
PreferredTransports: []a2a.TransportProtocol{a2a.TransportProtocolHTTPJSON},
}),
)
JSON-RPC를 선호하는 경우 사용합니다 a2a.TransportProtocolJSONRPC .
장기 실행 작업
A2A 작업은 에이전트 프레임워크 연속 토큰을 통해 표시됩니다. 명시적 세션과 agent.AllowBackgroundResponses(true)을 사용해 실행을 시작한 다음, 새 메시지 없이 continuation token을 사용하여 Run를 호출해 폴링합니다.
session, err := a.CreateSession(ctx)
if err != nil {
panic(err)
}
resp, err := a.RunText(
ctx,
"Process this large dataset.",
agent.WithSession(session),
agent.AllowBackgroundResponses(true),
).Collect()
if err != nil {
panic(err)
}
for resp.ContinuationToken != "" {
resp, err = a.Run(
ctx,
nil,
agent.WithSession(session),
agent.WithContinuationToken(resp.ContinuationToken),
).Collect()
if err != nil {
panic(err)
}
}
중단된 스트리밍 실행의 경우, 마지막으로 수신한 업데이트에서 update.ContinuationToken를 캡처한 다음, 이후의 스트리밍 실행에 agent.WithContinuationToken(token) 및 agent.Stream(true)와 함께 전달합니다.
원격 A2A 에이전트를 도구로 사용
각 원격 에이전트를 확인하고, 래핑 a2aprovider.NewAgent하고, 를 사용하여 도구로 agenttool.New변환합니다.
tools := make([]tool.Tool, 0, len(agentURLs))
for _, agentURL := range agentURLs {
card, err := agentcard.DefaultResolver.Resolve(ctx, agentURL)
if err != nil {
panic(err)
}
client, err := a2aclient.NewFromCard(ctx, card)
if err != nil {
panic(err)
}
remoteAgent := a2aprovider.NewAgent(client, a2aprovider.AgentConfig{
Config: agent.Config{
Name: card.Name,
Description: card.Description,
},
})
tools = append(tools, agenttool.New(remoteAgent, agenttool.Config{}))
}
Tip
전체 실행 가능한 예제는 A2A 공급자 샘플 및 A2A 에이전트를 도구 샘플로 참조하세요.
다음 단계
더 자세히 살펴보기: