Echtzeit-Sprachagenten

Echtzeitagenten ermöglichen sprachbasierte Interaktionen mit KI-Assistenten mithilfe bidirektionaler Audiostreams. Das adk-realtime-Crate bietet eine einheitliche Schnittstelle zum Erstellen sprachfähiger Agenten, die mit dem Realtime API von OpenAI und Googles Gemini Live API funktionieren.

Überblick

Echtzeitagenten unterscheiden sich in mehreren wichtigen Punkten von textbasierten LlmAgents:

FunktionLlmAgentRealtimeAgent
EingabeTextAudio/Text
AusgabeTextAudio/Text
VerbindungHTTP AnfragenWebSocket
LatenzAnfrage/AntwortEchtzeit-Streaming
VADNicht zutreffendServerseitige Spracherkennung

Architektur

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

RealtimeAgent implementiert dasselbe Agent-Trait wie LlmAgent und teilt:

  • Anweisungen (statisch und dynamisch)
  • Werkzeugregistrierung und -ausführung
  • Rückrufe (before_agent, after_agent, before_tool, after_tool)
  • Übergaben an Unteragenten

Schnellstart

Installation

Füge es zu deinem Cargo.toml hinzu:

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

Grundlegende Verwendung

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

Unterstützte Anbieter

AnbieterModellTransportFeature-FlagAudioformat
OpenAIgpt-realtime-2.1WebSocketopenaiPCM16 24kHz
OpenAIgpt-realtime-2.1WebRTCopenai-webrtcOpus
Googlegemini-3.1-flash-live-previewWebSocketgeminiPCM16 16kHz/24kHz
GoogleGemini über Vertex AIWebSocket + OAuth2vertex-livePCM16 16kHz/24kHz
LiveKitBeliebig (Brücke zu Gemini/OpenAI)WebRTClivekitPCM16

Hinweis: gpt-realtime-2.1 ist ADK-Rusts aktueller OpenAI-Echtzeitstandard.

Transportoptionen

ADK-Realtime unterstützt mehrere Transportschichten:

  • WebSocket (Standard): Direkte Verbindung zu OpenAI oder Gemini. Einfach, latenzarm und überall einsetzbar.
  • Vertex AI Live: Verbindet sich über Google Cloud mit Gemini und verwendet OAuth2-Authentifizierung (Application Default Credentials). Verwenden Sie diese Option, wenn Sie 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. Für die Erstellung der Opus-C-Bibliothek ist cmake erforderlich.

RealtimeAgent Builder

RealtimeAgentBuilder stellt eine Fluent-API zur Konfiguration von Agents bereit:

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

Spracherkennungsaktivität (VAD)

VAD ermöglicht einen natürlichen Gesprächsfluss, indem erkannt wird, 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

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

Semantische VAD (Gemini)

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

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

Tool-Aufrufe

Realtime-Agents unterstützen Tool-Aufrufe während Sprachgesprächen:

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

Übergaben zwischen Agents

Übertragen Sie Gespräche 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, verarbeitet 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

Serverereignisse

EreignisBeschreibung
SessionCreatedVerbindung hergestellt
AudioDeltaAudio-Chunk (Base64-PCM)
TextDeltaTextantwortabschnitt
TranscriptDeltaTranskript der Audioeingabe
FunctionCallDoneTool-Aufrufanforderung
ResponseDoneAntwort abgeschlossen
SpeechStartedVom VAD erkannter Sprachbeginn
SpeechStoppedVom VAD erkanntes Sprachende
ErrorFehler aufgetreten

Client-Ereignisse

EreignisBeschreibung
AudioInputAudioteil senden
AudioCommitAudiopuffer übergeben
ItemCreateText oder Tool-Antwort senden
CreateResponseEine Antwort anfordern
CancelResponseAktuelle Antwort abbrechen
SessionUpdateKonfiguration aktualisieren

Vertex AI Live (Google Cloud)

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

Es gibt außerdem einen komfortablen Konstruktor für ADC:

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

Vertex AI Live mit Tool-Aufrufen

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

Funktionsflags

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

LiveKit WebRTC Brücke

Für produktive Sprachanwendungen leitet die LiveKit-Brücke Audio über einen LiveKit-Server für skalierbare Szenarien mit mehreren Teilnehmenden.

LiveKitConfig

Konfigurieren Sie die LiveKit-Anmeldedaten sicher. API-Schlüssel und Geheimnisse werden mithilfe von 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 weist leere Anmeldedaten bereits bei der Erstellung zurück.

LiveKitRoomBuilder

Ein Typestate-Builder zum Verbinden mit LiveKit-Räumen. Das Feld identity ist zur Compile-Zeit erforderlich — connect() ist erst verfügbar, nachdem es festgelegt 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-Übertragung

Verwenden Sie die Brückenhilfsprogramme, 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 Vorgehensweisen

  1. Server-VAD verwenden: Lassen Sie den Server die Spracherkennung übernehmen, um eine geringere Latenz zu erreichen
  2. Unterbrechungen verarbeiten: Aktivieren Sie interrupt_response für natürliche Gespräche
  3. Anweisungen präzise halten: Sprachantworten sollten kurz sein
  4. Zuerst mit Text testen: Debuggen Sie Ihre Agentenlogik mit Text, bevor Sie Audio hinzufügen
  5. Fehler angemessen behandeln: Netzwerkprobleme treten bei WebSocket-Verbindungen häufig auf

Vergleich mit OpenAI-Agenten SDK

Die Echtzeitimplementierung von ADK-Rust folgt dem Muster der OpenAI-Agenten SDK:

FunktionOpenAI SDKADK-Rust
Basisklasse des AgentenAgentAgent trait
Echtzeit-AgentRealtimeAgentRealtimeAgent
WerkzeugeFunktionsdefinitionenTool trait + ToolDefinition
Übergabentransfer_to_agentsub_agents + automatisch generiertes Werkzeug
RückrufeHooksbefore_* / after_* Rückrufe

Zurück: ← Graph-Agenten | Weiter: Modellanbieter →

Echtzeit-Sprachagenten - ADK-Rust Dokumentation | ADK-Rust