Skip to main content
النصّ المُفرَّغ ليس ملاحظات. إنه الاجتماع نفسه من جديد، لكنّ قراءته أطول من الجلوس فيه. ما يريده الناس فعلًا بعد الاجتماع مختصر: ماذا قرّرنا، ومن وافق على أن يفعل ماذا، وما الذي لا يزال مفتوحًا. يبني هذا الدليل ذلك، ويربط كل بند بالثانية التي قيل فيها لتتمكّن من الرجوع والاستماع إلى الجزء الذي تعترض عليه:
على طول الطريق سنقوم بما يلي:
  1. تفريغ تسجيل صوتي باستخدام /audio/transcriptions
  2. طلب التوقيتات الزمنية، وهو ما لا يوفّره كل نموذج
  3. استخراج القرارات والمهام الإجرائية وفق مخطط
  4. التعامل مع حقيقة أن التفريغ لا يذكر أبدًا من يتحدّث
  5. تقسيم تسجيل طويل دون فقدان الساعة الزمنية

الإعداد

تحتاج إلى Python 3.9 أو أحدث، وحزمة requests، ومفتاح Venice API. راجع إنشاء مفتاح API إن لم يكن لديك واحد. أحضر أي تسجيل لمحادثة بصيغة wav أو mp3 أو m4a أو flac أو aac أو mp4 أو ogg أو webm.

1. تفريغ التسجيل

نقطة النهاية /audio/transcriptions متوافقة مع OpenAI وتستقبل رفعًا متعدّد الأجزاء (multipart). يجب أن يكون الملف جزءًا حقيقيًا من نوع ملف، إذ لا يُقبل ترميز base64 على هذه النقطة.
تُحتسب تكلفة التفريغ بحسب طول الصوت، لا بحسب مقدار ما قيل فيه، مما يجعل تكلفة الاجتماع سهلة التوقّع قبل تشغيله: استدعِ GET /models?type=asr للحصول على القائمة الحالية بدلًا من تثبيت هذه، إذ إن المكتبة تتغيّر.

2. اطلب التوقيتات

التوقيتات هي ما يجعل الملاحظات قابلة للتحقّق، ولذلك فإن هذا الخيار هو الأهم، والإعداد الافتراضي لن يعطيك إياها:
nvidia/parakeet-tdt-0.6b-v3 هو النموذج الافتراضي، وهو يقبل timestamps=true ثم يتجاهله. لا خطأ ولا تحذير، مجرد استجابة لا تحوي سوى text. إن احتجت إلى التوقيتات، اطلب نموذجًا يُعيدها وتحقّق من وجود المفتاح.
عندما يُعيد نموذج ما التوقيتات فعلًا، يكون timestamps كائنًا لا قائمة، ويعتمد المفتاح داخله على النموذج. يجمع Whisper حسب العبارة، بينما يجمع Scribe حسب الكلمة:
المقاطع على مستوى العبارة هي الحجم الصحيح لهذه المهمة. توقيتات الكلمات مفيدة للترجمات المصاحبة، لكنها أدقّ من أن يُبنى عليها قرار. سنُسطّح هذه المقاطع إلى أسطر يسبقها الوقت، وهو كل ما يحتاجه النموذج للاستشهاد بها لاحقًا:

3. استخراج الملاحظات

صِف الملاحظات التي تريدها بوصفها مخططًا (schema)، فتكون النتيجة سجلًّا بدلًا من نصٍّ نثري عليك تحليله:
يوجد disable_thinking هنا للسبب نفسه الذي يجعله ضروريًا في أي خطوة استخراج. المخطط يُحدّد سلفًا شكل الإجابة، لذا فإن دفع تكاليف نموذج تفكير ليتداول بشأنها لا يُضيف شيئًا، ويجعل تكلفة كل تشغيل مختلفة عن سابقتها. يقيس دليل استخراج البيانات المُهيكلة من المستندات هذا الفرق. شغّله على اجتماع “standup” مدته ثلاث وخمسون ثانية، فتعود الملاحظات مرفقةً بالساعة الزمنية:
كل قيمة spoken_at حقيقية. اقفز إلى الثانية 19.6 لتسمع الجملة التي أنشأت المهمة.
إن أعطيت النموذج أسطرًا بلا توقيتات، فستعود كل قيمة spoken_at صفرًا. الحقل مطلوب، والنموذج لا يملك ما يضعه فيه، والحقل المطلوب توجيهٌ بإنتاج شيء، لا دعوة للإعلان عن الجهل. هذا يستحقّ التذكّر كلما بدا لك أن المخطط يعمل كما ينبغي: صحّة الشكل ليست صحّة القيم.

4. لا أحد مُصنَّف

