Alejandro Rioja.
AI Agents Operations

أنماط تنسيق الوكلاء المتعددين: الطوابير والحالة وعمليات التسليم

Alejandro Rioja
Alejandro Rioja
6 د قراءة
TL;DR

أنظمة الوكلاء المتعددين الموثوقة لا تعتمد على مطالبات ذكية — بل تعتمد على انضباط الأنظمة الموزعة المُمِلّ: طوابير دائمة بين الوكلاء، وحالة محفوظة خارج النموذج، وعمليات تسليم متكافئة (idempotent) تصمد أمام إعادات المحاولة. النموذج هو العامل؛ والطابور هو العمود الفقري.

نشرة بريدية مجانية

كل أربعاء. أكثر من 28,400 مشترك. بدون حشو.

جدول المحتويات

محدَّث يونيو 2026.

خلاصة: أنظمة الوكلاء المتعددين الموثوقة لا تُكسَب بمطالبات ذكية — بل تُكسَب بانضباط الأنظمة الموزعة المُمِلّ. ضع طابورًا دائمًا بين الوكلاء، واحفظ الحالة خارج النموذج، واجعل كل عملية تسليم متكافئة بحيث لا يمكن لإعادة المحاولة أن تتصرف مرتين. النموذج هو العامل؛ والطابور هو العمود الفقري. أتقِن هذه الثلاثة ويتوقف التنسيق عن كونه مخيفًا.

قراءة المُشغِّل: معظم وكلائي الذين يزيد عددهم عن 100 وكيل هم بخطوة واحدة. أما الذين ليسوا كذلك — خطوط المعالجة التي تُصنِّف ثم تُثري ثم تتصرف — فلم تصبح موثوقة إلا عندما توقّفت عن التفكير في “سلسلة مطالبات” وبدأت أفكر في “طابور مهام بعمال LLM”. هذه هندسة معمارية، وليست هندسة مطالبات.

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

النمط 1: ضع طابورًا دائمًا بين كل وكيل

الغريزة الأولى هي استدعاء الوكيل B مباشرة من داخل الوكيل A. لا تفعل. الاستدعاءات المباشرة تقرن الاثنين: إذا كان B بطيئًا، يتوقف A؛ وإذا فشل B، يُفقَد عمل A؛ وإذا احتجت إلى توسيع نطاق B، فلا يمكنك ذلك دون المساس بـ A.

بدلًا من ذلك، يُنهي A عمله ويضع رسالة في الطابور لـ B. الوكيل B عامل منفصل يُفرِّغ الطابور بوتيرته الخاصة.

typescript
// الوكيل A ينتهي ويُسلِّم عبر الطابور — دون استدعاء مباشر لـ B
await env.ENRICH_QUEUE.send({
  traceId,
  type: "enrich",
  payload: classifierResult,
});
// مهمة A أُنجِزت. سيلتقطها B بشكل مستقل.

على Cloudflare أستخدم Workers Queues لهذا الغرض بالضبط — نفس البِنى الأساسية وراء حزمة الوكلاء التي أستخدمها. يمنحك الطابور أربعة أشياء مجانًا: التخزين المؤقت (يمكن أن يتعطل B دون فقدان العمل)، وإعادات المحاولة (تُعاد الرسائل الفاشلة)، والضغط العكسي (تتراكم القفزة في الطابور بدلًا من الانهيار)، وفك الاقتران (وسِّع أو أعِد نشر B دون المساس بـ A). كل واحد من هذه أمر كنت ستضطر لولا ذلك إلى بنائه يدويًا وارتكاب أخطاء فيه.

النمط 2: احفظ الحالة خارج النموذج دائمًا

أكثر أخطاء الوكلاء المتعددين شيوعًا هو افتراض أن النموذج يتذكر أي شيء بين الخطوات. إنه لا يتذكر. كل استدعاء للنموذج عديم الحالة؛ والذاكرة الوحيدة هي ما تضعه في المطالبة. لذا يجب أن يعيش مصدر الحقيقة لـ “أين تقف هذه المهمة في خط المعالجة” في قاعدة بيانات، لا في محادثة.

أحتفظ بسجل مهمة واحد يقرأه كل وكيل ويُحدِّثه:

typescript
interface JobState {
  traceId: string;
  stage: "classified" | "enriched" | "acted" | "done" | "failed";
  data: Record<string, unknown>;
  attempts: number;
  updatedAt: number;
}

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

النمط 3: اجعل كل عملية تسليم متكافئة

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

الحل هو جعل الإجراءات متكافئة بمفتاح:

typescript
async function handleEnrich(msg: QueueMessage, env: Env) {
  const job = await getJob(env, msg.traceId);
  if (job.stage !== "classified") {
    // تمت معالجته بالفعل بعد هذه المرحلة — هذا تسليم مكرر. تخطَّ.
    return;
  }
  const result = await enrich(job.data);
  await advanceJob(env, msg.traceId, "enriched", result);
  await env.ACT_QUEUE.send({ traceId: msg.traceId, type: "act" });
}

