بناء Voice AI Agent باستخدام Laravel 🔥

تخيل أن لديك CRM لشركة شحن، والمستخدم بدل أن يكتب:

"أين طلبي رقم 5832؟"

يضغط على زر الميكروفون ويقول:

"مرحبا، ممكن تخبرني وين وصل طلبي 5832؟"

خلال ثوانٍ قليلة يسمع:

"طلبك خرج من المستودع، وهو الآن في طريقه إلى مركز التوزيع."

لكن الذي حدث في الخلفية ليس مجرد Speech-to-Text ثم Chatbot. المنظومة الحقيقية أقرب إلى:

User Voice
   │
   ▼
Audio Capture
   │
   ▼
Speech-to-Text
   │
   ▼
Laravel Agent
   │
   ├── CRM
   ├── Orders
   ├── RAG
   ├── APIs
   └── Business Tools
   │
   ▼
Agent Response
   │
   ▼
Text-to-Speech
   │
   ▼
Audio Response
   │
   ▼
User

وهنا يتحول الذكاء الاصطناعي من chatbot يجيب عن الأسئلة إلى واجهة صوتية قادرة على تنفيذ أعمال فعلية داخل النظام.

Laravel AI SDK الحالي، ضمن Laravel 13، يوفر Agents وTools وconversation persistence وSpeech-to-Text وspeaker diarization وText-to-Speech وQueueing وStreaming، مع دعم عدة مزودين للصوت.

لكن توجد نقطة معمارية مهمة جدًا قبل أن نبدأ: Laravel AI SDK يوفر اللبنات اللازمة لبناء Voice Agent، لكنه لا يعني تلقائيًا أنك حصلت على اتصال صوتي full-duplex منخفض الكمون مثل أنظمة Realtime Voice المتخصصة.

لذلك سنبني أولًا Voice Agent production-friendly يعتمد على:

STT → Agent → Tools → TTS

ثم نناقش في النهاية كيف نطوره إلى Realtime Voice باستخدام WebRTC عندما تكون المحادثة الفورية والمقاطعة الطبيعية أولوية. أنظمة Realtime المتخصصة، مثل OpenAI Realtime API، تستخدم WebRTC/WebSocket وتتعامل مع الصوت مباشرة في الاتجاهين.


1. لماذا نحتاج Voice Agent أصلًا؟

في كثير من التطبيقات، واجهة المستخدم هي نفسها التي تمنع الوصول إلى الخدمة. خذ مثالًا على تطبيق تأمين:

User
 ↓
Login
 ↓
Orders
 ↓
Search
 ↓
Open order
 ↓
Read status

هذه الخطوات طبيعية للمستخدم التقني، لكنها ليست دائمًا أفضل تجربة. يمكن أن يكون البديل:

User: "هل تم قبول طلب التعويض الخاص بي؟"

ثم:

Voice Agent
   ↓
Find customer
   ↓
Get claim
   ↓
Check status
   ↓
Answer

وهنا يصبح الصوت مجرد واجهة الإدخال والإخراج، بينما القيمة الحقيقية موجودة في الـ Agent والـ Tools.

2. الفرق بين Voice Bot وVoice Agent

الفرق مهم جدًا.

Voice Bot تقليدي

Voice
 ↓
Speech-to-Text
 ↓
Keyword Matching
 ↓
Predefined Response
 ↓
Text-to-Speech

مثلاً: "ساعات العمل" ثم "نحن نفتح من الساعة 8 إلى 5." هذا Bot.

Voice Agent

أما الـ Agent:

Voice
 ↓
STT
 ↓
LLM
 ↓
Decision
 ├── CRM Tool
 ├── Order Tool
 ├── Search Tool
 ├── RAG
 └── External API
 ↓
LLM
 ↓
TTS

مثلاً: "هل يمكنك إخباري إذا كان طلبي قد تم شحنه؟" الـ Agent قد يقرر بنفسه:

Need order information
      ↓
GetOrder tool
      ↓
GetShipmentStatus tool
      ↓
Generate response

وهذا هو النموذج الذي يستحق بناء architecture مستقلة له.

Laravel تعرف Agent على أنه class مخصص يجمع instructions وconversation context وtools وoutput schema، وهو الأساس الذي يُبنى عليه هذا النوع من التطبيقات.

3. Architecture التي سنبنيها

سنفترض أننا نبني Voice Customer Support داخل CRM:

                         ┌─────────────────────┐
                         │       User          │
                         │   Microphone        │
                         └──────────┬──────────┘
                                    │
                                 Audio
                                    │
                                    ▼
                         ┌─────────────────────┐
                         │   Laravel API       │
                         └──────────┬──────────┘
                                    │
                                    ▼
                         ┌─────────────────────┐
                         │ Speech-to-Text      │
                         └──────────┬──────────┘
                                    │
                                 Text
                                    │
                                    ▼
                         ┌─────────────────────┐
                         │   Laravel Agent    │
                         └──────────┬──────────┘
                                    │
                   ┌────────────────┼────────────────┐
                   │                │                │
                   ▼                ▼                ▼
                CRM Tool         RAG Tool        Order Tool
                   │                │                │
                   └────────────────┼────────────────┘
                                    │
                                    ▼
                              Agent Answer
                                    │
                                    ▼
                         ┌─────────────────────┐
                         │    Text-to-Speech   │
                         └──────────┬──────────┘
                                    │
                                  Audio
                                    │
                                    ▼
                         ┌─────────────────────┐
                         │       User          │
                         └─────────────────────┘

وهذا التصميم مناسب جدًا لـ Laravel لأن الـ Agent والـ Tools والـ Queue والـ Storage والـ Authentication كلها يمكن أن تبقى داخل نفس التطبيق بدل فصلها منذ البداية إلى microservices. Laravel AI SDK مصمم تحديدًا للتكامل مع هذه المكونات.

4. البداية: تثبيت Laravel AI SDK

نثبت الحزمة:

composer require laravel/ai

ثم ننشر الإعدادات وmigrations:

php artisan vendor:publish --provider="Laravel\Ai\AiServiceProvider"

php artisan migrate

الـ SDK ينشئ جداول agent_conversations وagent_conversation_messages لتخزين conversation state.

ثم نضع API keys في .env حسب provider الذي سنستخدمه:

OPENAI_API_KEY=
ELEVENLABS_API_KEY=
GEMINI_API_KEY=

