Agents Vocaux en Temps Réel

Les agents en temps réel permettent des interactions vocales avec des assistants IA à l'aide d'un 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 importantes :

CaractéristiqueLlmAgentRealtimeAgent
EntréeTexteAudio/Texte
SortieTexteAudio/Texte
ConnexionRequêtes HTTPWebSocket
LatenceRequête/réponseStreaming en temps réel
Détection d'activité vocale (VAD)N/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 des outils
  • Callbacks (before_agent, after_agent, before_tool, after_tool)
  • Transferts vers des sous-agents

Démarrage rapide

Installation

Ajoutez ceci à votre Cargo.toml :

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

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-4o-realtime-preview-2024-12-17")
    );

    // 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èleDrapeau de FonctionnalitéFormat Audio
OpenAIgpt-4o-realtime-preview-2024-12-17openaiPCM16 24kHz
OpenAIgpt-realtimeopenaiPCM16 24kHz
Googlegemini-2.0-flash-live-preview-04-09geminiPCM16 16kHz/24kHz

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

Générateur RealtimeAgent

Le RealtimeAgentBuilder fournit une API fluide pour la configuration des 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.

VAD Serveur (Recommandé)

let agent = RealtimeAgent::builder("assistant")
    .model(model)
    .server_vad()  // Utilise des valeurs par défaut pertinentes
    .build()?;

Configuration VAD Personnalisée

use adk_realtime::{VadConfig, VadMode};

let vad = VadConfig {
    mode: VadMode::ServerVad,
    threshold: Some(0.5),           // Sensibilité de détection de la parole (0.0-1.0)
    prefix_padding_ms: Some(300),   // Audio à inclure avant la parole
    silence_duration_ms: Some(500), // Silence avant de terminer le tour de parole
    interrupt_response: Some(true), // Autoriser l'interruption de l'assistant
    eagerness: None,                // Pour le mode SemanticVad
};

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()),  // faible, moyen, élevé
    ..Default::default()
};

Appel d'outils

Les agents temps réel prennent en charge l'appel d'outils pendant les conversations vocales :

use adk_realtime::{config::ToolDefinition, ToolResponse};
use serde_json::json;

// Définir les outils
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?;

// Gérer les appels d'outils dans la boucle d'événements
while let Some(event) = session.next_event().await {
    match event? {
        ServerEvent::FunctionCallDone { call_id, name, arguments, .. } => {
            // Exécuter l'outil
            let result = execute_tool(&name, &arguments);

            // Envoyer la réponse
            let response = ToolResponse::new(&call_id, result);
            session.send_tool_response(response).await?;
        }
        _ => {}
    }
}

Transferts multi-agents

Transférer les conversations entre des agents spécialisés :

// Créer des sous-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()?);

// Créer l'agent principal avec des sous-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};

// Créer un format audio
let format = AudioFormat::pcm16_24khz();

// Travailler avec des morceaux audio
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 serveur

ÉvénementDescription
SessionCreatedConnexion établie
AudioDeltaMorceau audio (PCM base64)
TextDeltaMorceau de réponse texte
TranscriptDeltaTranscription audio d'entrée
FunctionCallDoneRequête d'appel d'outil
ResponseDoneRéponse terminée
SpeechStartedDébut de parole détecté par VAD
SpeechStoppedFin de parole détectée par VAD
ErrorErreur survenue

Événements client

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

Exemples

Exécuter les exemples inclus :

# Session texte basique uniquement
cargo run --example realtime_basic --features realtime-openai

# Assistant vocal avec VAD
cargo run --example realtime_vad --features realtime-openai

# Appel d'outils
cargo run --example realtime_tools --features realtime-openai

# Transferts multi-agents
cargo run --example realtime_handoff --features realtime-openai

Bonnes Pratiques

  1. Utiliser le VAD du serveur : Laisser le serveur gérer la détection vocale pour une latence plus faible
  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 de réseau sont courants avec les connexions WebSocket

Comparaison avec le SDK OpenAI Agents

L'implémentation temps réel d'adk-rust suit le modèle du SDK OpenAI Agents :

FonctionnalitéOpenAI SDKADK-Rust
Classe de base d'AgentAgentTrait Agent
Agent en temps réelRealtimeAgentRealtimeAgent
OutilsDéfinitions de fonctionsTrait Tool + ToolDefinition
Transfertstransfer_to_agentsub_agents + outil auto-généré
RappelsHooksCallbacks before_* / after_*

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