Transcript
00:00:00مرحباً، شكراً جزيلاً لانضمامكم إلينا اليوم.
00:00:02أنا برانيت من فريق سير العمل (workflow) هنا في Vercel.
00:00:05مرحباً، أنا نيت، أيضاً من فريق سير العمل.
00:00:08نيت، لقد كنا معاً في فريق سير العمل منذ البداية،
00:00:12ومن بين كل الأشياء التي أطلقناها في الأشهر الستة الماضية،
00:00:15أعتقد أن الـ hooks والـ webhooks هي إحدى ميزاتي المفضلة،
00:00:18وهذا هو بالضبط ما جئت لتتحدث عنه اليوم.
00:00:21الـ Hooks والـ webhooks هي أيضاً ميزتي المفضلة.
00:00:23إنها قوية بشكل لا يصدق، وسأعرض لكم بعض العروض التوضيحية لشرح السبب.
00:00:28العرض الأول هو شيء ربما نعرفه جميعاً، وهو الروابط السحرية (Magic Links).
00:00:33الرابط السحري هو نموذج تسجيل دخول. تكتب بريدك الإلكتروني، وتتلقى رسالة في بريدك،
00:00:40وعندما تنقر على ذلك الرابط، يتم تسجيل دخولك إلى الخدمة.
00:00:44نعم، وإذا كنت أتذكر بشكل صحيح، مع Vercel، حتى قبل أن يطلق عليها اسم Vercel،
00:00:48ستحتاج إلى إشراك قاعدة بيانات لتتبع حالتك، وسرعان ما ستصبح الأمور فوضوية.
00:00:52نعم، لقد كنت أفكر بالفعل في كيفية هيكلة هذا الأمر وقاعدة البيانات التي سأستخدمها،
00:00:56لأن هذه تبدو كأنها مشكلة مألوفة وقد قمت ببناء أشياء مثلها من قبل.
00:01:01لذا نعم، أود رؤية كيف يبدو ذلك.
00:01:08نعم، فقط لأوضح ما أتحدث عنه، نقاط الألم التي أشير إليها،
00:01:12بدأت بتنفيذ نسخة "تقليدية" من تسجيل الدخول عبر Magic Link بدون استخدام Workflow.
00:01:19وهناك ثلاث نقاط نهاية معنية.
00:01:24الأولى هي عند إرسال نموذج تسجيل الدخول،
00:01:28وتحتاج إلى إنشاء جلسة وتخزين تلك الجلسة في قاعدة بيانات، مثل Redis.
00:01:30عليك تنفيذ وقت انتهاء الصلاحية (TTL)، ولا يمكنك ترك البيانات معلقة للأبد، بل يجب إنهاؤها.
00:01:38ثم إرسال البريد الإلكتروني، وهذا الشيء قد يفشل، وعندها لن يعمل تسجيل الدخول، وتلك تجربة محبطة.
00:01:43وهناك ثلاث نقاط نهاية (endpoints) معنية.
00:01:47الأولى هي عند إرسال نموذج تسجيل الدخول،
00:01:50وتحتاج إلى إنشاء جلسة وتخزينها في قاعدة بيانات، مثل Redis.
00:01:57عليك تنفيذ مدة بقاء (TTL)، ولا يمكنك ترك البيانات معلقة للأبد، بل يجب إنهاؤها.
00:02:06ثم ترسل البريد الإلكتروني، وهذا الشيء قد يفشل، وعندها لن يعمل تسجيل الدخول، وهي تجربة محبطة.
00:02:14صحيح، وبعد ذلك عليك العودة واستخدام مهمة مجدولة (cron job) أو متدرب لتنظيف قاعدة البيانات.
00:02:19ربما كنت أنا ذلك المتدرب في ذلك الوقت.
00:02:22ولكن هناك نقطة نهاية ثانية، وهي التي تحدث عندما ينقر المستخدم على الرابط في بريده.
00:02:28وهذه تحتاج أساساً للاستعلام من قاعدة البيانات واستعادة الحالة التي أُنشئت في نقطة النهاية الأولى.
00:02:36لقد بدأنا بالفعل في الوصول إلى كود معقد ومتشابك (spaghetti code).
00:02:38عندما كنت أحاول تخيل كيف سيبدو هذا، وجدته مألوفاً جداً وهو الطريقة التي كنت سأهيكله بها أيضاً.
00:02:48نرى أن هذا يتعقد بسرعة رغم أنه شيء بسيط جداً كفكرة.
00:02:54لذا دعونا نلقي نظرة على كيفية تنفيذ هذه الميزة في Workflow.
00:02:59تنفيذ الرابط السحري باستخدام Workflow SDK يبدو هكذا.
00:03:05يمكننا رؤية وظيفتنا، وبها توجيه useWorkflow، مما يعني أن هذه هي وظيفة Workflow الخاصة بنا.
00:03:11وأول شيء نفعله هو استدعاء وظيفة createWebhook، التي تأتي من حزمة Workflow.
00:03:18ونحن نستخدم أيضاً خيار respondWithManual في هذه الحالة، مما يعني أن وظيفة Workflow ستكون مسؤولة عن كتابة أو إرسال الاستجابة لطلب HTTP الذي يحفز هذا الـ Webhook.
00:03:36وهذا لكي تتمكن من القيام بعملية إعادة توجيه (redirect) أو شيء مشابه بعد ذلك عندما يسجلون الدخول؟
00:03:40نعم، فإذا كانت هناك بعض المعلومات في وظيفة Workflow نحتاجها لنعرف نوع الاستجابة التي سنرسلها.
00:03:51وعلى غرار نقطة النهاية الأولى، نرسل بريد تسجيل الدخول. هذه وظيفة useStep.
00:03:57فإذا فشل شيء كهذا، فإن Workflow SDK يحتوي على عمليات إعادة محاولة تلقائية.
00:04:03جانب المتانة يوفر بالفعل بعض الفوائد مقارنة بالنهج التقليدي هنا.
00:04:10لذا إرسال بريد تسجيل الدخول هو خطوة، وإذا فشل البريد، فستتم إعادة محاولة الإرسال بنفس عنوان URL الذي أنشأته بالفعل للـ Webhook.
00:04:21وإذا نظرنا هنا، فهذا نمط مثير جداً للاهتمام.
00:04:26نحن نستخدم promise race مع انتظار (sleep) لمدة خمس دقائق.
00:04:30هذا ممكن لأن كائن Webhook هذا ينفذ وعداً (promise).
00:04:35لذا لانتظار طلب هذا الـ Webhook، ما عليك سوى كتابة await Webhook.
00:04:40أو في هذه الحالة، تفعل ذلك مع السباق (race). وهذا رائع لأنني كنت أتوقع أن تحتوي ميزة Webhook هذه على مهلة (timeout) أو خيار ما كمعامل آخر.
00:04:50لكنني أحببت حقاً أنه أصبح أنظف الآن، حيث لعمل مهلة، كل ما تفعله هو نمذجتها كسباق بين الـ Webhook والـ sleep.
00:04:58أشعر أنه يمكنني فعل الكثير بهذا. ربما يمكنني إجراء سباق بين اثنين من الـ Webhooks المختلفة.
00:05:02لا يوجد الكثير مما يمكنك فعله عندما يكون لديك معاملان فقط في الوظيفة.
00:05:06لكن حقيقة أنه مجرد وعد ويمكنني عمل promise.race مقابل sleep، وربما الحصول على خطوة أخرى.
00:05:12أحب هذا النمط. أشعر أن عقلي بدأ يتسابق في التفكير في كل الأشياء التي يمكنني بناؤها باستخدامه.
00:05:16صحيح، وهذا هو الشيء الجميل في اللبنات الأساسية التي توفرها Workflow SDK.
00:05:21يتم كشف كل شيء كـ promise.
00:05:23لذا فإن أنماط JavaScript القياسية مثل await promise.race تعمل ببساطة.
00:05:28وهناك شيء آخر تجدر الإشارة إليه هنا وهو عدم وجود Redis هنا. لا توجد قاعدة بيانات.
00:05:33في المثال التقليدي، كنا نستخدم Redis TTL لتنفيذ هذه المهلة.
00:05:41وفي هذه الحالة، نستخدم خاصية Workflow sleep الأساسية.
00:05:44وأيضاً لا يوجد متدرب يضطر للذهاب لتنظيف قاعدة بيانات فوضوية بعد ذلك.
00:05:50هذا هو الجزء الأفضل.
00:05:51وهكذا يمكنك رؤية أن الـ Workflow يستجيب للطلب العام عبر إعادة التوجيه لصفحة نجاح تسجيل الدخول.
00:05:59ثم يسترجع معلومات عن مستخدمك لإعادتها إلى العميل الذي بدأ صفحة تسجيل الدخول.
00:06:07وهذا هو الـ Workflow بالكامل. تنفيذ الرابط السحري لدينا يتكون من 50 سطر برمجي.
00:06:12من المذهل رؤية هذا. هل يمكننا رؤيته وهو يعمل؟
00:06:17ها هو العرض التوضيحي للرابط السحري. سأقوم فقط بإدخال بريدي الإلكتروني.
00:06:24وقد بدأ الـ Workflow الخاص بنا هناك وأرسل البريد. وهناك webhook ينتظر حالياً.
00:06:31وفي الواقع، الـ Workflow الخاص بنا معلق الآن. لذا هناك صفر استهلاك للمعالجة أثناء انتظار قيام الشخص بالنقر على الرابط في البريد.
00:06:41أوه، وكيف يبدو هذا على Vercel؟ هل يمكنني رؤية تشغيل (run) معلق حالياً؟
00:06:47حسناً، لقد تلقينا البريد. وقبل أن أنقر على ذلك، دعونا نلقي نظرة على المراقبة (observability).
00:06:52أعلم أنني أنتقل من شيء لآخر، لكنني أحب أننا ننظر إلى هذا.
00:06:57حسناً، يمكننا أن نرى أن التشغيل الخاص بنا موجود هنا وقد بدأ قبل 40 ثانية.
00:07:02وإذا ألقينا نظرة، لدينا ميزات المراقبة القياسية التي يوفرها Workflow.
00:07:08يمكننا رؤية المدخلات لتشغيل الـ Workflow. يمكنك رؤية بريدي الإلكتروني الذي كتبته في نموذج تسجيل الدخول.
00:07:13ومن المثير للاهتمام، يمكننا أن نرى الـ hook الخاص بنا هنا وهو ينتظر فقط.
00:07:17وقد قلت إنه لا توجد معالجة تعمل حالياً. هذه هي المراقبة، ولكن لا يوجد شيء جالس بالفعل ينتظر مني النقر على هذا الـ hook.
00:07:25هذا صحيح. فالـ hook ينتظر، والـ sleep في حالة سكون، وكلاهما لا يتطلبان في الواقع أي معالجة (compute).
00:07:39ولكن يمكننا رؤية الـ hook الخاص بنا، وإذا تذكرتم، كلاهما يتسابقان في promise.race.
00:07:46لذا يجب أن ينتهي أحدهما أولاً حتى يستمر الـ Workflow.
00:07:50لذا إذا قمت بالنقر على ذلك الرابط، حسناً، نرى أنه تم توجيهي لصفحة نجاح تسجيل الدخول، والتي كانت إحدى الخطوات في منطق الـ Workflow الخاص بنا.
00:07:59وإذا عدت إلى نموذج تسجيل الدخول...
00:08:01صحيح، ثم بالعودة إلى لوحة التحكم، يجب أن يكون ذلك قد اكتمل أيضاً.
00:08:05هذا صحيح. لقد اكتمل الـ Workflow الخاص بنا.
00:08:08ويمكنك رؤية المؤقت يتوقف أيضاً بمجرد فوز الـ hook.
00:08:11نعم، لقد تمكنا من تنفيذ الرابط السحري بحوالي 50 سطر برمجي فقط.
00:08:17هذا أنيق حقاً. من الرائع رؤية كيف أنه، لو اضطررنا لرسم مخطط توضيحي لكيفية عمل الرابط السحري وشرحه لشخص ما.
00:08:27فإن الخطوات التي لديك في الكود هي بالضبط ما كنت سترسمه، وهذا هو الكود النهائي أيضاً.
00:08:34لم تكن هناك قاعدة بيانات إضافية في المنتصف. ولم تكن هناك مسارات API متعددة. الكود الذي أريتني إياه كان واضحاً وسلساً جداً.
00:08:41وأعتقد أن هذا هو الجانب الأكثر قوة في Workflow SDK، الطريقة التي يتيح لك بها هيكلة منطق تطبيقك ليتدفق بشكل منطقي بدلاً من التوسع ليتناسب مع البنية التحتية.
00:08:59صحيح. أيضاً، أعجبتني فكرة الـ webhooks هنا، تسميتها بـ webhook لأنها تمنحني طريقة مختلفة تماماً للتفكير فيها.
00:09:07إنها مجرد رابط URL مؤقت يمكنني إنشاؤه والتعليق عليه.
00:09:10في الواقع، هذا انتقال جيد لأنني أفكر في أننا نبني الكثير من هؤلاء الوكلاء (agents) في Vercel.
00:09:16نحن نبني مجموعة من وكلاء Slack ووكلاء GitHub، وغالباً ما نشترك في webhooks من GitHub أو Slack، أليس كذلك؟
00:09:23في كل مرة يوجد فيها تعليق جديد في طلب سحب (PR) ونريد بدء وكيل Vercel، نريد القيام بذلك بناءً على حدث ترسله GitHub عبر الـ webhooks، صح؟
00:09:31هل يمكننا استخدام Workflow webhooks للاشتراك فعلياً في الأحداث من GitHub، على سبيل المثال؟
00:09:36حسناً، لشيء مثل webhook يتم إرساله من Slack أو GitHub، عادةً ما يتعين عليك الدخول إلى لوحة التحكم يدوياً وتكوين رابط URL ثابت للرد (callback).
00:09:49صحيح. لا يمكنك إنشاء هذا لمرة واحدة، لا يمكنني إعطاؤه رابط URL لمرة واحدة بنفس الطريقة التي أفعلها مع البريد الإلكتروني هنا.
00:09:54صحيح، صحيح. ميزة إنشاء webhook هي في مستوى أعلى قليلاً بهذا المعنى، حيث توفر رابط webhook فريد يتم إنشاؤه عشوائياً.
00:10:04والذي يرتبط بتشغيل Workflow واحد محدد.
00:10:07في حالة مسار webhook الخاص بـ GitHub أو Slack، قد يرتبط ذلك بأي عدد من تشغيلات الـ Workflow.
00:10:14صحيح. يجب عليك تكوين شيء مسبقاً، فلديك طلبات سحب متعددة، لكنها جميعاً تذهب إلى نفس نقطة النهاية.
00:10:20لذا لتنفيذ ذلك باستخدام Workflow SDK، سننتقل لمستوى أدنى، سنستخدم ميزة الـ hook البدائية الأكثر انخفاضاً.
00:10:28ولدي عرض توضيحي فقط لأريك ذلك.
00:10:31دعونا نلقي نظرة.
00:10:32حسناً. هذا هو بوت وقت القصة (storytime bot).
00:10:35هذا هو أول تطبيق كتبته باستخدام Workflow SDK منذ ما يزيد قليلاً عن عام.
00:10:40وكيفية عمله هي أنك تكتب فقط أمر storytime/ وسنرى هذا الخيط (thread) يتم إنشاؤه.
00:10:47كل خيط يتم تمثيله بتشغيل Workflow فردي.
00:10:52وهكذا عندما نوسع الخيط، نرى أن نموذج لغة كبيراً (LLM) بدأ هذه القصة لنا، ويمكنك أنت أو أنا أو أياً كان في هذه القناة متابعة القصة.
00:11:05وسوف يساعدنا الـ LLM في الوصول بها إلى نتيجتها النهائية.
00:11:09حسناً. لونا لديها بذرة سحرية وماذا يحدث بعد ذلك؟ قامت بزرع البذرة.
00:11:13حسناً. ونرى بعض النشاط يحدث هنا.
00:11:17ماذا يحدث بعد ذلك؟ شيء سحري.
00:11:20انتهت قصتنا ولدينا قصة نهائية وسيكون هناك صورة صغيرة يتم إنشاؤها أيضاً.
00:11:26لكننا سننتقل إلى ذلك، سنعود إلى ذلك.
00:11:28أنا مهتم جداً بالفعل لأنني لاحظت أنني كنت أتوقع طلباً واحداً للـ webhook ولكن كان هناك في الواقع طلبان على الأقل لأنك أرسلت رسالتين.
00:11:35لذا أنا فضولي حقاً لرؤية كيف يبدو هذا في الكود.
00:11:38حسناً. هذه هي وظيفة الـ Workflow لبوت وقت القصة الخاص بنا.
00:11:44يمكننا أن نرى أنها تأخذ معرف القناة (channel ID)، وهي قناة بوت وقت القصة.
00:11:50ولديها بعض خيارات التكوين التي يمكنك تمريرها.
00:11:53لكن المثير للاهتمام، هو وجود مصفوفة الرسائل (messages array) هذه، وإذا كنت معتاداً على AISDK، فهذا هو تنسيق البيانات الذي تخزن فيه محادثة الذكاء الاصطناعي الخاصة بك.
00:12:04وفي تطبيق Slack bot نموذجي مثل الذي أنشأناه هنا، ستقوم عادةً بتخزين هذا النوع من الأشياء في قاعدة بيانات وفي كل تكرار أو كل حدث webhook، مع كل رسالة تتم كتابتها في الخيط، ستقوم باستعادة حالتك، وستبحث عن المحادثة في قاعدة البيانات.
00:12:23وهذا ليس ما يحدث هنا. هذه مجرد مصفوفة في وظيفتك.
00:12:27نعم. في الواقع، هذا رائع لأنني ضحكت سابقاً عندما رأيت المقدمة وكان لديك هذا التعليق الذي يقول: "انظري يا أمي، لا توجد طوابير (queues) أو قواعد بيانات (KB)".
00:12:34ولا يوجد استيراد (import) هنا لقاعدة بيانات. أنت فقط تستورد Workflow.
00:12:40وبالعودة لموضوع الرسالة الأخيرة، قد يغفل المرء عن هذا، لكن لديك فعلاً متغيراً هنا يسمى القصة النهائية والذي يفترض أنه بمرور الوقت، سنضيف الرسائل إلى هذه المصفوفة، كما أتخيل،
00:12:55وستظهر القصة النهائية كسلسلة نصية (string) هنا، لكن لا توجد قاعدة بيانات يجب أن تذهب إليها هذه المعلومات. لذا يبدو الأمر وكأن كلمة let هي قاعدة بياناتك هنا.
00:13:04نعم، "let هي قاعدة بياناتك" هو مصطلح رائع يجب أن نصيغه.
00:13:10ربما أكون قد سرقت ذلك منك، ولكن.
00:13:14الشيء المثير للاهتمام هنا، والشيء الذي نحن هنا لنتحدث عنه هو ميزة الـ hook التي نرى أننا ننشئها هنا. وما يختلف عن مثال الـ web hook الذي رأيناه مع الرابط السحري هو أننا في هذه الحالة نوفر رمزاً (token)،
00:13:28وهو عبارة عن سلسلة نصية تتضمن معلومات تعريف فريدة لتشغيل الـ Workflow هذا.
00:13:35الـ TS هو معرف الخيط. لذا فإن هذه السلسلة هي الرمز الذي يحدد بشكل فريد تشغيل الـ Workflow هذا.
00:13:44لذلك عندما ننظر إلى الكود الخاص بمسار الـ web hook، سنرى أن بيانات الحدث (payload) التي يرسلها Slack تحتوي على جميع المعلومات التي نحتاجها لإعادة إنشاء هذا المعرف بشكل حتمي.
00:13:58وهذا هو سحر كيفية ربط الـ web hook بتشغيل Workflow فردي.
00:14:04نعم، كنت فضولياً عندما رأيت الـ web hook لأنك تنشئ روابط URL جديدة لمثال السحر، ولكن لأننا بنينا بوتات Slack هذه من قبل، فلا يمكنك الحصول على رابط URL جديد في كل خيط.
00:14:17لذا فإن الطريقة لفهم كيفية قيامك بذلك هنا هي أن لديك واجهة برمجة تطبيقات (API) وهي مرتبطة بالفعل بـ Slack، ولكن في كل مرة تتلقى فيها رسالة هناك، تقوم أساساً بحساب نفس الرمز في جانب الاستئناف.
00:14:29لذا يمكن للـ Workflow الخاص بك الانتظار أساساً لهذا الرمز ويمكنك إنشاء نفس الرمز من حمولة الرسالة لاستئناف تشغيل الـ Workflow هذا.
00:14:37بالضبط. نعم. تم تكوين بوت Slack مرة واحدة عبر النقر اليدوي في لوحة تحكم Slack، وتحتاج إلى تحديد رابط URL ثابت لرد الـ webhook.
00:14:50هذا هو السبب في أن الـ hook البدائي ذو المستوى الأدنى يعمل بشكل أفضل في هذه الحالة، لأننا نستطيع إعادة إنشاء الرمز ديناميكياً.
00:14:59لذا لننظر سريعاً، هذا هو مسار الـ webhook، ولا يوجد الكثير مما يحدث هنا في الواقع.
00:15:07الشيء الرئيسي هو كيف نعيد إنشاء الرمز من البيانات التي مررها Slack.
00:15:13ثم نستدعي وظيفة الاستئناف (resume) وهذا يستأنف تشغيل الـ Workflow الفريد لذلك الرمز.
00:15:20أتخيل فعلاً أن هذا رائع حقاً. وأعتقد أن ما تفعله مع الـ webhooks هو نوعاً ما نفس الشيء.
00:15:28هل الـ webhook في الأساس يقوم فقط بعمل رمز عشوائي ثم يكون لديك نقطة نهاية HTTP تقوم بحل نفس الرمز العشوائي؟
00:15:35نعم، حسناً، الفرق مع ميزة الـ webhook هو أنك لست بحاجة إلى تحديد مسار API هذا في كودك.
00:15:44إن Workflow SDK ينفذ في الواقع مساراً افتراضياً لميزة الـ webhook من أجلك.
00:15:50ولكن نعم، بخلاف ذلك، فهو رمز يتم إنشاؤه عشوائياً وفريد لتشغيل Workflow واحد محدد.
00:15:55ولكن في هذه الحالة، لدينا الـ hook الخاص بنا مع الرمز، وبالعودة لشيء ذكرته منذ قليل، يمكن لهذا الـ hook استقبال البيانات عدة مرات.
00:16:06لكنها كانت دائماً جيدة للعروض التوضيحية فحسب، ولم أتمكن من إيجاد طريقة جيدة لاستخدامها.
00:16:11في هذه الحالة، نريد أن يعمل الـ hook مع كل رسالة فريدة يكتبها شخص ما في خيط Slack.
00:16:17وللقيام بذلك، تستخدم صيغة for await في JavaScript، وهي شائعة مع المكررات غير المتزامنة (async iterators).
00:16:25ولكن في هذه الحالة، نحن نتلقى حمولات أحداث متعددة من webhook الخاص بـ Slack باستخدام الـ hook الخاص بنا.
00:16:33هذا رائع جداً. لم أجد أبداً حالة استخدام رائعة لـ... أنا أحب المكررات غير المتزامنة وأحب المولدات (generators) وحتى أنني قدمت عرضاً عن هذا منذ زمن طويل.
00:16:42لكنها كانت دائماً جيدة للعروض التوضيحية ولم أتمكن من العثور على طريقة جيدة لاستخدامها.
00:16:46هنا يبدو لي الأمر وكأنك تملك حلقة تكرار (loop).
00:16:50ولكن بدلاً من التكرار على مجموعة ثابتة من العناصر أو التكرار بناءً على طابع زمني، لأنك تستخدم for await ثم تفعل ذلك على الـ hook، فإليك حلقة تكرار ترتبط بالضبط.
00:17:01كل شيء داخل حلقة التكرار يرتبط برسالة مستخدم واحدة.
00:17:05وهذه طريقة لطيفة للتفكير في هذا، حيث ستؤدي رسالة مستخدم جديدة إلى تكرار آخر لهذه الحلقة وتستمر في التراكم والاستمرار.
00:17:12الجزء الجميل في هذا هو أنه مع كل تكرار لهذه الحلقة، وبينما ننتظر المستخدم لكتابة الرسالة التالية، لا يتم استهلاك أي معالجة (compute) على الإطلاق.
00:17:22يتم تعليق سير العمل بالكامل وقد تصل الرسالة التالية في الخيط بعد دقائق أو أيام أو ربما لا تصل أبداً وهذا أمر مقبول تماماً.
00:17:33لذا من المحتمل وجود خيوط Slack في نفس تلك القناة مع Slackbot حيث يمكنني العودة الآن ولا يزال هناك تشغيل ينتظر لعدة أسابيع إذا لم يرد أحد.
00:17:42هذا رائع حقاً.
00:17:43وبالعودة لمصفوفة الرسائل التي ذكرناها سابقاً، نحن الآن نقوم بتعديل المصفوفة.
00:17:48نحن ندفع (push) رسالة المستخدم الجديدة وهذا هو تعديل قاعدة البيانات الخاص بنا لأن مصفوفة الرسائل هي مجرد متغير محلي.
00:17:57هذا رائع جداً. وبعد ذلك أرى أنك تقوم بالمزيد من promise.alls لعمل خطوات متوازية في المنتصف.
00:18:03هذا الكود واضح جداً لكل حلقة تكرار، لكل رسالة على Slack.
00:18:08أحب كيف أن هذا هو بالضبط ما سأفعله إذا كنت تحاول بناء هذا في هاكاثون أو شيء من هذا القبيل.
00:18:12الأمر يشبه: "إليك كيف سأكتب ما يحدث في كل رسالة".
00:18:16نعم، لذا نموذج promise.all، هذه مجرد وظائف use step عادية والفكرة هي تشغيلها بالتوازي.
00:18:23وشيء مثل إضافة تفاعل (reaction) لرسالة Slack هو فقط لإعطاء ملاحظات فورية للمستخدم بأن شيئاً ما يحدث.
00:18:32ولكن في نفس الوقت، نريد بدء الـ LLM للمساعدة في تحريك عملية إنشاء القصة.
00:18:39سأكون مهتماً جداً برؤية كيف تبدو المراقبة (observability) أيضاً عندما نصل إليها، لأنني أتخيل تلك النطاقات تبدأ في نفس الوقت وتجعل الأمر واضحاً للغاية.
00:18:49لدينا بالفعل المراقبة لوقت القصة الخاص بنا.
00:18:52لقد اكتملت، لذا سيتعين علينا العودة والتحقق من تلك الصورة.
00:18:56لذا يمكننا رؤية الـ hook الخاص بنا.
00:18:58والمثير للاهتمام هنا هو أنه في هذه الحالة، لدينا حدثان لاستقبال الـ hook.
00:19:05وهذا يرتبط بالرسالتين اللتين كتبتهما في خيط Slack الخاص بنا.
00:19:10والمراقبة تتيح لنا رؤية البيانات الفردية التي تم تزويد الـ hook بها.
00:19:14أوه، هذا رائع حقاً.
00:19:16لذا فهذه هي حمولات Slack الأساسية. ولم تضطر لتسجيل هذا بشكل إضافي. حمولات Slack تظهر فقط كأحداث يمكنني العودة وفحصها في لوحة التحكم.
00:19:25صحيح. ومن ثم يمكننا أن نرى أنه في كل مرة يتم فيها استلام حمولة الـ hook، فإن ذلك يواصل تنفيذ سير العمل الخاص بنا وتستمر خطواتنا.
00:19:34وفي النهاية لدينا نتيجة إنشاء صورة لوحة القصة الخاصة بنا، والتي تبدو هكذا.
00:19:40هذا هو بوت وقت القصة.
00:19:42هذا رائع حقاً.
00:19:43أعتقد أن رؤية الـ webhooks يتم إنشاؤها من الروابط السحرية والآن رؤيتك تستخدم الميزة البدائية ذات المستوى الأدنى مع الـ hooks وأيضاً القيام بها في حلقة تكرار حتى تتمكن من القيام بأحداث متعددة.
00:19:54هذا رائع حقاً.
00:19:55أشعر أن النموذج أصبح واضحاً حقاً لكيفية قيامي بالعمليات البشرية باستخدام الـ webhooks.
00:20:02هل هناك أي شيء آخر يمكن استخدام الـ hooks لأجله؟
00:20:05نعم، بالتأكيد.
00:20:06العرض التوضيحي الأخير الذي خططت له من أجلك هنا هو نمط مشابه جداً للرد على webhook من Slack.
00:20:17ولكن في هذه الحالة، سنستخدم الـ webhook كوسيلة لتسليم تنفيذ كود تطبيقنا ثم الانتظار لانتهاء بعض العمليات في مكان آخر بينما يتم تعليق سير العمل والانتظار.
00:20:32ثم استخدام رابط الـ webhook هذا للاتصال بنا مرة أخرى وعندها يمكننا إنهاء القيام بكل ما نحتاج للقيام به في منطق تطبيقنا.
00:20:41لهذا المثال، سنستخدم Vercel Sandbox وسنقوم ببعض عمليات الحوسبة الطويلة مثل استخدام FFmpeg لإجراء تحويل لملف.
00:20:51لذا فهذا هو سير عمل تحويل FFmpeg الخاص بنا.
00:20:56من أول الأشياء التي تحدث هي إنشاء Vercel Sandbox.
00:21:00المثير للاهتمام في هذا هو أن حزمة Sandbox في NPM تكشف عن وظائف تحتوي في داخلها على use step.
00:21:09لذا، في الواقع، هذه العملية هي عبارة عن "Step" (خطوة).
00:21:12بإمكانك إذاً شحن حزمة NPM.
00:21:15تقوم Sandbox ببساطة بشحن حزمة NPM تحتوي على توجيه "use Step" داخل هذه الدالة.
00:21:21وعندما تستوردها وتستخدمها داخل سير عمل، فإنها تحول Sandbox تلقائياً إلى Step دون الحاجة لكتابة أي كود.
00:21:29هذا لا يعني أنه لا يمكنك استخدام Sandbox للإنشاء خارج سير العمل.
00:21:32ماذا يحدث عند استدعاء هذا بدون سير عمل؟
00:21:35إذا لاحظت، التوجيه هو مجرد سلسلة نصية، وإذا كنت ستنفذ هذا بدون مترجم سير العمل،
00:21:47فإن تلك السلسلة لن تفعل شيئاً. لذا، الأمر يعمل ببساطة.
00:21:49إضافة "use Step" في حزم NPM الخاصة بك تعمل بشكل جيد بدون حزمة تطوير البرمجيات (SDK) لسير العمل.
00:21:55وبمجرد استخدام تلك الدالة داخل SDK سير العمل، ستحصل على مزايا المتانة والموثوقية بشكل تلقائي.
00:22:03حسناً، يقوم Sandbox ببعض الأمور المعتادة.
00:22:07يقوم بتثبيت FFmpeg لأنها ليست متوفرة افتراضياً.
00:22:11ويقوم بتحميل رابط الملف الذي سنحدده.
00:22:14وهل كل عملية تشغيل من هذه العمليات تعتبر "Steps" حالياً أيضاً؟
00:22:17نعم، هذه تشغل أمراً فردياً في Sandbox، وهي عبارة عن Steps. سنتمكن من رؤيتها في شاشة المراقبة.
00:22:29ثم نعود لاستدعاء "create-webhook"، والتي قد تتذكرها من عرض الرابط السحري (Magic Link).
00:22:36لكن في هذه الحالة، سنقوم بتمرير رابط الـ webhook هذا إلى سكربت Bash الذي سنقوم بتشغيله في Sandbox.
00:22:43ما يحدث هنا هو أننا سنقوم بتشغيل FFmpeg وتحويل الملف إلى التنسيق الذي نطلبه في واجهة المستخدم.
00:22:53وعندما ينتهي ذلك، سيقوم سكربت Bash بتشغيل أمر cURL ضد رابط الاستدعاء (callback URL) الخاص بالـ webhook.
00:22:59وعندما يحدث طلب cURL هذا، يستأنف منطق سير العمل لدينا عمله.
00:23:04حسناً، فهمت. هذا رائع. أستطيع أن أرى بالفعل، كنت أستبق الأمور قليلاً، لكني لاحظت وجود رمز "&" في عملية التشغيل هذه.
00:23:11أنت تقوم فعلياً بكتابة السكربت وتشغيله في الخلفية لأن خطوة FFmpeg مثل هذه قد تستغرق وقتاً طويلاً.
00:23:17فلا تريد أن تظل الـ Step متوقفة وتنتظرها.
00:23:20صحيح، صحيح. هذا السطر هنا يبدأ سكربت تحويل FFmpeg في الخلفية.
00:23:28ثم يتم تعليق دالة سير العمل، وننتظر استئناف الـ webhook.
00:23:34وأرى استخدام "promise race" مرة أخرى مع مهلة ساعة واحدة. هذا نمط رائع جداً.
00:23:40بالضبط، وهذه المرة، كما تعلم، قد تستغرق عملية تحويل FFmpeg وقتاً طويلاً.
00:23:46قد يكون ملف وسائط كبيراً جداً. لذا، قمنا بتحديد مهلة لمدة ساعة في هذه الحالة.
00:23:51وهذا جيد تماماً. في سير العمل، يمكنك الانتظار لفترة غير محدودة تقريباً.
00:23:56ومرة أخرى، لا يتم استهلاك أي موارد حوسبة بينما ننتظر استئناف الـ webhook.
00:24:01وهل يمكننا رؤية هذا؟ هل يمكننا رؤية هذا قيد التشغيل؟ هل لدينا عرض توضيحي؟
00:24:04لدينا بالفعل.
00:24:05إنه مثال بسيط ومضحك نوعاً ما.
00:24:07نعم، لقد عرفت مثال "الأرنب الكبير" (big bunny) فوراً. إنه من Blender.
00:24:12أجل، أتذكر مشاهدة هذه الفيديوهات عندما كنت أتعلم Blender منذ زمن طويل.
00:24:16أوه واو، أنا أحسدك.
00:24:19لدينا رابط ملف الوسائط ملصق هنا. في هذه الحالة، سنقوم فقط باستخراج طبقة الصوت منه.
00:24:26بمجرد الضغط على الزر، يبدأ ذلك سير العمل ويجب أن نتمكن من الانتقال إلى شاشة المراقبة.
00:24:33أوه، ها هو. نعم، يمكننا رؤية إنشاء الـ Sandbox.
00:24:37وهذا يعيد لنا نسخة الـ Sandbox الخاصة بنا. رائع جداً.
00:24:42وهذا لأن كل شيء في سير العمل يجب أن يكون قابلاً للتسلسل (serializable).
00:24:46وكما قلت، فإن الـ Sandboxes تدعم التسلسل. لذا فهي تظهر فعلياً في سير العمل.
00:24:53صحيح. نعم، حزمة Vercel sandbox تحتوي على فئة (class) تسمى Sandbox، وتلك الفئة تنفذ دوال تسلسل سير العمل.
00:25:03وبالتالي فهي تعمل ببساطة في شاشة المراقبة الخاصة بنا.
00:25:06ويمكن لأي حزمة القيام بذلك، أليس كذلك؟ لا يقتصر الأمر على Sandbox. يمكن لأي فئة تريد العمل داخل سير العمل تنفيذ نفس الرموز واستخدام توجيهات "use Step".
00:25:17هذا صحيح تماماً. يمكننا أن نرى أنه تم استدعاء الـ hook الخاص بنا في غضون 20 ثانية هذه المرة.
00:25:25كان التحويل سريعاً قليلاً لأنه ملف صغير نوعاً ما، لكن كان من الممكن أن يستغرق أي وقت.
00:25:31نلاحظ أنه بعد إنشاء وتهيئة Sandbox، تم إنشاء الـ hook ومررناه إلى Sandbox لبدء أمر FFmpeg.
00:25:43وعندما انتهى ذلك، استلمنا البيانات (payload) من الـ Sandbox الخاص بنا.
00:25:48وهذا هو الـ cURL الذي حدث داخل سكربت bash سابقاً. فهو يكتب الأمر ثم يستخدم cURL حرفياً في Sandbox لإكمال الـ webhook.
00:25:57صحيح. لقد أنهى الـ Sandbox العمل الذي كان يقوم به، لذا فهو يعيد التحكم إلى سير العمل لدينا.
00:26:04الطريقة التي أفكر بها في هذا الآن هي أنه مع الـ Steps في سير العمل، تقوم بتشغيل خطوة، فتنفذ الكود في الخلفية، ثم يكمل سير العمل.
00:26:13لكن كلاً من hook و webhook يبدوان وكأنهما في مستوى أدنى. يمكنني فقط إنشاء رمز (token) أو رابط والانتظار لأي شيء.
00:26:21قد يكون ذلك رابطاً سحرياً بشرياً، أو إيميلاً، أو Sandbox، أو أي نوع من الحوسبة، مهما كان ما يجب أن يحدث.
00:26:27ويقوم سير العمل الخاص بي بمجرد التوقف مؤقتاً مع كل حالته حتى يقع ذلك الحدث. يبدو تقريباً أنه في مستوى أدنى من الـ Step نفسها.
00:26:34نعم. الطريقة التي أفكر بها هي أن webhook و hook هما وسيلة لتمرير بيانات خارجية إلى سير العمل الخاص بك.
00:26:42والطريقة التي أرى بها الأمر هي أن الـ Step وسيلة ليقوم سير العمل بالتعليق ثم انتظار انتهاء نوع من الحوسبة قبل الاستئناف.
00:26:50لكن hook و webhook كلاهما يبدوان فعلياً في مستوى أدنى لأنك تنشئ فقط رمزاً أو رابطاً يمكنك إرساله، في هذه الحالة لـ sandbox، ولكن يمكنك إرساله لأي مكان.
00:27:01قد يكون لإنسان، أو إيميل، أو حتى سير عمل آخر على سبيل المثال.
00:27:05وعندما يكتمل ذلك، يستيقظ سير العمل الأساسي ويستأنف من حيث توقف تماماً.
00:27:12إذاً، هو بطريقة ما في مستوى أدنى حتى من الـ Step. إنه وسيلة لتعليق سير العمل لأي نوع من الإجراءات الخارجية.
00:27:19أجل. الطريقة التي أحب التفكير بها هي أن الـ hook وسيلة لتعليق سير العمل وانتظار عودة بيانات خارجية إليه، وهو أمر قوي جداً.
00:27:31هذا رائع حقاً. أعلم أن وقتنا قد نفد اليوم، ولكن مع هذه العروض التوضيحية، أكدت لي مرة أخرى لماذا الـ hook هي ميزتي المفضلة في سير العمل، وأنا متحمس جداً لمواصلة البناء بها.
00:27:42عظيم. أجل، يسعدني أنها أعجبتك.
Community Posts
No posts yet. Be the first to write about this video!
Write about this video