أجرت الحلقة أربعين تشغيلاً سليماً في مرحلة الاختبار. في التشغيل الحادي والأربعين، داخل بيئة الإنتاج، استدعت الأداة ذاتها للـ SQL بالاستعلام المعطوب نفسه مراراً وتكراراً حتى استنزفت ميزانية API اليومية كاملةً، فأيقظ تنبيه الفوترة أحدهم أخيراً. لم يكتب أحد نموذجاً سيئاً. لم يتغيّر أيّ موجّه (prompt). ببساطة، لم يقرّر الوكيل أنه انتهى.
هذا هو النمط الذي تكرر مشاهدته مع الفرق التي تنقل وكيلاً من نموذج أولي إلى عبء عمل يعمل على مدار الساعة. كثيراً ما تفشل حلقات وكيل AI في الإنتاج ليس لأن النموذج ساء فجأة، بل لأن طبقة التنفيذ تفتقر إلى آلية إنهاء منضبطة، وعقود أدوات مُتحقَّق منها، وسياق محدود المدى، وحالة دائمة. حلقة الوكيل هي نظام احتمالي يتخذ قراراً تسلسلياً تلو الآخر. وبدون عدد من الضمانات المحددة، يتحوّل الفشل النادر إلى حتمي بمجرد أن يعمل النظام طويلاً بما يكفي. تُدمج منصات الوكلاء المُدارة (Vertex AI Agent Builder, Bedrock Agents, Azure AI Foundry) بعض هذه الضمانات تلقائياً؛ هذا الدليل موجّه لمن اختاروا الاستضافة الذاتية وإدارة الحلقة بأنفسهم.
الرهان حقيقي لدرجة أن Gartner تتوقع إلغاء أكثر من 40% من مشاريع AI الوكيلية بحلول نهاية 2027، مستشهدةً بتصاعد التكاليف وضبابية القيمة. فيما يلي ستة أوجه محددة لانهيار الحلقات في الإنتاج، والآلية الكامنة وراء كل منها، ونمط الإطار الذي يُصلحها، مع تفاصيل LangGraph وn8n وما يلزم لتشغيل هذا النظام فعلاً على مدار الساعة.
النسخة المختصرة
- الحلقات اللانهائية: لا يقرّر الوكيل أنه انتهى أبداً. ادمج سقفاً صارماً لعدد الخطوات (recursion_limit في LangGraph،
recursion_limitالافتراضي 25) مع آلية كشف عدم التقدم التي تُنهي استدعاءات الأداة المتكررة بنفس الوسائط. - تجاوز نافذة السياق: تملأ الحلقة نافذة سياقها بالتاريخ المتراكم حتى تُقتطع المحادثات أو تفشل. لخّص التاريخ على فترات منتظمة للحفاظ على السياق العامل محدود المدى.
- فشل الأداة الصامت: تُعيد الأداة سلسلة نصية فارغة، يقرأها النموذج كعملية نجحت بنتيجة صفرية، فـ"ينجح" الوكيل دون أن يُنجز شيئاً فعلياً. تحقّق من كل نتيجة أداة قبل أن يراها النموذج.
- تدهور التفكير: تتراجع الجودة كلّما نما السياق، حتى قبل الوصول إلى الحد الأقصى. اضغط السياق في منتصف الحلقة، لكن احتفظ بالتعليمات الأمنية المثبّتة عند الضغط.
- فقدان الحالة عند إعادة التشغيل: يعني التعطّل البدء من الصفر. خزّن نقاط التفتيش في Postgres (LangGraph
PostgresSaver)، ليس SQLite، في الإنتاج. - عواصف إعادة المحاولة: عشرة وكلاء يُعيد كل منهم المحاولة عشر مرات يُغرقون خدمة معطّلة بمائة طلب. أضف تراجعاً أسياً مع تشويش عشوائي (jitter) وقاطع دائرة عالمي.
ما لا يغطيه هذا الدليل
هذا دليل لإطار عمل الوكيل، يُركّز على الهندسة المحيطة بالحلقة لا النموذج في داخلها. بعض الموضوعات ذات الصلة خارج نطاقه عمداً:
- إخفاقات تنسيق الوكلاء المتعددة (القراءات القديمة، الحالة المعلّقة بين الوكلاء): مشكلة مختلفة تستحق مقالتها الخاصة.
- أمان الوكلاء (حقن الموجّه، تسميم الأدوات): فئة فشل مستقلة بنموذج تهديد خاص بها.
- اختيار النموذج والضبط الدقيق. يفترض هذا الدليل أنك اخترت النموذج وتُصحّح النظام المحيط به.
- خدمات الوكلاء المُدارة، المُشار إليها أعلاه؛ الأنماط هنا مخصصة للمسار المُستضاف ذاتياً.
الحلقات اللانهائية: حين لا يقرّر الوكيل أنه انتهى
تدور الحلقة إلى الأبد حين لا يوجد سقف صارم لعدد الخطوات ولا آلية للكشف عن توقف التقدم. الإصلاح من جزأين: احتفظ بسقف صارم كحاجز تكلفة، وأضف آلية كشف عدم التقدم تُشفّر (hash) كل استدعاء أداة مع وسائطه وتُنهي الحلقة حين يتكرر الاستدعاء ذاته. في LangGraph هذا السقف هو recursion_limit، recursion_limitالافتراضي 25 خطوة؛ تجاوزه يُطلق خطأ من الرسم البياني (graph)، GraphRecursionError.
توثيق LangGraph يُعرّف هذا الحد بأنه الوصول إلى "الحد الأقصى من الخطوات قبل الوصول إلى شرط الإيقاف"، وهنا الفخ الذي يستحق الفهم: recursion_limit ليس حماية من الحلقة. إنه حاجز احتياطي يُطلَق بعد بعد أن تكون الحلقة قد أضاعت خمساً وعشرين خطوة والإنفاق على API المرتبط بها. من المفترض أن منطق الإنهاء المكتسب للوكيل يوقفه قبل ذلك بكثير، وهذا المنطق يمكن أن يفشل باستقلالية. إحدى حالات LangGraph الموثّقة تُظهر وكيل نص-إلى-SQL يدور في حلقة حتى يبلغ recursion_limit رغم وجود شروط إيقاف واضحة في الموجّه. استمر في استدعاء أداة الاستعلام ذاتها بنفس SQL الفاشل، وأُغلقت المسألة بـ"غير مخطط للتنفيذ". أقرأ هذا كإشارة واضحة: لا تعدّ السقف شرط إيقاف. إنه حزام الأمان، لا المكابح.
رفع السقف أمر بسيط؛ تمرّره عبر الإعداد (config) عند استدعاء الرسم البياني:
# The hard ceiling -- a backstop, not loop protection
graph.invoke(
{"messages": [("user", "Generate the quarterly report")]},
{"recursion_limit": 50},
)
الجزء الذي يوقف الحلقة العالقة فعلاً هو كشف التقدم. الآلية بسيطة: شفرة (hash) اسم الأداة مع وسائطها في كل خطوة، احتفظ بنافذة قصيرة من الهاشات الأخيرة، وأنهِ التشغيل حين يتكرر هاش.
import hashlib
def step_signature(tool_name: str, tool_args: dict) -> str:
payload = f"{tool_name}:{sorted(tool_args.items())}"
return hashlib.sha256(payload.encode()).hexdigest()
# Inside your loop: terminate if the same tool+args repeats within the window
seen = recent_signatures[-WINDOW:]
sig = step_signature(tool_name, tool_args)
if sig in seen:
raise StopReason("no_progress: repeated tool call detected")
recent_signatures.append(sig)
هذا يرصد الوكيل الذي يعمل تقنياً (يستدعي أدوات، يولّد رموزاً) لكنه يدور على نفس الإجراء الفاشل. نمط الفشل هذا يتوافق مع ما يسمّيه تصنيف MAST (IBM Research وUC Berkeley) عدم الوعي بشروط الإنهاء (FM-1.5)، أحد أنماط الفشل التي يُرجعها تحليلهم إلى فشل المهمة كلياً.
سقف الخطوات يوقف التكلفة الجامحة. كشف عدم التقدم يوقف الحلقة التي "تتقدم" تقنياً لكنها تُعيد نفسها. الإنتاج يحتاج كليهما.
تجاوز نافذة السياق: حين تملأ الحلقة سياقها بمخلّفات لا قيمة لها
تتراكم في الحلقة طويلة الأمد كل ناتج أداة، وكل فكرة وسيطة، وكل رسالة أنتجتها، ثم تُعيد حشو كل ذلك في نافذة السياق في كل دورة. في نهاية المطاف تمتلئ النافذة، إما بالاقتطاع الصامت أو بالفشل الصريح. الإصلاح هو ضغط السياق على فترات منتظمة: كل N خطوة، اضغط التاريخ المتراكم في ملخص متجدد ليبقى السياق العامل محدود المدى.
تخيّل وكيل بحث يعمل منذ ساعة. بحلول الخطوة 60 يحمل النص الكامل لكل صفحة جلبها، وكل نتيجة بحث، وكل تتبّع استنتاج. لا شيء من هذا التاريخ الخام يفيده في الخطوة 61، غير أنه كله يُحسب على النافذة، والنموذج يدفع ثمن الانتباه لرموز لم يعد بحاجة إليها. حين تمتلئ النافذة، يقتطع المزوّد من أحد الطرفين، ويفقد الوكيل بهدوء التعليمة التي أُعطيت له في البداية.
المشغّل قرار ضبط دقيق، وهناك مرجع مفيد له. يُشير شرح Mem0 لنظام إنتاج فعلي إلى أن مضغوط وكيل Hermes "يُطلَق عند 50% من نافذة سياق النموذج بشكل افتراضي"، مع شبكة أمان ثانوية عند 85% للجلسات التي تنتفخ بين الدورات. خمسون بالمائة نقطة بداية معقولة: اضغط مبكراً بما يكفي لئلا يتجاوز ناتج أداة ضخم واحد الحد قبل الضغط المجدول التالي.
ملاحظة: يُعدّ overflow وتدهور التفكير مشكلتين مختلفتين، وسيتناول القسم التالي المشكلة الثانية. فالـ overflow حاجز صارم: تنفد منك الـ tokens. أما التدهور فهو تراجع تدريجي: يصبح أداء النموذج أضعف. قبل أن تبلغ الجدار. عليك التعامل مع كليهما، وعتبة التشغيل أعلاه تحمي من الجدار الصلب.
السياق المحدود مسؤولية الإطار، لا ميزة في النموذج. لخّص على فترات منتظمة قبل أن تفرض النافذة اقتطاعاً صامتاً.
إخفاقات استدعاء الأداة الصامتة: حين "ينجح" الوكيل دون أن يفعل شيئاً
تُعيد استدعاء الأداة سلسلة فارغة أو رسالة ناعمة "لا نتائج"، يُفسّرها النموذج كنتيجة صالحة، فيستمر الوكيل كأن الخطوة نجحت، ويبدو ناجحاً وهو لا يُنجز شيئاً أصلاً. الإصلاح هو بوابة تحقق على كل نتيجة أداة: افحص المخطط (schema) أو تحقق من المنطق قبل أن يرى النموذج الناتج، واكشف فشلاً حقيقياً تضطر الحلقة للتعامل معه بدلاً من نجاح فارغ.
هذا النمط خبيث لأن لا شيء يتعطّل. مطوّر يوثّق أنماط الفشل الصامت في وكلاء الإنتاج عبّر عن ذلك مباشرة: تُفسّر النماذج السلاسل الفارغة العامة كعمليات صالحة بدون نتيجة وتستمر في التنفيذ دون أي وعي بالفشل. استعلام قاعدة البيانات الذي أعاد صفر صفوف لأن الاتصال انقطع يبدو متطابقاً، للنموذج، مع الاستعلام الذي لم يجد شيئاً بشكل مشروع. فيُبلّغ الوكيل بـ"لا سجلات مطابقة" ويمضي، وتكتشف بعد أسبوع أن ثلث تشغيلاته كانت معطوبة بهدوء.
بوابة التحقق تجلس بين الأداة والنموذج:
def gate_tool_result(tool_name: str, result):
# Reject empties and soft errors before the model can rationalize them
if result is None or (isinstance(result, str) and not result.strip()):
raise ToolFailure(f"{tool_name} returned empty -- treat as failure, not no-op")
if isinstance(result, str) and result.lower().startswith(("error", "exception")):
raise ToolFailure(f"{tool_name} returned a soft error: {result[:120]}")
return result # validated -- safe to hand back to the model
المقصود ليس الفحوصات بعينها؛ ستختلف حسب ما تُعيده كل أداة مشروعاً. المقصود أن قيمة الإعادة غير المُتحقَّق منها قرار فوّضته لنموذج احتمالي، والخطوة الافتراضية للنموذج هي الاستمرار.
نتيجة الأداة غير المُتحقَّق منها فشل صامت ينتظر الوقوع. تحقّق من الناتج، لا تثق بالاستدعاء.
تدهور التفكير في السياقات الطويلة: حين يزداد الوكيل سوءاً كلّما طال تشغيله
حتى حين تبقى دون الحد الصلب للسياق، تتراجع جودة التفكير كلّما نما السياق. هذا هو تأثير "الضياع في الوسط"، حيث يُولي النموذج اهتمامه بشكل موثوق لبداية السياق الطويل ونهايته لكنه يُضيّع الوسط. الإصلاح هو الضغط في منتصف الحلقة مع حفظ القيود المثبّتة: اضغط الضوضاء وأبقِ التعليمات الجوهرية.
للآلية اسم. تُشير مدونة هندسة Anthropic إليها بـ "تعفّن السياق": "كلّما زاد عدد الرموز في نافذة السياق، تراجعت قدرة النموذج على استرجاع المعلومات بدقة من ذلك السياق." لأن "كل رمز يُولي انتباهه لكل رمز آخر"، تحصل على علاقات ثنائية بترتيب n² لـ n رمزاً، وتمتد انتباه النموذج أكثر رقةً كلّما طال السياق.
هذا المحدّد، حفظ التعليمات الجوهرية، هو جوهر المسألة بأكملها، وهناك حادثة موثّقة تُبيّن السبب. في حالة موثّقة، حذف وكيل OpenClaw صندوق وارد مستخدم بشكل جماعي أثناء ضغط السياق، لأن تعليمة السلامة التي أُعطيت له ("لا تتخذ إجراء حتى أُخبرك") سقطت من السياق النشط حين ضُغط التاريخ. القيد الذي كان يجب أن يكون آخر ما يُسقَط عومِل كتاريخ عادي وخُلاصته.
إذن، "لخّص كل ما هو أقدم من N دورة" بشكل ساذج أمر خطير. يجب أن يعرف الضغط ما لا يجوز إسقاطه أبداً:
PINNED = {"system_constraints", "safety_instructions", "active_task_spec"}
def compress_history(messages):
pinned = [m for m in messages if m.tag in PINNED] # never summarized
transient = [m for m in messages if m.tag not in PINNED]
summary = summarize(transient) # lossy is fine here
return pinned + [summary] # constraints survive intact
هذا مختلف عن مشكلة التجاوز في القسم السابق. التجاوز يتعلق بنفاد المساحة؛ التدهور يتعلق بسوء النموذج بينما لا تزال هناك مساحة متبقية. يمكن أن تكون عند 60% من نافذتك وتكون قد بدأت بالتفكير بشكل سيئ.
ملاحظة: الضغط الذي يُسقط قيداً أمنياً فئة مختلفة من الأخطاء عن الضغط الذي يفقد نتيجة بحث قديمة. وسّم القيود ومواصفة المهمة وأي تعليمة "لا تفعل X" باعتبارها مثبّتة، واستبعدها من أداة التلخيص كلياً.
الضغط الذي يُسقط تعليمة سلامة أسوأ من غياب الضغط. احمِ القيود المثبّتة حين تضغط.
فقدان الحالة عند إعادة التشغيل: حين يعني التعطّل البدء من الصفر
حين يتعطّل وكيل يعمل لفترة طويلة، سواء بسبب إعادة تشغيل أو انهيار بسبب نفاد الذاكرة أو انقطاع الشبكة، لا يوجد استئناف من نقطة تفتيش بشكل افتراضي. تبدأ الحلقة من الصفر: تُعيد العمل الذي أنجزته بالفعل، والأسوأ أنها قد تُنفّذ مجدداً إجراءات اتخذتها فعلاً، كإرسال البريد الإلكتروني ذاته مرتين أو إعادة تشغيل استدعاء API مدفوع. الإصلاح هو نقاط التفتيش: أبقِ حالة الحلقة بعد كل خطوة لتُعيد التحميل من حيث توقفت بدلاً من الصفر عند إعادة التشغيل.
في LangGraph، اختيار خلفية نقطة التفتيش هو الاختيار بين التطوير والإنتاج. توثيق استمرارية LangGraph يصف SqliteSaver بأنه "مثالي للتجريب وسير العمل المحلي" و PostgresSaver بأنه "مثالي للاستخدام في الإنتاج"، والأخير هو ما تعمل عليه LangSmith نفسها. الاثنان متوازيان عمداً في الكود، مما يجعل المقارنة واضحة:
# Development -- single file, no server, do not ship this
from langgraph.checkpoint.sqlite import SqliteSaver
# Production -- survives the box it runs on
from langgraph.checkpoint.postgres import PostgresSaver
تفصيلان يُلدغ منهما الناس. أولاً، تُثبَّت حزم نقطة التفتيش بشكل منفصل عن LangGraph الأساسي (langgraph-checkpoint-sqlite و langgraph-checkpoint-postgres تبعيات مستقلة)، لذا لن يحتوي الجهاز الجديد على حافظ Postgres حتى تُضيفه. ثانياً، تحتاج كل عملية نقطة تفتيش إلى thread_id في الإعداد (config). هذا المعرف هو ما يربط تشغيلاً معيناً بحالته المحفوظة، وإعادة التشغيل بدون thread_id الصحيح لا يُعيد تحميل شيئاً.
نصيحة احترافية: حزم نقطة تفتيش LangGraph تُثبَّت بشكل منفصل.
langgraph-checkpoint-postgresلا يُسحب مع حزمةlanggraphالأساسية، لذا ثبّته في ملف متطلبات الإنتاج قبل أن تكتشف ذلك بالطريقة الصعبة في وقت حادثة.
n8n لديه نفس الانقسام بين التطوير والإنتاج، لكن بأسماء مختلفة. خيار الذاكرة المدمجة يُسمى أيضاً Simple Memory (أو Buffer Window Memory)، ومسار الإنتاج هو عقدة Postgres Chat Memory للحالة التي يجب أن تصمد عند إعادة التشغيل. تحتفظ الذاكرة المدمجة بالمحادثة في العملية الجارية، وهذا مقبول للاختبار لكنه خطر في عبء العمل على مدار الساعة. ممارسون يشغّلون وكلاء n8n في الإنتاج أبلغوا عن اضطرارهم للترحيل إلى مخزن مدعوم بـ Postgres بعد أن نمت الذاكرة الداخلية للعملية حتى أسقطت النسخة معها. إن كنت تستخدم n8n وتحتاج وكيلك لتذكّر أي شيء عبر إعادة التشغيل، اربطه بـ Postgres Chat Memory من البداية.
نقاط التفتيش بـ SQLite مريحة للتطوير. البقاء بعد إعادة تشغيل الإنتاج يعني Postgres (LangGraph) أو مخزن مدعوم بـ Postgres (n8n).
عواصف إعادة المحاولة: حين يُشنّ وكلاؤك هجوم حجب خدمة على خدمة معطّلة
حين تتعطّل خدمة تدفّق سفلي، تتحوّل إعادات المحاولة الساذجة لكل تنفيذ على حدة إلى حجب خدمة ذاتي الإيقاع. الإصلاح من نصفين: تراجع أسي مع تشويش عشوائي (jitter) في كل وكيل لتوزيع إعادات المحاولة زمنياً، وقاطع دائرة عالمي ينطلق بعد حد فشل مشترك ويمنع القطيع بأكمله من قصف خدمة واضح أنها معطّلة.
الرياضيات لا ترحم. كما يُصيغه مرجع أنماط إعادة المحاولة ، مع عشرة وكلاء متوازيين يُعيد كل منهم المحاولة عشر مرات، ترسل مائة طلب إلى خدمة على أرضها، لأن التراجع لكل وكيل حسب تنفيذه ليس عالمياً. التراجع لكل وكيل بمفرده لا يحلّ هذا. عشرة وكلاء تتراجع كل منهم بأدب لا تزال تتراجع بتزامن إن بدأت جميعها في الوقت ذاته، فتُعيد المحاولة في موجات منسّقة. التشويش العشوائي يكسر التزامن بتعشية انتظار كل وكيل؛ وقاطع الدائرة يكسر القطيع بمشاركة قطعة واحدة من حالة الفشل عبر الجميع.
النصف المتعلق بالتراجع مسألة محلولة في Python؛ مكتبة tenacity تتعامل مع التراجع الأسي مع التشويش العشوائي بشكل نظيف:
from tenacity import retry, stop_after_attempt, wait_random_exponential
@retry(wait=wait_random_exponential(multiplier=1, max=60), stop=stop_after_attempt(5))
def call_flaky_service(payload):
return downstream.post(payload)
قاطع الدائرة هو النصف الذي يجب أن يكون عالمياً: مشتركاً عبر كل الوكلاء، لا يُنشأ من جديد مع كل تنفيذ. حين تتجاوز الإخفاقات حداً معيناً، ينفتح، ويفشل كل وكيل بسرعة بدلاً من الاستدعاء الخارجي، وبعد فترة تهدئة يُمرّر استعلام اختباري واحد للتحقق من عودة الخدمة. القاطع الذي يعيش داخل عملية كل وكيل لا يحمي شيئاً، لأنه لا شيء مشترك؛ الخدمة المعطّلة لا تزال تتلقى المائة طلب كاملة.
التراجع لكل تنفيذ لا يزال يسمح لعشرة وكلاء بقصف خدمة معطّلة بشكل متزامن. يجب أن يكون قاطع الدائرة عالمياً لإيقاف القطيع.
الإخفاقات الستة في لمحة واحدة
قبل الجزء المتعلق بالبنية التحتية، إليك الفهرس الكامل في مكان واحد: الإخفاق، والآلية المسببة، وإصلاح الإطار، وأين تعيش المعاملة ذات الصلة في كل إطار.
| نمط الفشل | الآلية | إصلاح الإطار | معامل الإطار |
|---|---|---|---|
| حلقة لانهائية | لا سقف للخطوات ولا فحص للتقدم | سقف صارم + كشف عدم التقدم | LangGraph recursion_limit recursion_limit (25) / n8n Max Iterations |
| تجاوز السياق | ينمو التاريخ حتى تمتلئ النافذة | تلخيص دوري | على مستوى التطبيق (اضغط عند ~50% من النافذة) |
| فشل الأداة الصامت | إعادات الإخراج الفارغة/الناعمة تُقرأ كعمليات صالحة | بوابة تحقق على كل نتيجة أداة | غلاف الأداة على مستوى التطبيق |
| تدهور التفكير | يتراجع الانتباه مع نمو السياق ("تعفّن السياق") | ضغط منتصف الحلقة يحمي القيود المثبّتة | على مستوى التطبيق، واعٍ بالقيود |
| فقدان الحالة عند إعادة التشغيل | لا نقطة تفتيش؛ الحلقة تبدأ من الصفر | نقاط تفتيش دائمة | LangGraph PostgresSaver / n8n Postgres Chat Memory |
| عاصفة إعادة المحاولة | إعادات المحاولة لكل تنفيذ تتراكم على خدمة معطّلة | تراجع أسي + تشويش + قاطع دائرة عالمي | tenacity + حالة قاطع مشتركة |
ملاحظة للقرّاء الذين يستخدمون CrewAI أو AutoGen أو Dify أو حلقة Python مكتوبة يدوياً: معاملات الإطار تتغيّر، لكن الأنماط الستة لا تتغيّر. إلغاء التكرار، والتلخيص الدوري، والتحقق من المخطط (schema validation)، والضغط الواعي بالقيود، ونقاط التفتيش، وقاطع الدائرة العالمي كلّها مفاهيم مستقلة عن الإطار. تفاصيل LangGraph وn8n هنا مقابض ملموسة، لا حدود لتطبيق هذه الأنماط.
تحديد حجم نشر وكيل الإنتاج
كل نمط أعلاه يفترض أنك تتحكم في مدير العمليات، وقاعدة البيانات، وسلوك إعادة التشغيل. نقاط التفتيش لا تفيد إن لم تعُد حلقة متعطّلة تعمل أبداً، وقاطع الدائرة العالمي يحتاج مكاناً لحفظ حالته المشتركة. هذا التحكم بالضبط ما تمنحك إياه الاستضافة الذاتية وما لا تمنحه الصندوق الأسود المُدار، لذا القرار الأخير هو تحديد حجم الخادم الذي يُشغّله على مدار الساعة.
لمعظم نشرات الوكيل المفرد (وكيل واحد، استدعاءات LLM لـ API خارجي، نقاط تفتيش Postgres أساسية) نسخة صغيرة تكفي: حوالي 2 GB RAM, 1 vCPU, and 60 GB of NVMe storage. الحوسبة الثقيلة تعيش على جانب مزود النموذج؛ جهازك يُنسّق ويحفظ نقاط التفتيش ويحتفظ بالحالة، لا يشغّل الاستنتاج. ارتقِ إلى حوالي 4 GB RAM, 2 vCPU, and 120 GB NVMe حين يكون الوكيل ذو حالة ومتعدد الخطوات مع نقاط تفتيش Postgres وRedis لترطيب الجلسة، أو حين تشغّل سير عمل متزامنة تتشارك المضيف.
السبب في الرغبة بـ VPS مُدار ذاتياً بدلاً من منصة مقيّدة هو نفس سبب نجاح الإصلاحات: تحتاج صلاحية root. Postgres الخاص بك لنقاط التفتيش، وRedis الخاص بك لحالة الجلسة، ومدير عمليات حقيقي مثل systemd or pm2، بحيث حين تموت حلقة، يُعيد المشرف تشغيلها وتُعيد التحميل من آخر نقطة تفتيش بدلاً من إعادة تشغيل المهمة من الصفر. قصة الاسترداد كاملها تعتمد على امتلاك دورة حياة العملية.
لأننا نُشغّل n8n كتطبيق بنقرة واحدة في سوقنا الخاص، هذا الجزء من الإعداد هو أقصر مسار على جانبنا: يمكنك نشر n8n على VPS من Cloudzy بالتكوين المدعوم بـ Postgres الذي يحتاجه مسار الإنتاج، على نسخة تملك فيها صلاحية root لإضافة Redis الخاص بك وإشراف العمليات. هذه نفس البصمة المُستضافة ذاتياً الموصوفة أعلاه، حيث تمتلك قاعدة البيانات وسلوك إعادة التشغيل، وهو ما يجعل نقاط التفتيش والاسترداد التلقائي تعمل فعلاً.
أنماط الإطار لا تكون موثوقة إلا بقدر موثوقية الخادم الذي تعمل عليه. نقاط التفتيش لا تفيد إن لم تُعَد تشغيل العملية أبداً.
الأسئلة الشائعة
كيف أمنع وكيل LangGraph من الدوران في حلقة للأبد؟
استخدم آليتين معاً. اضبط recursion_limit كسقف صارم للخطوات (الافتراضي 25) لمنع حلقة جامحة من حرق ميزانية غير محدودة، وأضف كشف عدم التقدم يُشفّر كل استدعاء أداة مع وسائطه ويُنهي التشغيل حين يتكرر الاستدعاء ذاته في نافذة حديثة. السقف وحده حاجز يُطلَق بعد وقوع الهدر، لا حماية حقيقية من الحلقة. كشف التقدم هو ما يوقف الحلقة العالقة فعلاً.
ما هو recursion_limit الصحيح لـ LangGraph في الإنتاج؟
لا يوجد رقم شامل. حجّمه وفق الحد الأقصى لعدد الخطوات المشروعة التي يحتاجها وكيلك، مع هامش، وعامله صارماً كحاجز تكلفة فحسب. رفع الحد لا يجعل الوكيل الدائر يتقارب. إن كان وكيلك يبلغ حداً عالياً، الإصلاح هو كشف التقدم، لا سقف أعلى.
لماذا يستمر وكيل AI في n8n في بلوغ Max Iterations؟
بلوغ حد Max Iterations يعني أن الوكيل لا يتقارب: يتخذ خطوات أكثر مما يسمح به الحد دون الوصول إلى نقطة إيقاف. ارفع الحد فقط إن كانت المهمة بحاجة فعلية لخطوات أكثر؛ وإلا عامله كإشارة على أن الوكيل عالق. انتبه لفخ محدد: GitHub issue #22771 يُبلّغ أنه حين يُبلَغ حد التكرار مع تعيين "On Error: Continue"، يمكن أن يُوجَّه التنفيذ إلى مخرج النجاح بدلاً من مخرج الخطأ، فيبدو التشغيل الفاشل المحدود ناجحاً في سير عملك.
كيف أُبقي حالة الوكيل عبر إعادات التشغيل؟
في LangGraph، استخدم نقاط التفتيش بـ PostgresSaver بدلاً من SqliteSaver، المخصص للتطوير المحلي. في n8n، استخدم عقدة Postgres Chat Memory بدلاً من الذاكرة المدمجة الداخلية. كلاهما يحتاج قاعدة بيانات دائمة، وفي LangGraph كل عملية نقطة تفتيش تحتاج thread_id يربط تشغيلاً معيناً بحالته المحفوظة.
ما الذي يتسبب في تدهور التفكير في تشغيلات الوكيل الطويلة؟
تتراجع جودة التفكير مع نمو السياق، حتى قبل بلوغ الحد الصلب للرموز. هذا هو تأثير "الضياع في الوسط"، حيث يُولي النموذج انتباهه لبداية السياق الطويل ونهايته لكنه يُضيّع الوسط. تُشير مدونة هندسة Anthropic إلى الآلية الكامنة بـ"تعفّن السياق": لأن كل رمز يُولي انتباهه لكل رمز آخر، تحصل على علاقات ثنائية بترتيب n² وتمتد انتباه النموذج أكثر رقةً مع إطالة السياق. الإصلاح هو الضغط في منتصف الحلقة الذي يُلخّص التاريخ القديم مع إبقاء القيود المثبّتة وتعليمات السلامة سليمة.
موسوم بـ