والنسخة الحالية من SDK تدعم TTS عبر OpenAI وElevenLabs وGemini، وتدعم STT عبر OpenAI وOpenAI-compatible providers وElevenLabs وMistral وGemini.

5. اختيار طبقة الصوت

لدينا خياران معماريان:

Architecture A — Turn Based Voice

Record
 ↓
Upload
 ↓
STT
 ↓
Agent
 ↓
TTS
 ↓
Play

هذه أبسط وأكثر وضوحًا، ومناسبة جدًا لـ:

  • تطبيقات الدعم.
  • CRM.
  • تطبيقات المبيعات.
  • Voice forms.
  • Voice search.
  • تطبيقات accessibility.

Architecture B — Realtime Voice

Microphone
   ↕
Realtime Session
   ↕
AI Model
   ↕
Audio Output

وهنا الصوت يدخل ويخرج بشكل مستمر مع voice activity detection والمقاطعة. هذه تجربة أقرب إلى المكالمة الهاتفية.

أنظمة Realtime الحديثة تستطيع نقل الصوت عبر WebRTC أو WebSocket وتدعم speech-to-speech مباشرة، بدل تحويل كل turn إلى ملف صوتي مستقل.

سنركز في الجزء الأكبر من المقال على Architecture A لأنها تعتمد مباشرة على APIs الرسمية التي يوفرها Laravel AI SDK للصوت، ثم نضيف Realtime Architecture في النهاية.

6. Database Design

لأن التطبيق Voice وليس مجرد Chat، نحتاج الاحتفاظ بأكثر من نوع من البيانات:

users
   │
   ├── conversations
   │
   ├── voice_sessions
   │
   └── voice_messages

7. جدول Voice Sessions

Schema::create('voice_sessions', function (Blueprint $table) {
    $table->id();

    $table->foreignId('user_id')
        ->constrained()
        ->cascadeOnDelete();

    $table->string('status')
        ->default('active');

    $table->string('language')
        ->nullable();

    $table->string('stt_provider')
        ->nullable();

    $table->string('tts_provider')
        ->nullable();

    $table->timestamp('started_at')
        ->nullable();

    $table->timestamp('ended_at')
        ->nullable();

    $table->timestamps();
});

هذا الجدول يمثل جلسة صوتية، لكن ليس الرسالة الصوتية نفسها.

8. جدول Voice Messages

Schema::create('voice_messages', function (Blueprint $table) {
    $table->id();

    $table->foreignId('voice_session_id')
        ->constrained()
        ->cascadeOnDelete();

    $table->string('role');

    $table->string('audio_path')
        ->nullable();

    $table->text('transcript')
        ->nullable();

    $table->text('response_text')
        ->nullable();

    $table->string('status')
        ->default('pending');

    $table->unsignedInteger('duration_ms')
        ->nullable();

    $table->unsignedInteger('processing_ms')
        ->nullable();

    $table->timestamps();
});

ثم role يمكن أن يكون user أو assistant. وبالتالي:

Voice Session #12

Message #1
user
"أين طلبي؟"

Message #2
assistant
"طلبك تم شحنه."

Message #3
user
"متى سيصل؟"

9. لماذا نخزن الـ transcript؟

لأن الصوت ليس أفضل format لكل شيء. نريد Audio + Transcript، لأن transcript يمكن استخدامه في:

  • Conversation history.
  • Search.
  • Analytics.
  • CRM notes.
  • Summarization.
  • Auditing.
  • RAG.
  • Customer support dashboards.

Laravel AI SDK يوفر transcription من path أو storage أو upload مباشرة، ويمكن أيضًا تشغيل diarization عندما نحتاج تقسيم النص حسب المتحدث.

10. Model

class VoiceSession extends Model
{
    protected $fillable = [
        'user_id',
        'status',
        'language',
        'stt_provider',
        'tts_provider',
        'started_at',
        'ended_at',
    ];

    public function messages()
    {
        return $this->hasMany(VoiceMessage::class);
    }
}

و:

class VoiceMessage extends Model
{
    protected $fillable = [
        'voice_session_id',
        'role',
        'audio_path',
        'transcript',
        'response_text',
        'status',
        'duration_ms',
        'processing_ms',
    ];

    public function session()
    {
        return $this->belongsTo(VoiceSession::class);
    }
}

11. تسجيل الصوت

من جهة browser، أفضل أن يكون لدينا:

User presses microphone
        ↓
MediaRecorder
        ↓
Audio Blob
        ↓
POST /api/voice/messages

لا نرسل الصوت كـ Base64 داخل JSON إلا عند وجود سبب حقيقي. الأفضل Content-Type: multipart/form-data وملفات الصوت تذهب إلى private storage.

12. Validation

$request->validate([
    'audio' => [
        'required',
        'file',
        'mimes:webm,mp3,wav,m4a,ogg',
        'max:25600',
    ],

    'session_id' => [
        'required',
        'integer',
        'exists:voice_sessions,id',
    ],
]);

لكن validation لا ينتهي هنا. في Production من الأفضل أيضًا فرض:

  • Maximum recording duration.
  • Maximum decoded audio size.
  • MIME verification.
  • Antivirus scanning عندما تكون الملفات غير موثوقة.
  • Private object storage.
  • Rate limits.

13. لا تثق بالـ filename

لا تعتمد على $audio->getClientOriginalName(); لتحديد نوع الملف أو صلاحيته. هذا اسم يرسله العميل.

اعتمد على Laravel validation وعلى فحص MIME الحقيقي عند الحاجة، وأعد تسمية الملفات داخليًا. مثلاً:

voice_messages/
    01/
    02/
    03/

بدل ../../../invoice.mp3

14. حفظ الملف

$path = $request
    ->file('audio')
    ->store('voice-messages');

ثم:

$message = VoiceMessage::create([
    'voice_session_id' => $session->id,
    'role' => 'user',
    'audio_path' => $path,
    'status' => 'processing',
]);

ثم لا نبدأ كل عمليات AI داخل request مباشرة، بل:

ProcessVoiceMessage::dispatch($message);

ونعيد 202 Accepted.

15. لماذا Queue؟

تدفق voice turn قد يكون:

Upload
 ↓
STT API
 ↓
Agent
 ↓
Tool
 ↓
Database
 ↓
LLM
 ↓
TTS API

