Echtzeit-Sprach-Agents

Realtime agents ermöglichen sprachbasierte Interaktionen mit KI-Assistenten unter Verwendung von bidirektionalem Audio-Streaming. Das adk-realtime crate bietet eine einheitliche Schnittstelle zum Erstellen von sprachfähigen Agents, die mit OpenAI's Realtime API und Google's Gemini Live API funktionieren.

Übersicht

Realtime agents unterscheiden sich in mehreren wesentlichen Punkten von textbasierten LlmAgents:

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

Architektur

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

RealtimeAgent implementiert dasselbe Agent trait wie LlmAgent, wobei Folgendes geteilt wird:

  • Anweisungen (statisch und dynamisch)
  • Tool-Registrierung und -Ausführung
  • Callbacks (before_agent, after_agent, before_tool, after_tool)
  • Sub-Agent-Übergaben

Schnellstart

Installation

Fügen Sie Ihrem Cargo.toml hinzu:

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

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

    // Erstellen des Echtzeit-Modells
    let model: Arc<dyn RealtimeModel> = Arc::new(
        OpenAIRealtimeModel::new(&api_key, "gpt-4o-realtime-preview-2024-12-17")
    );

    // Erstellen des Echtzeit-Agents
    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()?;

    // Oder verwenden Sie die Low-Level-Session-API direkt
    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?;

    // Text senden und Antwort erhalten
    session.send_text("Hello!").await?;
    session.create_response().await?;

    // Ereignisse verarbeiten
    while let Some(event) = session.next_event().await {
        match event? {
            ServerEvent::TextDelta { delta, .. } => print!("{}", delta),
            ServerEvent::AudioDelta { delta, .. } => {
                // Audio abspielen (delta ist base64-kodiertes PCM)
            }
            ServerEvent::ResponseDone { .. } => break,
            _ => {}
        }
    }

    Ok(())
}

Unterstützte Anbieter

AnbieterModellFeature-FlagAudioformat
OpenAIgpt-4o-realtime-preview-2024-12-17openaiPCM16 24kHz
OpenAIgpt-realtimeopenaiPCM16 24kHz
Googlegemini-2.0-flash-live-preview-04-09geminiPCM16 16kHz/24kHz

Hinweis: gpt-realtime ist OpenAI's neuestes Echtzeitmodell mit verbesserter Sprachqualität, Emotionen und Function Calling-Fähigkeiten.

RealtimeAgent Builder

Der RealtimeAgentBuilder bietet eine fließende API zur Konfiguration von Agents:

let agent = RealtimeAgent::builder("assistant")
    // Erforderlich
    .model(model)

    // Anweisungen (wie bei LlmAgent)
    .instruction("You are helpful.")
    .instruction_provider(|ctx| format!("User: {}", ctx.user_name()))

    // Spracheinstellungen
    .voice("alloy")  // Optionen: alloy, coral, sage, shimmer, etc.

    // Spracherkennung (VAD)
    .server_vad()  // Standardwerte verwenden
    .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 (wie bei LlmAgent)
    .tool(Arc::new(weather_tool))
    .tool(Arc::new(search_tool))

    // Sub-Agents für Übergaben
    .sub_agent(booking_agent)
    .sub_agent(support_agent)

    // Callbacks (wie bei 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-spezifische Callbacks
    .on_audio(|audio_chunk| { /* Audio abspielen */ })
    .on_transcript(|text| { /* Transkript anzeigen */ })

    .build()?;

Spracherkennung (VAD)

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

Server-VAD (Empfohlen)

let agent = RealtimeAgent::builder("assistant")
    .model(model)
    .server_vad()  // Verwendet sinnvolle Standardwerte
    .build()?;

Benutzerdefinierte VAD-Konfiguration

use adk_realtime::{VadConfig, VadMode};

let vad = VadConfig {
    mode: VadMode::ServerVad,
    threshold: Some(0.5),           // Empfindlichkeit der Spracherkennung (0.0-1.0)
    prefix_padding_ms: Some(300),   // Audio, das vor der Sprache eingeschlossen werden soll
    silence_duration_ms: Some(500), // Stille, bevor die Sprechphase beendet wird
    interrupt_response: Some(true), // Assistenten unterbrechen erlauben
    eagerness: None,                // Für den SemanticVad-Modus
};

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()),  // niedrig, mittel, hoch
    ..Default::default()
};

Tool-Aufrufe

adk-realtime-Agenten unterstützen Tool-Aufrufe 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 Agenten:

// 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)
TextDeltaTextantwort-Chunk
TranscriptDeltaTranskript der Audioeingabe
FunctionCallDoneTool-Aufrufanfrage
ResponseDoneAntwort abgeschlossen
SpeechStartedVAD hat Sprachbeginn erkannt
SpeechStoppedVAD hat Sprach_ende_ erkannt
ErrorFehler aufgetreten

Client-Ereignisse

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

Beispiele

Führen Sie die enthaltenen Beispiele aus:

# Basic text-only session
cargo run --example realtime_basic --features realtime-openai

# Voice assistant with VAD
cargo run --example realtime_vad --features realtime-openai

# Tool calling
cargo run --example realtime_tools --features realtime-openai

# Multi-agent handoffs
cargo run --example realtime_handoff --features realtime-openai

Best Practices

  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 Gespräche
  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 üblich

Vergleich mit dem OpenAI Agents SDK

Die Echtzeit-Implementierung von adk-rust folgt dem OpenAI Agents SDK-Muster:

FunktionOpenAI SDKADK-Rust
Agent-BasisklasseAgentAgent trait
Echtzeit-AgentRealtimeAgentRealtimeAgent
ToolsFunktionsdefinitionenTool trait + ToolDefinition
Übergabentransfer_to_agentsub_agents + automatisch generierte Tool
CallbacksHooksbefore_* / after_* Callbacks

Vorherige: ← Graph Agents | Nächste: Modell-Anbieter →