Dashbord
14. september 2026

Underagenter er rett og slett feil

Explore with AI

Polylanes autofiks-agent ble opprinnelig bygget som en arbeidsflyt med flere underagenter, hver med ansvar for én enkelt oppgave:

  • en triage for å sortere gjennom tusenvis av varsler og signaler
  • en koordinator for å styre underagentene
  • opptil femten underagenter for å undersøke alle mulige hypoteser for issuet
  • og en kodeagent for til slutt å sende inn en 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

Et bekreftet issue kunne føre til opptil 18 agenter og underagenter for undersøkelsen og fiksen. I dag gjøres det samme arbeidet av én enkelt agent.

Arbeidsflyten var et fornuftig design basert på det vi visste den gangen: små prompts, små verktøysett, en orkestrator som fraktet resultater mellom spesialister. Den var imidlertid ekstremt kostbar og vanskelig å drifte og resonnere rundt.

Hva er et issue?

Polylane skanner logger, metrikker og tracer fra hver skyressurs (Lambda-funksjon, Cloudflare Worker, Vercel-prosjekt, osv.) hos hver tilkoblede leverandør. For hver ressurs lagrer vi baselines på tvers av flere tidshorisonter, slik at vi kan fange opp sesongvariasjoner i dataene. Vi vurderer så skyressursens nåværende tilstand og sammenligner den med historiske data. Verdier som bryter baselinen, registreres som issues.

Issues kan også opprettes av varsler vi mottar fra observerbarhets- og feilsporingsløsninger.

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

Hvordan løser vi issues?

For å løse et issue må vi:

  1. Triagere: Samle inn data for å bekrefte eller avkrefte issuet.
  2. Undersøke: Vurdere flere hypoteser og fastslå rotårsaken.
  3. Åpne en pull request eller eskalere. Enten skrive en pull request for å løse issuet, eskalere til en ingeniør hvis fiksen ikke er en kodeendring, eller skrive en rapport som fremhever funnene og stoppe.

De aller fleste kjøringer avsluttes før en pull request eller en eskalering, når Polylane konkluderer med at issuet enten er en falsk positiv eller ufarlig.

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

Underagentarkitektur

Vår opprinnelige arkitektur var basert på flere underagenter.

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

Vi brukte en orkestratoragent for å koordinere arbeidet mellom flere underagenter. Underagenter ble instansiert for å undersøke hypoteser, og forsøkte å bevise eller avkrefte hver hypotese med forskjellige utgangspunkt (for eksempel ved å starte med loggene, eller ved å starte med kodebasen, osv.).

Funnene fra underagentene ble stemt over med enkel aritmetikk og oppsummert av en annen agent før de ble rullet opp til koordinatoragenten.

Konklusjonen for hver hypotese var en konfidensvektet avstemning på tvers av kjøringene dens.

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

Når alle hypotesene var undersøkt, og en rotårsak var identifisert, ville koordinatoren enten eskalere til en ingeniør eller skrive en plan for fiksen. Denne planen ble så overlevert til en kodende underagent for å implementere fiksen og sende inn pull requesten.

Hovedproblemet i denne arkitekturen er konteksttapet mellom underagentene. Hver agent sendte bare brøkdeler av konteksten sin opp eller ned, gjennom oppsummering, og både oppstrøms- og nedstrømsagenter måtte jevnlig gjøre om det samme arbeidet.

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

I tillegg hadde agenten som skrev fiksen, bare planen, og manglet kontekst om det opprinnelige issuet eller bevisene som var samlet inn under undersøkelsen. Dette førte til pull requests av lavere kvalitet, som ofte fokuserte på symptomer i stedet for rotårsaken.

Vi valgte denne arkitekturen primært fordi da vi først designet den i mars 2026, var ikke frontier-modellene alltid i stand til å kjøre en undersøkelse fra ende til annen, inkludert å skrive fiksen, og enkelt og greit, vår harness var heller ikke god nok.

Det ble stadig vanskeligere å bygge videre på dette fundamentet fordi:

  • Feilsøking krevde mange tracer. Å resonnere rundt en undersøkelse fra ende til annen krevde å gå gjennom flere, noen ganger dusinvis av tracer
  • Evalueringen var per steg. Hver underagent kunne evalueres individuelt og bestå, mens sluttresultatet var dårlig, fordi feilene lå i overleveringene.

