إصداركود / SDK · جاهز للعملHabr AI→ المصدر

بوت يستخدم Chrome مجهول الهوية داخل Docker سجّل 1,170 اجتماعًا ويُصدر ملخصًا خلال 4.6 دقائق

طوّر كاتب على Habr بوتًا داخليًا للاجتماعات بديلًا من Copilot: خلال أربعة أشهر سجّل نحو 1,170 اجتماعًا و920 ساعة من الكلام لـ93 منظّمًا. وبلغ الزمن الوسيط من نهاية التسجيل إلى صدور الملخص المصحوب بالمهام 4.6 دقائق. تجري عملية التعرّف على الكلام وفصل المتحدثين على خوادم الشركة، ولا تُرسل الملفات الصوتية إلى الخارج. ويعتمد النظام مسارين: بوت داخل الفريق لاجتماعات قنوات Teams، وChrome مجهول الهوية داخل حاوية لبقية الاجتماعات.

معالج بواسطة الذكاء الاصطناعي من Habr AI؛ بتحرير Hamidun News
بوت يستخدم Chrome مجهول الهوية داخل Docker سجّل 1,170 اجتماعًا ويُصدر ملخصًا خلال 4.6 دقائق
المصدر: Habr AI. كولاج: Hamidun News.
استمع للمقال
بطاقة الحقائق والأرقام
المعيارالتفاصيل
المصدر الأصليHabr AI ↗
رئيس التحرير
حقيقة رئيسية #1تفقد ميزة Teams الأصلية لنسخ الروسية ثلثي النص: 26–37 كلمة في الدقيقة مقابل 99–108
حقيقة رئيسية #2تمنح الأسماء المستخرجة من ملف VTT في Teams، مع فصل المتحدثين، الاسم الصحيح لـ94–99% من المقاطع
حقيقة رئيسية #3لا ترى Graph API خادم Exchange المحلي، لذلك كان لا بد من قراءة التقويم عبر EWS

لا تعيش الاتفاقات التي تُبرم في الاجتماعات عادةً إلا حتى الاجتماع التالي. فلا أحد يدوّن محضرًا، وبعد أسبوع لا يتذكر المشاركون سوى نحو 10% مما قيل. وهناك مشكلة ثانية أحدث عهدًا: تعمل داخل الشركة وكلاء ذكاء اصطناعي، لكنها لا تعرف سوى ما تم تسجيله وفهرسته. أما كل ما اتُخذ من قرارات شفهيًا فلا وجود له بالنسبة إليها. لذلك قرر كاتب المقال المنشور على Habr سد الفجوتين بمساعد اجتماعات من تطويره، وعرض أرقامًا من بيئة الإنتاج.

لماذا ليس Copilot ولا النسخ الأصلي

لا يعمل Copilot إلا لمن لديهم اشتراك، وهذا لا يناسب أتمتة «جميع الفرق من دون إجراءات يدوية». كما أنه لا يغطي المستأجرين الخارجيين ولا Telemost وMTS Link وTrueConf. وإضافة إلى ذلك، أراد الكاتب معالجة صوت اجتماعات العملاء على خوادم الشركة نفسها.

ولم تناسبه أيضًا ميزة Teams الأصلية لنسخ الروسية. ففي الاجتماعات نفسها كانت تنتج 26–37 كلمة في الدقيقة، بينما يبلغ المعدل الطبيعي للكلام المهني نحو 100 كلمة، وكان خط المعالجة الخاص بالكاتب ينتج 99–108. كما يكتب Teams المصطلحات التقنية وفق نطقها، مثل «ed baton» بدل add button. والعينة صغيرة، لذلك يصف الكاتب المقارنة بصراحة بأنها مؤشر تقريبي لا benchmark كاملًا.

مساران بدلًا من مسار واحد

تعذر بناء حل شامل، لأن اجتماعات Teams تنقسم إلى نوعين.

تُسجَّل اجتماعات القنوات — الاجتماعات اليومية والتخطيط والاستعراضات اللاحقة — تلقائيًا وفق السياسة المعتمدة، ثم تُحفظ على SharePoint. ولا تتضمن محادثة جماعية تقليدية؛ إذ تعيش المراسلات داخل سلسلة أسفل المنشور الأساسي للقناة، ولا يُسمح للضيف المجهول بالدخول إليها. لذلك يُثبَّت بوت داخل الفريق، يقرأ القناة ويلتقط حدث الخدمة CallRecording عندما تكون حالته Success. بعد ذلك يجلب ملف MP4، ويحوّله إلى WAV بتردد 16 كيلوهرتز، ويرسله إلى نظام التعرّف على الكلام، ثم ينشر الملخص في سلسلة الاجتماع. ويتطلب ذلك إذن RSC باسم ChannelMessage.Read.Group، يمنحه مالك الفريق من دون موافقة إدارية تشمل المستأجر بأكمله.

أما جميع الاجتماعات الأخرى فيتولاها «مساعد الاجتماعات». يُضاف المساعد إلى الدعوة بوصفه مشاركًا عاديًا، وعند بدء الاجتماع تدخل إليه نسخة مجهولة الهوية من Chrome تعمل داخل Docker. ولا يبدأ التسجيل إلا بموافقة صريحة: إذا دُعي المساعد وسُمح له بالدخول، يبدأ التسجيل؛ وإلا فلا. والمساران منفصلان ماديًا، ولكل منهما تسجيلاته وتطبيقاته وصلاحياته، لذلك لا يؤثر تسرب مفتاح أحدهما في الآخر.