يجعل فحص المرحلة العملية آمنة للتشغيل مرتين: يرى التسليم الثاني أن المهمة قد تقدمت بالفعل ولا يفعل شيئًا. للآثار الجانبية الخارجية (إرسال بريد إلكتروني، خصم بطاقة)، مرِّر مفتاح تكافؤ إلى واجهة برمجة التطبيقات النهائية حتى تُزيل هي أيضًا التكرار. افترض أن كل رسالة ستُسلَّم مرتين وصمِّم بحيث يكون ذلك غير ضار — لأنها ستفعل في نهاية المطاف.

النمط 4: المنسِّق مقابل الكوريغرافيا — اختر بتأنٍّ

هناك طريقتان لتوصيل التدفق، والخيار الصحيح يعتمد على التعقيد.

الكوريغرافيا (ما ألجأ إليه افتراضيًا): يعرف كل وكيل الخطوة التالية فقط ويضعها في الطابور. ينبثق التدفق من السلسلة. بسيطة، لامركزية، سهلة التوسيع — أضِف مرحلة بإدراج طابور. العيب هو أنه لا يوجد مكان واحد يصف التدفق بأكمله، لذا قد يصبح خط المعالجة المعقّد صعب الفهم.

التنسيق (منسِّق مركزي): يمتلك منسِّق واحد التدفق، ويستدعي كل وكيل بدوره، ويقرر ما يلي بناءً على النتائج. يعيش التدفق بأكمله في مكان واحد قابل للقراءة، ومنطق التفرّع صريح. والثمن هو مكوّن مركزي يجب أن يكون هو نفسه دائمًا — إذا لم تكن حالة المنسِّق نفسها مُخرَجة خارجيًا (النمط 2)، يصبح نقطة الفشل الوحيدة.

قاعدتي: الكوريغرافيا حتى يصبح التفرّع معقّدًا، ثم منسِّق دائم. خط معالجة خطي من ثلاث مراحل هو كوريغرافيا. أما التدفق ذو التوجيه الشرطي، والتوزيع المتوازي، وعمليات الدمج فيتطلب منسِّقًا تعيش حالته في قاعدة البيانات حتى يتمكن من الاستئناف بعد انهيار.

النمط 5: التوزيع والتجميع دون فقدان أجزاء

عندما تُولِّد مهمة واحدة N مهامًا فرعية متوازية (إثراء 50 سجلًا، تلخيص 20 مستندًا) وتحتاج إلى انتظارها جميعًا قبل المتابعة، فأنت بحاجة إلى دمج (join). الحيلة هي عدّاد في حالة المهمة:

  1. يضع الأب N رسالة ابنة في الطابور ويكتب expected: N, completed: 0 في سجل المهمة.
  2. يؤدي كل ابن عمله ويزيد ذرّيًا completed.
  3. الابن الذي يرفع completed ليساوي expected يضع المرحلة التالية في الطابور.

الزيادة الذرّية حاملة للوزن — بدونها، يمكن لابنين ينتهيان في وقت واحد أن يظنّا كلاهما أنهما ليسا الأخير، ولا يُطلَق الدمج أبدًا. استخدم عدّادًا يستطيع مخزن البيانات زيادته ذرّيًا، أو معاملة. يتيح لك هذا النمط موازاة الوسط المكلِّف من خط المعالجة (غالبًا عمل رخيص لـ Haiku — انظر حسابات تكلفة Haiku مقابل Sonnet) مع الحفاظ على دمج نظيف في النهاية.

ما سأتجاوزه

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

الخلاصة: الوكلاء عمّال عديمو الحالة، والطوابير هي العمود الفقري الدائم، والحالة تعيش في قاعدة بيانات، وكل عملية تسليم آمنة للتشغيل مرتين. هذه هي اللعبة كلها.

الأسئلة الشائعة

هل ينبغي للوكلاء استدعاء بعضهم مباشرة أم المرور عبر طابور؟

عبر طابور. الاستدعاءات المباشرة تقرن الوكلاء — فشل أحدهم أو بطؤه ينتشر إلى الآخر، ولا يمكنك التوسيع أو إعادة النشر بشكل مستقل. يمنحك الطابور الدائم التخزين المؤقت وإعادات المحاولة والضغط العكسي وفك الاقتران مجانًا.

أين يجب أن تعيش حالة الوكلاء المتعددين؟

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

كيف أمنع وكيلًا من التصرف مرتين على نفس المهمة؟

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

هل أحتاج إلى إطار عمل متعدد الوكلاء؟

عادةً لا. تغطي الطوابير الدائمة، وجدول الحالة، ومفاتيح التكافؤ معظم احتياجات الإنتاج ببِنى أساسية توفرها منصتك بالفعل. تبنَّ إطار عمل فقط عندما تصطدم بمشكلة ملموسة يحلّها بشكل فريد، لا افتراضيًا.

تابع القراءة

احصل على دليل الذكاء الاصطناعي في صندوق بريدك

كل أربعاء. أكثر من 28,400 مشترك. بدون حشو.

↵ لعرض كل النتائج esc esc للإغلاق