تبدو إضافة زر للميكروفون إلى موقع ويب مهمة بسيطة: تسجيل صوت المستخدم، والتعرّف على الكلمات، وإدراجها في حقل نصي. لكن جعل ذلك يعمل بموثوقية على متصفحات الحواسيب والهواتف أصعب بكثير في الواقع، ولا سيما عندما يجب أن تتم جميع عمليات التعرّف على الكلام محليًا، من دون رفع صوت الميكروفون إلى خادم.
لا يكمن التحدي في اختيار نموذج للتعرّف على الكلام فحسب. بل يتطلب التنفيذ السليم التعامل بدقة مع أذونات الميكروفون، والتقاط الصوت، وإعادة أخذ العينات، وعمليات العامل في المتصفح، وWebAssembly، وتهيئة نموذج ONNX، والترميز إلى رموز، والتخزين المؤقت، والربط بحقل إدخال النص في التطبيق.
تشرح هذه المقالة كيفية إنشاء ميزة عملية لتحويل الكلام إلى نص مع الحفاظ على الخصوصية باستخدام OpenAI Whisper وTransformers.js وONNX Runtime Web وAudioWorklet وعامل Web Worker مخصص. كما تتناول عدة أعطال حقيقية أثناء الدمج، قد تجعل زر الميكروفون عالقًا في حالة التحميل حتى بعد تنزيل ملفات النموذج بنجاح.
الهدف بسيط: يضغط الزائر زر الميكروفون، ويتحدث بصورة طبيعية، ثم يرى النص المتعرّف عليه في حقل إدخال موجود بالفعل. ويمكنه مراجعة النتيجة أو تعديلها قبل إرسالها. ولا يلزم أن يغادر التسجيل الصوتي نفسه المتصفح مطلقًا.
ما المقصود بالتعرّف على الكلام بالكامل من جهة العميل؟
هناك نهجان مختلفان جذريًا لإضافة الإدخال الصوتي إلى موقع ويب.
يعتمد النهج الأول على خدمة للتعرّف على الكلام؛ يلتقط المتصفح الصوت، ويرسله إلى خدمة بعيدة، ثم يتلقى النص المتعرّف عليه. قد يبسّط ذلك التنفيذ، لكنه يفرض الاعتماد على الشبكة، واعتبارات تتعلق بالخصوصية، وتوفّر المزوّد، وربما تكاليف مرتبطة بحجم الاستخدام.
أما النهج الثاني فينفّذ الاستدلال مباشرة داخل المتصفح. ينزّل الموقع نموذج التعرّف على الكلام وبيئة التشغيل اللازمة له؛ ثم يلتقط المتصفح صوت الميكروفون، ويحوّله إلى عينات رقمية، ويشغّل النموذج محليًا، وينتج النص من دون خادم للتعرّف على الكلام.
تركّز هذه المقالة بالكامل على النهج الثاني.
ينبغي عدم الخلط بين التعرّف المعتاد على الكلام في المتصفح والتعرّف المضمون محليًا. فقد تستخدم Web Speech API محرّك تعرّف يعمل على خادم. وتوفّر بعض التطبيقات الأحدث خيار processLocally، لكن دعمه يختلف باختلاف المتصفح ونظام التشغيل. وإذا كانت المعالجة المحلية الصارمة شرطًا، فلا يجوز استخدام SpeechRecognition العام بوصفه خيارًا احتياطيًا تلقائيًا.
بدلًا من ذلك، يتحكم التطبيق في مسار الاستدلال كاملًا.
Microphone
|
v
AudioWorklet
|
v
Float32 PCM audio
|
v
Dedicated Web Worker
|
v
16 kHz resampling
|
v
Whisper + ONNX Runtime Web
|
v
Tokenizer and text decoding
|
v
React input stateيظل الموقع يقدّم ملفات JavaScript وWebAssembly والإعدادات وأوزان النموذج؛ وهي تنزيلات ثابتة عادية. والفرق هو أن صوت الميكروفون يُعالج محليًا بدلًا من رفعه بغرض التعرّف. وإذا أرسل المستخدم لاحقًا النص الناتج عبر نموذج إدخال عادي، فهذا إجراء منفصل في التطبيق.
اختيار نموذج عملي: Whisper Tiny أم Base؟
Whisper عائلة من نماذج التعرّف على الكلام متعددة اللغات. وتُعد قدرتها على التعرّف على لغات كثيرة بنموذج واحد مفيدة على وجه الخصوص للمواقع متعددة اللغات.
بالنسبة إلى الرسائل الصوتية القصيرة، يقدّم نموذجان توازنًا مناسبًا بين جودة التعرّف واستهلاك موارد المتصفح: Whisper Tiny وWhisper Base.
| النموذج | الحجم التقريبي لأوزان ONNX المختارة بتكميم Q8 | الاستخدام الموصى به |
|---|---|---|
| Whisper Tiny | 41 MB | خيار متحفظ للهواتف والأجهزة محدودة الموارد |
| Whisper Base | 77 MB | خيار أعلى جودة للأجهزة القادرة التي جرى اختبارها |
تشير هذه الأحجام إلى ملفات ONNX المختارة للمشفّر ومفكّك الترميز في الإصدارات المصدّرة التي فُحصت. وهي لا تشمل جميع الملفات المساندة، ولا ينبغي الخلط بينها وبين ذروة استهلاك ذاكرة المتصفح.
يمتاز Whisper Tiny بسرعة التنزيل، وباحتياجه المعتاد إلى موارد حوسبة أقل، وبملاءمته الأكبر لبيئات الهواتف غير المعروفة الإمكانات. أما Whisper Base فيقدّم عمومًا جودة تعرّف أفضل، ولا سيما في بعض اللغات التي يرتكب فيها النموذج الأصغر أخطاء أكثر.
لا ينبغي اختيار النموذج اعتمادًا على سعة ذاكرة RAM الفعلية وحدها. فقد تكون الذاكرة المتاحة للمتصفح أقل بكثير مما توحي به مواصفات الجهاز. وتؤثر أيضًا ذاكرة الصفحة المستخدمة بالفعل، وتخصيصات WebAssembly، والمصفوفات المؤقتة، وضغط الذاكرة في نظام التشغيل.
يمكن اتباع نهج متحفظ بالبدء باستخدام Tiny على الهواتف مجهولة الإمكانات، وقصر Base على فئات الأجهزة المؤهلة، والعودة من Base إلى Tiny إذا فشلت تهيئة النموذج.
يجب تحرير نسخة Base الفاشلة قبل تحميل Tiny. فالإبقاء على النموذجين في الذاكرة يُفقد آلية الرجوع فائدتها.
لماذا يُعد WebAssembly خيارًا افتراضيًا مناسبًا للإنتاج؟
يدعم ONNX Runtime Web تنفيذ الشبكات العصبية داخل المتصفحات. وبالنسبة إلى هذا النوع من الأحمال، يوفّر WebAssembly مسارًا عمليًا للاستدلال باستخدام المعالج CPU في متصفحات الحواسيب والهواتف الرئيسية.
يمكن لـWebGPU تسريع النماذج المدعومة على عتاد مناسب، لكن توفره لا يعني أن مسار التعرّف على الكلام يعمل. فوجود navigator.gpu في المتصفح لا يضمن دعم ONNX Runtime للمنصة، ولا نجاح تهيئة مخطط Whisper المحدد، ولا خلو الاستدلال المتكرر من مشكلات الموارد.
لذلك يمكن للتنفيذ المتحفظ اعتماد WASM بوصفه واجهة التنفيذ الخلفية في الإنتاج، وتقييم WebGPU بصورة مستقلة بعد اختبارات التوافق والدقة والذاكرة والجلسات المتكررة.
وفي التنفيذ الأولي، يجنّب WASM أحادي الخيط كذلك جعل الموقع كله معتمدًا على العزل بين المصادر (cross-origin isolation) وSharedArrayBuffer.
env.backends.onnx.wasm.numThreads = 1;
env.backends.onnx.wasm.proxy = false;يبقي تنفيذ الاستدلال داخل عامل مخصص العمليات الحسابية المكلفة بعيدًا عن خيط JavaScript الرئيسي. وبذلك تظل حقول الإدخال ولوحة المفاتيح الافتراضية والتمرير وواجهة التطبيق مستجيبة أثناء معالجة النموذج للصوت.
إبعاد التعرّف على الكلام تمامًا عن التحميل الأولي للصفحة
يجب ألا تُحمِّل ميزة الميكروفون الزوار الذين لا يستخدمونها مطلقًا تكلفة كبيرة في الشبكة والذاكرة.
لا تستورد Transformers.js مبكرًا ضمن حزمة التطبيق الأولية، ولا تهيّئ ONNX Runtime عند فتح الصفحة، ولا تنزّل أوزان النموذج تلقائيًا أثناء فترات الخمول.
بدلًا من ذلك، حمّل مكوّنات التعرّف فقط بعد أن ينقر المستخدم زر الميكروفون صراحةً.
يمكن أن يكون تسلسل الاستخدام الأول كما يلي:
- استقبل نقرة زر الميكروفون.
- اطلب إذن استخدام الميكروفون عبر
getUserMedia(). - إذا رُفض الإذن، فأظهر خطأ من دون تنزيل النموذج.
- إذا مُنح الإذن، فأغلق تدفق الميكروفون المؤقت أثناء تجهيز النموذج.
- هيّئ العامل المخصص ديناميكيًا وحمّل النموذج المحلي المختار.
- انتظر تأكيد العامل جاهزية مسار التعرّف.
- ابدأ التسجيل تلقائيًا ما لم يكن المستخدم قد ألغى العملية.
- اعرض حالة استماع واضحة ومفهومة.
يجب أن يرتبط طلب الإذن بتفاعل المستخدم. وينبغي للتطبيق أيضًا التعامل مع العمليات الملغاة وقيود التقاط الصوت الخاصة بالمتصفح، بدلًا من افتراض إمكان إعادة بدء التسجيل دائمًا من دون إجراء آخر.
يجب أن يكون لتحضير النموذج مؤشر حالة ظاهر، مثل جارٍ تجهيز الإدخال الصوتي. فقد يوحي مؤشر تحميل دوّار بلا انتقال واضح بأن الميكروفون معطّل حتى لو اكتمل تنزيل النموذج بنجاح.
بعد التهيئة، يمكن إبقاء العامل متاحًا لفترة قصيرة لإملاء آخر. وتُعد مهلة خمول تبلغ نحو دقيقة نقطة بداية معقولة. بعدها يُنهى العامل ويُحرّر النموذج الموجود في ذاكرته. ويمكن أن تظل ملفات النموذج المخزنة مؤقتًا في المتصفح متاحة في الزيارات التالية.
التقاط الصوت الخام باستخدام AudioWorklet
يحتاج Whisper إلى عينات صوتية رقمية. ورغم أن MediaRecorder ممتاز لتسجيل ملفات الصوت، فإن استخدامه للتعرّف المحلي قد يضيف دورة غير ضرورية من الترميز وفك الترميز.
مسار المعالجة الأبسط هو:
getUserMedia()
|
v
AudioContext
|
v
AudioWorklet
|
v
Float32 PCM buffers
|
v
Dedicated Workerيعمل AudioWorklet على معالجة الصوت خارج خيط JavaScript الرئيسي للتطبيق. ويمكنه التقاط عينات PCM، وتحويل المدخل متعدد القنوات إلى قناة واحدة، وحساب قياسات بسيطة لمستوى الصوت، ونقل المخازن المؤقتة المتراكمة إلى العامل.
يبدو إعداد مبسط للميكروفون كما يلي:
const stream = await navigator.mediaDevices.getUserMedia({
audio: {
channelCount: { ideal: 1 },
echoCancellation: true,
noiseSuppression: true,
autoGainControl: true,
},
});
const context = new AudioContext();
await context.audioWorklet.addModule(
'/assets/speech/pcm-worklet.js'
);
const source = context.createMediaStreamSource(stream);
const processor = new AudioWorkletNode(
context,
'pcm-capture'
);
source.connect(processor);
// Connect the worklet through an inaudible output path
// if required to keep the audio processing graph active.قيود الميكروفون المذكورة تفضيلات وليست ضمانات. فقد تطبق المتصفحات خوارزميات مختلفة لإلغاء الصدى وكبح الضوضاء والتحكم التلقائي في مستوى الصوت.
من التفاصيل المهمة أن معدل أخذ عينات الميكروفون ليس بالضرورة 16 kHz. فكثير من السياقات الصوتية تعمل بمعدل 44.1 kHz أو 48 kHz، بينما يتوقع Whisper إدخالًا بمعدل 16 kHz، لذا تحتاج العينات الملتقطة إلى إعادة أخذ عينات صحيحة.
لا يُعد إسقاط كل عينة ثانية أو ثالثة حلًا صحيحًا بوجه عام. فطريقة مناسبة لإعادة أخذ العينات مع مرشح تمرير منخفض، مثل تطبيق windowed-sinc، تقلل تراكب الترددات وتحافظ على ترددات الكلام المفيدة عند خفض معدل العينات.
نفّذ إعادة أخذ العينات داخل العامل المخصص، لا داخل دالة الاستدعاء الآنية لـAudioWorklet.
نقل الصوت بكفاءة بين خيوط المتصفح
يمكن أن ينتج AudioWorklet عددًا كبيرًا من المقاطع الصوتية الصغيرة. وقد يؤدي إرسال كل دفعة معالجة صوتية صغيرة برسالة مستقلة إلى عبء إضافي غير ضروري.
تتمثل استراتيجية عملية في تجميع دفعات قصيرة من PCM ونقل كل دفعة على هيئة ArrayBuffer.
worker.postMessage(
{
type: 'audio-chunk',
buffer,
},
[buffer]
);قائمة النقل مهمة، لأنها تنقل ملكية ArrayBuffer بدلًا من إنشاء نسخة كاملة غير ضرورية.
يمكن للعامل إلحاق هذه المقاطع بمجمّع صوتي ودمجها عند إيقاف التسجيل.
ومن المفيد أيضًا تتبع معدل أخذ العينات الفعلي، وعدد العينات المجموعة، ومدة التسجيل. تساعد هذه البيانات الوصفية على تشخيص التسجيلات الصامتة من دون تسجيل بيانات صوتية حساسة.
اكتشاف فترات الصمت من دون إضافة شبكة عصبية أخرى
لا يتطلب إملاء الرسائل النصية القصيرة بالضرورة كاشفًا عصبيًا لنشاط الصوت.
يمكن لكاشف RMS تكيّفي تقديم تقدير خفيف لنشاط الكلام. يقيس RMS سعة الإشارة، ما يتيح للتطبيق تقدير اللحظة التي يصمت فيها متحدث كان نشطًا قبل قليل.
يجب أن تتيح الواجهة دائمًا إيقاف التسجيل يدويًا بنقرة ثانية على الميكروفون. فاكتشاف الصمت التلقائي ميزة مريحة، وليس شرطًا لنجاح التعرّف.
تشمل القيم الأولية المعقولة للإملاء القصير ما يلي:
| المعامل | القيمة الأولية |
|---|---|
| فاصل التحليل | 20 ms |
| معايرة الضوضاء | 300 ms |
| تأكيد بدء الكلام | 140 ms |
| الصمت قبل الإيقاف التلقائي | 1.4 ثانية |
| مهلة عدم وجود كلام | 8 ثوانٍ |
| الحد الأقصى لمدة التسجيل | 30 ثانية |
هذه نقاط انطلاق لضبط الإعدادات، وليست عتبات مثالية لكل الحالات. فقد يؤثر الكلام الخافت والموسيقى في الخلفية ومعالجة ضوضاء الميكروفون في قياسات RMS.
فضّل تمكين المستخدم من إيقاف التسجيل يدويًا بدلًا من قطع الكلمات الأخيرة للجملة تلقائيًا.
بالنسبة إلى الإدخال النصي القصير المعتاد، يكون التفريغ النصي لمرة واحدة أبسط غالبًا من تشغيل Whisper مرارًا لإنتاج نتائج مؤقتة. سجّل العبارة، وأوقف التسجيل، وشغّل النموذج مرة واحدة، ثم أدرج النص النهائي.
يتطلب مسار Whisper الكامل أكثر من نموذج ONNX
هذه واحدة من أهم التفاصيل في التنفيذ.
استدلال Whisper ليس مجرد استدعاء لمشفّر ONNX يتبعه مفكّك ترميز ONNX. فمسار التعرّف التلقائي على الكلام الكامل يتطلب أيضًا استخراج خصائص الصوت، وتحويل البيانات إلى رموز، وإعدادات التوليد، وفك رموز التنبؤ إلى نص مقروء.
سلسلة المعالجة الكاملة هي:
Raw microphone PCM
|
v
Resampling to 16 kHz
|
v
Whisper feature extractor
|
v
ONNX encoder
|
v
ONNX decoder and token generation
|
v
Whisper tokenizer
|
v
Recognized textإن نجاح تهيئة جلسة ONNX يثبت فقط إمكان تحميل مخطط النموذج، ولا يثبت عمل مسار التعرّف بالكامل.
في أحد تطبيقات الدمج المختبرة باستخدام Transformers.js، أمكن تهيئة جلسات النموذج، لكن التفريغ النصي ظل يفشل لأن المسار المُنشأ محليًا لم يُهيَّأ فيه معالج الصوت ومحوّل الرموز المطلوبان تهيئة صحيحة.
أجرى التنفيذ المتوافق تهيئة صريحة لهذه المكوّنات اعتمادًا على البيانات الوصفية المحلية للنموذج.
المثال التالي توضيح مختصر لهذا النهج الخاص بإصدار معيّن، وليس عاملًا مستقلًا مكتملًا:
import {
WhisperTokenizer,
WhisperFeatureExtractor,
WhisperProcessor,
} from '@huggingface/transformers';
// These values come from local model configuration files.
const tokenizer = new WhisperTokenizer(
tokenizerJson,
tokenizerConfig
);
const featureExtractor = new WhisperFeatureExtractor(
preprocessorConfig
);
const processor = new WhisperProcessor(
{},
{
tokenizer,
feature_extractor: featureExtractor,
},
''
);
// In the affected version-specific integration:
recognizer.tokenizer = tokenizer;
recognizer.processor = processor;يجب التحقق من تواقيع المُنشئات وطريقة الدمج الدقيقة وفق إصدار Transformers.js المثبّت. فبعض الإصدارات وملفات النماذج المصدّرة تستطيع إنشاء هذه المكوّنات تلقائيًا؛ ولا ينبغي اعتبار إنشائها يدويًا شرطًا عامًا.
بعد اكتمال المسار، يمكن استدعاء التفريغ النصي القصير بتحديد اللغة والمهمة صراحةً:
const result = await recognizer(audio16k, {
language: 'en',
task: 'transcribe',
return_timestamps: false,
});إذا كان التطبيق يعرف لغة المستخدم مسبقًا، فإن تمريرها صراحةً يتجنب اكتشاف اللغة غير الضروري وقد يحسّن قابلية التنبؤ بالنتيجة في التسجيلات القصيرة.
تثبيت إصدارات متوافقة من Transformers.js وONNX Runtime
يتعلق نمط فشل مهم ثانٍ بالعلاقة بين Transformers.js وONNX Runtime Web.
يوفّر Transformers.js مسار التعرّف عالي المستوى، بينما ينفّذ ONNX Runtime Web مخططات الشبكات العصبية. ويجب أن تتوافق الحزمتان مع بعضهما ومع ملفات ONNX المصدّرة المحددة المستخدمة.
في التنفيذ الذي جرى فحصه، نجحت نسخة تطويرية من ONNX Runtime Web في تهيئة مشفّر Whisper، لكنها فشلت عند إنشاء جلسة مفكّك ترميز مكمّم. وتعلق الخطأ بغياب مقياس التكميم في عملية DequantizeLinear.
لم تكن هذه مشكلة إذن الميكروفون أو التنزيل أو تنسيق الصوت. فقد استطاع المتصفح استقبال ملفات النموذج، لكن بيئة التشغيل لم تستطع تهيئة أحد مخططات النموذج المطلوبة.
أدى اختبار المشفّر ومفكّك الترميز مباشرةً باستخدام إصدار مستقر ومثبّت من ONNX Runtime Web إلى حل عدم التوافق المحدد هذا.
كانت قيمة اختبار التوافق كبيرة خصوصًا لأنه ميّز بين ادعاءين مختلفين:
- نُزّل ملف النموذج بنجاح.
- أنشأت بيئة التشغيل بنجاح جلسة استدلال قابلة للاستخدام من ملف النموذج.
وهذان ليسا الشيء نفسه.
بالنسبة إلى ملفات النماذج المصدّرة التي اختُبرت، استخدم توافق ناجح Transformers.js 4.2.0 مع ONNX Runtime Web 1.24.3. وفشلت بيئة تشغيل تطويرية أحدث مع مخطط مفكّك الترميز المكمّم. وهذه ملاحظة تخص إصدارات بعينها، وليست دليلًا على أن البيئات الأقدم أفضل على نحو عام.
عند استبدال إصدار اعتماد غير مباشر لبيئة التشغيل، ثبّت الإصدار الدقيق في ملف قفل الحزم ووثّق سبب الاستبدال. وينبغي أن تتطلب ترقية بيئة التشغيل اختبار انحدار كاملًا للاستدلال داخل المتصفح، لا مجرد نجاح تثبيت الحزمة.
تحقق من تنسيق حزمة العامل قبل تصحيح أخطاء Whisper
يمكن إنشاء Web Worker بلغة JavaScript باستخدام تنسيقات وحدات مختلفة.
وقع أحد أعطال الدمج لأن التطبيق أنشأ العامل بوصفه وحدة ES، بينما استخدمت حزمة العامل الناتجة آلية العمال التقليدية importScripts().
لا يمكن استبدال نموذجي التنفيذ هذين أحدهما بالآخر. فلا يستطيع عامل الوحدات ببساطة تنفيذ شيفرة مخصصة حصريًا للعامل التقليدي.
الدرس العملي هو مطابقة مُنشئ Worker مع الحزمة الناتجة فعلًا، بدلًا من نسخ خيار إنشاء من شرح عام.
على سبيل المثال، الخياران التاليان ليسا متكافئين تلقائيًا:
// Module worker: requires a compatible module bundle.
new Worker(workerUrl, { type: 'module' });
// Classic worker: requires a compatible classic bundle.
new Worker(workerUrl);يعتمد الاختيار الصحيح على أداة تجميع الحزم وشيفرة JavaScript الناتجة.
عند تشخيص خطأ تهيئة العامل، افحص وحدة تحكم المتصفح Console، وملف العامل الناتج، ولوحة Network. وحدد ما إذا كان الفشل يحدث قبل أن يبدأ ONNX Runtime التهيئة أصلًا.
تحسب بعض مكتبات المتصفح المجمّعة عناوين URL نسبةً إلى موقع الوحدة الخاصة بها. وعند تضمينها داخل العمال، اختبر المسارات الناتجة لملفات بيئة التشغيل بدلًا من افتراض أن عناوين URL النسبية للحزمة الأصلية ما زالت تعمل على نحو صحيح.
تقديم وحدات WASM وJavaScript بأنواع MIME الصحيحة
حتى ملفات JavaScript ونماذج ONNX المتوافقة تمامًا قد تفشل إذا أعاد خادم الويب ترويسات استجابة غير صحيحة.
تعلق فشل فعلي بوحدة JavaScript ذات الامتداد .mjs قُدّمت بنوع application/octet-stream. رفض المتصفح الوحدة لأن نوع الاستجابة غير مناسب لتنفيذ وحدات JavaScript.
تكون أنواع الاستجابة المتوقعة عادةً كما يلي:
| امتداد الملف | Content-Type |
|---|---|
| .mjs | application/javascript |
| .wasm | application/wasm |
تحقق من استجابة الإنتاج الفعلية، لا من ملف إعدادات الخادم وحده.
curl -I https://example.com/runtime/speech/v2/runtime.mjs
curl -I https://example.com/runtime/speech/v2/runtime.wasmعناوين URL أعلاه توضيحية. استخدم أسماء ملفات الأصول الفعلية، مع إصداراتها، من نسخة بيئة التشغيل المختارة.
لا يكفي رمز حالة HTTP صحيح بمفرده؛ فقد يرفض المتصفح ملفًا بسبب نوع MIME أو سياسة أمن المحتوى (Content Security Policy) أو نمط تنفيذ الوحدة أو عنوان URL غير صحيح لأحد الأصول.
تذكّر أن الطلب نفسه قد ينجح عند فتحه مباشرةً في تبويب المتصفح، لكنه يفشل عند تحميله بوصفه وحدة قابلة للتنفيذ أو تابعًا لـWebAssembly.
استخدام عناوين ثابتة للأصول من دون إبقاء المستخدمين عالقين بملفات تشغيل معطوبة
ملفات نماذج Whisper كبيرة بما يكفي لجعل التخزين المؤقت في المتصفح مهمًا.
تتيح مسارات الأصول الثابتة المقرونة بإصدارات للمتصفح إعادة استخدام ملفات النماذج وبيئة التشغيل التي لم تتغير، من دون تنزيلها في كل مرة يتحدث فيها المستخدم.
من سياسات التخزين المؤقت المعتادة:
Cache-Control: public, max-age=31536000, immutableلكن ذلك يفرض شرطًا عند النشر: متى أُعلن أن عنوان URL ثابت لا يتغير، يجب عدم استبدال محتواه بصمت.
إذا وجب تغيير الملف الثنائي لبيئة التشغيل، فانشره تحت عنوان URL جديد يتضمن إصدارًا جديدًا.
/models/speech/v1/
/runtime/speech/v1/
/runtime/speech/v2/ليس الغرض من تغيير مسار بيئة التشغيل تجميليًا. فقد يواصل المتصفح استخدام ملف WebAssembly ثنائي قديم وغير متوافق مخزّن مؤقتًا إذا أعاد التطبيق استخدام عنوان URL ثابت بعد تغيير محتواه.
ومن المفيد أيضًا استخدام ملفات الربط الخاصة بـJavaScript وملفات WebAssembly من حزمة توزيع بيئة التشغيل نفسها. فقد يؤدي خلط ملفات بيئات التشغيل بين الإصدارات إلى إخفاقات تهيئة يصعب تشخيصها.
يمكن لبيان ملفات يُنشأ وقت البناء، ويضم الملفات المتوقعة وأحجامها وبصماتها التشفيرية، اكتشاف الملفات الناقصة أو غير المتطابقة قبل النشر.
اضبط مكتبة التعرّف لاستخدام أصول مستضافة ضمن الموقع نفسه، وعطّل تحميل النماذج عن بُعد. وبذلك يؤدي غياب النموذج المحلي إلى خطأ واضح بدلًا من بدء تنزيل غير مخطط له من مضيف خارجي للنماذج.
لماذا يستمر مؤشر الميكروفون بالدوران رغم أن شيئًا لا يعمل؟
من الأعراض الشائعة أثناء التطوير أن يطلب زر الميكروفون الإذن، ثم يعرض حركة تحميل، ويعود في النهاية إلى حالة خطأ من دون إدراج أي نص.
وقد يُظهر المتصفح حتى أن الميكروفون قيد الاستخدام.
هذا لا يثبت أن نظام التعرّف على الكلام يعمل؛ بل يثبت فقط أن التقاط صوت الميكروفون سُمح به أو كان نشطًا في لحظة ما.
قد تنتج أعراض متشابهة تقريبًا عن عدة أعطال مستقلة:
- نوع MIME خاطئ لوحدة JavaScript. ينزّل المتصفح وحدة بيئة التشغيل، لكنه يرفض تنفيذها.
- عدم تطابق تنسيق تنفيذ العامل. تستخدم حزمة العامل المجمّعة واجهات العامل التقليدي مع تشغيلها بوصفها عامل وحدات، أو العكس.
- عدم توافق مفكّك ترميز ONNX. تنشئ بيئة التشغيل جلسة المشفّر لكنها تفشل مع مخطط مفكّك الترميز المكمّم.
- مسار تعرّف غير مكتمل. توجد جلسات ONNX لكن محوّل الرموز أو معالج الصوت لم يُهيَّأ بصورة صحيحة.
يجب تشخيص هذه المشكلات كلٌّ على حدة. فقد يكشف إصلاح إحداها المشكلة التالية.
أهم مبدأ في تصحيح الأخطاء هو أن HTTP 200 لا يعني نجاح تهيئة النموذج، ونجاح تهيئة النموذج لا يعني نجاح التفريغ النصي.
وحتى نجاح التفريغ النصي لا يثبت وصول النتيجة إلى الحقل النصي الفعلي في التطبيق.
تحتاج كل نقطة انتقال إلى إشارة تشخيص ذات معنى.
إضافة إمكانية تتبع الحالة من دون جمع الكلام الخاص
لا يتطلب التسجيل التشخيصي المفيد الاحتفاظ بما يقوله المستخدم.
يمكن للتطبيق تتبع حالات تقنية مثل:
permission-granted
worker-created
runtime-loading
runtime-ready
encoder-ready
decoder-ready
recording-started
pcm-received
recording-stopped
inference-started
inference-completed
result-insertedعند حدوث فشل، سجّل المرحلة ورمز خطأ تقنيًا ثابتًا وبيانات وصفية للخطأ بعد تنقيتها من المعلومات الحساسة.
ينبغي التمييز بين فشل تهيئة النموذج ورفض إذن الميكروفون والتسجيل الفارغ. كما ينبغي أن يظهر فشل العامل في بيئة التطوير بدلًا من أن تخفيه معالجة عامة للأخطاء.
لا تسجّل عينات الميكروفون أو الجمل المتعرّف عليها أو تسلسلات الرموز أو أي محتوى كلامي آخر. ويمكن أن تقتصر بيانات قياس الأداء على فئات المدد، وفئة النموذج المختار، وزمن التهيئة، وزمن الاستدلال، وفئات الأخطاء.
يجعل ذلك تشخيص تعطل الميكروفون أسهل بكثير من دون إضعاف متطلبات الخصوصية.
إدراج النص المتعرّف عليه عبر حالة React
لا تفيد ميزة التعرّف على الكلام إلا إذا وصل النص النهائي إلى حقل النموذج الموجود فعلًا.
في تطبيق React، غالبًا ما تكون قيمة ذلك الحقل مرتبطة بحالة المكوّن. وقد يتعارض الإسناد المباشر إلى input.value مع سلوك العرض في إطار العمل.
بدلًا من ذلك، ينبغي تمرير النص المتعرّف عليه عبر مسار تحديث الحالة نفسه الذي تستخدمه الكتابة العادية.
ويجب أن يراعي الدمج كذلك السلوك غير المتزامن؛ فقد ينتهي التعرّف بعد أن يكون المستخدم قد كتب أحرفًا إضافية أو نقل المؤشر.
يمكن لاستخدام قيمة إدخال قديمة حُفظت عند بدء التسجيل أن يستبدل التعديلات الأحدث عن طريق الخطأ.
إليك نمط دمج مبسطًا:
const insertion = insertTranscript({
currentValue: latestValue,
transcript: recognizedText,
selection: currentSelection,
maxLength: inputLimit,
});
setMessage(insertion.value);
restoreCaret(insertion.caret);ينبغي لدالة الإدراج المساعدة الحفاظ على النص الحالي، واستبدال النطاق المحدد اختياريًا، وإضافة المسافات المناسبة، واحترام الحد الأقصى لطول الحقل.
تستحق Unicode الانتباه، لأن طول السلسلة في JavaScript يُحسب بوحدات UTF-16. وقد يؤدي قص السلسلة عند موضع اعتباطي إلى فصل زوج من الوحدات البديلة (surrogate pair).
والأهم أن التعرّف على الكلام يجب ألا يرسل النموذج تلقائيًا مطلقًا؛ إذ لا بد أن يتمكن المستخدم من مراجعة النص وتصحيحه قبل الإرسال.
التعامل مع الأعطال من دون تعطيل الإدخال النصي المعتاد
يجب أن يكون نظام الكلام اختياريًا.
إذا رُفض إذن الميكروفون، أو عجز المتصفح عن تخصيص ذاكرة للنموذج، أو تعطل العامل، أو لم يحتوِ التسجيل على كلام، فيجب أن يستمر حقل الإدخال العادي بالعمل.
مثّل دورة حياة الميكروفون بحالات صريحة مثل:
idle
preparing
listening
recognizing
errorاستخدم تسميات ميسّرة للمستخدمين ذوي الاحتياجات المختلفة ومؤشرات حالة مرئية. فالميكروفون الذي يتحول ببساطة إلى الأحمر أو الوردي بعد التحميل لا يوضح للمستخدم شيئًا يُذكر.
يجب أن ينظّف الإلغاء مسارات الصوت وسياق الصوت والمخازن المؤقتة المعلّقة وجلسة التسجيل النشطة. وينبغي للواجهة أيضًا منع النتائج القديمة لتسجيل ملغى من استبدال حالة إدخال أحدث.
إذا فشل نموذج كبير بسبب قيود الذاكرة أو التهيئة، فحرّره بالكامل قبل تجربة نموذج أصغر. وحدّد عدد محاولات الإعادة حتى لا يكرر المتصفح تنزيل نماذج غير قابلة للتشغيل أو تهيئتها.
لا تنتقل بصمت إلى خدمة سحابية للتعرّف على الكلام عند فشل التعرّف المحلي. فإذا كانت المعالجة المحلية فقط شرطًا صارمًا، وجب أن ينتهي فشل نظام التعرّف المحلي محليًا.
اختبار مسار المتصفح كاملًا
لا يكفي نجاح فحص TypeScript أو بناء نسخة الإنتاج دليلًا على عمل الإدخال الصوتي.
وبالمثل، لا يثبت التأكد من قدرة المتصفح على تنزيل ملفات ONNX نجاح تهيئة جلستي النموذج كلتيهما.
ينبغي لاختبار الانحدار الأكثر فائدة تشغيل العامل نفسه وملفات بيئة التشغيل والنموذج والمعالج ومحوّل الرموز المستخدمة في الإنتاج.
مرّر تسجيل PCM قصيرًا معروف المحتوى إلى مسار الاستدلال الحقيقي في المتصفح، وتأكد من عودة نص غير فارغ.
ثم اختبر التفاعل الكامل مع الميكروفون في متصفح فعلي.
- تحميل الصفحة لأول مرة: تحقق من عدم تنزيل نماذج الكلام أو ملفات بيئة التشغيل قبل استخدام الميكروفون.
- التهيئة الباردة: تحقق من إذن الميكروفون وطلبات النماذج وأنواع MIME لملفات JavaScript وWASM واستجابة فعلية تفيد بجاهزية العامل.
- تهيئة ONNX: تحقق من إمكان إنشاء جلستي المشفّر ومفكّك الترميز.
- التقاط الصوت: تأكد من إنتاج AudioWorklet مخازن PCM غير فارغة.
- إعادة أخذ العينات: تأكد من وصول معدل العينات الحقيقي إلى العامل ومن إنتاج صوت صالح بمعدل 16 kHz.
- الإيقاف اليدوي: تحقق من موثوقية توقف التسجيل عند النقر مرة ثانية على الميكروفون.
- الإيقاف التلقائي: تحقق من أن فترات الصمت الطبيعية تنهي التسجيل دون قطع الكلام الخافت.
- التفريغ النصي: تحقق من أن المعالج والنموذج ومحوّل الرموز معًا يعيدون نصًا مفهومًا.
- الربط بحقل الإدخال: تحقق من ظهور النص المتعرّف عليه في الحقل المرتبط بحالة React فعلًا، من دون محو تعديلات المستخدم.
- التعافي من الأعطال: اختبر رفض الأذونات والملفات المفقودة وأخطاء العامل وفشل تهيئة النموذج والإلغاء والرجوع إلى نموذج أصغر.
- الخصوصية: افحص طلبات الشبكة وتأكد من عدم رفع صوت الميكروفون إلى خادم أو خدمة تحليلات.
- التوافق مع الهواتف: كرر الاختبارات على هواتف حقيقية، بما يشمل البداية الباردة والدافئة وجلسات الإملاء المتكررة.
تُعد اختبارات تسجيلات PCM الجاهزة ممتازة للتحقق من مكونات الاستدلال، لكنها لا تثبت عمل التقاط الصوت بالميكروفون. لذا يجب أن يشمل اختبار القبول النهائي الميكروفون الحقيقي وواجهة المستخدم الفعلية.
بنية عملية للإدخال الصوتي الموثوق في المتصفح
لموقع يحتاج إلى إدخال صوتي قصير ومتعدد اللغات مع الحفاظ على الخصوصية، تكون البنية الأكثر عملية بسيطة عن قصد:
React microphone button
|
v
Permission request
|
v
Lazy model initialization
|
v
AudioWorklet PCM capture
|
v
Adaptive RMS / manual stop
|
v
Dedicated Worker
|
v
16 kHz audio resampling
|
v
Whisper Tiny or Base Q8
|
v
Compatible ONNX Runtime WASM
|
v
Whisper processor + tokenizer
|
v
Final transcript
|
v
Controlled React inputينبغي للتطبيق استضافة ملفات النماذج وبيئة التشغيل المقرونة بإصداراته بنفسه، وإبقاء الاستدلال بعيدًا عن الخيط الرئيسي، وتجنب تنزيل النماذج بلا حاجة، وتحرير الموارد بعد الاستخدام، وعدم رفع صوت الميكروفون للتعرّف عليه مطلقًا.
يمكن النظر لاحقًا في خيارات أكثر تعقيدًا، مثل التسريع باستخدام WebGPU، والكشف العصبي عن نشاط الكلام، والتفريغ النصي الجزئي المستمر، أو فئات إضافية من النماذج، عندما تبرّر القياسات هذا التعقيد.
أهم درس هنا ليس إصدار مكتبة بعينه ولا حجم نموذج محددًا، بل أهمية التحقق من كل نقطة انتقال في مسار التعرّف على الكلام داخل المتصفح.
لا تعني نافذة إذن الميكروفون الناجحة أن الصوت يُحوّل إلى نص. ولا يعني تنزيل النموذج تهيئة مفكّك ترميزه. ولا تعني جلسة ONNX الناجحة توفر محوّل الرموز. كما لا يعني إنتاج نص بنجاح أن التطبيق أدرجه بصورة صحيحة.
لا تكتمل الميزة إلا حين يستطيع الزائر الضغط على الميكروفون والتحدث بصورة طبيعية وإيقاف التسجيل ورؤية الكلمات المتعرّف عليها في الحقل القابل للتحرير، من دون إرسال التسجيل إلى خادم للتعرّف على الكلام.