ثمة لحظة في المكالمة الباردة — رُفعت السماعة بالفعل، والشخص يتكلم وأنت تومئ. لكن داخلك صمت. لم تفهم كلمة.

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

أنا مصمم. أجيد تصميم الأنظمة المعقدة وأفهم المنتج. لكن الحديث هاتفياً مع لكنة غير مألوفة مهارة مستقلة، لا تظهر بمجرد الرغبة.

كان يمكن أن أتدرب وأنتظر فحسب. قررت بناء أداة.

ما هو CallCoach؟

تطبيق iOS أصلي مكتوب بـSwift مع Apple Intelligence وLive Translation.

أثناء المكالمة يتعرف آنياً على كلام الطرف الآخر ويعرض النص مباشرة على الشاشة. يحلل ما قاله ويقترح تلميحات — بماذا تجيب وكيف تواصل وأي سؤال تطرح.

تطبيق CallCoach داخل Xcode لتشغيل المحاكي والاختبارات

ليس تفريغاً للكلام من أجل التفريغ. إنه مساعد نصوص للمحادثة يظهر بالضبط عند الحاجة — في حوار حي، بلا تأخير.

لماذا Swift وApple Intelligence؟

كان يمكنني اختيار أي حزمة ويب. لكن الويب لا يوفر السرعة اللازمة والتكامل الأصلي مع الميكروفون وواجهات النظام البرمجية.

Apple Intelligence محرك لغوي محلي على الجهاز نفسه. لا طلبات إلى السحابة ولا تأخير ولا تسريب لبيانات المحادثة. كل شيء على الهاتف.

يتعامل Live Translation مع الإنجليزية ذات اللكنات بالدقة المطلوبة. تماماً ما تحتاج إليه دبي متعددة الجنسيات.

العمل على التطبيق داخل Codex — انتهت نافذة الرموز ذات الساعات الخمس، والآن ننتظر التالية

كيف تغير العمل؟

حين ترى نص ما يقوله الطرف الآخر، يزول القلق. تكف عن إنفاق الطاقة على «ماذا قال؟» وتبدأ التفكير في «بماذا أجيب؟».

من وضع النجاة إلى وضع الحوار. تحول جوهري.

أواصل تطويره. الخطوة التالية مكتبة نصوص لسيناريوهات مختلفة: أول مكالمة، اعتراض، توضيح الميزانية، الاتفاق على لقاء. تلميحات لا تظهر كتفريغ كلام فحسب، بل كرد سياقي ذكي على عبارة محددة.

لماذا أحكي هذا؟

CallCoach مثال على طريقتي في التفكير في المنتجات.

ليس «أي تقنية أجرب؟» بل «أي مشكلة أحل؟». كانت المشكلة محددة وشخصية. والحل أصلي وسريع وبلا اعتماديات زائدة.

المصمم الذي يجيد كتابة الكود ليس مطوراً. لكن هذا بالذات يسمح أحياناً للفكرة بألا تموت في طابور التنفيذ.

التنفيذ التقني

تقنياً بنيت CallCoach كأداة عمل فعلية، لا كمفهوم تصوري.

أولاً طورت الفكرة والمعمارية مع ChatGPT: صغت مهمة المنتج وسيناريو المستخدم وأعددت موجهاً افتتاحياً كبيراً لـCodex، لينشئ مشروع Swift أساسياً ويجهز الاعتماديات ويبني أول هيكل عامل للتطبيق.

عندما انتهت نافذة رموز Codex، نقلت المشروع إلى Claude Code. ومن هناك جرى القسم الأساسي الدسم من التطوير — بنموذج Opus 4.7.

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

بعدها صرت أنتقل بانتظام إلى Xcode: أبني التطبيق وأشغّل الاختبارات وأرصد الانهيارات وأعيد الأخطاء إلى Claude Code وأصلح المواضع المعيبة معه. وأجريت بعض التعديلات مباشرة في Xcode، حيث ربطت Codex وClaude أيضاً كمساعدين في المكان.

كيف يعمل منطق التطبيق؟

بُني التطبيق حول معالجة الكلام محلياً على الجهاز.

في الوضع العادي يستطيع CallCoach الاستماع إلى التدفق عبر الميكروفون وعرض تفريغ حي. لكن القرار التقني المحوري كان فصل الأصوات.

المشكلة الأساسية أن الميكروفون، عند الاتصال عبر مكبرات الصوت، يسمعك والطرف الآخر معاً. فتختلط جهتا الحوار في التفريغ ويصبح غير مفيد للتوجيه.

لحل ذلك صنعت وضعَي تشغيل.

الوضع 1 — System Audio

هذا هو الوضع الأساسي والموصى به.

