Agent-to-Agent (A2A) 프로토콜

Agent-to-Agent (A2A) 프로토콜은 agent들이 네트워크 경계를 넘어 통신하고 협업할 수 있도록 합니다. adk-rust는 A2A를 통해 agent를 노출하는 것과 원격 A2A agent를 사용하는 것 모두를 완벽하게 지원합니다.

개요

A2A는 다음 경우에 유용합니다:

  • 타사 agent 서비스와 통합할 때
  • 특수화된 agent로 마이크로서비스 아키텍처를 구축할 때
  • 크로스 언어 agent 통신을 가능하게 할 때
  • agent 시스템 간의 공식 계약을 강제할 때

간단한 내부 조직에는 더 나은 성능을 위해 A2A 대신 로컬 sub-agent를 사용하세요.

Agent 카드

모든 A2A agent는 자신의 기능을 설명하는 agent 카드를 노출합니다. 이 카드는 자동 생성되며 /.well-known/agent.json에서 제공됩니다.

use adk_server::a2a::build_agent_card;

let agent_card = build_agent_card(&agent, "http://localhost:8080");

println!("Agent: {}", agent_card.name);
println!("Skills: {}", agent_card.skills.len());
println!("Streaming: {}", agent_card.capabilities.streaming);

agent 카드에는 다음이 포함됩니다:

  • Agent 이름과 설명
  • 통신을 위한 기본 URL
  • 기능 (스트리밍, 상태 기록 등)
  • agent와 해당 sub-agent에서 파생된 Skill

A2A를 통해 agent 노출하기

다른 agent가 사용할 수 있도록 agent를 노출하려면 A2A 엔드포인트가 있는 HTTP 서버를 생성하세요:

use adk_server::{create_app_with_a2a, ServerConfig};
use adk_agent::LlmAgentBuilder;
use adk_model::gemini::GeminiModel;
use std::sync::Arc;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Create your agent
    let model = GeminiModel::new(&api_key, "gemini-2.5-flash")?;
    let agent = LlmAgentBuilder::new("weather_agent")
        .description("Answers weather questions")
        .model(Arc::new(model))
        .build()?;

    // Create server config
    let config = ServerConfig::new(
        Arc::new(SingleAgentLoader::new(Arc::new(agent))),
        Arc::new(InMemorySessionService::new()),
    );

    // Create app with A2A support
    let app = create_app_with_a2a(config, Some("http://localhost:8080"));

    // Serve
    let listener = tokio::net::TcpListener::bind("0.0.0.0:8080").await?;
    axum::serve(listener, app).await?;

    Ok(())
}

이는 다음을 노출합니다:

  • GET /.well-known/agent.json - Agent 카드
  • POST /a2a - A2A 프로토콜용 JSON-RPC 엔드포인트
  • POST /a2a/stream - SSE 스트리밍 엔드포인트

원격 Agent 사용하기

원격 A2A agent와 통신하려면 RemoteA2aAgent를 사용하세요:

use adk_server::a2a::RemoteA2aAgent;

let remote_agent = RemoteA2aAgent::builder("prime_checker")
    .description("Checks if numbers are prime")
    .agent_url("http://localhost:8001")
    .build()?;

// Use as a sub-agent
let root_agent = LlmAgentBuilder::new("root")
    .model(Arc::new(model))
    .sub_agent(Arc::new(remote_agent))
    .build()?;

RemoteA2aAgent는 다음을 수행합니다:

  • 원격 URL에서 agent 카드를 자동으로 가져옵니다.
  • ADK 이벤트를 A2A 프로토콜 메시지로 또는 그 반대로 변환합니다.
  • 스트리밍 응답을 처리합니다.
  • sub-agent로 원활하게 작동합니다.

A2A 클라이언트

원격 agent와 직접 통신하려면 A2aClient를 사용하세요:

use adk_server::a2a::{A2aClient, Message, Part, Role};

// Create client from URL (fetches agent card)
let client = A2aClient::from_url("http://localhost:8080").await?;

// Build a message
let message = Message::builder()
    .role(Role::User)
    .parts(vec![Part::text("What's the weather?".to_string())])
    .message_id(uuid::Uuid::new_v4().to_string())
    .build();

// Send message (blocking)
let response = client.send_message(message.clone()).await?;

// Or send with streaming
let mut stream = client.send_streaming_message(message).await?;
while let Some(event) = stream.next().await {
    match event? {
        UpdateEvent::TaskArtifactUpdate(artifact) => {
            println!("Artifact: {:?}", artifact);
        }
        UpdateEvent::TaskStatusUpdate(status) => {
            println!("Status: {:?}", status.status.state);
        }
    }
}

