لوحة المعلومات
14 سبتمبر 2026

الوكلاء الفرعيون خطأ ببساطة

Explore with AI

كان وكيل الإصلاح التلقائي في Polylane مبنيًا في الأصل كمسار عمل يضم عدة وكلاء فرعيين، كل واحد مسؤول عن مهمة واحدة:

  • وكيل فرز يفرز آلاف التنبيهات والإشارات
  • وكيل منسّق يدير الوكلاء الفرعيين
  • ما يصل إلى خمسة عشر وكيلًا فرعيًا للتحقق من كل الفرضيات الممكنة للمشكلة
  • ووكيل برمجة يقدّم في النهاية طلب pull request

graph TB
    S["Alerts and signals"] --> T["Triage agent"]
    T -->|"confirmed issue"| C["Coordinator agent"]
    C -->|"hypotheses"| H["Up to 15 hypothesis sub-agents"]
    H -->|"verdicts"| C
    C -->|"plan"| A["Coding agent"]
    A --> PR["Pull request"]
    style C fill:#fee2e2,stroke:#fca5a5,color:#7f1d1d
    style H fill:#fee2e2,stroke:#fca5a5,color:#7f1d1d

يمكن أن تؤدي مشكلة مؤكدة إلى ما يصل إلى 18 وكيلًا ووكيلًا فرعيًا للتحقيق والإصلاح. أما اليوم، فينجز الوكيل نفس العمل وكيل واحد.

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

ما هي المشكلة؟

يفحص Polylane السجلات والمقاييس والتتبعات من كل مورد سحابي (دالة Lambda، عامل Cloudflare Worker، مشروع Vercel، وما إلى ذلك) على كل مزود متصل. لكل مورد، نخزّن خطوط أساس عبر آفاق زمنية متعددة، بحيث نتمكن من رصد الموسمية في البيانات. ثم نقيّم الحالة الراهنة للمورد السحابي ونقارنها بالبيانات التاريخية. تُسجَّل القيم التي تخرق خط الأساس كمشكلات.

يمكن أيضًا إنشاء المشكلات من خلال التنبيهات التي نتلقاها من حلول قابلية الملاحظة وتتبع الأخطاء.

graph TB
    R["Cloud resource"] -->|"telemetry"| B["Compare with its baselines"]
    A["Alert from an observability<br/>or error tracking provider"] --> I
    B -->|"breaks the baseline"| I["Issue"]
    B -->|"within the baseline"| N["No issue"]
    style I fill:#fee2e2,stroke:#fca5a5,color:#7f1d1d

كيف نحل المشكلات؟

لحل مشكلة، نحتاج إلى:

  1. الفرز: جمع البيانات لتأكيد المشكلة أو رفضها.
  2. التحقيق: تقييم فرضيات متعددة وتحديد السبب الجذري.
  3. فتح طلب pull request أو التصعيد. إما كتابة طلب pull request لحل المشكلة، أو التصعيد إلى مهندس إذا لم يكن الإصلاح تغييرًا في الكود، أو كتابة تقرير يوضّح النتائج ثم التوقف.

تنتهي الغالبية العظمى من الجولات قبل الوصول إلى طلب pull request أو تصعيد، عندما يخلص Polylane إلى أن المشكلة إما إيجابية زائفة أو غير ضارة.

graph TB
    I["Triage the issue"] --> V["Investigate the root cause"]
    V -->|"dismiss, fold, or reopen"| E["Report and stop"]
    V -->|"confirm"| X["Open a pull request"]
    V -->|"confirm, no code change"| Z["Escalate to an engineer"]
    style V fill:#fef3c7,stroke:#fcd34d,color:#78350f
    style X fill:#d1fae5,stroke:#6ee7b7,color:#065f46

معمارية الوكلاء الفرعيين

كانت معماريتنا الأولية مبنية على عدة وكلاء فرعيين.

