Dashboard
14 września 2026

Subagenty są po prostu błędne

Explore with AI

Agent Autofix w Polylane pierwotnie był zbudowany jako przepływ złożony z wielu subagentów, z których każdy odpowiadał za jedno zadanie:

  • agent triażu przesiewający tysiące alertów i sygnałów
  • koordynator zarządzający subagentami
  • do piętnastu subagentów badających wszystkie możliwe hipotezy dotyczące problemu
  • oraz agent kodujący, który ostatecznie otwiera 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

Potwierdzony problem mógł uruchomić nawet 18 agentów i subagentów zaangażowanych w dochodzenie i poprawkę. Dziś tę samą pracę wykonuje jeden agent.

Ten przepływ był rozsądnym rozwiązaniem, biorąc pod uwagę ówczesną wiedzę: małe prompty, małe zestawy narzędzi, orkiestrator przekazujący wyniki między specjalistami. Był jednak niezwykle kosztowny oraz trudny w obsłudze i analizie.

Czym jest problem?

Polylane skanuje logi, metryki i ślady z każdego zasobu chmurowego (funkcja Lambda, Cloudflare Worker, projekt Vercel itd.) u każdego podłączonego dostawcy. Dla każdego zasobu przechowujemy baseline’y obejmujące wiele horyzontów czasowych, dzięki czemu możemy uchwycić sezonowość w danych. Następnie oceniamy bieżący stan zasobu chmurowego i porównujemy go z danymi historycznymi. Wartości, które przekraczają baseline, są rejestrowane jako problemy.

Problemy mogą też powstawać z alertów otrzymywanych z rozwiązań do obserwowalności i śledzenia błędów.

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

Jak rozwiązujemy problemy?

Aby rozwiązać problem, musimy:

  1. Triaż: zbierz dane, żeby potwierdzić lub odrzucić problem.
  2. Zbadaj: oceń wiele hipotez i ustal przyczynę źródłową.
  3. Otwórz pull request albo eskaluj. Napisz pull request, który rozwiąże problem, eskaluj do inżyniera, jeśli poprawka nie jest zmianą kodu, albo napisz raport z ustaleniami i zakończ.

Zdecydowana większość przebiegów kończy się jeszcze przed pull requestem czy eskalacją, gdy Polylane uzna, że problem jest fałszywym alarmem albo jest niegroźny.

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

Architektura oparta na subagentach

Nasza pierwotna architektura opierała się na wielu subagentach.

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

Używaliśmy agenta orkiestrującego do koordynowania pracy wielu subagentów. Subagenty tworzono, aby badały hipotezy, próbując potwierdzić lub obalić każdą z nich z różnych punktów startowych (na przykład zaczynając od przeglądu logów albo od kodu bazowego).

Ustalenia subagentów poddawano prostemu głosowaniu arytmetycznemu i podsumowywano przez kolejnego agenta, zanim trafiały do agenta koordynującego.

Werdykt dla każdej hipotezy powstawał z głosowania ważonego pewnością w poszczególnych przebiegach.

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 };
}

Gdy zbadano już wszystkie hipotezy, jeśli udało się ustalić przyczynę źródłową, koordynator eskalował do inżyniera albo pisał plan poprawki. Ten plan trafiał do kodującego subagenta, który wdrażał poprawkę i otwierał pull request.

Głównym problemem tej architektury jest utrata kontekstu między subagentami. Każdy agent przekazywał w górę lub w dół tylko fragmenty swojego kontekstu, w formie podsumowań, przez co agenci na obu końcach regularnie musieli powtarzać tę samą pracę.

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

Co więcej, agent piszący poprawkę dysponował wyłącznie planem, bez kontekstu na temat pierwotnego problemu czy dowodów zebranych podczas dochodzenia. Prowadziło to do słabszych pull requestów, które często skupiały się na objawach, a nie na przyczynie źródłowej.

Wybraliśmy tę architekturę przede wszystkim dlatego, że kiedy projektowaliśmy ją po raz pierwszy w marcu 2026 roku, najbardziej zaawansowane modele nie zawsze potrafiły przeprowadzić dochodzenie od początku do końca, łącznie z napisaniem poprawki, a poza tym, mówiąc wprost, nasz harness nie był wtedy zbyt dobry.

Budowanie na tym fundamencie stawało się coraz trudniejsze, ponieważ:

  • Debugowanie wymagało wielu śladów. Prześledzenie całego dochodzenia od początku do końca wymagało przejrzenia wielu, czasem kilkudziesięciu śladów.
  • Ewaluacja odbywała się etapami. Każdy subagent mógł zostać oceniony osobno i przejść ocenę pozytywnie, mimo że wynik końcowy był słaby, bo błędy tkwiły w przekazaniach między agentami.

