
بناء وكيل صوتي على OpenAI Realtime API: دليل الإنتاج في 7 خطوات (2026)
ردّ وكيلنا التجريبي على مكالمة Twilio ونطق كلمته الأولى بعد 1.1 ثانية من توقّف المتصل عن الكلام. هذا هو زمن الذهاب والإياب p50، مقيساً عبر 40 مكالمة باستخدام gpt-realtime-2 وsemantic_vad. ليس سحراً. يُجري OpenAI Realtime API تحويل الكلام إلى كلام داخل مقبس واحد، فتتجاوز سلسلة STT ← LLM ← TTS التي تضيف نحو 600 مللي ثانية من الكود الرابط. لكن القيم الافتراضية لن توصلك إلى الثانية الواحدة. هذا هو البناء في 7 خطوات الذي أطلقناه، مع الكود وجدول زمن الاستجابة.
هذا دليل بناء، وليس شرحاً للمفاهيم. إن أردت التفصيل الطبقي أولاً، اقرأ ما هو الوكيل الصوتي بالذكاء الاصطناعي فعلاً ثم عُد. كل ما يلي يفترض أن لديك مفتاح OpenAI API وبيئة تشغيل Node.
أبرز النقاط:
- يُجري gpt-realtime-2 تحويل الكلام إلى كلام في مقبس واحد، دون سلسلة STT/LLM/TTS، ~600 مللي ثانية موفّرة.
- أنشئ المفاتيح المؤقتة على جانب الخادم؛ لا ترسل أبداً مفتاح API القياسي إلى متصفح.
- تدفّق وسائط Twilio بصيغة 8 كيلوهرتز μ-law؛ أعِد التحجيم إلى 24 كيلوهرتز PCM16 لـ Realtime API.
- قِسنا p50 1.1 ث / p95 1.9 ث للذهاب والإياب. تجري المقاطعة عبر
response.cancel.
ماذا ستبني في 7 خطوات
يبني هذا الدليل وكيلاً صوتياً على OpenAI Realtime API يردّ على الهاتف في أقل من 1.5 ثانية، ويستدعي دالة حقيقية في منتصف المحادثة، ويسمح للمتصل بمقاطعته. التدفّق قصير: يتصل المتصل برقم، يتدفّق الصوت إلى خادمك، يجسره خادمك عبر مقبس واحد إلى gpt-realtime-2، يتكلم النموذج ويمكنه إطلاق استدعاءات الأدوات، ثم يعود الصوت متدفّقاً.
إليك المسار، ويمكنك التوقف عند أي خطوة تناسب حالة استخدامك:
- إنشاء مفتاح مؤقت (مسار الخادم)
- فتح الجلسة وتهيئتها
- إضافة استدعاء الدوال
- الجسر إلى رقم هاتف عبر Twilio
- معالجة المقاطعة والاعتراضات
- ضبط زمن الاستجابة إلى أقل من ثانية
- النشر والتقوية للإنتاج
تحمل ثلاثة وسائط نقل الصوت، واختيارك يعتمد على مصدر الصوت. متصفح يلتقطه مباشرة (WebRTC)، أو خادمك لديه بالفعل تدفّق خام (WebSocket)، أو شبكة هاتفية تسلّمه (SIP). سنستخدم WebSocket لجسر Twilio ونشير إلى الباقي حيثما يناسب.
الخطوة 1: إنشاء مفتاح مؤقت (المسار الذي لا يمكنك تخطّيه)
لا تعرّض أبداً مفتاح OpenAI API القياسي لمتصفح أو جهاز عميل. يصدر Realtime API مفاتيح مؤقتة قصيرة العمر لهذا الغرض بالضبط. يستدعي خادمك POST /v1/realtime/client_secrets بمفتاحك الحقيقي، ويسلّم العميل رمزاً ينتهي خلال دقيقة تقريباً، ويتصل العميل به بدلاً من ذلك.
إليك مسار Express بسيط ينشئ واحداً:
// server.js
import express from "express";
const app = express();
app.get("/session", async (req, res) => {
const r = await fetch("https://api.openai.com/v1/realtime/client_secrets", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.OPENAI_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
session: { type: "realtime", model: "gpt-realtime-2" },
}),
});
const data = await r.json();
res.json({ client_secret: data.value, expires_at: data.expires_at });
});
app.listen(3000);يجلب المتصفح /session، ويقرأ السر قصير العمر، ويفتح اتصال Realtime به. إذا كان وكيلك على جانب الخادم فقط (حالة Twilio في الخطوة 4)، فيمكنك تخطّي التسليم للعميل وفتح المقبس من الواجهة الخلفية مباشرة بالمفتاح القياسي. يوجد التدفّق المؤقت لحماية العملاء غير الموثوقين.
الخطوة 2: فتح الجلسة وتهيئة gpt-realtime-2
افتح اتصالاً، ثم أرسل session.update يضبط النموذج وصيغة الصوت والصوت واكتشاف الدور. توصي وثائق OpenAI بالبدء بـ reasoning.effort على low ورفعه فقط إن كان منطق أدواتك يحتاج دقة أكبر، لأن جهداً أعلى يكلّفك زمن استجابة. يعمل الصوت بصيغة 24 كيلوهرتز PCM16 في كلا الاتجاهين.
ws.send(JSON.stringify({
type: "session.update",
session: {
type: "realtime",
model: "gpt-realtime-2",
output_modalities: ["audio"],
audio: {
input: { format: "pcm16", sample_rate: 24000 },
output: { format: "pcm16", sample_rate: 24000, voice: "marin" },
},
instructions: "أنت وكيل حجوزات لمطعم. كن موجزاً.",
reasoning: { effort: "low" },
turn_detection: { type: "semantic_vad" },
},
}));يعتمد وسيط النقل الذي تلفّ به ذلك المقبس على مصدر الصوت:
| وسيط النقل | يُستخدم عندما | مصدر الصوت |
|---|---|---|
| WebRTC | يلتقط متصفح أو تطبيق جوال الميكروفون مباشرة | جهاز العميل |
| WebSocket | لدى خادمك بالفعل تدفّق صوت خام | خط أنابيب الخادم |
| SIP | تريد أن يتولّى OpenAI الجزء الهاتفي | PSTN / الهاتف |
للقائمة الكاملة لحقول الجلسة ومجموعة ميزات GA، تُعدّ وثائق OpenAI Realtime API مصدر الحقيقة. نستخدم WebSocket لأن Twilio يسلّمنا صوتاً خاماً في الخطوة 4.
الخطوة 3: إضافة استدعاء الدوال (كي يستطيع الوكيل فعل شيء حقاً)
الوكيل الصوتي الذي لا يستطيع التصرّف هو تعليق صوتي. يسمح استدعاء الدوال لـ gpt-realtime-2 بالتوقّف في منتصف المحادثة، وطلب تشغيل شيء من كودك، ومتابعة الكلام بالنتيجة. تعلن أداة في الجلسة، يطلق النموذج حدث function_call_arguments.done حين يريدها، تنفّذ العمل، وترسل المخرجات.
أعلن الأداة، ثم عالج الحدث:
// في session.update -> session.tools:
tools: [{
type: "function",
name: "book_reservation",
description: "يحجز طاولة لعدد أشخاص ووقت محددين.",
parameters: {
type: "object",
properties: {
party_size: { type: "integer" },
time: { type: "string", description: "تاريخ/وقت ISO 8601" },
},
required: ["party_size", "time"],
},
}]
// معالجة الاستدعاء:
if (event.type === "response.function_call_arguments.done") {
const args = JSON.parse(event.arguments);
const result = await bookTable(args); // منطقك الحقيقي
ws.send(JSON.stringify({
type: "conversation.item.create",
item: {
type: "function_call_output",
call_id: event.call_id,
output: JSON.stringify(result),
},
}));
ws.send(JSON.stringify({ type: "response.create" })); // دعه ينطق النتيجة
}السبب الأكثر شيوعاً لعدم إطلاق الأدوات بصمت: عدم الإصغاء لـ function_call_arguments.done وعدم إرسال response.create بعده. أنتج النموذج الاستدعاء، فتجاهلته أنت، فيسمع المتصل صمتاً تاماً.
إن كان وكيلك يتعامل مع أدوات كثيرة، فقد غيّر OpenAI Agents SDK الحسبة هنا. جعل تحديثه الكبير في 15 أبريل 2026 بروتوكول سياق النموذج (MCP) من الدرجة الأولى وحوّل عمليات التسليم بين الوكلاء الفرعيين إلى عنصر تشغيل أساسي. فبدلاً من حشر كل أداة في موجّه واحد، يمكن لوكيل موجِّه أن يسلّم حجزاً إلى وكيل فرعي للحجوزات وسؤال فوترة إلى آخر. تلفّ البداية السريعة الصوتية لـ Agents SDK جلسة Realtime نفسها في RealtimeAgent وتمنحك عمليات التسليم دون كتابة حلقة التنسيق الخاصة بك.
الخطوة 4: الجسر إلى رقم هاتف (Twilio)
للردّ على مكالمات حقيقية، تجسر مزوّد هاتف داخل المقبس. مع Twilio، توجّه مكالمة واردة إلى TwiML <Connect><Stream> يفتح WebSocket إلى خادمك، وتنقل إطارات الصوت بين Twilio و Realtime API. SIP هو البديل. يقبل OpenAI Realtime بروتوكول SIP مباشرة، ما يلغي مُرحِّل الوسائط لديك تماماً إن لم تكن بحاجة لمسّ الصوت.
الـ TwiML الذي يبدأ التدفّق:
<Response>
<Connect>
<Stream url="wss://your-server.com/twilio-stream" />
</Connect>
</Response>إليك الفخّ الذي يلتهم يوماً إن فاتك: تدفّق وسائط Twilio بصيغة 8 كيلوهرتز μ-law، و Realtime API يريد 24 كيلوهرتز PCM16. تعيد التحجيم في كلا الاتجاهين، وإلا حصلت على صوت مشوّه يشبه السنجاب.
// وارد: Twilio (8kHz μ-law base64) -> Realtime (24kHz PCM16)
const pcm16 = upsample(muLawDecode(Buffer.from(msg.media.payload, "base64")), 8000, 24000);
realtime.send(JSON.stringify({
type: "input_audio_buffer.append",
audio: pcm16.toString("base64"),
}));
// صادر: Realtime (24kHz PCM16) -> Twilio (8kHz μ-law)
const ulaw = muLawEncode(downsample(modelPcm16, 24000, 8000));
twilioWs.send(JSON.stringify({
event: "media",
media: { payload: ulaw.toString("base64") },
}));صيغة الإطار الكاملة موجودة في وثائق Twilio Media Streams. أبقِ إعادة التحجيم رخيصة، لأن مكتبة ثقيلة هنا تضيف زمن استجابة تدفعه في كل إطار.
الخطوة 5: معالجة المقاطعة والاعتراضات
الوكيل الإنتاجي يسمح للمتصل بالكلام فوقه. تعني المقاطعة (barge-in) اكتشاف أن المتصل بدأ يتكلم بينما الوكيل في منتصف جملة، ثم قطع الوكيل بنظافة. يعالج Realtime API ذلك بـ response.cancel: عندما يُبلغ اكتشاف الدور بأن الكلام بدأ أثناء التشغيل، تلغي الردّ النشط وتفرّغ الصوت المُخزّن مؤقتاً نحو المتصل.
if (event.type === "input_audio_buffer.speech_started") {
realtime.send(JSON.stringify({ type: "response.cancel" }));
twilioWs.send(JSON.stringify({ event: "clear" })); // أسقِط التشغيل في الطابور
}لاكتشاف الدور وضعان، والاختيار مهمّ. يُطلِق server_vad عند عتبات صمت خام ويميل إلى قطع المتصل عند الوقفات الطبيعية. ينتظر semantic_vad حتى يظنّ النموذج أن المتصل أنهى فكرة فعلاً، فيُنتج بذلك مقاطعات خاطئة أقل بكثير عند وقفة تفكير. للمكالمات الهاتفية، الـ VAD الدلالي هو الذي يبدو بشرياً.
الخطوة 6: ضبط زمن الاستجابة إلى أقل من ثانية
هنا يتحوّل العرض التوضيحي إلى منتج، فإليك الأرقام من نظامنا الخاص، لا ميزانية نظرية. شغّلنا وكيل المطعم نفسه عبر 40 مكالمة تجريبية في مايو 2026، على خادم صغير واحد قريب من منطقة OpenAI، مع تبديل إعدادات اكتشاف الدور والاستدلال فقط.
| التهيئة | p50 ذهاب وإياب | p95 | ملاحظات |
|---|---|---|---|
| server_vad, reasoning low | ~1.4 ث | ~2.3 ث | مقاطعات خاطئة أكثر عند الوقفات |
| semantic_vad, reasoning low | ~1.1 ث | ~1.9 ث | إعدادنا الافتراضي للإنتاج |
| semantic_vad, reasoning medium | ~1.8 ث | ~3.1 ث | دقة أدوات أفضل، أبطأ |
الروافع التي حرّكت الإبرة فعلاً، بترتيب الأثر:
- أبقِ
reasoning.effortعلى low ما لم تكن أداة محددة تحتاج الدقة فعلاً. ضاعف الوضع medium زمن p50 لدينا تقريباً. - لا تدفع الصوت أسرع من الزمن الحقيقي. إغراق
input_audio_buffer.appendيُفيض المخزن المؤقت ويسبّب انجرافاً؛ اضبط إيقاع الإطارات على ساعة الحائط. - أبقِ المقبس دافئاً. فتح اتصال على البارد لكل مكالمة يضيف المصافحة إلى زمن أول كلمة لديك. جمّع الاتصالات حيث يسمح حجم المكالمات.
- أعِد التحجيم بكفاءة. أضاف مُعيد تحجيم ساذج في المسار الساخن ~80 مللي ثانية لكل دور لدينا.
كم يكلّف التشغيل بالدقيقة بمجرد أن يصبح مباشراً؟ أجرينا حساب أحضر-مفتاحك-الخاص على حدة، انظر كم يكلّف وكيل صوتي بنظام BYOK في الدقيقة بدلاً من إعادة اشتقاقه هنا.
الخطوة 7: النشر والتقوية للإنتاج
الفجوة بين «اشتغل على حاسوبي المحمول» و«يصمد أمام 500 مكالمة يومياً» هي حفنة من الأعطال المعروفة. إليك قائمة التقوية، مستخلصة من الأخطاء التي تكسر وكلاء Realtime فعلاً:
| الفخّ | العَرَض | الإصلاح |
|---|---|---|
| معدّل عيّنات خاطئ | صوت مشوّه / يشبه السنجاب | 24 كيلوهرتز PCM16 في الاتجاهين |
تجاهل function_call_arguments.done | الأدوات لا تُطلَق أبداً | أصغِ وأرسل response.create |
| دفع الصوت أسرع من الزمن الحقيقي | فيض المخزن المؤقت، انجراف | اضبط إيقاع الإطارات على الزمن الحقيقي |
| لا منطق لإعادة الاتصال | تسقط المكالمات عند تعثّر المقبس | إعادة اتصال تلقائية + استئناف الجلسة |
لا معالجة لـ response.done | أدوار متداخلة | اربط الدور التالي بـ response.done |
أمران آخران لحركة المرور الحقيقية. في المكالمات الطويلة، دوّر الجلسة أو أعِد بذرها كل بضعة أدوار كي لا ينجرف السياق، لأن مكالمة من 20 دقيقة تراكم حالة يبدأ النموذج يتعثّر بها. وسجّل كل استدعاء أداة بوسائطه ونتيجته؛ حين يقول متصل «الوكيل حجز الوقت الخطأ»، فالنص وحده لن يخبرك إن أخطأ النموذج أم كودك.
إذا اعتمدت مسار Agents SDK من الخطوة 3، فإن صندوقه الرملي الجديد بالحاويات يشغّل كود الأدوات معزولاً، وهو ما يهمّ بمجرد أن تلمس أدواتك نظام ملفات أو صدفة بدلاً من واجهة برمجية فقط.
متى ينبغي أن تشتري منصة مُدارة بدلاً من ذلك
البناء مباشرة على Realtime API يمنحك أقصى تحكّم وأقل تكلفة بالدقيقة، لكنك تملك منطق إعادة الاتصال والجسر الهاتفي والامتثال والمراقبة، أي كل الأجزاء غير البرّاقة من الخطوات 4 إلى 7. إن احتجت وكيلاً هاتفياً مباشراً هذا الأسبوع ولا تريد صيانة مُرحِّل وسائط، فالمنصة المُدارة هي الخيار الأسرع.
بنينا الوكيل نفسه على المنصات الثلاث الكبرى وقارنّاها بصدق: Retell أو Vapi أو Bland. إن كنت لا تزال تقرّر أيّ جانب من الخط أنت فيه، فاطّلع على إطار قرار البناء مقابل الشراء الكامل قبل أن تلتزم بوقت هندسي.
عندما تريد الفرق التحكّم في بناء Realtime مخصّص دون توظيف فريق له، فهذا هو العمل الذي نقوم به: تطوير الوكلاء الصوتيين للإنتاج، من الجسر الهاتفي إلى ضبط زمن الاستجابة أعلاه. يسعدنا النظر في حالة استخدامك إن كنت توازنها.
عن الكاتب — مرت باتور هو الشريك المؤسّس لـ Techsy.io، حيث يطلق الفريق وكلاء ذكاء اصطناعي وأنظمة أتمتة وخطوط أنابيب صوتية/SDR لعملاء B2B. يكتب عن حزمة أدوات LLM التي يستخدمها فريق Techsy فعلاً في الإنتاج. LinkedIn
الأسئلة الشائعة
ما هو زمن استجابة الوكيل الصوتي على OpenAI Realtime API؟
في نظامنا على gpt-realtime-2 مع semantic_vad وجهد استدلال منخفض، قاس زمن الذهاب والإياب p50 1.1 ث و p95 1.9 ث عبر 40 مكالمة تجريبية. تحويل الكلام إلى كلام في مقبس واحد يتجاوز سلسلة STT/LLM/TTS، وهو ما يجعل الردود دون ثانية ممكنة أصلاً.
هل أحتاج WebRTC أم WebSocket أم SIP لوكيلي الصوتي؟
استخدم WebRTC حين يلتقط متصفح أو تطبيق جوال الميكروفون مباشرة، وWebSocket حين يكون لدى خادمك بالفعل تدفّق صوت خام (حالة جسر Twilio)، وSIP حين تريد أن يتولّى OpenAI الجزء الهاتفي دون مُرحِّل وسائط خاص بك. معظم الوكلاء الهاتفيين يستخدمون WebSocket أو SIP.
كيف أربط OpenAI Realtime API بـ Twilio؟
وجّه مكالمة Twilio واردة إلى TwiML <Connect><Stream> يفتح WebSocket إلى خادمك، ثم انقل الصوت بين Twilio ومقبس Realtime. أعِد تحجيم 8 كيلوهرتز μ-law الخاص بـ Twilio إلى 24 كيلوهرتز PCM16 للواجهة في الاتجاهين، وإلا خرج الصوت مشوّهاً.
كيف يعمل استدعاء الدوال في Realtime API؟
تعلن الأدوات في تهيئة الجلسة. حين يريد النموذج واحدة، يطلق حدث function_call_arguments.done. تنفّذ العمل، وترسل النتيجة كعنصر محادثة function_call_output، ثم ترسل response.create كي ينطق الوكيل النتيجة. نسيان هذه الخطوة الأخيرة هو سبب فشل الأدوات «بصمت» غالباً.
كيف تُعالَج المقاطعة (barge-in) في Realtime API؟
عندما يُبلغ اكتشاف الدور بـ input_audio_buffer.speech_started أثناء التشغيل، أرسل response.cancel لإيقاف الردّ النشط وفرّغ أي صوت صادر مُخزّن مؤقتاً نحو المتصل. اقرنه بـ semantic_vad كي لا تُطلِق الوقفات الطبيعية مقاطعات خاطئة في منتصف الجملة.
ما معدّل عيّنات الصوت الذي يستخدمه OpenAI Realtime API؟
يستخدم Realtime API صوتاً بصيغة 24 كيلوهرتز PCM16 في كلا الاتجاهين. يسلّم مزوّدو الهاتف مثل Twilio صيغة 8 كيلوهرتز μ-law، لذا يجب على الجسر الهاتفي إعادة التحجيم صعوداً عند الدخول ونزولاً عند الخروج. معدّلات العيّنات غير المتطابقة هي السبب الأكثر شيوعاً للصوت المشوّه.
كم يكلّف تشغيل وكيل صوتي على Realtime API؟
تُحرّك التكلفةَ دقائقُ الصوت الواردة والصادرة على gpt-realtime-2، واقتصاد أحضر-مفتاحك-الخاص يختلف بشدّة عن منصة مُدارة بالدقيقة. أجرينا الحساب الكامل في تحليل أسعار الوكلاء الصوتيين بدلاً من تقديره هنا.
هل أبني على Realtime API أم أستخدم Retell أو Vapi أو Bland؟
ابنِ مباشرة حين تريد أقصى تحكّم وأقل تكلفة بالدقيقة ويمكنك امتلاك إعادة الاتصال والهاتف والامتثال. اشترِ منصة مُدارة حين تكون سرعة الإطلاق أهمّ. تغطّي مقارنة Retell مقابل Vapi مقابل Bland وإطار البناء مقابل الشراء المفاضلات.
ماذا غيّر تحديث OpenAI Agents SDK في أبريل 2026 للوكلاء الصوتيين؟
جعل التحديث الكبير في 15 أبريل 2026 بروتوكول سياق النموذج من الدرجة الأولى، وأضاف صندوقاً رملياً بالحاويات لكود الأدوات، وحوّل عمليات التسليم بين الوكلاء الفرعيين إلى عنصر تشغيل أساسي. للوكلاء الصوتيين، يعني هذا أن وكيلاً موجِّهاً يمكنه التسليم إلى وكلاء فرعيين متخصّصين بدلاً من حشر كل أداة في موجّه واحد.