الوكلاء الصوتيون الآنيون

يتيح الوكلاء الآنيون تفاعلات قائمة على الصوت مع المساعدين الذكيين باستخدام بث صوتي ثنائي الاتجاه. توفر حزمة adk-realtime واجهة موحدة لبناء وكلاء مُمكّنين صوتيًا يعملون مع OpenAI ‏Realtime API وGemini Live API من Google.

نظرة عامة

يختلف الوكلاء الآنيون عن LlmAgents القائم على النص بعدة طرق رئيسية:

الميزةLlmAgentRealtimeAgent
الإدخالنصصوت/نص
الإخراجنصصوت/نص
الاتصالطلبات HTTPWebSocket
زمن الاستجابةطلب/استجابةالبث في الوقت الفعلي
VADلا ينطبقاكتشاف الصوت من جانب الخادم

البنية

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

ينفّذ RealtimeAgent سمة Agent نفسها التي ينفّذها LlmAgent، مع مشاركة ما يلي:

  • التعليمات (الثابتة والديناميكية)
  • تسجيل الأدوات وتنفيذها
  • عمليات الاستدعاء (before_agent، after_agent، before_tool، after_tool)
  • تسليم المهام إلى الوكلاء الفرعيين

البدء السريع

التثبيت

أضف إلى Cargo.toml:

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

الاستخدام الأساسي

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

موفرو الخدمة المدعومون

المزوّدالنموذجالنقلعلامة الميزةتنسيق الصوت
OpenAIgpt-realtime-2.1WebSocketopenaiPCM16 24kHz
OpenAIgpt-realtime-2.1WebRTCopenai-webrtcOpus
Googlegemini-3.1-flash-live-previewWebSocketgeminiPCM16 16kHz/24kHz
GoogleGemini عبر Vertex AIWebSocket + OAuth2vertex-livePCM16 16kHz/24kHz
LiveKitأي (جسر إلى Gemini/OpenAI)WebRTClivekitPCM16

ملاحظة: يُعدّ gpt-realtime-2.1 الإعداد الافتراضي الحالي لـ ADK-Rust للوقت الفعلي OpenAI.

خيارات النقل

يدعم ADK-Realtime طبقات نقل متعددة:

  • WebSocket (الافتراضي): اتصال مباشر بـ OpenAI أو Gemini. بسيط، منخفض زمن الاستجابة، ويعمل في كل مكان.
  • Vertex AI Live: يتصل بـ Gemini عبر Google Cloud باستخدام مصادقة OAuth2 (بيانات الاعتماد الافتراضية للتطبيق). يُستخدم عند الحاجة إلى مصادقة مؤسسية وتكامل مع GCP.
  • LiveKit WebRTC: جسر WebRTC بمستوى جاهزية للإنتاج. يوجّه الصوت عبر خادم LiveKit لدعم السيناريوهات القابلة للتوسع ومتعددة المشاركين.
  • OpenAI WebRTC: اتصال مباشر بـ WebRTC إلى OpenAI باستخدام برنامج ترميز Opus وقنوات بيانات. يتطلب cmake لبناء مكتبة Opus بلغة C.

منشئ RealtimeAgent

يوفّر RealtimeAgentBuilder واجهة API متسلسلة لضبط الوكلاء:

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

اكتشاف النشاط الصوتي (VAD)

يتيح VAD تدفقًا طبيعيًا للمحادثة من خلال اكتشاف وقت بدء المستخدم في التحدث وتوقفه.

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

إعداد VAD مخصص

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 الدلالي (Gemini)

بالنسبة إلى نماذج Gemini، يمكنك استخدام VAD الدلالي الذي يراعي المعنى:

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

استدعاء الأدوات

تدعم الوكلاء الآنيون استدعاء الأدوات أثناء المحادثات الصوتية:

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

تسليم المحادثات بين الوكلاء

انقل المحادثات بين الوكلاء المتخصصين:

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

عندما يستدعي النموذج transfer_to_agent، يتولى RealtimeRunner عملية التسليم تلقائيًا.

تنسيقات الصوت

التنسيقمعدل أخذ العيناتالبتاتالقنواتحالة الاستخدام
PCM1624000 Hz16أحاديOpenAI (الافتراضي)
PCM1616000 Hz16أحاديإدخال Gemini
G711 u-law8000 هرتز8أحاديالاتصالات الهاتفية
G711 A-law8000 هرتز8أحاديالاتصالات الهاتفية
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)?;

