بناء 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 والأخطاء.
التعليقات (0)
لا توجد تعليقات بعد — كن أول من يشارك رأيه.
أضف تعليقك