مطبات جديرة بالتذكر

حالة تسابق أثناء التسجيل. في البداية كانت الحالة تُحفظ في ملفات JSON، وخلال الأسبوع الأول أرسل كل من listener ومهمة cron احتياطية التسجيل نفسه مرتين. وكان الحل استخدام Postgres مع INSERT … ON CONFLICT DO NOTHING وSELECT … FOR UPDATE، بينما تعيد عملية claim أحد الأوضاع new أو retake أو attach أو skip.

طرق مسدودة مع النصوص المنسوخة. كان إذن RSC المسمى ChannelMeetingTranscript.Read.Group يعيد الخطأ 403، ولم يكن SharePoint يقبل رمز app-only من دون شهادة، فيما كانت Graph onlineMeetings/transcripts تتطلب application access policy للمنظّم. لذلك يعتمد خط المعالجة أساسًا على ملف MP4 ونظام التعرّف الداخلي على الكلام، بينما يُستخدم ملف VTT من Teams مصدرًا مساعدًا.

أسماء المتحدثين. يُستخرج من ملف VTT عدد المتحدثين — بوصفه تلميحًا للمعامل num_speakers — وأسماؤهم. ثم تُطابق المقاطع مع مجموعات فصل المتحدثين وفق الفارق الزمني. وفي ثلاثة اجتماعات جرى التحقق منها يدويًا، حصل 94–99% من المقاطع على الاسم الصحيح. وأي إخفاق يتحول إلى «مجهول الهوية»، لأن نسبة جملة إلى الشخص الخطأ أسوأ من تركها مجهولة.

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

ماذا يعني ذلك عمليًا

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

للنظام قيود أيضًا. لا يدعم المسار 1 القنوات الخاصة، وفي الاجتماعات الخارجية يجب السماح للبوت بالدخول من الردهة، كما تظهر في Teams نفسه أحيانًا أخطاء يتوقف بسببها البوت عن تلقي تحديثات القناة — issue msteams-docs #11106. وتأتي الأرقام من شركة واحدة من دون تحقق مستقل، لكن أربعة أشهر في بيئة الإنتاج مع 93 منظّمًا تظل حجة مقنعة.

القصة

أسئلة وأجوبة حول الخبر

ما أبرز التفاصيل حول لماذا ليس Copilot ولا النسخ الأصلي؟

لا يعمل Copilot إلا لمن لديهم اشتراك، وهذا لا يناسب أتمتة «جميع الفرق من دون إجراءات يدوية». كما أنه لا يغطي المستأجرين الخارجيين ولا Telemost وMTS Link وTrueConf. وإضافة إلى ذلك، أراد الكاتب معالجة صوت اجتماعات العملاء على خوادم الشركة نفسها. ولم تناسبه أيضًا ميزة Teams الأصلية لنسخ الروسية. ففي الاجتماعات نفسها كانت تنتج 26–37 كلمة في الدقيقة، بينما يبلغ المعدل الطبيعي للكلام المهني نحو 100 كلمة، وكان خط المعالجة الخاص بالكاتب ينتج 99–108.

ما أبرز التفاصيل حول مساران بدلًا من مسار واحد؟

تعذر بناء حل شامل، لأن اجتماعات Teams تنقسم إلى نوعين. تُسجَّل اجتماعات القنوات — الاجتماعات اليومية والتخطيط والاستعراضات اللاحقة — تلقائيًا وفق السياسة المعتمدة، ثم تُحفظ على SharePoint. ولا تتضمن محادثة جماعية تقليدية؛ إذ تعيش المراسلات داخل سلسلة أسفل المنشور الأساسي للقناة، ولا يُسمح للضيف المجهول بالدخول إليها. لذلك يُثبَّت بوت داخل الفريق، يقرأ القناة ويلتقط حدث الخدمة CallRecording عندما تكون حالته Success.

ما أبرز التفاصيل حول مطبات جديرة بالتذكر؟

حالة تسابق أثناء التسجيل. في البداية كانت الحالة تُحفظ في ملفات JSON، وخلال الأسبوع الأول أرسل كل من listener ومهمة cron احتياطية التسجيل نفسه مرتين. وكان الحل استخدام Postgres مع INSERT … ON CONFLICT DO NOTHING وSELECT … FOR UPDATE، بينما تعيد عملية claim أحد الأوضاع new أو retake أو attach أو skip. طرق مسدودة مع النصوص المنسوخة.

ZK
جمال حميدون — مدير المنتجات في Alpina Digital ومدير منتج AlpinaGPT. يبني منتجات الذكاء الاصطناعي منذ 2019. بدأ Hamidun News كمشروع شخصي ثم أصبح مُجمِّعًا إنتاجيًا بأكثر من 6000 قارئ.
✦ Hands-On AI Engineering · Hamidun Academy

Build & Ship Production AI Agents Without Boilerplate Code

Start your free 72-hour trial at Hamidun Academy (academy.hamidun.com) — interactive lessons on Claude Code & Cursor, live Tech Radar, and production agent templates.

Start 72h Free Trial →Vibe-Coding Intensive
ما رأيك؟
جارٍ تحميل التعليقات…