JSON-RPC 프로토콜

adk-rust는 JSON-RPC 2.0을 사용하여 A2A 프로토콜을 구현합니다. 지원되는 메서드는 다음과 같습니다:

message/send

agent에게 메시지를 보냅니다:

{
  "jsonrpc": "2.0",
  "method": "message/send",
  "params": {
    "message": {
      "role": "user",
      "messageId": "msg-123",
      "parts": [{"text": "Hello!"}]
    }
  },
  "id": 1
}

응답에는 status 및 artifacts를 포함하는 task 객체가 있습니다.

message/stream

message/send와 동일하지만 스트리밍 응답을 위해 Server-Sent Events (SSE)를 반환합니다.

tasks/cancel

실행 중인 task를 취소합니다:

{
  "jsonrpc": "2.0",
  "method": "tasks/cancel",
  "params": {
    "taskId": "task-123"
  },
  "id": 1
}

다중 Agent 예제

로컬 agent와 원격 agent를 결합합니다:

// 로컬 agent
let roll_agent = LlmAgentBuilder::new("roll_agent")
    .description("Rolls dice")
    .model(Arc::new(model.clone()))
    .tool(Arc::new(roll_die_tool))
    .build()?;

// 원격 agent
let prime_agent = RemoteA2aAgent::builder("prime_agent")
    .description("Checks if numbers are prime")
    .agent_url("http://localhost:8001")
    .build()?;

// 루트 agent가 둘 다 오케스트레이션합니다
let root_agent = LlmAgentBuilder::new("root_agent")
    .instruction("Delegate dice rolling to roll_agent and prime checking to prime_agent")
    .model(Arc::new(model))
    .sub_agent(Arc::new(roll_agent))
    .sub_agent(Arc::new(prime_agent))
    .build()?;

오류 처리

A2A 작업은 표준 adk 오류를 반환합니다:

match client.send_message(message).await {
    Ok(response) => {
        if let Some(error) = response.error {
            eprintln!("RPC error: {} (code: {})", error.message, error.code);
        }
    }
    Err(e) => {
        eprintln!("Request failed: {}", e);
    }
}

일반적인 오류 코드:

  • -32600: 잘못된 요청
  • -32601: 메서드를 찾을 수 없음
  • -32602: 잘못된 params
  • -32603: 내부 오류

모범 사례

  1. agent card 사용: 통신 전에 항상 agent card를 가져오고 유효성을 검사합니다.
  2. 스트리밍 처리: 장기 실행 작업에 스트리밍을 사용합니다.
  3. 오류 복구: 네트워크 오류에 대한 재시도 로직을 구현합니다.
  4. 타임아웃: 원격 호출에 적절한 타임아웃을 설정합니다.
  5. 보안: 프로덕션 환경에서 HTTPS를 사용하고 인증을 구현합니다.

보안 구성

프로덕션 배포를 위해 CORS, 타임아웃 및 보안 헤더를 구성합니다:

use adk_server::{ServerConfig, SecurityConfig};
use std::time::Duration;

// 프로덕션 구성
let config = ServerConfig::new(agent_loader, session_service)
    .with_allowed_origins(vec![
        "https://myapp.com".to_string(),
        "https://admin.myapp.com".to_string(),
    ])
    .with_request_timeout(Duration::from_secs(30))
    .with_max_body_size(10 * 1024 * 1024);  // 10MB

// 또는 사전 설정을 사용합니다.
let dev_config = ServerConfig::new(agent_loader, session_service)
    .with_security(SecurityConfig::development());  // Permissive for dev

let prod_config = ServerConfig::new(agent_loader, session_service)
    .with_security(SecurityConfig::production(allowed_origins));

보안 기능은 다음과 같습니다:

  • CORS: 구성 가능한 허용된 origin (기본값: 개발 환경에서 허용적)
  • 요청 타임아웃: 구성 가능한 타임아웃 (기본값: 30초)
  • 본문 크기 제한: 최대 요청 본문 크기 (기본값: 10MB)
  • 보안 헤더: X-Content-Type-Options, X-Frame-Options, X-XSS-Protection
  • 오류 필터링: 내부 오류는 로깅되지만 프로덕션 환경에서는 클라이언트에 노출되지 않습니다.

관련 항목


이전: ← Server | 다음: Evaluation →