أي أن request واحدًا قد يعتمد على خدمات خارجية متعددة. لو نفذنا كل ذلك داخل HTTP request، فكل شيء ينتظر كل شيء. الأفضل:

Browser → Create Message → Queue → Return

ثم:

Queue Worker → STT → Agent → TTS

Laravel AI SDK يدعم queueing للـ audio generation وtranscription، كما يدعم queued Agents.

16. Job لمعالجة Voice Message

class ProcessVoiceMessage implements ShouldQueue
{
    use Queueable;

    public int $tries = 4;

    public function __construct(
        public VoiceMessage $message
    ) {}

    public function handle(): void
    {
        $this->message->update([
            'status' => 'transcribing',
        ]);

        // STT
        // Agent
        // TTS
        // Update database
    }
}

من الأفضل تقسيم العملية لاحقًا إذا أصبح النظام كبيرًا:

TranscribeVoiceMessage
        ↓
RunVoiceAgent
        ↓
GenerateVoiceResponse

بدل Job ضخمة واحدة.

17. Speech-to-Text

Laravel توفر:

use Laravel\Ai\Transcription;

$transcript = Transcription::fromStorage(
    $message->audio_path
)->generate();

ويمكن أيضًا Transcription::fromPath(...) وTranscription::fromUpload(...).

وإذا كانت المكالمة تحتوي على أكثر من متحدث:

$transcript = Transcription::fromStorage(
    $message->audio_path
)
    ->diarize()
    ->generate();

Laravel توضح أن diarization يعيد transcript مقسمًا حسب المتحدث، وهو مفيد في مكالمات الدعم والاجتماعات وتحليل المحادثات.

18. ما الفرق بين STT وDiarization؟

لو كان لدينا:

Person A: "مرحبًا"
Person B: "أهلًا، كيف أساعدك؟"

بدون diarization قد تحصل على: "مرحبًا أهلًا كيف أساعدك"

أما مع diarization:

Speaker 1: مرحبًا
Speaker 2: أهلًا، كيف أساعدك؟

وهذا مفيد جدًا في: Call Center، Sales Calls، Interviews، Legal recordings، Meeting assistants. لكن في Voice Assistant أحادي المستخدم لا تحتاج diarization في كل turn.

19. تحويل Transcript إلى Agent Prompt

بعد STT:

$transcript = (string) $transcript;

لدينا "وين طلبي 5832؟" الآن نمررها إلى Agent:

Audio → "وين طلبي 5832؟" → SupportAgent

20. بناء Agent

أنشئ Agent:

php artisan make:agent SupportVoiceAgent

ثم:

namespace App\Ai\Agents;

use App\Ai\Tools\GetOrder;
use App\Ai\Tools\SearchKnowledgeBase;
use Laravel\Ai\Contracts\Agent;
use Laravel\Ai\Contracts\HasTools;
use Laravel\Ai\Promptable;

class SupportVoiceAgent implements Agent, HasTools
{
    use Promptable;

    public function instructions(): string
    {
        return <<<'PROMPT'
            You are a customer support voice assistant.

            Speak clearly and concisely.

            Never invent customer or order information.

            Use tools whenever the user asks about:
            - orders
            - shipments
            - payments
            - account information

            Answers should be suitable for voice output.
            Avoid markdown, tables, URLs, and long paragraphs.
        PROMPT;
    }

    public function tools(): iterable
    {
        return [
            new GetOrder,
            new SearchKnowledgeBase,
        ];
    }
}

آخر سطر مهم جدًا: "Answers should be suitable for voice output." لأن النص الموجه للشاشة ليس بالضرورة مناسبًا للصوت.

21. Voice AI يحتاج Prompt مختلفًا

الـ chatbot العادي يمكن أن يقول:

### Order Status

- Status: Shipped
- Carrier: DHL
- ETA: Tomorrow

لكن الـ TTS سيقرأ شيئًا غير طبيعي. للصوت:

"طلبك تم شحنه عن طريق DHL، والمتوقع أن يصل غدًا."

إذن لدينا Chat Prompt ≠ Voice Prompt، ويفضل أن يكون voice agent:

  • مختصرًا.
  • مباشرًا.
  • بدون markdown.
  • بدون جمل طويلة جدًا.
  • بدون رموز غير ضرورية.
  • بدون إعادة السؤال كاملًا.

22. Tool للوصول إلى CRM

لننشئ:

php artisan make:tool GetCustomerProfile

ومثلاً:

class GetCustomerProfile implements Tool
{
    public function description(): string
    {
        return 'Retrieve the authenticated customer profile.';
    }

    public function schema(JsonSchema $schema): array
    {
        return [];
    }

    public function handle(Request $request): string
    {
        $user = auth()->user();

        return json_encode([
            'name' => $user->name,
            'account_status' => $user->status,
        ]);
    }
}

لاحظ أننا لم نسمح للـ LLM بإرسال {"user_id": 999} لأن الـ authenticated identity تأتي من Laravel.

23. Tool للطلب

مثلاً:

class GetOrder implements Tool
{
    public function description(): string
    {
        return 'Get an order belonging to the authenticated customer.';
    }

    public function schema(JsonSchema $schema): array
    {
        return [
            'order_id' => $schema
                ->integer()
                ->required(),
        ];
    }

    public function handle(Request $request): string
    {
        $order = Order::query()
            ->whereKey($request['order_id'])
            ->where('user_id', auth()->id())
            ->first();

        if (! $order) {
            return json_encode([
                'found' => false,
            ]);
        }

        return json_encode([
            'found' => true,
            'status' => $order->status,
            'total' => $order->total,
            'currency' => $order->currency,
        ]);
    }
}

هذه ليست مجرد ممارسة جيدة، هذه حدود أمان. الـ LLM يقرر أي Tool يحتاجها، لكنه لا يقرر ما إذا كان المستخدم يحق له الوصول إلى البيانات.

24. RAG داخل Voice Agent

ماذا لو قال المستخدم:

"ما سياسة الإرجاع عندكم؟"

ليس لدينا سبب لكتابة كل سياسة الإرجاع داخل prompt. الأفضل:

Voice → STT → Agent → RAG → Knowledge Base → Answer → TTS

Laravel AI SDK الحالي يوفر SimilaritySearch كأداة يمكن ربطها بالـ Agent، كما يوفر vector stores وfile search حسب الـ provider. مثال:

use App\Models\Document;
use Laravel\Ai\Tools\SimilaritySearch;