يلتقط التطبيق عبر ScreenCaptureKit خرج صوت النظام، أي ما يصل إلى مكبرات الصوت فقط. فيمكن بذلك تفريغ صوت الطرف الآخر تحديداً، من دون التقاط صوتي من الميكروفون.

ويُستخدم أيضاً الخيار excludesCurrentProcessAudio = true، لاستبعاد أصوات التطبيق نفسه.

عند التشغيل الأول يطلب macOS إذن تسجيل الشاشة — ومن دونه يستحيل التقاط صوت النظام.

الوضع 2 — Microphone + Push-to-Mute

هذا سيناريو احتياطي.

يستخدم الالتقاط العادي من الميكروفون، لكن حين أتكلم أنا يمكن ضغط زر مطولاً لإيقاف إرسال مخازن الصوت مؤقتاً إلى مفرغ الكلام.

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

ما الذي كُتب في الكود؟

تتكون معمارية التطبيق من عدة مديرات منفصلة، كل منها مسؤول عن جزء من مسار المعالجة.

AudioCaptureManager

مسؤول عن التقاط الصوت عبر AVAudioEngine.

  • يستخدم installTap على inputNode
  • startRecording() يُرجع AsyncStream<AVAudioPCMBuffer>
  • يُمرر تدفق المخازن لاحقاً دون مشكلات العبور بين الممثلين (actor crossing)
  • يُحسب مستوى الإشارة بالتوازي باستخدام RMS والقياس بالديسيبل
  • تُرسل تحديثات الواجهة إلى @MainActor عبر Task

SystemAudioCaptureManager

صنف مستقل أضفته لالتقاط صوت النظام عبر SCStream.

هو تحديداً ما أتاح سيناريو لا يدخل فيه التفريغ إلا صوت الطرف الآخر من مكبرات الصوت، بدلاً من كل الضوضاء المحيطة.

TranscriptionManager

تستقبل هذه الطبقة تدفق مخازن الصوت وترسله إلى SpeechAnalyzer من إطار Speech.

ثم يمر على عناصر AsyncThrowingStream<SpeechTranscriptionResult>، ويفصل التفريغ الجزئي عن النهائي ويدير الانتقال صراحة إلى @MainActor.

أي إن الأمر لم يعد «التعرف على الصوت» فقط، بل إدماج التفريغ المتدفق بعناية في واجهة حية.

CoachingEngine

طبقة منطق مستقلة تحول التفريغ العادي إلى أداة مفيدة للمحادثة.

  • processNewTranscript(_:) يستخدم تأخير debounce قدره 1.5 ثانية
  • السابق من Task يُلغى إذا وصل مقطع كلام جديد
  • LanguageModelSession تُنشأ مرة واحدة لكل جلسة ثم يعاد استخدامها
  • يُحفظ سجل السياق
  • يحلل المحلل الإجابات بالصيغة [النوع]: النص مع رجوع احتياطي إلى .phrase
  • تُحدد مرحلة المحادثة بالكلمات المفتاحية
  • يُرسل إلى النموذج آخر نحو 300 كلمة، لا التفريغ الكامل

وأضفت أيضاً سياقاً لسوق دبي: إعمار Emaar, DAMAC, Binghatti, ROI، الحجز واللقاء وZoom وغيرها من الإشارات المعتادة في الحوار.

ContentView

هنا تُربط الأجزاء كلها في واجهة المستخدم.

  • .task يطلب إذن الميكروفون عند التشغيل
  • .onChange(segments.count) يُشغّل coach.processNewTranscript
  • toggleSession() يمرر التدفق والصيغة إلى startTranscription
  • تُعرض أخطاء المديرَات عبر معالجات مستقلة من .onChange

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

كيف دربت التلميحات؟

غذّيت النظام أيضاً بسياق عملي.

لسيناريو المكالمات الباردة استخدمت نصوص مبيعات حقيقية: التحية والحفاظ على الحوار والتأهيل الأولي للعميل وتوضيح الميزانية والتعامل مع الشكوك والإقفال على لقاء أو جلسة Zoom مفصلة.

ولم تكن قوالب مجردة من الإنترنت، بل المنطق الذي تعلمناه في وكالة SPI Dubai.

بذلك أصبحت التلميحات أكثر عملية. ليست «أجب بشيء مهذب» فقط، بل مواصلة للحوار ضمن البنية المناسبة وفي مرحلته المناسبة.

السيناريو الثاني — مقابلات مصمم المنتجات

بالتوازي طورت استخداماً ثانياً للنهج نفسه: خوض مقابلات مصمم المنتجات.

هنا دربت النظام على مشاريعي وحالاتي ومحفظة أعمالي، ليساعد في نوع آخر من الحوار لا في المكالمات الباردة — حين تحتاج إلى إجابات سريعة وواثقة ومنظمة عن خبرتك.

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