Iterowaliśmy nad tą architekturą przez miesiące, ulepszając każdego agenta z osobna i dopracowując prompty oraz przekazania, ale efekty nigdy nie były proporcjonalne do włożonego wysiłku. Było to kosztowne, trudne do debugowania, a pull requesty nie były wystarczająco dobre.

Jeden agent

3 września zastąpiliśmy przepływ oparty na subagentach jednym agentem, który robi wszystko: od triażu aż po pull request. Ten sam agent zbiera dowody, bada wszystkie hipotezy i otwiera 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

Usunęliśmy agenta triażu, koordynatora, agentów hipotez oraz agenta Autofix, razem z przepływami przekazującymi wyniki między nimi i bramkami stojącymi przed pull requestem. Korzyści operacyjne pojawiły się natychmiast: jeden ślad do oceny i brak podsumowań między subagentami.

Co ważniejsze, poprawiła się jakość pull requestów, a czas między wykryciem problemu a otwarciem pull requesta, który go naprawia, gwałtownie się skrócił. W miesiącu wokół tej zmiany mediana spadła z 2.2 godziny do 35 minut, a p90 z dziewięciu dni do mniej niż dwóch godzin. W pipeline ustalenie zwykle czekało wiele godzin w łańcuchu przepływów, z których każdy czekał na poprzedni. Przy jednym agencie pull request otwiera się już kilka minut po wykryciu problemu.

15 min 1 godz. 4 godz. 1 dzień 4 dni 2 tyg. 14 Aug 18 Aug 22 Aug 26 Aug 30 Aug 3 Sep 7 Sep 11 Sep 3 wrz: jeden agent 30 min
Mediana dzienna Mediana dzienna to p90 Axis not to scale
Rysunek 1
Liczba godzin od wykrycia problemu do otwarcia jego pull requesta

Teraz otwieramy też dużo więcej pull requestów. Gdy korzystaliśmy z subagentów, 0.6% wykrytych problemów kończyło się pull requestem. Przy jednym agencie jest to 4.2%, i ten odsetek rośnie. Różnica leży w trybach awarii. Pipeline z wieloma subagentami musi przetrwać każde przekazanie: triaż musi przepuścić ustalenie dalej, koordynator musi wygenerować hipotezy, fan-out musi dojść do werdyktu itd. Każde przekazanie to potencjalny tryb awarii.

0% 2% 4% 6% 8% 10% 14 Aug 18 Aug 22 Aug 26 Aug 30 Aug 3 Sep 7 Sep 11 Sep 3 wrz: jeden agent 9.1%
Rysunek 2
Odsetek wykrytych problemów zakończonych pull requestem

Koszt na pull request też spadł. Każdy przebieg teraz zaczyna się na mocniejszym modelu, więc odrzucone ustalenie kosztuje więcej niż w pipeline. Mimo to średni koszt na pull request spadł z $111 do około $18 w ciągu pierwszych dziewięciu dni działania jednego agenta. Ten spadek nie wynika wyłącznie ze zmiany architektury, bo aktywnie iterujemy nad każdym aspektem Polylane. Część z niego to zwykły dryf systemu poddawanego ciągłej iteracji.

$1 $10 $100 $1,000 14 Aug 18 Aug 22 Aug 26 Aug 30 Aug 3 Sep 7 Sep 11 Sep 3 wrz: jeden agent $2.88
Logarithmic scale
Rysunek 3
Koszt modelu na jeden otwarty pull request
Wszystkie wartości stojące za trzema wykresami, w podziale na dni UTC
DzieńWykryte problemy z pull requestemMedianap90Wydatek na 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 · jeden agent 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 · do 16:00 8.2% 30 min 55 min $2.88

Nie buduj subagentów

  • Przekazania tracą więcej, niż oszczędzają. Każde podsumowanie przekazywane między agentami to kontekst, którego kolejny agent już nigdy nie pozna. Agent, który zbiera dowody, powinien być tym, który na ich podstawie działa.
  • Oceniaj przebieg, nie agentów. Ewaluacje poszczególnych agentów mogą przechodzić pozytywnie, podczas gdy cały system zawodzi, bo błędy tkwią między agentami.
  • Trzymaj jeden ślad na przebieg. Błędną decyzję rozproszoną po kilkunastu śladach trzeba tłumaczyć całe popołudnie. W jednym śladzie wystarczy przewinięcie.

W 2026 nikt nie powinien mieć dyżuru. Polylane obserwuje twoją infrastrukturę, prowadzi dochodzenia i naprawia to, co się psuje.

Dołącz do listy oczekujących