public function tools(): iterable
{
    return [
        new SimilaritySearch(
            using: function (string $query) {
                return Document::query()
                    ->where('tenant_id', $this->tenant->id)
                    ->whereVectorSimilarTo('embedding', $query)
                    ->limit(8)
                    ->get();
            }
        ),
    ];
}

وهنا Voice Agent يستطيع فهم "هل أقدر أرجع المنتج بعد أسبوعين؟" ثم يبحث في المعرفة الداخلية.

25. Metadata Filtering مهم جدًا في RAG

في SaaS لا تريد أن يصل User A إلى Documents من tenant B. استخدم:

->where('tenant_id', $tenantId)

قبل semantic similarity أو ضمن البحث. يمكن أيضًا تخزين metadata مثل department وlanguage وyear وtenant_id وdocument_type. والـ vector-store features في Laravel AI SDK تدعم metadata المرتبطة بالملفات واستخدامها في filtering لدى provider tools.

26. تشغيل Agent

لدينا transcript:

$transcript = (string) $transcript;

ثم:

$response = (new SupportVoiceAgent)
    ->forUser($message->session->user)
    ->prompt($transcript);

ثم:

$text = $response->text;

النتيجة قد تكون: "طلبك تم شحنه، والمتوقع أن يصل غدًا."

27. لماذا Conversation Persistence مهمة؟

لأن المستخدم لا يتحدث مرة واحدة فقط. قد يقول "وين طلبي 5832؟" ثم "ومتى بيوصل؟" ثم "هل أقدر أغير عنوان التوصيل؟" الـ Agent يحتاج context:

Conversation
   │
   ├── User: Order 5832?
   ├── Assistant: Shipped.
   ├── User: When?
   └── User: Can I change address?

Laravel AI SDK يوفر conversation persistence وطرق forUser() وcontinue() لإدارة هذا السياق. كما يجب تطبيق authorization على conversation قبل الاستمرار بها، لأن continue() لا يثبت ownership بنفسه.

28. Text-to-Speech

الآن نريد إعادة النص كصوت. Laravel AI SDK توفر:

use Laravel\Ai\Audio;

$audio = Audio::of(
    $response->text
)->generate();

ويمكن اختيار صوت:

$audio = Audio::of(
    $response->text
)
    ->voice('voice-id')
    ->generate();

أو:

$audio = Audio::of(
    $response->text
)
    ->female()
    ->generate();

كما يمكن توجيه أسلوب الصوت باستخدام instructions().

29. تخزين الصوت

$path = $audio->storeAs(
    "voice-responses/{$message->id}.mp3"
);

ثم:

$message->update([
    'response_text' => $response->text,
    'audio_path' => $path,
    'status' => 'completed',
]);

Laravel AI SDK يدعم تخزين الصوت الناتج باستخدام Laravel filesystem، ويمكن استخدام private disks مثل S3 بدل جعل الصوت عامًا.

30. لماذا لا نجعل ملفات الصوت Public؟

لأن الصوت قد يحتوي على: Customer information، Payment information، Health information، Private conversations. لا تفعل $audio->storePublicly(); بدون سبب.

الأفضل:

Private S3 → Signed URL → Authorized User

أو إرجاع streaming endpoint يتحقق من authorization قبل السماح بالتحميل.

31. خدمة Voice Agent

بدل أن يتحول الـ Job إلى 500 سطر، أنشئ Service:

class VoiceAgentService
{
    public function handle(
        VoiceMessage $message
    ): void {
        $transcript = $this->transcribe($message);

        $response = $this->runAgent(
            $message,
            $transcript
        );

        $audio = $this->synthesize(
            $response->text
        );

        $this->storeResult(
            $message,
            $transcript,
            $response,
            $audio
        );
    }

    protected function transcribe(
        VoiceMessage $message
    ) {
        return Transcription::fromStorage(
            $message->audio_path
        )->generate();
    }

    protected function runAgent(
        VoiceMessage $message,
        string $transcript
    ) {
        return (new SupportVoiceAgent)
            ->forUser($message->session->user)
            ->prompt($transcript);
    }

    protected function synthesize(
        string $text
    ) {
        return Audio::of($text)
            ->voice('support-voice')
            ->generate();
    }
}

وهذا يمنحنا architecture:

Controller
    ↓
Job
    ↓
VoiceAgentService
    ├── STT
    ├── Agent
    └── TTS

32. Job النهائي

class ProcessVoiceMessage implements ShouldQueue
{
    use Queueable;

    public int $tries = 4;

    public function __construct(
        public VoiceMessage $message
    ) {}

    public function handle(
        VoiceAgentService $service
    ): void {
        $service->handle($this->message);
    }
}

أصبح لدينا separation واضح: HTTP → Queue → Service → AI

33. إرسال النتيجة إلى المستخدم

هنا يبدأ الجزء الحقيقي من UX. لدينا خيار بسيط: POST → 202 → Browser polls. لكن polling متكرر (GET /status بشكل مستمر) ليس مثاليًا.

الأفضل استخدام broadcasting أو Reverb للأحداث. Laravel AI SDK يدعم streaming وbroadcasting، كما أن Reverb يوفر WebSocket server مدمجًا مع Laravel Broadcasting.

34. Voice Events

بدل event واحد voice.completed، يفضل أن يكون لدينا:

voice.processing
voice.transcribed
voice.agent_started
voice.tool_started
voice.tool_completed
voice.response_ready
voice.audio_ready
voice.completed
voice.failed

وهذا يسمح للواجهة بعرض state حقيقية بدل spinner فقط.

35. Reverb Architecture

في هذا السيناريو:

Browser
   │
   │ WebSocket
   ▼
Laravel Reverb
   ▲
   │ Broadcast
   │
Queue Worker
   │
   ▼
Voice Agent

يمكن للمستخدم أن يرى: 🎙️ Listening... 📝 Understanding... 🤖 Thinking... 🔍 Checking your order... 🔊 Speaking...

Laravel Reverb مصمم ليكون WebSocket server قابلًا للتوسع داخل منظومة Laravel، ويمكن تشغيله عبر:

php artisan reverb:start

وتوصي الوثائق بوضعه خلف reverse proxy مثل Nginx في Production.

36. Private Channel

لا تستخدم voice-session.123 كـ public channel. استخدم private channel private-voice-session.{sessionId}. ثم:

