الوكلاء الفرعيون خطأ ببساطة
Explore with AI
كان وكيل الإصلاح التلقائي في Polylane مبنيًا في الأصل كمسار عمل يضم عدة وكلاء فرعيين، كل واحد مسؤول عن مهمة واحدة:
- وكيل فرز يفرز آلاف التنبيهات والإشارات
- وكيل منسّق يدير الوكلاء الفرعيين
- ما يصل إلى خمسة عشر وكيلًا فرعيًا للتحقق من كل الفرضيات الممكنة للمشكلة
- ووكيل برمجة يقدّم في النهاية طلب pull request
يمكن أن تؤدي مشكلة مؤكدة إلى ما يصل إلى 18 وكيلًا ووكيلًا فرعيًا للتحقيق والإصلاح. أما اليوم، فينجز الوكيل نفس العمل وكيل واحد.
كان مسار العمل تصميمًا سليمًا بناءً على ما كنا نعرفه في حينه: مطالبات صغيرة، مجموعات أدوات صغيرة، ومنسّق ينقل النتائج بين المتخصصين. لكنه كان مكلفًا للغاية وصعبًا في التشغيل والتفكير فيه.
ما هي المشكلة؟
يفحص Polylane السجلات والمقاييس والتتبعات من كل مورد سحابي (دالة Lambda، عامل Cloudflare Worker، مشروع Vercel، وما إلى ذلك) على كل مزود متصل. لكل مورد، نخزّن خطوط أساس عبر آفاق زمنية متعددة، بحيث نتمكن من رصد الموسمية في البيانات. ثم نقيّم الحالة الراهنة للمورد السحابي ونقارنها بالبيانات التاريخية. تُسجَّل القيم التي تخرق خط الأساس كمشكلات.
يمكن أيضًا إنشاء المشكلات من خلال التنبيهات التي نتلقاها من حلول قابلية الملاحظة وتتبع الأخطاء.
كيف نحل المشكلات؟
لحل مشكلة، نحتاج إلى:
- الفرز: جمع البيانات لتأكيد المشكلة أو رفضها.
- التحقيق: تقييم فرضيات متعددة وتحديد السبب الجذري.
- فتح طلب pull request أو التصعيد. إما كتابة طلب pull request لحل المشكلة، أو التصعيد إلى مهندس إذا لم يكن الإصلاح تغييرًا في الكود، أو كتابة تقرير يوضّح النتائج ثم التوقف.
تنتهي الغالبية العظمى من الجولات قبل الوصول إلى طلب pull request أو تصعيد، عندما يخلص Polylane إلى أن المشكلة إما إيجابية زائفة أو غير ضارة.
معمارية الوكلاء الفرعيين
كانت معماريتنا الأولية مبنية على عدة وكلاء فرعيين.
استخدمنا وكيلًا منسّقًا لتنسيق العمل بين وكلاء فرعيين متعددين. كانت الوكلاء الفرعيون يُنشَؤون للتحقق من الفرضيات، محاولين إثبات أو دحض كل فرضية بنقاط انطلاق مختلفة (على سبيل المثال البدء بالنظر إلى السجلات، أو البدء بقاعدة الكود، وما إلى ذلك).
كانت نتائج الوكلاء الفرعيين تُصوَّت عليها بحساب بسيط ثم تُلخَّص من قبل وكيل آخر قبل رفعها إلى الوكيل المنسّق.
كان حكم كل فرضية عبارة عن تصويت مرجّح بالثقة عبر جولاتها.
const CONFIDENCE_RANK = { definitive: 4, strong: 3, moderate: 2, weak: 1, speculative: 0 };
function aggregatePassVerdicts(passes: PassResult[]): Verdict {
const weights: Record<string, number> = {};
const counts: Record<string, number> = {};
for (const p of passes) {
weights[p.verdict] = (weights[p.verdict] ?? 0) + CONFIDENCE_RANK[p.confidence];
counts[p.verdict] = (counts[p.verdict] ?? 0) + 1;
}
const totalWeight = Object.values(weights).reduce((a, b) => a + b, 0);
const [winner, winnerWeight] = Object.entries(weights).sort((a, b) => b[1] - a[1])[0];
const countMajority = counts[winner] > passes.length / 2;
const weightMajority = winnerWeight > totalWeight / 2;
if (!countMajority && !weightMajority) {
return { verdict: "inconclusive", confidence: "weak", summary: `No consensus across ${passes.length} passes.` };
}
const majority = passes.filter((p) => p.verdict === winner);
const confidence = majority.reduce((min, p) => (CONFIDENCE_RANK[p.confidence] < CONFIDENCE_RANK[min] ? p.confidence : min), majority[0].confidence);
const lead = majority.reduce((best, p) => (CONFIDENCE_RANK[p.confidence] > CONFIDENCE_RANK[best.confidence] ? p : best));
return { verdict: winner, confidence, summary: lead.summary };
}
بعد التحقق من كل الفرضيات، إذا تم تحديد سبب جذري، كان المنسّق إما يصعّد إلى مهندس أو يكتب خطة للإصلاح. كانت هذه الخطة تُسلَّم إلى وكيل برمجة فرعي لتنفيذ الإصلاح وتقديم طلب pull request.
المشكلة الرئيسية في هذه المعمارية هي فقدان السياق بين الوكلاء الفرعيين. كان كل وكيل ينقل إلى الأعلى أو الأسفل أجزاءً فقط من سياقه، عبر التلخيص، وكان الوكلاء الأعلى والأسفل في السلسلة يضطرون بانتظام إلى إعادة العمل نفسه.
علاوة على ذلك، لم يكن لدى الوكيل الذي يكتب الإصلاح سوى الخطة، من دون سياق عن المشكلة الأصلية أو الأدلة التي جُمعت خلال التحقيق. أدى هذا إلى طلبات pull request دون المستوى، غالبًا ما كانت تركّز على الأعراض بدلًا من السبب الجذري.
اخترنا هذه المعمارية أساسًا لأنه عندما صمّمناها لأول مرة في مارس 2026، لم تكن النماذج المتقدمة قادرة دائمًا على إجراء تحقيق كامل من البداية إلى النهاية، بما في ذلك كتابة الإصلاح، وبصراحة، لم تكن بنيتنا التقنية جيدة إلى ذلك الحد.
أصبح البناء على هذا الأساس أصعب باطراد للأسباب التالية:
- تطلّب تصحيح الأخطاء عدة تتبعات. استلزم التفكير في تحقيق كامل من البداية إلى النهاية النظر عبر تتبعات متعددة، بل أحيانًا عشرات منها
- كان التقييم يتم لكل مرحلة على حدة. يمكن أن يُقيَّم كل وكيل فرعي على حدة وينجح، بينما تكون النتيجة النهائية ضعيفة، لأن الإخفاقات كانت تكمن في نقاط التسليم.
كررنا العمل على هذه المعمارية لأشهر، محسّنين كل وكيل على حدة ومعدّلين المطالبات وعمليات التسليم، لكن النتائج لم تضاهِ الجهد المبذول أبدًا. كانت مكلفة، وصعبة التصحيح، ولم تكن طلبات pull request جيدة بما يكفي.
وكيل واحد
في 3 سبتمبر، استبدلنا مسار عمل الوكلاء الفرعيين بوكيل واحد ينجز كل شيء من الفرز إلى طلب pull request. يقوم الوكيل نفسه بجمع الأدلة، والتحقق من كل الفرضيات، وتقديم طلب pull request.
حذفنا وكيل الفرز، والمنسّق، ووكلاء الفرضيات، ووكيل الإصلاح التلقائي، إلى جانب مسارات العمل التي كانت تنقل النتائج بينها والبوابات التي كانت تسبق طلب pull request. كانت الفوائد التشغيلية فورية: تتبع واحد يُقيَّم، ولا ملخصات بين الوكلاء الفرعيين.
والأهم من ذلك، تحسّنت جودة طلبات pull request، وانهار الوقت الفاصل بين اكتشاف مشكلة وفتح طلب pull request الذي يحلّها. خلال الشهر المحيط بنقطة التحول، انخفض الوسيط من 2.2 ساعة إلى 35 دقيقة، وانخفضت النسبة المئوية التسعون من تسعة أيام إلى أقل من ساعتين. في ظل خط الأنابيب، كانت النتيجة عادةً تبقى راكدة لساعات عديدة في سلسلة من مسارات العمل، كل واحد ينتظر السابق. أما في ظل الوكيل الواحد، فيُفتح طلب pull request بعد دقائق فقط من اكتشاف المشكلة.
نفتح الآن أيضًا عددًا أكبر بكثير من طلبات pull request. عندما كنا نستخدم الوكلاء الفرعيين، انتهت 0.6% من المشكلات المكتشفة بطلب pull request. في ظل الوكيل الواحد، تبلغ النسبة 4.2%، وهي في ازدياد. الفارق يكمن في أنماط الفشل. يجب على خط أنابيب متعدد الوكلاء الفرعيين أن ينجو من كل عملية تسليم: يجب على الفرز أن يرقّي النتيجة، ويجب على المنسّق أن ينتج فرضيات، ويجب على مرحلة التفرّع أن تصل إلى حكم، وهكذا. كل عملية تسليم هي نمط فشل محتمل.
انخفضت التكلفة لكل طلب pull request أيضًا. تبدأ كل جولة الآن بالنموذج الأقوى، لذا فإن النتيجة المرفوضة تكلّف أكثر مما كانت تكلّفه في ظل خط الأنابيب. لكن متوسط التكلفة لكل طلب pull request انخفض من $111 إلى نحو $18 في أول تسعة أيام من الوكيل الواحد. هذا الانخفاض ليس نتيجة حصرية لتغيير المعمارية، إذ إننا نعمل باستمرار على تحسين كل جانب من جوانب Polylane. جزء منه هو الانحراف الطبيعي لنظام يخضع لتحسين مستمر.
كل القيم وراء الرسوم الثلاثة، حسب اليوم بتوقيت UTC
| اليوم | المشكلات المكتشفة التي لها طلب pull request | الوسيط | p90 | التكلفة لكل طلب pull request |
|---|---|---|---|---|
| 14 Aug | 1.9% | 2.3 h | 2.7 h | $144 |
| 15 Aug | 1.1% | 5.6 h | 5.9 h | $270 |
| 16 Aug | 2.3% | 1.2 h | 4.2 d | $121 |
| 17 Aug | 1.5% | 38 min | 4.5 d | $120 |
| 18 Aug | 0.9% | 20 min | 27 min | $57 |
| 19 Aug | 0.7% | 49 min | 12.2 h | $157 |
| 20 Aug | 0.7% | 1 h | 35.9 h | $202 |
| 21 Aug | 0.8% | 44.2 h | 4.2 d | $211 |
| 22 Aug | 0.8% | 7.7 d | 10.4 d | $563 |
| 23 Aug | 1.3% | 32 min | 35.3 h | $205 |
| 24 Aug | 0.6% | 2.5 d | 5.6 d | $90 |
| 25 Aug | 1.4% | 1.5 h | 13.4 h | $43 |
| 26 Aug | 0.3% | 1.5 h | 2.1 h | $45 |
| 27 Aug | 0.7% | 1.9 h | 2.5 h | $58 |
| 28 Aug | 1% | 11.7 d | 14.1 d | $49 |
| 29 Aug | 1.5% | 26.1 h | 10.4 d | $32 |
| 30 Aug | 0.5% | 2 d | 2.8 d | $51 |
| 31 Aug | 0.2% | 9 d | 10.4 d | $77 |
| 1 Sep | 0.1% | 4.2 d | 4.2 d | $169 |
| 2 Sep | 0.1% | 6.7 d | 6.7 d | $93 |
| 3 Sep · وكيل واحد | 0.4% | 29 min | 2.6 d | $69 |
| 4 Sep | 2.4% | 23 min | 5.4 h | $25 |
| 5 Sep | 2.9% | 34 min | 3.4 d | $15 |
| 6 Sep | 1.4% | 34 min | 43.7 h | $23 |
| 7 Sep | 1.8% | 32 min | 4.5 h | $16 |
| 8 Sep | 4% | 41 min | 19.4 h | $26 |
| 9 Sep | 7.6% | 34 min | 1.2 h | $15 |
| 10 Sep | 5% | 34 min | 1.3 h | $17 |
| 11 Sep | 5.4% | 56 min | 2.1 h | $12 |
| 12 Sep | 9.1% | 35 min | 1.3 h | $3.46 |
| 13 Sep · حتى 16:00 | 8.2% | 30 min | 55 min | $2.88 |
لا تبنِ وكلاء فرعيين
- عمليات التسليم تفقد أكثر مما توفّر. كل ملخّص يُنقل بين الوكلاء هو سياق لن يمتلكه الوكيل التالي أبدًا. ينبغي أن يكون الوكيل الذي يجمع الأدلة هو نفسه الوكيل الذي يتصرف بناءً عليها.
- قيِّم الجولة، لا الوكلاء. تنجح التقييمات الفردية لكل وكيل بينما يفشل النظام، لأن الإخفاقات تكمن بين الوكلاء.
- احتفظ بتتبع واحد لكل جولة. قرار خاطئ موزّع على عشرات التتبعات يستغرق شرحه بعد ظهر كامل. وفي تتبع واحد، يستغرق الأمر مجرد تمرير للنظر.