Agents Vocaux en Temps Réel

Les agents en temps réel permettent des interactions vocales avec des assistants IA en utilisant le streaming audio bidirectionnel. Le crate adk-realtime fournit une interface unifiée pour la création d'agents vocaux compatibles avec l'API temps réel d'OpenAI et l'API Gemini Live de Google.

Aperçu

Les agents en temps réel diffèrent des LlmAgents basés sur le texte de plusieurs manières clés :

CaractéristiqueLlmAgentRealtimeAgent
EntréeTextAudio/Text
SortieTextAudio/Text
ConnexionHTTP requestsWebSocket
LatenceRequête/réponseStreaming en temps réel
VADN/ADé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, partageant :

  • Instructions (statiques et dynamiques)
  • Enregistrement et exécution d'outils
  • Callbacks (before_agent, after_agent, before_tool, after_tool)
  • Transferts de sous-agents

Démarrage Rapide

Installation

Ajoutez à votre Cargo.toml :

[dependencies]
adk-realtime = { version = "2.0.0", features = ["openai"] }

# For Vertex AI Live (Google Cloud with ADC auth)
# adk-realtime = { version = "2.0.0", features = ["vertex-live"] }

# For LiveKit WebRTC bridge
# adk-realtime = { version = "2.0.0", features = ["livekit"] }

# For all transports (except WebRTC which needs cmake)
# adk-realtime = { version = "2.0.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")
    );

    // 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

FournisseurModèleTransportDrapeau de FonctionnalitéFormat Audio
OpenAIgpt-realtimeWebSocketopenaiPCM16 24kHz
OpenAIgpt-realtimeWebRTCopenai-webrtcOpus
Googlegemini-live-2.5-flash-native-audioWebSocketgeminiPCM16 16kHz/24kHz
GoogleGemini via Vertex AIWebSocket + OAuth2vertex-livePCM16 16kHz/24kHz
LiveKitN'importe quel (pont vers Gemini/OpenAI)WebRTClivekitPCM16

gpt-realtime est le dernier modèle temps réel d'OpenAI avec une qualité de parole, une émotion et des capacités d'appel de fonctions améliorées.

Options de Transport

ADK-Realtime prend en charge plusieurs couches de transport :

  • WebSocket (par défaut) : Connexion directe à OpenAI ou Gemini. Simple, faible latence, fonctionne partout.
  • Vertex AI Live : Se connecte à Gemini via Google Cloud avec l'authentification OAuth2 (Application Default Credentials). À utiliser lorsque vous avez besoin d'une authentification d'entreprise et d'une intégration GCP.
  • LiveKit WebRTC : Pont WebRTC de qualité production. Achemine l'audio via un serveur LiveKit pour des scénarios évolutifs et multi-participants.
  • OpenAI WebRTC : Connexion WebRTC directe à OpenAI avec codec Opus et canaux de données. Nécessite cmake pour la construction de la bibliothèque C Opus.

Constructeur RealtimeAgent

Le 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 flux de conversation naturel en détectant quand l'utilisateur commence et arrête de parler.

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 Realtime prennent en charge l'appel 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 Inter-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, le RealtimeRunner gère automatiquement le transfert.

Formats Audio

FormatFréquence d'échantillonnageBitsCanauxCas d'utilisation
PCM1624000 Hz16MonoOpenAI (par défaut)
PCM1616000 Hz16MonoEntrée Gemini
G711 u-law8000 Hz8MonoTéléphonie
G711 A-law8000 Hz8MonoTé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énementDescription
SessionCreatedConnexion établie
AudioDeltaMorceau audio (PCM base64)
TextDeltaMorceau de réponse textuelle
TranscriptDeltaTranscription audio d'entrée
FunctionCallDoneRequête d'appel d'outil
ResponseDoneRéponse terminée
SpeechStartedDébut de la parole détecté par VAD
SpeechStoppedFin de la parole détectée par VAD
ErrorErreur survenue

Événements du client

ÉvénementDescription
AudioInputEnvoyer un morceau audio
AudioCommitValider le tampon audio
ItemCreateEnvoyer une réponse textuelle ou d'outil
CreateResponseDemander une réponse
CancelResponseAnnuler la réponse actuelle
SessionUpdateMettre à jour la configuration

Vertex AI Live (Google Cloud)

Connecter Gemini Live via Vertex AI avec l'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-live-2.5-flash-native-audio");

    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 de commodité pour ADC :

let model = GeminiRealtimeModel::vertex_adc(
    "us-central1",
    "my-project-id",
    "models/gemini-live-2.5-flash-native-audio",
).await?;

Vertex AI Live avec appel d'outil

L'exemple vertex_live_tools démontre l'appel de fonction sur 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és

FonctionnalitéDépendancesCas d'utilisation
vertex-livegemini + google-cloud-authVertex AI Live avec authentification ADC/compte de service
livekitlivekit + livekit-apiPont LiveKit WebRTC
openai-webrtcopenai + str0m + audiopusOpenAI WebRTC avec Opus (nécessite cmake)
fullopenai + gemini + vertex-live + livekitTous les transports sauf WebRTC
full-webrtcfull + openai-webrtcTout (nécessite cmake)

Pont LiveKit WebRTC

Pour les applications vocales de production, le pont LiveKit achemine l'audio via un serveur LiveKit pour des scénarios évolutifs et multi-participants.

LiveKitConfig

Configurez les identifiants LiveKit de manière sécurisée. Les clés API et les secrets 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 de l'URL et rejette les identifiants vides au moment de la construction.

LiveKitRoomBuilder

Un constructeur typestate pour se connecter aux salles LiveKit. Le champ identity est requis au moment de la compilation — connect() n'est disponible qu'après qu'il soit 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 pontage 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

  1. Utiliser le VAD du serveur: Laisser le serveur gérer la détection vocale pour une latence réduite
  2. Gérer les interruptions: Activer interrupt_response pour des conversations naturelles
  3. Garder les instructions concises: Les réponses vocales doivent être brèves
  4. Tester d'abord avec du texte: Déboguer la logique de votre agent avec du texte avant d'ajouter l'audio
  5. Gérer les erreurs avec élégance: Les problèmes réseau sont fréquents avec les connexions WebSocket

Comparaison avec le SDK OpenAI Agents

L'implémentation en temps réel de ADK-Rust suit le modèle du SDK OpenAI Agents :

FonctionnalitéSDK OpenAIADK-Rust
Classe de base de l'agentAgentAgent trait
Agent en temps réelRealtimeAgentRealtimeAgent
OutilsDéfinitions de fonctionsTool trait + ToolDefinition
Passages de relaistransfer_to_agentsub_agents + outil auto-généré
CallbacksHooksbefore_* / after_* callbacks

Précédent: ← Graph Agents | Suivant: Model Providers →