Broadcast::channel(
    'voice-session.{session}',
    function (User $user, VoiceSession $session) {
        return $session->user_id === $user->id;
    }
);

وبهذا لا يستطيع User B الاستماع إلى User A.

37. لا تجعل Voice Session ID كافيًا للوصول

هذا خطأ شائع: GET /voice-session/123 ثم VoiceSession::findOrFail($id); بدون owner filter.

الأصح:

VoiceSession::query()
    ->whereKey($id)
    ->where('user_id', auth()->id())
    ->firstOrFail();

أو استخدام Laravel Policies.

38. Tool Security

الصوت لا يغير قواعد الأمان. لو قال المستخدم "أريد تحويل 10,000 دولار." قد يقرر Agent استدعاء TransferMoney، لكن لا يجب أن يكون Voice → LLM → TransferMoney() مباشرة.

بل:

Voice → Agent → Sensitive Tool → Authorization → Human Approval → Execution

Laravel AI SDK توفر Human Tool Approval ويمكنها إيقاف تنفيذ الـ Agent إلى أن تتم الموافقة، مع دعم ذلك عبر prompt وstream وqueue وbroadcast وbroadcastOnQueue.

39. Read Tools مقابل Write Tools

صنف الأدوات:

READ
────
GetOrder
GetProfile
SearchKnowledge
GetShipment


WRITE
─────
CancelOrder
ChangeAddress
Refund
TransferMoney

ثم Read → Automatic، وWrite → Strict authorization → Possibly human approval. وهذه قاعدة ممتازة في أي Agent يملك صلاحيات حقيقية.

40. Tool Results لا يجب أن تكون صوتية

افترض أن tool ترجع:

{
    "order_id": 5832,
    "warehouse_id": 9921,
    "internal_note": "VIP customer",
    "carrier_api_token": "...",
    "status": "shipped"
}

لا ترسل كل هذه المعلومات Tool → TTS. الـ Agent يأخذ status = shipped ثم ينشئ user-facing response: "طلبك تم شحنه."

إذن: Internal Tool Data → Agent → Safe User Response → TTS

41. مشكلة Latency

الصوت حساس جدًا للتأخير. في النظام الحالي لدينا STT + LLM + Tool Calls + TTS. أي:

T_total = T_STT + T_agent + T_tools + T_TTS + Network

إذا أخذت:

STT = 700ms
Agent = 1000ms
Tool = 800ms
TTS = 900ms

فأنت قريب من 3.4 ثانية قبل أن يسمع المستخدم الإجابة. وهذا قد يكون مقبولًا في customer support، لكنه ليس مثاليًا لمحادثة طبيعية.

42. كيف نقلل الـ Latency؟

أولًا: اجعل الصوت قصيرًا

لا تطلب من المستخدم تسجيل 60 ثانية لكل turn.

ثانيًا: لا تستخدم model ثقيل لكل شيء

Agent بسيط قد لا يحتاج أذكى model. Laravel AI SDK توفر attributes مثل UseCheapestModel وUseSmartestModel، أو يمكنك تثبيت model محدد بشكل صريح للحفاظ على predictability في السلوك والتكلفة.

ثالثًا: قلل Tool Calls

بدل GetCustomer → GetOrder → GetShipment → GetPayment، قد تستطيع إنشاء Tool واحدة GetOrderContext ترجع البيانات الضرورية فقط.

43. Deferred Tools

إذا كان Agent لديه عشرات الـ tools (Weather، SearchInvoices، Refund، CancelOrder، SearchCRM، GetShipment، GetCustomer...)، فإرسال كل تعريف Tool إلى model في كل request يستهلك tokens ويزيد صعوبة اختيار الأداة المناسبة.

Laravel AI SDK يدعم Deferred Tool Loading باستخدام ToolSearch مع OpenAI وAnthropic، بحيث لا يتم تحميل كل tool definitions إلا عندما تكون ذات صلة. وهذا مهم جدًا عندما تتحول Voice Agent من demo إلى نظام enterprise فيه عشرات القدرات.

44. لا ترسل تاريخ المحادثة كاملًا دائمًا

لو استمرت المكالمة 40 دقيقة (200 رسالة)، لا تريد إرسال كل شيء إلى model في كل turn. يمكن استخدام:

Recent messages
+
Conversation summary
+
Relevant CRM state
+
RAG

بدل Entire conversation. مثال:

Conversation Summary:
The user owns order 5832.
The order was shipped today.
The user asked about delivery ETA.

Recent Turns:
...

هذا يخفض التكلفة ويحسن context quality.

45. Voice-specific Summarization

في Voice Agent، summary مهمة جدًا. مثلاً:

User name: Ahmad
Order: 5832
Issue: Delivery delay
Last status: Shipment delayed
Preferred language: Arabic

يمكن حفظ هذا كـ structured state بدلاً من الاعتماد على transcription فقط.

46. Language Detection

إذا كان المستخدم يتحدث Arabic، فـ TTS يجب أن يجيب بالعربية. إذا English، فالإجابة بالإنجليزية. يمكن تخزين voice_sessions.language (مثلاً ar، en، tr). ومن ثم:

STT → Language Detection → Agent → TTS language/voice

لكن لا تعتمد على model وحده لتحديد اللغة في كل request إذا كان التطبيق يعرف لغة المستخدم مسبقًا.

47. Arabic Voice Applications

اللغة العربية تضيف تعقيدات إضافية: اللهجات، أسماء المدن، أسماء الشركات، الأرقام، أرقام الطلبات، الاختصارات، كلمات إنجليزية داخل الكلام.

مثلاً: "الطلب 5832 تبع DHL، وين صار؟" — STT قد ينتج عدة أشكال. لذلك لا تستخدم transcript فقط كـ raw string عند التعامل مع identifiers. من الأفضل أن يكون Agent قادرًا على استخراج:

order_id = 5832
carrier = DHL
intent = shipment_status

مع structured output أو Tool invocation.

48. الأرقام مهمة جدًا

في Voice UI، هناك خطر أن يتحول 5832 إلى 583 أو 5823. لا تعتمد على transcription وحدها عندما تكون identifiers حساسة. في الحالات المهمة يمكن أن يسأل Agent: "هل تقصد الطلب رقم 5832؟" قبل تنفيذ عملية حساسة. وهذا أفضل من تنفيذ tool مباشرة.

49. Confirmation Pattern

للبيانات الحساسة:

User: "حول 500 دولار إلى محمد."

Agent: "للتأكيد، تريد تحويل 500 دولار إلى محمد علي؟"

User: "نعم."

Agent: Execute

وهذا يقلل مخاطر: STT error، Ambiguous entities، Prompt misunderstanding، Accidental tool calls.

50. Error Handling

هناك عدد كبير من أماكن الفشل: Microphone → Upload → STT → Agent → Tool → Database → TTS → Playback. أي طبقة قد تفشل. لذلك لا تستخدم status = processing إلى الأبد. استخدم lifecycle:

uploaded
transcribing
transcribed
thinking
tool_calling
synthesizing
completed
failed

51. Retry Strategy

ليس كل failure قابلًا للـ retry. مثلاً 429 أو timeout أو provider unavailable قد تحتاج retry. أما invalid audio أو validation failed أو authorization denied فلا معنى لإعادة الطلب أربع مرات. مثلاً:

public int $tries = 4;

public function backoff(): array
{
    return [
        5,
        15,
        30,
        60,
    ];
}

52. Failover بين Providers

الـ SDK الحالي يدعم provider failover عندما تحدث حالات معينة مثل rate limits أو provider overload أو insufficient credits. مثلاً من حيث المبدأ:

Primary Provider
      │
      ├── Success → Done
      │
      └── Rate Limited
             ↓
       Backup Provider

وهذا مفيد في Voice لأن فشل provider يعني تجربة سيئة جدًا للمستخدم. لكن لا تستخدم failover بشكل عشوائي إذا كانت جودة الصوت أو voice identity يجب أن تبقى ثابتة.

53. Observability

في Voice Agent نحتاج metrics مختلفة عن chatbot. راقب: TTFT، Time to first audio، STT latency، Agent latency، Tool latency، TTS latency، Total turn latency، Failure rate، Retry rate، Audio duration، Cost per minute. مثلاً:

voice_turn
──────────

STT:       620ms
Agent:     910ms
Tool:      210ms
TTS:       770ms
Network:   140ms

Total:     2.65s

وهذا يوضح أين المشكلة.

54. Laravel AI Events

Laravel AI SDK يوفر events مثل:

GeneratingAudio
AudioGenerated

GeneratingTranscription
TranscriptionGenerated

PromptingAgent
AgentPrompted

InvokingTool
ToolInvoked

StreamingAgent
AgentStreamed

ويمكن استخدام هذه الأحداث لتسجيل usage وlatency ومراقبة النظام.

55. Cost

التكلفة هنا مركبة: Audio Input + STT + LLM + Tool calls + TTS. لذلك لا تفكر "سعر الـ chatbot" بل "كم تكلفني دقيقة Voice Agent واحدة؟" مثلاً من الناحية المعمارية:

Cost per minute
=
STT cost
+
LLM input/output
+
TTS cost
+
Storage
+
Infrastructure

ولهذا يجب أن يكون لديك limits.

56. Limits

ضع: max voice duration / turn، max turns / minute، max turns / session، max concurrent sessions، max tool calls، max conversation duration. مثلاً:

Maximum voice turn: 60 seconds
Maximum session: 30 minutes
Maximum concurrent: 10 per user

القيم الفعلية تعتمد على طبيعة التطبيق.

57. منع Abuse

Voice endpoint قد يكون مكلفًا جدًا. لا تترك POST /voice بدون rate limiting. يمكن أن يكون لدينا:

Guest → 5 turns/day
Free  → 30 turns/day
Pro   → 500 turns/day

وفي backend:

RateLimiter::for('voice', function ($request) {
    return Limit::perMinute(10)
        ->by($request->user()->id);
});

58. Privacy

Voice data أكثر حساسية من النص في كثير من التطبيقات. قد تحتوي التسجيلات على: Names، Addresses، Phone Numbers، Financial Details، Customer complaints، Personal conversations.

لذلك اسأل دائمًا: أين تخزن الملفات؟ متى تحذف؟ من يمكنه الوصول؟ هل provider يستلم الملف؟ ما سياسة الاحتفاظ؟ هل التسجيل مطلوب أصلًا؟ هل تحتاج transcript دائمًا؟

وLaravel AI SDK يسمح بتوجيه provider requests إلى custom base URLs، وهو مفيد عندما تحتاج إلى gateway مركزي للـ AI أو إدارة API keys أو rate limits أو routing.

59. Delete Policy

من الأفضل أن يكون لديك policy واضحة:

Raw audio: delete after 7 days
Transcript: retain 90 days
Conversation: retain according to business policy

بدل الاحتفاظ بكل شيء إلى الأبد. وأحيانًا لا تحتاج أصلًا إلى تخزين raw audio بعد استخراج transcript:

Audio → STT → Transcript → Delete Audio

هذا يقلل Storage وPrivacy risk وAttack surface.

60. Testing

لا تجعل كل test يستدعي STT وTTS الحقيقيين. Laravel AI SDK توفر fake support للصوت وtranscription وكذلك agents. مثلًا:

Audio::fake();

Transcription::fake();

SupportVoiceAgent::fake([
    'Your order has shipped.',
]);

ثم:

ProcessVoiceMessage::dispatchSync($message);

واختبر:

  • transcription stored
  • agent invoked
  • tool authorization
  • audio generated
  • final status completed

61. Testing Tools أهم من Testing Voice

قد يكون STT مثاليًا، لكن إذا كان Tool يستطيع قراءة بيانات عميل آخر فالنظام ما زال خطيرًا. لذلك اختبر:

User A → Order A   ✓
User A → Order B   ✗
User A → Private CRM document B   ✗

واختبر: Read Tool، Write Tool، Approval، Tenant Isolation.

62. مشكلة Reverb والانفصال

في حال استخدمنا Reverb، انقطاع Browser عن Reverb لا يعني أن الـ Agent توقف. الـ Agent قد يستمر:

Queue Worker → AI → TTS → Database

لذلك Reverb ≠ source of truth. المصدر الحقيقي: Database. أما Reverb فينقل updates لحظيًا. Laravel Reverb يوفر أيضًا دعمًا للـ scaling عبر Redis، ويمكن وضع عدة Reverb servers خلف load balancer.

63. Production Reverb

في Production:

Browser
   │
   │ WSS
   ▼
Nginx / Load Balancer
   │
   ▼
Reverb