كيف جرى التطوير فعلياً؟

وبصراحة تامة، هذا عندي أيضاً مثال مهم على شكل التطوير الفعلي للمنتجات اليوم.

الفكرة تولد من مشكلة شخصية.

تُبنى المعمارية سريعاً بمساعدة النماذج اللغوية.

يُكتب الكود الصعب بالتعاون مع الوكلاء.

أما الصقل النهائي فيتم يدوياً في Xcode — عبر البناء والانهيارات وتصحيح الأخطاء والتكرارات المتتابعة.

ليست «سحر شبكة عصبية»، بل عملية هندسية عادية سرّعتها الأدوات الجديدة.


سجل التغييرات / كيف بُني CallCoach

1. صياغة الفكرة

  • حددت مشكلة ملموسة: في المكالمات الباردة ذات اللكنات البريطانية والهندية، لا أتمكن ببساطة من استيعاب جزء من الكلام بالسرعة الكافية.
  • حددت مهمة المنتج: ليس تفريغ المحادثة فقط، بل المساعدة على مواصلتها.
  • طورت مع ChatGPT المفهوم ومسار المستخدم وبنية التطبيق المقبل.
  • ولّدت موجهاً افتتاحياً كبيراً لـCodex لبناء هيكل مشروع Swift بسرعة.

2. البناء الأولي للمشروع

  • أنشأ Codex مشروع Swift الأساسي.
  • جُهزت الاعتماديات الرئيسية.
  • أُعدت المعمارية الأساسية.
  • بُني أول نموذج أولي قابل للتشغيل.

3. التنفيذ الهندسي الأساسي

  • بعد نفاد نافذة رموز Codex، نُقل المشروع إلى Claude Code.
  • استمر التطوير الرئيسي باستخدام Opus 4.7.
  • بُني عبر Claude Code القسم الدسم من التطبيق: التقاط الصوت والتفريغ ومنطق التوجيه والربط بالواجهة.

4. حل مشكلة المنتج الرئيسية — فصل الأصوات

  • كُشفت المشكلة المحورية: الميكروفون يسمع المستخدم والطرف الآخر معاً.
  • نُفذ وضعان:
    • System Audio — التقاط خرج صوت النظام وحده عبر ScreenCaptureKit
    • Microphone + Push-to-Mute — وضع احتياطي مع إيقاف يدوي لإرسال المخازن إلى مفرغ الكلام
  • أُضيف excludesCurrentProcessAudio = true، لاستبعاد أصوات التطبيق نفسه.

5. المكونات المنفذة

  • SystemAudioCaptureManager.swift — صنف جديد لالتقاط صوت النظام عبر SCStream
  • AudioCaptureManager.swift — التقاط الصوت عبر AVAudioEngine، أُضيف isMuted
  • TranscriptionManager — تفريغ متدفق عبر SpeechAnalyzer
  • CoachingEngine — debounce وإعادة استخدام جلسة النموذج وتحليل العبارات ومنطق مراحل المكالمة
  • ContentView.swift — إدارة الجلسة واختيار مصدر الصوت والأخطاء والأزرار والواجهة
  • Info.plist — أذونات التقاط صوت النظام
  • project.pbxproj — ربط ScreenCaptureKit.framework

6. تصحيح الأخطاء والاختبارات

  • جرى البناء والتشغيل عبر Xcode.
  • عند كل انهيار للتطبيق، أُعيد الخطأ إلى Claude Code.
  • أُجريت الإصلاحات تكرارياً: Xcode ← انهيار/خطأ ← Claude Code ← إصلاح ← بناء جديد.
  • أُجريت بعض التعديلات السريعة مباشرة في Xcode مع ربط Codex وClaude.

7. التدريب على سيناريوهات فعلية

  • أُضيفت إلى النظام نصوص مبيعات حقيقية للمكالمات الباردة:
    • التحية
    • حديث عابر معتاد
    • تأهيل العميل
    • تحديد الاهتمام
    • الإقفال على لقاء أو Zoom
  • استندت النصوص إلى الممارسة والتدريب الفعليين في SPI Dubai.
  • أُنشئ بالتوازي سيناريو استخدام ثان: المساعدة في مقابلات وظيفة مصمم المنتجات.
  • لهذا دُرب النموذج على مشاريعي وحالاتي ومحفظة أعمالي.

8. النتيجة النهائية

  • تفريغ محلي دون إرسال البيانات إلى السحابة
  • تلميحات آنية أثناء المحادثة
  • منطق منفصل للمبيعات والمقابلات
  • حزمة تقنية أصلية بلا ترقيعات ويب
  • منتج عامل بُني بتكامل ChatGPT + Codex + Claude Code + Xcode