أنواع الأحداث

أحداث الخادم

الحدثالوصف
SessionCreatedتم إنشاء الاتصال
AudioDeltaمقطع صوتي (PCM بترميز base64)
TextDeltaجزء من استجابة نصية
TranscriptDeltaتفريغ صوت الإدخال
FunctionCallDoneطلب استدعاء أداة
ResponseDoneاكتملت الاستجابة
SpeechStartedاكتشف VAD بداية الكلام
SpeechStoppedاكتشف VAD نهاية الكلام
Errorحدث خطأ

أحداث العميل

الحدثالوصف
AudioInputإرسال جزء صوتي
AudioCommitتثبيت مخزن الصوت
ItemCreateإرسال نص أو استجابة أداة
CreateResponseطلب استجابة
CancelResponseإلغاء الاستجابة الحالية
SessionUpdateتحديث الإعدادات

Vertex AI Live (Google Cloud)

اتصل بـ Gemini Live عبر Vertex AI باستخدام مصادقة المؤسسات (ADC وحسابات الخدمة و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(())
}

يتوفر أيضًا مُنشئ ملائم لـ ADC:

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

Vertex AI Live مع استدعاء الأدوات

يوضح المثال vertex_live_tools استدعاء الدوال عبر جلسة 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,
        _ => {}
    }
}

أعلام الميزات

الميزةالتبعياتحالة الاستخدام
vertex-livegemini + google-cloud-authVertex AI Live مع مصادقة ADC/حساب الخدمة
livekitlivekit + livekit-apiجسر LiveKit WebRTC
openai-webrtcopenai + str0m + audiopusOpenAI WebRTC مع Opus (يتطلب cmake)
fullopenai + gemini + vertex-live + livekitجميع وسائل النقل باستثناء WebRTC
full-webrtcfull + openai-webrtcكل شيء (يتطلب cmake)

جسر LiveKit WebRTC

بالنسبة إلى تطبيقات الصوت المخصصة للإنتاج، يوجّه جسر LiveKit الصوت عبر خادم LiveKit لتوفير سيناريوهات قابلة للتوسع ومتعددة المشاركين.

LiveKitConfig

اضبط بيانات اعتماد LiveKit بأمان. تُخزَّن مفاتيح وأسرار API باستخدام secrecy::SecretString، وتُحجَب في مخرجات التصحيح:

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() من تنسيق URL ويرفض بيانات الاعتماد الفارغة أثناء الإنشاء.

LiveKitRoomBuilder

منشئ typestate للاتصال بغرف LiveKit. يُطلب الحقل identity في وقت الترجمة — ولا يتوفر connect() إلا بعد تعيينه:

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;

ربط الصوت

استخدم أدوات الجسر لربط صوت LiveKit بـ 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));

أمثلة

شغّل الأمثلة المضمّنة:

# 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

أفضل الممارسات

  1. استخدم VAD من الخادم: اترك للخادم مهمة اكتشاف الكلام لتقليل زمن الاستجابة
  2. تعامل مع المقاطعات: فعّل interrupt_response لإجراء محادثات طبيعية
  3. اجعل التعليمات موجزة: ينبغي أن تكون الاستجابات الصوتية قصيرة
  4. اختبر باستخدام النص أولًا: صحّح منطق الوكيل باستخدام النص قبل إضافة الصوت
  5. تعامل مع الأخطاء بسلاسة: مشكلات الشبكة شائعة في اتصالات WebSocket

المقارنة مع وكلاء OpenAI SDK

يتبع التنفيذ الآني في ADK-Rust نمط وكلاء OpenAI SDK:

الميزةOpenAI SDKADK-Rust
الفئة الأساسية للوكيلAgentسمة Agent
وكيل في الوقت الفعليRealtimeAgentRealtimeAgent
الأدواتتعريفات الدوالTool trait + ToolDefinition
التسليماتtransfer_to_agentsub_agents + أداة مولدة تلقائيًا
عمليات الاستدعاءالخطافاتعمليات استدعاء before_* / after_*

السابق: ← وكلاء الرسم البياني | التالي: موفرو النماذج →