Agents vocaux en temps réel
Les agents en temps réel permettent des interactions vocales avec des assistants d’IA grâce à la diffusion audio bidirectionnelle. La crate adk-realtime fournit une interface unifiée pour créer des agents dotés de fonctionnalités vocales qui fonctionnent avec le API Realtime de OpenAI et le API Live de Gemini de Google.
Vue d’ensemble
Les agents en temps réel diffèrent des LlmAgents basés sur le texte de plusieurs façons essentielles :
| Fonctionnalité | LlmAgent | RealtimeAgent |
|---|---|---|
| Entrée | Texte | Audio/Texte |
| Sortie | Texte | Audio/Texte |
| Connexion | requêtes HTTP | WebSocket |
| Latence | Requête/réponse | Streaming en temps réel |
| VAD | N/A | Détection vocale côté serveur |
Architecture
┌─────────────────────────────────────────┐
│ Agent Trait │
│ (name, description, run, sub_agents) │
└────────────────┬────────────────────────┘
│
┌───────────────────────┼───────────────────────┐
│ │ │
┌──────▼──────┐ ┌─────────▼─────────┐ ┌─────────▼─────────┐
│ LlmAgent │ │ RealtimeAgent │ │ SequentialAgent │
│ (text-based)│ │ (voice-based) │ │ (workflow) │
└─────────────┘ └───────────────────┘ └───────────────────┘
RealtimeAgent implémente le même trait Agent que LlmAgent, en partageant :
- Instructions (statiques et dynamiques)
- Enregistrement et exécution des outils
- Rappels (before_agent, after_agent, before_tool, after_tool)
- Transferts vers des sous-agents
Démarrage rapide
Installation
Ajoutez à votre Cargo.toml :
[dependencies]
adk-realtime = { version = "2.1.0", features = ["openai"] }
# For Vertex AI Live (Google Cloud with ADC auth)
# adk-realtime = { version = "2.1.0", features = ["vertex-live"] }
# For LiveKit WebRTC bridge
# adk-realtime = { version = "2.1.0", features = ["livekit"] }
# For all transports (except WebRTC which needs cmake)
# adk-realtime = { version = "2.1.0", features = ["full"] }
Utilisation de base
use adk_realtime::{
RealtimeAgent, RealtimeModel, RealtimeConfig, ServerEvent,
openai::OpenAIRealtimeModel,
};
use std::sync::Arc;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let api_key = std::env::var("OPENAI_API_KEY")?;
// Create the realtime model
let model: Arc<dyn RealtimeModel> = Arc::new(
OpenAIRealtimeModel::new(&api_key, "gpt-realtime-2.1")
);
// Build the realtime agent
let agent = RealtimeAgent::builder("voice_assistant")
.model(model.clone())
.instruction("You are a helpful voice assistant. Be concise.")
.voice("alloy")
.server_vad() // Enable voice activity detection
.build()?;
// Or use the low-level session API directly
let config = RealtimeConfig::default()
.with_instruction("You are a helpful assistant.")
.with_voice("alloy")
.with_modalities(vec!["text".to_string(), "audio".to_string()]);
let session = model.connect(config).await?;
// Send text and get response
session.send_text("Hello!").await?;
session.create_response().await?;
// Process events
while let Some(event) = session.next_event().await {
match event? {
ServerEvent::TextDelta { delta, .. } => print!("{}", delta),
ServerEvent::AudioDelta { delta, .. } => {
// Play audio (delta is base64-encoded PCM)
}
ServerEvent::ResponseDone { .. } => break,
_ => {}
}
}
Ok(())
}
Fournisseurs pris en charge
| Fournisseur | Modèle | Transport | Indicateur de fonctionnalité | Format audio |
|---|---|---|---|---|
| OpenAI | gpt-realtime-2.1 | WebSocket | openai | PCM16 24kHz |
| OpenAI | gpt-realtime-2.1 | WebRTC | openai-webrtc | Opus |
gemini-3.1-flash-live-preview | WebSocket | gemini | PCM16 16kHz/24kHz | |
| Gemini via Vertex AI | WebSocket + OAuth2 | vertex-live | PCM16 16kHz/24kHz | |
| LiveKit | Tout (pont vers Gemini/OpenAI) | WebRTC | livekit | PCM16 |
Remarque :
gpt-realtime-2.1est la valeur par défaut actuelle de OpenAI en temps réel de ADK-Rust.
Options de transport
ADK-Realtime prend en charge plusieurs couches de transport :
- WebSocket (par défaut) : Connexion directe à OpenAI ou Gemini. Simple, avec une faible latence, et fonctionne partout.
- Vertex AI Live : Se connecte à Gemini via Google Cloud avec une authentification OAuth2 (Application Default Credentials). À utiliser lorsque vous avez besoin d’une authentification d’entreprise et d’une intégration avec GCP.
- LiveKit WebRTC : Pont WebRTC de niveau production. Achemine l’audio via un serveur LiveKit pour les scénarios évolutifs avec plusieurs participants.
- OpenAI WebRTC : Connexion directe WebRTC à OpenAI avec le codec Opus et des canaux de données. Nécessite cmake pour compiler la bibliothèque C Opus.
Constructeur RealtimeAgent
RealtimeAgentBuilder fournit une API fluide pour configurer les agents :
let agent = RealtimeAgent::builder("assistant")
// Required
.model(model)
// Instructions (same as LlmAgent)
.instruction("You are helpful.")
.instruction_provider(|ctx| format!("User: {}", ctx.user_name()))
// Voice settings
.voice("alloy") // Options: alloy, coral, sage, shimmer, etc.
// Voice Activity Detection
.server_vad() // Use defaults
.vad(VadConfig {
mode: VadMode::ServerVad,
threshold: Some(0.5),
prefix_padding_ms: Some(300),
silence_duration_ms: Some(500),
interrupt_response: Some(true),
eagerness: None,
})
// Tools (same as LlmAgent)
.tool(Arc::new(weather_tool))
.tool(Arc::new(search_tool))
// Sub-agents for handoffs
.sub_agent(booking_agent)
.sub_agent(support_agent)
// Callbacks (same as LlmAgent)
.before_agent_callback(|ctx| async { Ok(()) })
.after_agent_callback(|ctx, event| async { Ok(()) })
.before_tool_callback(|ctx, tool, args| async { Ok(None) })
.after_tool_callback(|ctx, tool, result| async { Ok(result) })
// Realtime-specific callbacks
.on_audio(|audio_chunk| { /* play audio */ })
.on_transcript(|text| { /* show transcript */ })
.build()?;
Détection d’activité vocale (VAD)
La VAD permet un déroulement naturel de la conversation en détectant quand l’utilisateur commence et arrête de parler.
VAD côté serveur (recommandé)
let agent = RealtimeAgent::builder("assistant")
.model(model)
.server_vad() // Uses sensible defaults
.build()?;
Configuration VAD personnalisée
use adk_realtime::{VadConfig, VadMode};
let vad = VadConfig {
mode: VadMode::ServerVad,
threshold: Some(0.5), // Speech detection sensitivity (0.0-1.0)
prefix_padding_ms: Some(300), // Audio to include before speech
silence_duration_ms: Some(500), // Silence before ending turn
interrupt_response: Some(true), // Allow interrupting assistant
eagerness: None, // For SemanticVad mode
};
let agent = RealtimeAgent::builder("assistant")
.model(model)
.vad(vad)
.build()?;
VAD sémantique (Gemini)
Pour les modèles Gemini, vous pouvez utiliser la VAD sémantique, qui prend en compte le sens :
let vad = VadConfig {
mode: VadMode::SemanticVad,
eagerness: Some("high".to_string()), // low, medium, high
..Default::default()
};
Appel d’outils
Les agents en temps réel prennent en charge les appels d’outils pendant les conversations vocales :
use adk_realtime::{config::ToolDefinition, ToolResponse};
use serde_json::json;
// Define tools
let tools = vec![
ToolDefinition {
name: "get_weather".to_string(),
description: Some("Get weather for a location".to_string()),
parameters: Some(json!({
"type": "object",
"properties": {
"location": { "type": "string" }
},
"required": ["location"]
})),
},
];
let config = RealtimeConfig::default()
.with_tools(tools)
.with_instruction("Use tools to help the user.");
let session = model.connect(config).await?;
// Handle tool calls in the event loop
while let Some(event) = session.next_event().await {
match event? {
ServerEvent::FunctionCallDone { call_id, name, arguments, .. } => {
// Execute the tool
let result = execute_tool(&name, &arguments);
// Send the response
let response = ToolResponse::new(&call_id, result);
session.send_tool_response(response).await?;
}
_ => {}
}
}
Transferts entre agents
Transférez les conversations entre des agents spécialisés :
// Create sub-agents
let booking_agent = Arc::new(RealtimeAgent::builder("booking_agent")
.model(model.clone())
.instruction("Help with reservations.")
.build()?);
let support_agent = Arc::new(RealtimeAgent::builder("support_agent")
.model(model.clone())
.instruction("Help with technical issues.")
.build()?);
// Create main agent with sub-agents
let receptionist = RealtimeAgent::builder("receptionist")
.model(model)
.instruction(
"Route customers: bookings → booking_agent, issues → support_agent. \
Use transfer_to_agent tool to hand off."
)
.sub_agent(booking_agent)
.sub_agent(support_agent)
.build()?;
Lorsque le modèle appelle transfer_to_agent, RealtimeRunner gère automatiquement le transfert.
Formats audio
| Format | Fréquence d’échantillonnage | Bits | Canaux | Cas d’utilisation |
|---|---|---|---|---|
| PCM16 | 24000 Hz | 16 | Mono | OpenAI (par défaut) |
| PCM16 | 16000 Hz | 16 | Mono | Entrée Gemini |
| G711 u-law | 8000 Hz | 8 | Mono | Téléphonie |
| G711 A-law | 8000 Hz | 8 | Mono | Téléphonie |
use adk_realtime::{AudioFormat, AudioChunk};
// Create audio format
let format = AudioFormat::pcm16_24khz();
// Work with audio chunks
let chunk = AudioChunk::new(audio_bytes, format);
let base64 = chunk.to_base64();
let decoded = AudioChunk::from_base64(&base64, format)?;
Types d’événements
Événements du serveur
| Événement | Description |
|---|---|
SessionCreated | Connexion établie |
AudioDelta | Segment audio (PCM en base64) |
TextDelta | Fragment de réponse textuelle |
TranscriptDelta | Transcription audio d'entrée |
FunctionCallDone | Demande d'appel d'outil |
ResponseDone | Réponse terminée |
SpeechStarted | Début de la parole détecté par VAD |
SpeechStopped | Fin de la parole détectée par VAD |
Error | Une erreur s’est produite |
Événements client
| Événement | Description |
|---|---|
AudioInput | Envoyer un fragment audio |
AudioCommit | Valider la mémoire tampon audio |
ItemCreate | Envoyer du texte ou une réponse d’outil |
CreateResponse | Demander une réponse |
CancelResponse | Annuler la réponse actuelle |
SessionUpdate | Mettre à jour la configuration |
Vertex AI Live (Google Cloud)
Connectez-vous à Gemini Live via Vertex AI avec une authentification d’entreprise (ADC, comptes de service, WIF) :
use adk_realtime::gemini::{GeminiLiveBackend, GeminiRealtimeModel};
use adk_realtime::{RealtimeConfig, RealtimeModel};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let project_id = std::env::var("GOOGLE_CLOUD_PROJECT")?;
let region = std::env::var("GOOGLE_CLOUD_REGION")
.unwrap_or_else(|_| "us-central1".to_string());
// Use Application Default Credentials
let credentials = google_cloud_auth::credentials::Builder::default()
.build()
.await?;
let backend = GeminiLiveBackend::Vertex { credentials, region, project_id };
let model = GeminiRealtimeModel::new(backend, "models/gemini-3.1-flash-live-preview");
let config = RealtimeConfig::default()
.with_instruction("You are a helpful voice assistant.");
let session = model.connect(config).await?;
session.send_text("Hello from Vertex AI!").await?;
session.create_response().await?;
// Process events...
Ok(())
}
Il existe également un constructeur pratique pour ADC :
let model = GeminiRealtimeModel::vertex_adc(
"us-central1",
"my-project-id",
"models/gemini-3.1-flash-live-preview",
).await?;
Vertex AI Live avec appel d’outils
L’exemple vertex_live_tools montre l’appel de fonctions via une session Vertex AI Live :
use adk_realtime::config::ToolDefinition;
use adk_realtime::events::ToolResponse;
use serde_json::json;
// Declare tools
let tools = vec![
ToolDefinition {
name: "get_weather".to_string(),
description: Some("Get current weather for a city".to_string()),
parameters: Some(json!({
"type": "object",
"properties": {
"city": { "type": "string" }
},
"required": ["city"]
})),
},
];
let config = RealtimeConfig::default()
.with_tools(tools)
.with_instruction("Use tools to answer questions about weather.");
let session = model.connect(config).await?;
// Handle FunctionCallDone events and send ToolResponse back
while let Some(event) = session.next_event().await {
match event? {
ServerEvent::FunctionCallDone { call_id, name, arguments, .. } => {
let result = match name.as_str() {
"get_weather" => json!({"temperature": "22°C", "condition": "sunny"}),
_ => json!({"error": "unknown tool"}),
};
session.send_tool_response(ToolResponse::new(&call_id, result)).await?;
}
ServerEvent::TextDelta { delta, .. } => print!("{delta}"),
ServerEvent::ResponseDone { .. } => break,
_ => {}
}
}
Indicateurs de fonctionnalité
| Fonctionnalité | Dépendances | Cas d’utilisation |
|---|---|---|
vertex-live | gemini + google-cloud-auth | Vertex AI Live avec authentification ADC/par compte de service |
livekit | livekit + livekit-api | Passerelle LiveKit WebRTC |
openai-webrtc | openai + str0m + audiopus | OpenAI WebRTC avec Opus (nécessite cmake) |
full | openai + gemini + vertex-live + livekit | Tous les transports sauf WebRTC |
full-webrtc | full + openai-webrtc | Tout (nécessite cmake) |
Pont LiveKit WebRTC
Pour les applications vocales de production, le pont LiveKit achemine l’audio via un serveur LiveKit pour les scénarios évolutifs impliquant plusieurs participants.
LiveKitConfig
Configurez de manière sécurisée les identifiants LiveKit. Les clés et secrets API sont stockés à l’aide de secrecy::SecretString et masqués dans la sortie de débogage :
use adk_realtime::livekit::{LiveKitConfig, LiveKitRoomBuilder};
let config = LiveKitConfig::new(
"wss://your-server.livekit.cloud",
std::env::var("LIVEKIT_API_KEY")?,
std::env::var("LIVEKIT_API_SECRET")?,
)?;
LiveKitConfig::new() valide le format URL et rejette les identifiants vides lors de la construction.
LiveKitRoomBuilder
Un générateur typestate pour se connecter aux salles LiveKit. Le champ identity est requis à la compilation — connect() n’est disponible qu’une fois celui-ci défini :
let bundle = LiveKitRoomBuilder::new(config)
.identity("my-agent") // required — enables connect()
.name("Voice Agent") // optional display name
.room_name("session-room-123") // optional — auto-generated if omitted
.auto_subscribe(true) // subscribe to remote tracks
.with_audio(24_000, 1) // publish a local audio track (sample rate, channels)
.connect()
.await?;
// The bundle contains everything you need
let room = bundle.room;
let mut events = bundle.events;
let audio_source = bundle.audio_source; // for publishing audio
let audio_track = bundle.audio_track;
Pontage audio
Utilisez les utilitaires de pont pour connecter l’audio LiveKit à un RealtimeRunner :
use adk_realtime::livekit::{LiveKitEventHandler, bridge_input};
// Wrap your event handler to publish model audio to LiveKit
let lk_handler = LiveKitEventHandler::new(inner_handler, audio_source, 24000, 1);
// Bridge participant audio from LiveKit into the RealtimeRunner
tokio::spawn(bridge_input(remote_track, runner));
Exemples
Exécutez les exemples inclus :
# OpenAI Realtime (WebSocket)
cargo run -p adk-realtime --example openai_session_update --features openai
# Vertex AI Live (requires gcloud auth application-default login)
cargo run -p adk-realtime --example vertex_live_voice --features vertex-live
cargo run -p adk-realtime --example vertex_live_tools --features vertex-live
# LiveKit Bridge (requires LiveKit server)
cargo run -p adk-realtime --example livekit_bridge --features livekit,openai
cargo run -p adk-realtime --example livekit_gemini_bridge --features livekit,gemini
# Debug utilities
cargo run -p adk-realtime --example debug_gemini --features gemini
cargo run -p adk-realtime --example debug_livekit_auth --features livekit
# OpenAI WebRTC (requires cmake)
cargo run -p adk-realtime --example openai_webrtc --features openai-webrtc
Bonnes pratiques
- Utilisez le VAD côté serveur : laissez le serveur gérer la détection de la parole afin de réduire la latence
- Gérez les interruptions : activez
interrupt_responsepour des conversations naturelles - Gardez les instructions concises : les réponses vocales doivent être brèves
- Commencez par tester avec du texte : déboguez la logique de votre agent avec du texte avant d’ajouter l’audio
- Gérez les erreurs avec élégance : les problèmes réseau sont fréquents avec les connexions WebSocket
Comparaison avec les agents OpenAI SDK
L’implémentation temps réel de ADK-Rust suit le modèle des agents OpenAI SDK :
| Fonctionnalité | OpenAI SDK | ADK-Rust |
|---|---|---|
| Classe de base de l’agent | Agent | trait Agent |
| Agent en temps réel | RealtimeAgent | RealtimeAgent |
| Outils | Définitions de fonctions | Tool trait + ToolDefinition |
| Transferts | transfer_to_agent | sub_agents + outil généré automatiquement |
| Fonctions de rappel | Points d’accroche | Fonctions de rappel before_* / after_* |
Précédent : ← Agents graphiques | Suivant : Fournisseurs de modèles →