Agent-to-Agent (A2A) Protokoll
Das Agent-to-Agent (A2A) Protokoll ermöglicht Agents die Kommunikation und Zusammenarbeit über Netzwerk-Grenzen hinweg. adk-rust bietet volle Unterstützung sowohl für das Bereitstellen von Agents über A2A als auch für das Konsumieren entfernter A2A-Agents.
Übersicht
A2A ist nützlich, wenn:
- Integration mit Agent-Diensten von Drittanbietern
- Aufbau von Microservices-Architekturen mit spezialisierten Agents
- Ermöglichung der agentenübergreifenden Kommunikation in verschiedenen Sprachen
- Erzwingung formaler Verträge zwischen Agent-Systemen
Für eine einfache interne Organisation verwenden Sie lokale Sub-Agents anstelle von A2A, um eine bessere Leistung zu erzielen.
Agent-Karten
Jeder A2A-Agent stellt eine Agent-Karte bereit, die seine Fähigkeiten beschreibt. Die Karte wird automatisch generiert und unter /.well-known/agent.json bereitgestellt.
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);
Die Agent-Karte enthält:
- Agent-Name und -Beschreibung
- Basis-URL für die Kommunikation
- Fähigkeiten (Streaming, Verlauf des Zustands, etc.)
- Skills, die vom Agent und seinen Sub-Agents abgeleitet sind
Einen Agent über A2A bereitstellen
Um einen Agent bereitzustellen, damit andere Agents ihn verwenden können, erstellen Sie einen HTTP-Server mit A2A-Endpunkten:
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(())
}
Dies stellt bereit:
GET /.well-known/agent.json- Agent-KartePOST /a2a- JSON-RPC-Endpunkt für das A2A-ProtokollPOST /a2a/stream- SSE-Streaming-Endpunkt
Einen Remote-Agent konsumieren
Verwenden Sie RemoteA2aAgent, um mit einem Remote-A2A-Agent zu kommunizieren:
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()?;
Der RemoteA2aAgent:
- Ruft die Agent-Karte automatisch von der Remote-URL ab
- Konvertiert adk-Ereignisse in/aus A2A-Protokollnachrichten
- Verarbeitet Streaming-Antworten
- Funktioniert nahtlos als Sub-Agent
A2A-Client
Für die direkte Kommunikation mit Remote-Agents verwenden Sie den 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-Protokoll
ADK-Rust implementiert das A2A-Protokoll unter Verwendung von JSON-RPC 2.0. Unterstützte Methoden:
message/send
Eine Nachricht an den Agent senden:
{
"jsonrpc": "2.0",
"method": "message/send",
"params": {
"message": {
"role": "user",
"messageId": "msg-123",
"parts": [{"text": "Hello!"}]
}
},
"id": 1
}
Die Antwort enthält ein task-Objekt mit Status und Artefakten.
message/stream
Identisch mit message/send, gibt aber Server-Sent Events (SSE) für Streaming-Antworten zurück.
tasks/cancel
Eine laufende Aufgabe abbrechen:
{
"jsonrpc": "2.0",
"method": "tasks/cancel",
"params": {
"taskId": "task-123"
},
"id": 1
}
Multi-Agenten-Beispiel
Lokale und Remote-Agents kombinieren:
// Lokaler 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 orchestriert beide
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()?;
Fehlerbehandlung
A2A-Operationen geben standardmäßige ADK-Fehler zurück:
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);
}
}
Häufige Fehlercodes:
-32600: Ungültige Anfrage-32601: Methode nicht gefunden-32602: Ungültige Parameter-32603: Interner Fehler
Bewährte Praktiken
- Agent Cards verwenden: Agent Cards immer vor der Kommunikation abrufen und validieren
- Streaming handhaben: Streaming für langlaufende Operationen verwenden
- Fehlerbehebung: Wiederholungslogik für Netzwerkfehler implementieren
- Timeouts: Angemessene Timeouts für Remote-Aufrufe festlegen
- Sicherheit: In der Produktion HTTPS verwenden und Authentifizierung implementieren
Sicherheitskonfiguration
Konfigurieren Sie CORS, Timeouts und Sicherheits-Header für Produktions-Deployments:
use adk_server::{ServerConfig, SecurityConfig};
use std::time::Duration;
// Produktionskonfiguration
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); // 10 MB
// Oder Presets verwenden
let dev_config = ServerConfig::new(agent_loader, session_service)
.with_security(SecurityConfig::development()); // Permissiv für die Entwicklung
let prod_config = ServerConfig::new(agent_loader, session_service)
.with_security(SecurityConfig::production(allowed_origins));
Sicherheitsfunktionen umfassen:
- CORS: Konfigurierbare zulässige Ursprünge (Standard: permissiv für die Entwicklung)
- Anfrage-Timeout: Konfigurierbares Timeout (Standard: 30 Sekunden)
- Grenze der Body-Größe: Maximale Größe des Anfrage-Bodys (Standard: 10 MB)
- Sicherheits-Header: X-Content-Type-Options, X-Frame-Options, X-XSS-Protection
- Fehlerbereinigung: Interne Fehler werden protokolliert, aber in der Produktion nicht gegenüber Clients offengelegt
Verwandtes
- LlmAgent – Agents erstellen
- Multi-Agent Systems – Sub-Agents und Hierarchien
- Server Deployment – Agents als HTTP-Server betreiben
Zurück: ← Server | Weiter: Evaluation →