والـ Reverb server يمكن أن يعمل داخليًا على 0.0.0.0:8080، بينما public endpoint يكون wss://ws.example.com. Laravel توضح الفرق بين REVERB_SERVER_HOST/PORT وبين REVERB_HOST/PORT، وتوضح كيفية proxying عبر Nginx.

64. Allowed Origins

لا تستخدم * بدون سبب. حدد domains:

'allowed_origins' => [
    'https://app.example.com',
],

Reverb يرفض الاتصالات من origins غير المسموح بها.

65. هل نحتاج Reverb لإرسال الصوت نفسه؟

هنا نصل إلى نقطة مهمة جدًا. Reverb ممتاز لـ Events وStatus وTool calls وProgress وConversation updates، لكن لا أنصح بأن يصبح نقل مئات audio chunks عبر Laravel Reverb هو transport الرئيسي للصوت الحي في تطبيق Realtime Voice.

للمحادثة الصوتية الفورية، تحتاج عادةً إلى transport مصمم أصلًا للصوت منخفض الكمون مثل WebRTC. ولهذا تستخدم Reverb لإرسال Agent status وTool events وUI synchronization، بينما الصوت نفسه يمر عبر media transport مناسب.

66. Realtime Voice Architecture الحقيقية

عند الوصول إلى مستوى مثل المكالمات الحية:

                    ┌─────────────────────┐
                    │      Browser        │
                    │    Microphone       │
                    └──────────┬──────────┘
                               │
                            WebRTC
                               │
                               ▼
                    ┌─────────────────────┐
                    │ Realtime AI Model  │
                    └──────────┬──────────┘
                               │
                         Function Calls
                               │
                               ▼
                    ┌─────────────────────┐
                    │ Laravel Backend     │
                    │                     │
                    │ CRM / RAG / Tools   │
                    └─────────────────────┘

هنا Laravel يصبح Business Brain، وليس بالضرورة media server. وهذا distinction مهم جدًا.

67. Realtime API تختلف عن STT → Agent → TTS

في architecture التقليدية:

Audio → STT → Text → LLM → Text → TTS → Audio

أما Realtime speech-to-speech:

Audio ↕ Realtime Model ↕ Audio

ويمكن أن يكون النظام قادرًا على التعامل مع المقاطعة والتدفق المستمر للصوت. OpenAI توضح أن Realtime API تدعم low-latency interfaces مثل WebRTC وWebSocket، وتدعم speech-to-speech مباشرة.

68. ما دور Laravel في Realtime؟

Laravel يبقى مسؤولًا عن: Authentication، Authorization، Sessions، CRM، Orders، Payments، RAG، Tools، Audit Logs، Business Rules، Webhooks، Database.

والـ browser/Realtime layer مسؤولة عن: Microphone، Audio playback، VAD، WebRTC، Connection lifecycle.

وهكذا:

Browser
   ↕
Realtime Audio
   ↕
AI Model
   │
   │ Tool call
   ▼
Laravel
   │
   ├── CRM
   ├── Orders
   ├── RAG
   └── Internal APIs

هذه غالبًا architecture أفضل من محاولة جعل Laravel يعالج كل audio packet بنفسه.

69. Tool Calls في Realtime

حتى في Realtime architecture، Laravel يمكن أن تبقى backend للأفعال. مثلاً:

User: "ألغِ طلبي"

Realtime Agent
      ↓
CancelOrder()
      ↓
Laravel API
      ↓
Authorization
      ↓
Approval
      ↓
Database

إذن: Realtime = Voice Transport، Laravel = Business Logic. وهذا separation قوي جدًا.

70. Voice Agent + CRM

من أفضل استخدامات Voice AI: CRM + Voice Agent. مثلاً Sales Agent يقول "اتصل بالعميل أحمد." ثم:

Voice → CRM → Find Customer → Get Last Interaction → Get Open Deals → Agent

ثم يستطيع المستخدم قول "سجل ملاحظة أنه يريد عرض السعر يوم الأحد." الـ Agent: CreateCRMNote. لكن CreateCRMNote يجب أن يطبق tenant + user + permissions.

71. Voice Agent + RAG

سيناريو ممتاز: "ما شروط استرجاع المنتج؟" الـ Agent لا يحتاج إلى حفظ policy داخل prompt. بل:

Voice → STT → Agent → Similarity Search → Policy Documents → Answer → TTS

ويمكن للـ Agent استخدام SimilaritySearch tool، وهو مدعوم مباشرة في Laravel AI SDK.

72. Voice Agent + External APIs

مثلاً Weather Tool، Shipping API، Maps API، CRM API، Payment API. الـ Agent يختار الأداة المناسبة. لكن لا تجعل tool تستقبل arbitrary URL وتطلب منها fetch() بدون allowlist. استخدم APIs محددة.

73. MCP أيضًا خيار

Laravel AI SDK الحالي يدعم MCP tools، ويمكن للـ Agent تحميل أدوات من MCP server بنفس نمط الأدوات الأخرى. وهذا مفيد عندما تريد:

Laravel Agent
      ↓
MCP
 ┌────┼────┐
 ▼    ▼    ▼
CRM  ERP  Internal APIs

بدل كتابة كل integration مباشرة داخل Agent. لكن يجب التعامل مع MCP كـ privileged integration layer وليس كـ trust boundary تلقائي.

74. Voice-specific UX

لا تصمم Voice UI مثل Chat UI. في Chat لدينا [message box] [send]. أما Voice:

       🎙️

  Listening...

  ───────────

  "وين طلبي؟"

  🔊 Speaking...

وأضف: Mute، Stop، Interrupt، Retry، Replay. وخصوصًا Interrupt، لأن المستخدم لا يريد انتظار Agent لينهي جملة كاملة إذا بدأ يتحدث.

75. Barge-in

في المحادثات الطبيعية:

Agent: "طلبك تم شحنه ويمكن أن يصل غدًا..."

User: "طيب بس—"

Agent: STOP

هذه ليست detail بسيطة. إنها جزء أساسي من تجربة Realtime Voice. وهنا تظهر أهمية الأنظمة التي تدعم voice activity detection وإيقاف response أثناء الكلام. Realtime APIs الحديثة توفر أحداثًا وآليات مخصصة للمقاطعة وإيقاف audio output.

76. Best Practices

افصل Voice Layer عن Agent Layer

Voice → Agent، وليس Agent يعرف Microphone.