graph TB
    F["Finding"] --> T["Triage agent"]
    T -->|"confirm"| C["Orchestrator agent"]
    C -->|"hypotheses"| W["Fan-out workflow"]
    W --> H1["Hypothesis agent 1"]
    W --> H2["Hypothesis agent 2"]
    W --> H3["Hypothesis agent ..."]
    W --> H15["Hypothesis agent 15"]
    H1 --> AG["Vote and summarize"]
    H2 --> AG
    H3 --> AG
    H15 --> AG
    AG -->|"summary as a message"| C
    C -->|"confirmed hypothesis"| AF["Coding agent"]
    AF --> PR["Pull request"]
    style C fill:#fee2e2,stroke:#fca5a5,color:#7f1d1d
    style AG fill:#fee2e2,stroke:#fca5a5,color:#7f1d1d

استخدمنا وكيلًا منسّقًا لتنسيق العمل بين وكلاء فرعيين متعددين. كانت الوكلاء الفرعيون يُنشَؤون للتحقق من الفرضيات، محاولين إثبات أو دحض كل فرضية بنقاط انطلاق مختلفة (على سبيل المثال البدء بالنظر إلى السجلات، أو البدء بقاعدة الكود، وما إلى ذلك).

كانت نتائج الوكلاء الفرعيين تُصوَّت عليها بحساب بسيط ثم تُلخَّص من قبل وكيل آخر قبل رفعها إلى الوكيل المنسّق.

كان حكم كل فرضية عبارة عن تصويت مرجّح بالثقة عبر جولاتها.

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.

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

graph TB
    I["Issue"] --> O["Orchestrator<br/>splits the issue into briefs"]
    O -->|"brief A"| A["Sub-agent A<br/>investigates brief A"]
    O -->|"brief B"| B["Sub-agent B<br/>investigates brief B"]
    A -.->|"summary A"| M["Orchestrator, later<br/>writes the plan from the summaries"]
    B -.->|"summary B"| M
    M -.->|"plan"| F["Coding agent<br/>writes the fix"]
    F --> PR["Pull request"]
    style I fill:none,stroke:none
    style PR fill:none,stroke:none
    style O stroke:#4C8C57,color:#3d7046
    style A stroke:#3b7dd8,color:#2b5fa8
    style B stroke:#7c5cd6,color:#5b3fb0
    style M stroke:#a07d1c,color:#7a5f14
    style F stroke:#6b7280,color:#4b5563
    %% aside O right #4C8C57 Issue, alerts and history
    %% aside A left #3b7dd8 Brief A and its evidence
    %% aside B right #7c5cd6 Brief B and its evidence
    %% aside M right #a07d1c History and two summaries, no evidence
    %% aside F right #6b7280 The plan, nothing else

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

اخترنا هذه المعمارية أساسًا لأنه عندما صمّمناها لأول مرة في مارس 2026، لم تكن النماذج المتقدمة قادرة دائمًا على إجراء تحقيق كامل من البداية إلى النهاية، بما في ذلك كتابة الإصلاح، وبصراحة، لم تكن بنيتنا التقنية جيدة إلى ذلك الحد.

أصبح البناء على هذا الأساس أصعب باطراد للأسباب التالية:

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

كررنا العمل على هذه المعمارية لأشهر، محسّنين كل وكيل على حدة ومعدّلين المطالبات وعمليات التسليم، لكن النتائج لم تضاهِ الجهد المبذول أبدًا. كانت مكلفة، وصعبة التصحيح، ولم تكن طلبات pull request جيدة بما يكفي.

وكيل واحد

في 3 سبتمبر، استبدلنا مسار عمل الوكلاء الفرعيين بوكيل واحد ينجز كل شيء من الفرز إلى طلب pull request. يقوم الوكيل نفسه بجمع الأدلة، والتحقق من كل الفرضيات، وتقديم طلب pull request.

