أنا حالياً أقوم ببناء خط أنابيب لاستيعاب البحث الآلي باستخدام n8n لتشغيل نظام RAG أكاديمي (Generation-Augmented Retrieval).
الهدف من سير العمل:
يتم تفعيل سير العمل الآلي عند تسجيل موضوع بحثي جديد. ويستدعي محرك البنية التحتية للبيانات الأكاديمية المسمى ScholarAPI.net لسحب البيانات الأكاديمية بالنص الكامل وبيانات الاستشهادات التفصيلية للغاية. بمجرد استرجاع حمولة JSON، يمرر n8n البيانات إلى عقدة التضمين ويدفعها إلى مخزن المتجهات.
التحدي:
عند إجراء استدعاء عقدة HTTP Request إلى ScholarAPI.net، يمكن أن تكون استجابة JSON التي تحتوي على النص الكامل لعدة أوراق بحثية علمية ضخمة جداً (أحياناً عدة ميجابايتات من النصوص النظيفة والمنظمة لكل دفعة).
أواجه سؤالين معماريين محددين للحفاظ على تحسين سير العمل:
التعامل مع انتهاء المهل الزمنية / حدود التنفيذ: بالنسبة للدفعات الكبيرة من بيانات النص الكامل، قد تستغرق معالجة API العلوي بعض الوقت. ما هي أفضل ممارسة في n8n لإعداد ملفات إعادة محاولة مرنة أو تمديد المهل الزمنية على عقدة HTTP Request بحيث لا يفشل سير العمل قبل الأوان؟
تقسيم الذاكرة / البيانات: معالجة حمولات JSON الضخمة والمتداخلة مباشرة في خيط تنفيذ واحد يسبب استخدام ذاكرة ثقيل. هل يجب أن أستخدم عقدة “Item Lists” لتقسيم مصفوفات النصوص الواردة فوراً عند استقبالها من ScholarAPI.net، أم أن عقدة Code المخصصة (JavaScript/Python) أكثر كفاءة لتقطيع سلاسل النصوص قبل إرسالها إلى التضمينات المتجهة؟
سأكون ممتناً لأي نصيحة أو قوالب سير عمل من أي شخص قام ببناء خطوط أنابيب استيعاب / كشط نصوص على نطاق واسع داخل n8n!
@Asgef_Sha بخصوص انتهاءات المهلة الزمنية، يحتوي عقدة HTTP Request على حقل Timeout ضمن Options، قم بزيادته للاستدعاءات البطيئة، وفعّل Retry On Fail في Settings مع Max Tries و Wait Between Tries. حسناً، إعادة المحاولة تعمل فقط إذا كان On Error مضبوطاً على Stop Workflow، اضبطه على خيار Continue وستتجاهل n8n عدد المحاولات.
بخصوص الحمولات، لا تقم بتقسيمها يدويًا باستخدام Item Lists أو عقدة Code، لدى n8n عقدة Recursive Character Text Splitter للقيام بهذا تماماً (Chunk Size + Chunk Overlap) وتغذي Default Data Loader إلى مخزن المتجهات الخاص بك. قم بـ Split Out لمصفوفة papers أولاً حتى يمر كل واحد بمفرده بدلاً من الاحتفاظ بكتلة البيانات متعددة الميجابايت بأكملها في تنفيذ واحد.
أهلاً وسهلاً @Asgef_Sha! نصيحة achamm حول فاصل النصوص صحيحة تماماً. هناك نمط إضافي واحد لمعالجة دفعات متعددة الأوراق: بعد الحصول على استجابة HTTP، استخدم عقدة SplitInBatches لمعالجة الأوراق في مجموعات من 5 إلى 10 في المرة الواحدة، ثم مرر كل دفعة عبر فاصل النصوص وإدراج مخزن المتجهات. هذا يمنع حمولة واحدة كبيرة من البقاء في الذاكرة أثناء الإدراج، مما قد يسبب توقف التنفيذ حتى مع تمديد انتهاء المهلة الزمنية. من المفيد أيضاً تعيين مهلة انتظار طلب HTTP إلى 120 ثانية على الأقل لاستدعاءات ScholarAPI للنص الكامل، لأن التجميع من جانب الخادم قد يكون بطيئاً على الاستعلامات الكبيرة.
بالنسبة لهذا النوع من RAG ingestion، لن أحتفظ بالشيء كاملاً في عملية تنفيذ متزامنة واحدة طويلة. احفظ استجابة ScholarAPI الخام أولاً، ثم قسّم الحمل إلى أجزاء يمكن إدارتها، ثم قم بالمعالجة والتضمين في خطوات أصغر مع إعادة المحاولات والنقاط المرجعية. الـ JSON الكبير جنباً إلى جنب مع استدعاءات HTTP الطويلة هي حيث تتفوق الموثوقية الروتينية على سير عمل ذكي كل شيء في واحد. هل تستطيع حفظ الاستجابة الخام في مكان ما قبل بدء خطوة التضمين؟
الإجابات الجيدة أعلاه بالفعل كافية. أداة تقسيم النص بالإضافة إلى Split Out و SplitInBatches توفر القسيم، و OMGItsDerek محق في أنك تريد حفظ الاستجابة الخام قبل التضمين. أود البناء على هذه النقطة الأخيرة، لأن بالنسبة لهذا النوع من المتخذ، تكون البنية المعمارية مهمة أكثر من أي إعداد عقدة واحدة.
هناك بعض الأشياء التي أنقذتني في خطوط أنابيب البلع النصي الكبيرة:
قسمها إلى مسارين عمل، وليس واحدًا. مسار العمل A يستدعي فقط ScholarAPI ويكتب كل ورقة في جدول أو متجر كائنات بحالة “تم جلبها”. يلتقط مسار العمل B الصفوف التي تحمل حالة “تم جلبها”، ويقسمها، ويدرج المتجهات، ويحولها إلى “مضمنة”. يفشل سحب واجهة برمجية التطبيقات المكلفة والتضمين البطيء بشكل مستقل. إذا فشل التضمين في الورقة 40 من 50، فلن تقوم بسحب الدفعة مرة أخرى، بل سيستأنف B الصفوف غير المنتهية. هذا العمود الحالة هو نقطة التفتيش الخاصة بك.
اجعله عمليًا. صنف كل ورقة حسب معرّف ثابت (DOI أو معرّف ScholarAPI) وتحقق من المتجر قبل التضمين. تكون إعادة المحاولات وإعادة التشغيل حتمية في الوظائف الطويلة، وبدون مفتاح إزالة التكرار، تدفع لتضمين نفس الورقة مرتين وينتهي بك الحال مع متجهات مكررة تلوث الاسترجاع. الفوز بالموثوقية الأرخص هناك.
أصلح الحجم من المصدر إن أمكن. إذا كانت ScholarAPI تدعم الترقيم أو جلب لكل ورقة، اسحب صفحات أصغر بدلاً من دفعة واحدة بحجم عدة ميجابايت. الطريقة الأكثر موثوقية لعدم نفاد الذاكرة على JSON ضخم هي عدم الاحتفاظ بها كعنصر واحد في المقام الأول. يساعد Split Out في وقت مبكر، والمكالمات العكسية الأصغر تساعد أكثر.
توضيح بسيط واحد على نقطة إعادة المحاولة أعلاه: سيعاد محاولة Retry On Fail العقدة بغض النظر. يحدد إعداد On Error ما يحدث بعد استنفاد المحاولات، حيث يوقف Stop Workflow المسار، وتمرير Continue العنصر الفاشل في المنطقة الزمنية. بالنسبة لوظيفة البلع أحتفظ بإعادة المحاولات على، يتم ضبط On Error على المتابعة (باستخدام مخرجات الخطأ)، وتوجيه الأخطاء إلى جدول حروف ميتة حتى لا تغرق ورقة سيئة واحدة المسار برمته.
إذا كنت تستضيف ذاتيًا والأحمال النيء كبيرة بصراحة، فإن رافعتي env تساعد: رفع كومة Node باستخدام NODE_OPTIONS=–max-old-space-size، وتقليل احتفاظ بيانات التنفيذ بحيث لا تنتفخ المسارات متعددة الميجابايت قاعدة البيانات الخاصة بك.
بدلاً من ذلك، سيأخذك الانقسام الممل المنتج-المستهلك مع عمود حالة ومفتاح إزالة تكرار أبعد من ضبط المهل الزمنية على تنفيذ كبير واحد. يسعدني التوسع في أي من هذه.
مرحباً
أعتقد أنني أفهم ما تقصده — المشكلة هنا ليست مجرد timeout أو batching، بل حقيقة أن n8n يتعامل مع حمولة JSON ضخمة جداً داخل سياق تنفيذ واحد.
في الحالات التي يعيد فيها ScholarAPI نصاً منظماً ضخماً جداً، عادة ما تكون الاختناق الحقيقي هي ذاكرة التنفيذ + ربط العُقد، وليس مجرد حدود HTTP.
لقد شاهدت سلوكاً مشابهاً حيث أن القسمة أو “تحويل العملية بشكل غير متزامن” وحده لا يحل المشكلة بشكل كامل لأن البيانات لا تزال محفوظة في الذاكرة خلال دورة حياة التنفيذ.
فضولي إذا كنت قد حاولت فصل خطوة الاستيعاب بشكل كامل عن خطوة التحويل (وليس مجرد القسمة داخل سير العمل نفسه)؟
إعدادات n8n ملموسة لحل هذه المشكلة: أولاً، في العقدة HTTP Request > Options > Timeout، عيّن القيمة على 300000 (5 دقائق) بحيث لا تنقطع الاتصال في منتصف الاستجابة عند التعامل مع حمولات كبيرة. ثانياً، إذا أرجعت ScholarAPI مصفوفة من الأوراق البحثية، مرّر المخرجات عبر عقدة Split In Batches مضبوطة على حجم دفعة 1 قبل خطوة التضمين - هذا يبقي ورقة واحدة فقط في سياق التنفيذ في كل مرة بدلاً من جميعها. لعزل نطاق الذاكرة بشكل كامل كما اقترحت @Bella123، استدعِ سير عمل فرعي منفصل عبر Execute Workflow لخطوة التضمين والتخزين. بهذه الطريقة يتم حذف نص كل ورقة بحثية بعد عودة سير العمل الفرعي بدلاً من تراكمها في التنفيذ الأبوي.
كنت سأقسم هذا إلى البحث والتطبيع والتضمين بدلاً من محاولة جعل سير عمل واحد ضخم يتعامل مع كل شيء في وقت واحد.
بالنسبة لمسار ScholarAPI → RAG من هذا النوع، الجزء الخطير عادة ليس فقط طلب HTTP طويل الأجل. المشكلة أن حمولة ضخمة يمكن أن تفشل في عدة حدود مختلفة:
١. حد البحث
قد ينتهي انتظار استدعاء HTTP أو يعيد الكثير من البيانات بحيث لا يمكن لعملية واحدة الاحتفاظ بها بشكل مريح.
٢. حد التطبيع
تحتاج إلى تحديد معنى “المستند” الواحد قبل التقسيم: ورقة بحثية أو ملخص أو قسم أو كتلة اقتباس أو بيانات وصفية للمؤلف.
٣. حد التقسيم
يجب أن يتلقى خطوة التضمين وحدات قابلة للتنبؤ، وليس حمولات API الخام.
٤. حد إعادة المحاولة
إذا فشلت سجل واحد، فأنت لا تريد إعادة جلب وإعادة تضمين مجموعة النتائج بأكملها.
النمط الذي سأستخدمه:
• طلب صفحة/دفعة من السجلات؛
• اكتب فوراً بيانات الاستجابة الخام في مكان ما دائم؛
• قسم كل ورقة بحثية إلى عنصر موحد؛
• خزن معرف العنصر + حالة المعالجة؛
• قم بتشغيل التقسيم/التضمين كسير عمل ثانٍ على العناصر المعلقة؛
• حدد كل عنصر على أنه تم جلبه أو تطبيعه أو تقسيمه أو تضمينه أو فشل أو تم تخطيه.
هذا يعطيك قابلية إعادة التشغيل. كما أنه يجعل جودة RAG أسهل في تصحيح الأخطاء لأنه يمكنك فحص المرحلة التي أنتجت أجزاء سيئة.
سؤال واحد غير حساس: هل يسمح ScholarAPI بتقسيم الصفحات أو تصفية النتائج حسب التاريخ/الاستعلام، أم أنك تتلقى استجابة واحدة كبيرة يجب تقسيمها بعد الطلب؟