Protocolo Agente-a-Agente (A2A)
El Protocolo Agente-a-Agente (A2A) permite a los Agents comunicarse y colaborar a través de los límites de la red. adk-rust proporciona soporte completo tanto para exponer Agents vía A2A como para consumir Agents remotos A2A.
Resumen
A2A es útil cuando:
- Se integra con servicios de Agents de terceros
- Se construyen arquitecturas de microservicios con Agents especializados
- Se habilita la comunicación de Agents entre lenguajes
- Se aplican contratos formales entre sistemas de Agents
Para una organización interna sencilla, use sub-Agents locales en lugar de A2A para un mejor rendimiento.
Tarjetas de Agent
Cada Agent A2A expone una tarjeta de Agent que describe sus capacidades. La tarjeta se genera automáticamente y se sirve en /.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);
La tarjeta de Agent incluye:
- Nombre y descripción del Agent
- URL base para la comunicación
- Capacidades (streaming, historial de estado, etc.)
- Skills derivadas del Agent y sus sub-Agents
Exponiendo un Agent vía A2A
Para exponer un Agent de modo que otros Agents puedan usarlo, cree un servidor HTTP con endpoints A2A:
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(())
}
Esto expone:
GET /.well-known/agent.json- Tarjeta de AgentPOST /a2a- endpoint JSON-RPC para el protocolo A2APOST /a2a/stream- endpoint de streaming SSE
Consumiendo un Agent Remoto
Use RemoteA2aAgent para comunicarse con un Agent A2A remoto:
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()?;
El RemoteA2aAgent:
- Busca automáticamente la tarjeta de Agent desde la URL remota
- Convierte eventos de adk a/desde mensajes de protocolo A2A
- Maneja respuestas de streaming
- Funciona sin problemas como un sub-Agent
Cliente A2A
Para comunicación directa con Agents remotos, use el 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);
}
}
}
Protocolo JSON-RPC
ADK-Rust implementa el protocolo A2A usando JSON-RPC 2.0. Métodos soportados:
message/send
Envía un mensaje al agent:
{
"jsonrpc": "2.0",
"method": "message/send",
"params": {
"message": {
"role": "user",
"messageId": "msg-123",
"parts": [{"text": "Hello!"}]
}
},
"id": 1
}
La respuesta incluye un objeto task con el status y los artifacts.
message/stream
Igual que message/send pero devuelve Server-Sent Events (SSE) para respuestas en streaming.
tasks/cancel
Cancela una task en ejecución:
{
"jsonrpc": "2.0",
"method": "tasks/cancel",
"params": {
"taskId": "task-123"
},
"id": 1
}
Ejemplo Multi-Agent
Combina agents locales y remotos:
// Local agent
let roll_agent = LlmAgentBuilder::new("roll_agent")
.description("Rolls dice")
.model(Arc::new(model.clone()))
.tool(Arc::new(roll_die_tool))
.build()?;
// Remote agent
let prime_agent = RemoteA2aAgent::builder("prime_agent")
.description("Checks if numbers are prime")
.agent_url("http://localhost:8001")
.build()?;
// Root agent orchestrates both
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()?;
Manejo de Errores
Las operaciones A2A devuelven errores estándar de 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);
}
}
Códigos de error comunes:
-32600: Invalid request-32601: Method not found-32602: Invalid params-32603: Internal error
Mejores Prácticas
- Usar agent cards: Siempre obtén y valida las agent cards antes de la comunicación
- Manejar streaming: Usa el streaming para operaciones de larga duración
- Recuperación de errores: Implementa lógica de reintento para fallos de red
- Timeouts: Establece timeouts apropiados para llamadas remotas
- Seguridad: Usa HTTPS en producción e implementa autenticación
Configuración de Seguridad
Configura CORS, timeouts y encabezados de seguridad para despliegues en producción:
use adk_server::{ServerConfig, SecurityConfig};
use std::time::Duration;
// Production configuration
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
// Or use presets
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));
Las características de seguridad incluyen:
- CORS: Orígenes permitidos configurables (predeterminado: permisivo para desarrollo)
- Request timeout: Tiempo de espera configurable (predeterminado: 30 segundos)
- Body size limit: Tamaño máximo del cuerpo de la solicitud (predeterminado: 10MB)
- Security headers: X-Content-Type-Options, X-Frame-Options, X-XSS-Protection
- Error sanitization: Los errores internos se registran pero no se exponen a los clientes en producción
Relacionado
- LlmAgent - Creación de agents
- Sistemas Multi-Agent - Sub-agents y jerarquías
- Server Deployment - Ejecución de agents como HTTP servers
Anterior: ← Server | Siguiente: Evaluation →