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

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

نظرة عامة

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

الميزةLlmAgentRealtimeAgent
المدخلاتTextAudio/Text
المخرجاتTextAudio/Text
الاتصالHTTP requestsWebSocket
الاستجابةRequest/responseReal-time streaming
VADN/Aاكتشاف الصوت من جانب الخادم

البنية

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

يطبق RealtimeAgent نفس Agent trait مثل LlmAgent، ويتشاركان في:

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

البدء السريع

التثبيت

أضف إلى Cargo.toml الخاص بك:

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

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

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

    // أنشئ نموذج الوقت الفعلي
    let model: Arc<dyn RealtimeModel> = Arc::new(
        OpenAIRealtimeModel::new(&api_key, "gpt-4o-realtime-preview-2024-12-17")
    );

    // ابنِ وكيل الوقت الفعلي
    let agent = RealtimeAgent::builder("voice_assistant")
        .model(model.clone())
        .instruction("You are a helpful voice assistant. Be concise.")
        .voice("alloy")
        .server_vad()  // تفعيل اكتشاف النشاط الصوتي
        .build()?;

    // أو استخدم واجهة برمجة تطبيقات الجلسة ذات المستوى المنخفض مباشرةً
    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?;

    // أرسل نصًا واحصل على استجابة
    session.send_text("Hello!").await?;
    session.create_response().await?;

    // عالج الأحداث
    while let Some(event) = session.next_event().await {
        match event? {
            ServerEvent::TextDelta { delta, .. } => print!("{}", delta),
            ServerEvent::AudioDelta { delta, .. } => {
                // شغل الصوت (دلتا هي PCM مشفرة بـ base64)
            }
            ServerEvent::ResponseDone { .. } => break,
            _ => {}
        }
    }

    Ok(())
}

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

الموفرالنموذجعلامة الميزةتنسيق الصوت
OpenAIgpt-4o-realtime-preview-2024-12-17openaiPCM16 24kHz
OpenAIgpt-realtimeopenaiPCM16 24kHz
Googlegemini-2.0-flash-live-preview-04-09geminiPCM16 16kHz/24kHz

ملاحظة: gpt-realtime هو أحدث نموذج في الوقت الفعلي من OpenAI مع جودة كلام محسنة، وعواطف، وقدرات استدعاء الوظائف.

بناء RealtimeAgent

يوفر RealtimeAgentBuilder واجهة برمجة تطبيقات سلسة لتكوين العملاء (agents):

let agent = RealtimeAgent::builder("assistant")
    // مطلوب
    .model(model)

    // التعليمات (نفس LlmAgent)
    .instruction("You are helpful.")
    .instruction_provider(|ctx| format!("User: {}", ctx.user_name()))

    // إعدادات الصوت
    .voice("alloy")  // الخيارات: alloy, coral, sage, shimmer, إلخ.

    // كشف نشاط الصوت (Voice Activity Detection)
    .server_vad()  // استخدم الإعدادات الافتراضية
    .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,
    })

    // الأدوات (نفس LlmAgent)
    .tool(Arc::new(weather_tool))
    .tool(Arc::new(search_tool))

    // العملاء الفرعيون للتسليم
    .sub_agent(booking_agent)
    .sub_agent(support_agent)

    // ردود الاتصال (نفس 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) })

    // ردود الاتصال الخاصة بالوقت الفعلي
    .on_audio(|audio_chunk| { /* تشغيل الصوت */ })
    .on_transcript(|text| { /* عرض النص */ })

    .build()?;

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

يمكّن VAD تدفق المحادثة الطبيعي من خلال الكشف عن متى يبدأ المستخدم ويتوقف عن التحدث.

Server VAD (موصى به)

let agent = RealtimeAgent::builder("assistant")
    .model(model)
    .server_vad()  // يستخدم إعدادات افتراضية معقولة
    .build()?;

تكوين VAD مخصص

use adk_realtime::{VadConfig, VadMode};

let vad = VadConfig {
    mode: VadMode::ServerVad,
    threshold: Some(0.5),           // حساسية كشف الكلام (0.0-1.0)
    prefix_padding_ms: Some(300),   // الصوت المراد تضمينه قبل الكلام
    silence_duration_ms: Some(500), // الصمت قبل إنهاء الدور
    interrupt_response: Some(true), // السماح بمقاطعة المساعد
    eagerness: None,                // لوضع SemanticVad
};

let agent = RealtimeAgent::builder("assistant")
    .model(model)
    .vad(vad)
    .build()?;

Semantic VAD (Gemini)

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

let vad = VadConfig {
    mode: VadMode::SemanticVad,
    eagerness: Some("high".to_string()),  // منخفض، متوسط، مرتفع
    ..Default::default()
};

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

تدعم Agents في الوقت الفعلي استدعاء الأدوات أثناء المحادثات الصوتية:

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

عندما يقوم Model باستدعاء transfer_to_agent، يتعامل RealtimeRunner مع التسليم تلقائيًا.

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

التنسيقمعدل العينةالبتاتالقنواتحالة الاستخدام
PCM1624000 Hz16أحاديOpenAI (افتراضي)
PCM1616000 Hz16أحاديGemini input
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جزء صوتي (base64 PCM)
TextDeltaجزء استجابة نصية
TranscriptDeltaنص الصوت المدخل
FunctionCallDoneطلب استدعاء الأداة
ResponseDoneاكتملت الاستجابة
SpeechStartedVAD كشف بداية الكلام
SpeechStoppedVAD كشف نهاية الكلام
Errorحدث خطأ

أحداث العميل

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

أمثلة

قم بتشغيل الأمثلة المضمنة:

# 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

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

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

مقارنة مع OpenAI Agents SDK

يتبع التنفيذ في الوقت الفعلي في adk-rust نمط OpenAI Agents SDK:

الميزةOpenAI SDKadk-rust
Agent base classAgentAgent trait
Realtime agentRealtimeAgentRealtimeAgent
Toolsتعريفات الوظائفTool trait + ToolDefinition
Handoffstransfer_to_agentsub_agents + أداة يتم إنشاؤها تلقائياً
CallbacksHooksbefore_* / after_* callbacks

السابق: ← Graph Agents | التالي: Model Providers →