في هذا الإخراج شيئان خاطئان، وكلاهما ينبع من المصدر ذاته. مسؤول إطلاق الميزة هو May. اسمها الحقيقي Mei. التعرّف على الكلام أقل موثوقيته في أسماء العلم، والأسماء هي بالضبط ما تحتاجه الإسناد، فهذا الفشل متوقّع لا مصادفة سيئة الحظ. البند الأخير unassigned، رغم أن شخصًا تعهّد به بوضوح. كان السطر: “I’ll ask legal today and report back tomorrow”، وقد سجّل التفريغ الكلمات دون تسجيل من قالها. هذه الثانية ليست علّة يمكنك إصلاحها. لا يُجري أي نموذج تفريغ في Venice تمييزًا للمتحدّثين (diarization)، فليس هناك حقل speaker تلجأ إليه في أيٍّ منها. التفريغ تيّار نصّ واحد بلا صوت، ولا يمكن إسناد المهام إلا حين يُنطق اسم صاحبها بصوت مسموع، كما في “Tomas, can you put the deprecation notice in the changelog”. أما الأولى فيمكنك إصلاحها بأن تُخبر النموذج بمن كان في الغرفة:
يُحلّ May إلى Mei Lin لأن النموذج بات لديه قائمة قصيرة يقارن بها، والمسؤولون بأسمائهم الكاملة يمكن لأداة تتبّع المهام أن تبحث عنها. يظلّ البند الثالث دون إسناد، وذلك صحيح. تُصلح قائمة الحضور خطأ السماع، ولا شيء يُعيد معلومةً لم يحملها التسجيل أصلًا.
إن احتجت إلى إسناد حقيقي للمتحدّثين، فالتقطه في المصدر لا استنتجه في المصبّ. أدوات الاجتماعات قادرة على تسجيل مسار منفصل لكل مشارك، وتفريغ كل مسار على حِدة يمنحك المتحدّثين بلا مقابل، بتكلفة طلب واحد لكل شخص.

5. أطول من طلب واحد

يبلغ الحدّ الأقصى للرفع 25 ميغابايت، وهو ما تصل إليه أسرع مما تتوقّع مع الصوت غير المضغوط، كما أن الاجتماع الطويل يستحقّ التقسيم على أي حال حتى لا يُكلّفك فشل واحد التفريغ بأكمله. بالنسبة لملفات WAV، تكفي المكتبة القياسية، ولا حاجة إلى ffmpeg:
الإزاحة (offset) هي بيت القصيد. يُفرَّغ كل جزء كما لو أنه يبدأ من الصفر، ولذلك يجب إعادة توقيتاته إلى الجدول الزمني للتسجيل الأصلي قبل أن يراها النموذج. هذا ما تستخدمه معامِلة offset في timed_lines:
قسّم اجتماع “standup” نفسه إلى قطع طولها عشرون ثانية، فتبقى الساعة صادقةً عبر نقاط الوصل. أما الكلمات فلا:
جملة واحدة قيلت هناك: “Tomas, can you put the deprecation notice in the changelog by Friday?”. وقع القطع في منتصفها، فذهب الاسم إلى طلب والباقي إلى طلب آخر. سمع Whisper الاسم اليتيم سؤالًا، واخترع awesome. ليسدّ الفجوة في نهاية القطعة، وحوّل سطرًا واحدًا إلى ثلاثة. التوقيتات لا تزال صحيحة، وخطوة الملاحظات لا تزال تلتقط المهمة. ما تفقده هو الاسم، وهو ما تعتمد عليه الإسناد.
التقسيم على مدة ثابتة يقطع كلام أحدهم عند كل حدّ. عشرون ثانية قصيرة بما يكفي لتقع في منتصف جملة كل مرة تقريبًا؛ وعشر دقائق تجعل ذلك نادرًا لا مستحيلًا، وفي النهاية سيقع القطع على تلك الجملة الوحيدة التي تُسند العمل. التقسيم على الصمت يتجنّب المشكلة بشكل صحيح، ويحتاج إلى أداة قادرة على العثور على الفجوات، مثل ffmpeg أو pydub. اقسم فقط حين يستدعي الملف ذلك فعلًا.
لا يمكن تقطيع الصيغ المضغوطة بهذه الطريقة، إذ لا يمكنك قطع MP3 على حدود إطار (frame) بالمكتبة القياسية. استخدم ffmpeg معها:

تجميع القطع

الخطوات التالية

تحويل الكلام إلى نص

مرجع لنقطة نهاية التفريغ.

استخراج البيانات المُهيكلة من المستندات

الاستخراج نفسه المرتكز على المخطط، مطبَّقًا على الملفات.

الاستجابات المُهيكلة

كيف يقيّد json_schema إكمال المحادثة.

استنساخ الصوت

امنح الملخّص صوتًا خاصًّا به.