graph TB
    F["Issue"] --> A["Single agent"]
    A --> I["Triage"]
    I --> B["Investigate"]
    B --> V["One verdict"]
    V -->|"confirm"| X["Clone, edit, validate in the sandbox"]
    X --> PR["Open the pull request"]
    V -->|"dismiss, fold, or reopen"| E["Report and stop"]
    V -->|"confirm, no code change"| Z["Escalate to an engineer"]
    style A fill:#d1fae5,stroke:#6ee7b7,color:#065f46
    style PR fill:#d1fae5,stroke:#6ee7b7,color:#065f46

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

والأهم من ذلك، تحسّنت جودة طلبات pull request، وانهار الوقت الفاصل بين اكتشاف مشكلة وفتح طلب pull request الذي يحلّها. خلال الشهر المحيط بنقطة التحول، انخفض الوسيط من 2.2 ساعة إلى 35 دقيقة، وانخفضت النسبة المئوية التسعون من تسعة أيام إلى أقل من ساعتين. في ظل خط الأنابيب، كانت النتيجة عادةً تبقى راكدة لساعات عديدة في سلسلة من مسارات العمل، كل واحد ينتظر السابق. أما في ظل الوكيل الواحد، فيُفتح طلب pull request بعد دقائق فقط من اكتشاف المشكلة.

15 دقيقة 1 ساعة 4 ساعات 1 يوم 4 أيام أسبوعان 14 Aug 18 Aug 22 Aug 26 Aug 30 Aug 3 Sep 7 Sep 11 Sep 3 سبتمبر: وكيل واحد 30 min
الوسيط اليومي الوسيط اليومي to p90 Axis not to scale
الشكل 1
الساعات من اكتشاف مشكلة إلى فتح طلب pull request الخاص بها

نفتح الآن أيضًا عددًا أكبر بكثير من طلبات pull request. عندما كنا نستخدم الوكلاء الفرعيين، انتهت 0.6% من المشكلات المكتشفة بطلب pull request. في ظل الوكيل الواحد، تبلغ النسبة 4.2%، وهي في ازدياد. الفارق يكمن في أنماط الفشل. يجب على خط أنابيب متعدد الوكلاء الفرعيين أن ينجو من كل عملية تسليم: يجب على الفرز أن يرقّي النتيجة، ويجب على المنسّق أن ينتج فرضيات، ويجب على مرحلة التفرّع أن تصل إلى حكم، وهكذا. كل عملية تسليم هي نمط فشل محتمل.

0% 2% 4% 6% 8% 10% 14 Aug 18 Aug 22 Aug 26 Aug 30 Aug 3 Sep 7 Sep 11 Sep 3 سبتمبر: وكيل واحد 9.1%
الشكل 2
نسبة المشكلات المكتشفة التي انتهت بطلب pull request

انخفضت التكلفة لكل طلب pull request أيضًا. تبدأ كل جولة الآن بالنموذج الأقوى، لذا فإن النتيجة المرفوضة تكلّف أكثر مما كانت تكلّفه في ظل خط الأنابيب. لكن متوسط التكلفة لكل طلب pull request انخفض من $111 إلى نحو $18 في أول تسعة أيام من الوكيل الواحد. هذا الانخفاض ليس نتيجة حصرية لتغيير المعمارية، إذ إننا نعمل باستمرار على تحسين كل جانب من جوانب Polylane. جزء منه هو الانحراف الطبيعي لنظام يخضع لتحسين مستمر.

$1 $10 $100 $1,000 14 Aug 18 Aug 22 Aug 26 Aug 30 Aug 3 Sep 7 Sep 11 Sep 3 سبتمبر: وكيل واحد $2.88
Logarithmic scale
الشكل 3
تكلفة النموذج لكل طلب pull request مفتوح
كل القيم وراء الرسوم الثلاثة، حسب اليوم بتوقيت 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

لا تبنِ وكلاء فرعيين

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

لا ينبغي لأحد أن يكون في المناوبة عام 2026. تراقب Polylane بنيتك التحتية، وتحقق، وتصلح ما يتعطل.

انضم إلى قائمة الانتظار