Echtzeit-Sprachagenten

Echtzeit-Agenten ermöglichen sprachbasierte Interaktionen mit KI-Assistenten unter Verwendung von bidirektionalem Audio-Streaming. Das adk-realtime crate bietet eine vereinheitlichte Schnittstelle zum Erstellen sprachfähiger Agenten, die mit OpenAIs Realtime API und Googles Gemini Live API funktionieren.

Übersicht

Echtzeit-Agenten unterscheiden sich von textbasierten LlmAgents in mehreren wesentlichen Punkten:

MerkmalLlmAgentRealtimeAgent
EingabeTextAudio/Text
AusgabeTextAudio/Text
VerbindungHTTP-AnfragenWebSocket
LatenzAnfrage/AntwortEchtzeit-Streaming
VADN/AServerseitige Spracherkennung

Architektur

              ┌─────────────────────────────────────────┐
              │              Agent Trait                │
              │  (name, description, run, sub_agents)   │
              └────────────────┬────────────────────────┘
                               │
       ┌───────────────────────┼───────────────────────┐
       │                       │                       │
┌──────▼──────┐      ┌─────────▼─────────┐   ┌─────────▼─────────┐
│  LlmAgent   │      │  RealtimeAgent    │   │  SequentialAgent  │
│ (text-based)│      │  (voice-based)    │   │   (workflow)      │
└─────────────┘      └───────────────────┘   └───────────────────┘

RealtimeAgent implementiert denselben Agent trait wie LlmAgent, wobei sie Folgendes teilen:

  • Anweisungen (statisch und dynamisch)
  • Tool-Registrierung und -Ausführung
  • Rückruffunktionen (before_agent, after_agent, before_tool, after_tool)
  • Übergaben an Sub-agents

Schnellstart

Installation

Fügen Sie zu Ihrer Cargo.toml hinzu:

[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"] }

Grundlegende Nutzung

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(())
}

Unterstützte Anbieter

AnbieterModellTransportFeature FlagAudioformat
OpenAIgpt-realtimeWebSocketopenaiPCM16 24kHz
OpenAIgpt-realtimeWebRTCopenai-webrtcOpus
Googlegemini-live-2.5-flash-native-audioWebSocketgeminiPCM16 16kHz/24kHz
GoogleGemini via Vertex AIWebSocket + OAuth2vertex-livePCM16 16kHz/24kHz
LiveKitBeliebig (Bridge zu Gemini/OpenAI)WebRTClivekitPCM16

Hinweis: gpt-realtime ist das neueste Echtzeitmodell von OpenAI mit verbesserter Sprachqualität, Emotion und Funktionstool-Fähigkeiten.

Transportoptionen

ADK-Realtime unterstützt mehrere Transportschichten:

  • WebSocket (Standard): Direkte Verbindung zu OpenAI oder Gemini. Einfach, geringe Latenz, funktioniert überall.
  • Vertex AI Live: Verbindet sich mit Gemini über Google Cloud mit OAuth2-Authentifizierung (Anwendungsstandardanmeldeinformationen). Verwenden Sie dies, wenn Sie eine Enterprise-Authentifizierung und GCP-Integration benötigen.
  • LiveKit WebRTC: Produktionsreife WebRTC-Brücke. Leitet Audio über einen LiveKit-Server für skalierbare Szenarien mit mehreren Teilnehmern.
  • OpenAI WebRTC: Direkte WebRTC-Verbindung zu OpenAI mit Opus-Codec und Datenkanälen. Erfordert cmake zum Erstellen der Opus C-Bibliothek.

RealtimeAgent Builder

Der RealtimeAgentBuilder bietet eine flüssige API zur Konfiguration von Agenten:

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()?;

Spracherkennung (VAD)

VAD ermöglicht einen natürlichen Gesprächsfluss, indem es erkennt, wann der Benutzer zu sprechen beginnt und aufhört.

let agent = RealtimeAgent::builder("assistant")
    .model(model)
    .server_vad()  // Uses sensible defaults
    .build()?;

Benutzerdefinierte VAD-Konfiguration

Semantisches VAD (Gemini)

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()?;

Für Gemini Modelle können Sie semantisches VAD verwenden, das die Bedeutung berücksichtigt:

let vad = VadConfig {
    mode: VadMode::SemanticVad,
    eagerness: Some("high".to_string()),  // low, medium, high
    ..Default::default()
};

Tool Calling

Echtzeit-Agents unterstützen Tool Calling während Sprachkonversationen:

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?;
        }
        _ => {}
    }
}

Multi-Agenten-Übergaben