افصل Business Tools عن Voice

الأفضل GetOrder بدل VoiceGetOrder، لأن نفس tool قد يستخدمها Voice Agent وChat Agent وBackground Agent وAdmin Assistant.

صمم Responses للصوت

Short، Natural، Clear.

لا تجعل كل Tool تلقائية

العمليات الحساسة تحتاج approval أو policy صارمة.

خزّن transcript حتى لو لم تخزن الصوت

هذا غالبًا يعطيك analytics وسياقًا أفضل مع تقليل storage.

استخدم queues للعمليات الثقيلة

خصوصًا STT/TTS غير الفورية.

استخدم Reverb للأحداث وليس كبديل لـ WebRTC

Reverb ممتاز في real-time application events، وليس media transport متخصصًا.

77. ما الذي لا أنصح به؟

Browser → Upload audio → LLM مباشرة

Voice → Public AI Tool → Database

User sends user_id → Tool trusts it

Voice response → Public S3 URL

All conversation history → Every single turn

Every audio packet → PHP HTTP request

Realtime audio → Reverb as the primary media transport

78. Architecture النهائية — Turn-Based Voice Agent

للتطبيقات التي لا تحتاج محادثة WebRTC حقيقية:

                         USER
                           │
                       Microphone
                           │
                           ▼
                    ┌──────────────┐
                    │ Laravel API  │
                    └──────┬───────┘
                           │
                           ▼
                    Private Storage
                           │
                           ▼
                         Queue
                           │
                           ▼
                  ┌─────────────────┐
                  │ Voice Service   │
                  └────────┬────────┘
                           │
              ┌────────────┼────────────┐
              │            │            │
              ▼            ▼            ▼
             STT         Agent         TTS
                           │
                ┌──────────┼───────────┐
                │          │           │
                ▼          ▼           ▼
               CRM        RAG        APIs
                           │
                           ▼
                       Text Answer
                           │
                           ▼
                       Audio File
                           │
                           ▼
                        Storage
                           │
                           ▼
                        Reverb
                           │
                           ▼
                         Browser

79. Architecture النهائية — Realtime Voice

للتجارب التي تتطلب latency منخفضة:

                         USER
                           │
                      Microphone
                           │
                           ▼
                    ┌───────────────┐
                    │    WebRTC     │
                    └───────┬───────┘
                            │
                            ▼
                  Realtime AI Session
                            │
                            │ Tool Calls
                            ▼
                    ┌───────────────┐
                    │ Laravel API   │
                    └───────┬───────┘
                            │
                 ┌──────────┼──────────┐
                 │          │          │
                 ▼          ▼          ▼
                CRM        RAG       Tools
                 │          │          │
                 └──────────┼──────────┘
                            │
                            ▼
                       Business DB

Browser
   │
   └──── Laravel Reverb
           │
           └── UI Events / Status / Sync

هذه architecture تفصل media plane عن application plane:

Media Plane
───────────
WebRTC / Audio

Application Plane
──────────────────
Laravel / Reverb / DB / Tools

وهو فصل مهم جدًا عندما يصبح النظام كبيرًا.

80. الخلاصة

بناء Voice AI Agent ليس مجرد إضافة Speech-to-Text إلى Laravel. المشروع الحقيقي هو pipeline متكامل:

                 ┌───────────────┐
                 │ User Voice    │
                 └───────┬───────┘
                         │
                         ▼
                 Speech-to-Text
                         │
                         ▼
                   Laravel Agent
                         │
              ┌──────────┼──────────┐
              │          │          │
              ▼          ▼          ▼
             CRM        RAG       Tools
              │          │          │
              └──────────┼──────────┘
                         │
                         ▼
                  Response Text
                         │
                         ▼
                  Text-to-Speech
                         │
                         ▼
                    Audio Output
                         │
                         ▼
                       User

Laravel AI SDK الحالي يوفر معظم اللبنات التي تحتاجها لبناء هذا النظام داخل Laravel: Agents، Tools، conversation persistence، STT، diarization، TTS، queues، streaming، broadcasting، vector stores وtesting.

لكن أهم قرار معماري هو ألا تخلط بين نوعين مختلفين من المنتجات:

Voice Assistant Turn-Based

Audio → STT → Agent → TTS

وهذا ممتاز لتطبيقات CRM والدعم والخدمات الصوتية التي يمكن أن تتحمل latency بسيطة.

Realtime Voice Assistant

WebRTC ↕ Realtime Model ↕ Laravel Tools / Business Logic

وهذا هو الخيار الأفضل عندما تريد تجربة تشبه المكالمة الطبيعية، مع تدفق صوتي مستمر ومقاطعة واستجابة منخفضة الكمون. أنظمة Realtime الحديثة تدعم هذا النمط مباشرة عبر WebRTC/WebSocket.

وفي كلتا الحالتين، Laravel تبقى في مكان قوي جدًا:

Laravel
├── Authentication
├── Authorization
├── Agents
├── Tools
├── CRM
├── RAG
├── Database
├── Queues
├── Storage
├── Broadcasting
├── Reverb
└── Audit / Observability

بينما الصوت نفسه يمكن أن يستخدم STT / TTS في architecture التقليدية، أو WebRTC / Realtime عندما تصبح المحادثة الصوتية الفورية هي المنتج نفسه.

في النهاية، أفضل Voice Agent ليس الذي "يتكلم بشكل جميل"، بل الذي يستطيع فهم المستخدم، الوصول إلى البيانات التي يحق له الوصول إليها، تنفيذ الإجراءات الصحيحة، التعامل مع الأخطاء، ثم الإجابة بصوت طبيعي وآمن.

المصادر الرسمية

Laravel

  • Laravel AI SDK — Laravel 13.x Documentation
  • Laravel AI SDK — Audio / TTS
  • Laravel AI SDK — Transcription / Diarization
  • Laravel AI SDK — Agents
  • Laravel AI SDK — Tools
  • Laravel AI SDK — RAG / Similarity Search
  • Laravel Reverb
  • Introducing the Laravel AI SDK
  • Building AI Agents with Laravel: No Python Required
  • Laravel 13 Release Notes

Realtime Voice

  • OpenAI Realtime API Reference — WebRTC/WebSocket وspeech-to-speech.
  • OpenAI Realtime Client Events — audio input وinterruptions وsession events.
  • OpenAI Realtime Server Events — output audio lifecycle والأخطاء.