Vi itererte på denne arkitekturen i flere måneder, forbedret hver agent individuelt og finjusterte promptene og overleveringene, men resultatene sto aldri i forhold til innsatsen. Det var kostbart, vanskelig å feilsøke, og pull requestene var ikke gode nok.

Én agent

  1. september erstattet vi arbeidsflyten med underagenter med én enkelt agent som gjør alt fra triage til pull request. Den samme agenten samler inn bevisene, undersøker alle hypotesene og sender inn pull requesten.

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

Vi slettet triage-agenten, koordinatoren, hypoteseagentene og autofiks-agenten, sammen med arbeidsflytene som fraktet resultater mellom dem og sperrene som lå foran pull requesten. De driftsmessige fordelene var umiddelbare: én trace å evaluere, og ingen oppsummeringer mellom underagenter.

Enda viktigere: kvaliteten på pull requestene ble bedre, og tiden mellom oppdagelse av et issue og åpning av pull requesten som fikser det, ble kraftig redusert. I løpet av måneden rundt overgangen gikk medianen fra 2.2 timer til 35 minutter, og p90 fra ni dager til under to timer. Under pipelinen lå et funn typisk i flere timer i en kjede av arbeidsflyter, hver ventende på den forrige. Under den enkelte agenten åpnes pull requesten bare minutter etter at issuet er oppdaget.

15 min 1 t 4 t 1 d 4 d 2 uker 14 Aug 18 Aug 22 Aug 26 Aug 30 Aug 3 Sep 7 Sep 11 Sep 3. sep: én agent 30 min
Daglig median Daglig median to p90 Axis not to scale
Figur 1
Timer fra oppdagelse av et issue til åpning av pull requesten

Vi åpner nå også langt flere pull requests. Da vi brukte underagenter, endte 0.6% av oppdagede issues i en pull request. Under den enkelte agenten er det 4.2%, og økende. Forskjellen ligger i feilmodusene. En pipeline med flere underagenter må overleve hver overlevering: triage må fremme funnet, koordinatoren må produsere hypoteser, fan-out-en må komme frem til en konklusjon, osv. Hver overlevering er en potensiell feilmodus.

0% 2% 4% 6% 8% 10% 14 Aug 18 Aug 22 Aug 26 Aug 30 Aug 3 Sep 7 Sep 11 Sep 3. sep: én agent 9.1%
Figur 2
Andel oppdagede issues som endte i en pull request

Kostnaden per pull request falt også. Hver kjøring starter nå på den sterkere modellen, så et avvist funn koster mer enn det gjorde under pipelinen. Men gjennomsnittskostnaden per pull request gikk fra $111 til rundt $18 i løpet av de første ni dagene med den enkelte agenten. Dette fallet er ikke utelukkende et resultat av arkitekturendringen, ettersom vi aktivt itererer på alle aspekter av Polylane. Noe av det er den vanlige driften i et system under konstant iterasjon.

$1 $10 $100 $1,000 14 Aug 18 Aug 22 Aug 26 Aug 30 Aug 3 Sep 7 Sep 11 Sep 3. sep: én agent $2.88
Logarithmic scale
Figur 3
Modellforbruk per åpnet pull request
Alle verdiene bak de tre grafene, per UTC-dag
DagOppdagede issues med en pull requestMedianp90Forbruk per 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 · én 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 · til 16:00 8.2% 30 min 55 min $2.88

Ikke bygg underagenter

  • Overleveringer taper mer enn de sparer. Hver oppsummering som sendes mellom agenter, er kontekst den neste agenten aldri vil ha. Agenten som samler inn bevisene, bør være agenten som handler på dem.
  • Evaluer kjøringen, ikke agentene. Per-agent-evalueringer består mens systemet feiler, fordi feilene ligger mellom agentene.
  • Behold én trace per kjøring. En feil beslutning spredt over et dusin tracer tar en ettermiddag å forklare. I én trace tar det et scroll.

Ingen burde ha vakt i 2026. Polylane følger med på infrastrukturen din, undersøker og fikser det som går i stykker.

Bli med på ventelisten