Übertragen Sie Konversationen zwischen spezialisierten Agents:

// 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()?;

Wenn das Modell transfer_to_agent aufruft, übernimmt der RealtimeRunner die Übergabe automatisch.

Audioformate

FormatAbtastrateBitsKanäleAnwendungsfall
PCM1624000 Hz16MonoOpenAI (Standard)
PCM1616000 Hz16MonoGemini-Eingabe
G711 u-law8000 Hz8MonoTelefonie
G711 A-law8000 Hz8MonoTelefonie
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)?;

Ereignistypen

Server-Ereignisse

EreignisBeschreibung
SessionCreatedVerbindung hergestellt
AudioDeltaAudio-Chunk (base64 PCM)
TextDeltaText-Antwort-Chunk
TranscriptDeltaTranskript der Audioeingabe
FunctionCallDoneTool-Aufruf-Anfrage
ResponseDoneAntwort abgeschlossen
SpeechStartedVAD hat Sprachbeginn erkannt
SpeechStoppedVAD hat Sprachhälfte erkannt
ErrorFehler aufgetreten

Client-Ereignisse

EreignisBeschreibung
AudioInputAudio-Chunk senden
AudioCommitAudio-Puffer committen
ItemCreateText- oder Tool-Antwort senden
CreateResponseEine Antwort anfordern
CancelResponseAktuelle Antwort abbrechen
SessionUpdateKonfiguration aktualisieren

Vertex AI Live (Google Cloud)

Verbinden Sie sich mit Gemini Live über Vertex AI mit Unternehmensauthentifizierung (ADC, service accounts, 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(())
}

Es gibt auch einen Komfortkonstruktor für ADC:

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

Vertex AI Live mit Tool Calling

Das vertex_live_tools Beispiel demonstriert Funktionsaufrufe über eine Vertex AI Live-Sitzung:

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,
        _ => {}
    }
}

Funktions-Flags

FunktionAbhängigkeitenAnwendungsfall
vertex-livegemini + google-cloud-authVertex AI Live mit ADC/Dienstkonto-Authentifizierung
livekitlivekit + livekit-apiLiveKit WebRTC-Bridge
openai-webrtcopenai + str0m + audiopusOpenAI WebRTC mit Opus (benötigt cmake)
fullopenai + gemini + vertex-live + livekitAlle Transporte außer WebRTC
full-webrtcfull + openai-webrtcAlles (benötigt cmake)

LiveKit WebRTC-Bridge

Für Sprachapplikationen in der Produktion leitet die LiveKit-Bridge Audio über einen LiveKit-Server für skalierbare Szenarien mit mehreren Teilnehmern.

LiveKitConfig

LiveKit-Anmeldeinformationen sicher konfigurieren. API-Schlüssel und Geheimnisse werden mit secrecy::SecretString gespeichert und in der Debug-Ausgabe redigiert:

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() validiert das URL-Format und lehnt leere Anmeldeinformationen während der Konstruktion ab.

LiveKitRoomBuilder

Ein Typzustands-Builder zum Verbinden mit LiveKit-Räumen. Das Feld identity ist zur Kompilierzeit erforderlich – connect() ist erst verfügbar, nachdem es gesetzt wurde:

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;

Audio überbrücken

Verwenden Sie die Bridge-Dienstprogramme, um LiveKit-Audio mit einem RealtimeRunner zu verbinden:

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));

Beispiele

Führen Sie die enthaltenen Beispiele aus:

# 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

Bewährte Verfahren

  1. Server-VAD verwenden: Lassen Sie den Server die Spracherkennung für geringere Latenz handhaben
  2. Unterbrechungen handhaben: Aktivieren Sie interrupt_response für natürliche Unterhaltungen
  3. Anweisungen prägnant halten: Sprachantworten sollten kurz sein
  4. Zuerst mit Text testen: Debuggen Sie Ihre Agent-Logik mit Text, bevor Sie Audio hinzufügen
  5. Fehler elegant behandeln: Netzwerkprobleme sind bei WebSocket-Verbindungen häufig

Vergleich mit OpenAI Agents SDK

ADK-Rust's Echtzeit-Implementierung folgt dem OpenAI Agents SDK Muster:

FunktionOpenAI SDKADK-Rust
Agenten-BasisklasseAgentAgent trait
Echtzeit-AgentRealtimeAgentRealtimeAgent
ToolsFunktionsdefinitionenTool trait + ToolDefinition
Übergabentransfer_to_agentsub_agents + automatisch generiertes Tool
CallbacksHooksbefore_* / after_* callbacks

Zurück: ← Graph Agents | Weiter: Model Providers →