وكلاء الصوت في الوقت الفعلي

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

نظرة عامة

يختلف وكلاء الصوت في الوقت الفعلي عن 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.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"] }

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

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

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

مزودنموذجالنقلعلامة الميزةتنسيق الصوت
OpenAIgpt-realtimeWebSocketopenaiPCM16 24kHz
OpenAIgpt-realtimeWebRTCopenai-webrtcOpus
Googlegemini-live-2.5-flash-native-audioWebSocketgeminiPCM16 16kHz/24kHz
GoogleGemini عبر Vertex AIWebSocket + OAuth2vertex-livePCM16 16kHz/24kHz
LiveKitأي (جسر إلى Gemini/OpenAI)WebRTClivekitPCM16

ملاحظة: gpt-realtime هو أحدث نموذج في الوقت الفعلي من 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 Builder

يوفر 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()
};

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

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

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 Hz8أحاديالاتصالات الهاتفية
G711 A-law8000 Hz8أحاديالاتصالات الهاتفية
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 (جوجل كلاود)

اتصل بـ 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-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(())
}

يوجد أيضًا مُنشئ ملائم لـ ADC:

let model = GeminiRealtimeModel::vertex_adc(
    "us-central1",
    "my-project-id",
    "models/gemini-live-2.5-flash-native-audio",
).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-apiLiveKit 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 ويتم إخفاؤها في مخرجات Debug:

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 builder) للاتصال بغرف 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 Agents SDK

تطبيق ADK-Rust في الوقت الفعلي يتبع نمط OpenAI Agents SDK:

الميزةOpenAI SDKADK-Rust
فئة Agent الأساسيةAgentAgent trait
Agent في الوقت الفعليRealtimeAgentRealtimeAgent
الأدواتتعريفات الدوالTool trait + ToolDefinition
التسليماتtransfer_to_agentsub_agents + أداة تم إنشاؤها تلقائيًا
وظائف الاستدعاءالخطافاتbefore_* / after_* callbacks

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