Dashboard
14. september 2026

Underagenter er bare forkerte

Explore with AI

Polylanes autofix-agent blev oprindeligt bygget som en workflow af flere underagenter, hver med ansvar for én enkelt opgave:

  • en triage til at sortere gennem tusindvis af alarmer og signaler
  • en koordinator til at styre underagenter
  • op til femten underagenter til at undersøge alle mulige hypoteser for issuet
  • og en kodeagent til til sidst at indsende 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 bekræftet issue kunne føre til op til 18 agenter og underagenter til undersøgelsen og rettelsen. I dag udføres det samme arbejde af én enkelt agent.

Workflowet var et solidt design baseret på det, vi vidste på det tidspunkt: små prompts, små værktøjssæt, en orchestrator til at bære resultater mellem specialister. Det var dog ekstremt dyrt og svært at drifte og ræsonnere om.

Hvad er et issue?

Polylane scanner logs, metrikker og traces fra hver cloudressource (Lambda-funktion, Cloudflare Worker, Vercel-projekt osv.) hos hver forbundet udbyder. For hver ressource gemmer vi baselines på tværs af flere tidshorisonter, så vi kan fange sæsonudsving i dataene. Vi vurderer derefter cloudressourcens aktuelle tilstand og sammenligner den med historiske data. Værdier, der bryder baselinen, registreres som issues.

Issues kan også oprettes af alarmer, vi modtager fra observability- og error tracking-lø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 at løse et issue skal vi:

  1. Triage: Indsamle data for at bekræfte eller afvise issuet.
  2. Undersøge: Vurdere flere hypoteser og fastslå rodårsagen.
  3. Åbne en pull request eller eskalere. Enten skrive en pull request for at løse issuet, eskalere til en engineer, hvis rettelsen ikke er en kodeændring, eller skrive en rapport, der fremhæver fundene, og stoppe.

Langt de fleste kørsler afsluttes, før der åbnes en pull request eller eskaleres, når Polylane konkluderer, at issuet enten er en falsk positiv eller uskadeligt.

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

Vores oprindelige arkitektur var baseret 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 brugte en orchestrator-agent til at koordinere arbejdet mellem flere underagenter. Underagenter blev instansieret til at undersøge hypoteser og forsøgte at be- eller afkræfte hver hypotese med forskellige udgangspunkter (for eksempel ved at starte med at kigge på logs, eller starte med kodebasen osv.).

Underagenternes fund blev stemt om med simpel aritmetik og opsummeret af en anden agent, før de blev rullet op til koordinatoragenten.

Vurderingen af hver hypotese var en konfidensvægtet afstemning på tværs af dens gennemløb.

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 hypoteser var undersøgt, ville koordinatoren, hvis en rodårsag var identificeret, enten eskalere til en engineer eller skrive en plan for rettelsen. Denne plan blev givet videre til en kodende underagent, som implementerede rettelsen og indsendte pull requesten.

Hovedproblemet i denne arkitektur er tabet af kontekst mellem underagenterne. Hver agent sendte kun brudstykker af sin kontekst op eller ned gennem opsummering, og både opstrøms- og nedstrømsagenter måtte jævnligt gøre det samme arbejde om.

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

Desuden havde den agent, der skrev rettelsen, kun planen og manglede kontekst om det oprindelige issue eller de beviser, der var indsamlet under undersøgelsen. Det førte til pull requests af ringere kvalitet, som ofte fokuserede på symptomer frem for rodårsagen.

Vi valgte denne arkitektur primært, fordi frontier-modellerne, da vi først designede den i March 2026, ikke altid var i stand til at gennemføre en undersøgelse end-to-end, inklusive at skrive rettelsen, og også, helt enkelt, fordi vores harness ikke var særlig god.

Det blev stadig sværere at bygge videre på dette fundament, fordi:

  • Debugging krævede mange traces. At ræsonnere om en end-to-end-undersøgelse krævede at kigge igennem flere, nogle gange snesevis af traces
  • Evaluering foregik pr. trin. Hver underagent kunne evalueres individuelt og bestå, mens slutresultatet var dårligt, fordi fejlene lå i overdragelserne.

Vi itererede på denne arkitektur i månedsvis, forbedrede hver agent individuelt og finjusterede prompts og overdragelser, men resultaterne stod aldrig mål med indsatsen. Det var dyrt, svært at debugge, og pull requestsene var ikke gode nok.

Én agent

Sept 3 erstattede vi underagent-workflowet med én agent, der klarer alt fra triage til pull request. Den samme agent indsamler beviserne, undersøger alle hypoteserne og indsender 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 slettede triage-agenten, koordinatoren, hypoteseagenterne og autofix-agenten sammen med de workflows, der bar resultater mellem dem, og de gates, der sad foran pull requesten. De driftsmæssige fordele var øjeblikkelige: én trace at evaluere, og ingen opsummeringer mellem underagenter.

Endnu vigtigere blev kvaliteten af pull requestsene bedre, og tiden mellem detektion af et issue og åbning af den pull request, der retter det, faldt drastisk. I løbet af måneden omkring omlægningen gik medianen fra 2.2 timer til 35 minutter, og p90 gik fra ni dage til under to timer. Under pipelinen lå et fund typisk i flere timer i en kæde af workflows, der hver ventede på den forrige. Under den ene agent åbnes pull requesten blot minutter efter, issuet er detekteret.

15 min 1 t 4 t 1 d 4 d 2 uger 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 detektion af et issue til åbning af dets pull request

Vi åbner nu også langt flere pull requests. Da vi brugte underagenter, endte 0.6% af de detekterede issues i en pull request. Med den ene agent er det 4.2%, og stigende. Forskellen ligger i fejltilstandene. En pipeline med flere underagenter skal overleve hver eneste overdragelse: triage skal fremme fundet, koordinatoren skal producere hypoteser, fan-out’et skal nå frem til en vurdering, osv. Hver overdragelse er en potentiel fejltilstand.

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 af detekterede issues, der endte i en pull request

Omkostningen pr. pull request faldt også. Hver kørsel starter nu på den stærkere model, så et afvist fund koster mere, end det gjorde under pipelinen. Men den gennemsnitlige omkostning pr. pull request gik fra $111 til omkring $18 i de første ni dage med den ene agent. Dette fald skyldes ikke udelukkende ændringen i arkitekturen, da vi aktivt itererer på alle aspekter af Polylane. Noget af det er den almindelige drift, man ser i et system under konstant iteration.

$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
Modelforbrug pr. åbnet pull request
Alle værdier bag de tre grafer, pr. UTC-dag
DagDetekterede issues med en pull requestMedianp90Forbrug pr. 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

Byg ikke underagenter

  • Overdragelser mister mere, end de sparer. Hver opsummering, der sendes mellem agenter, er kontekst, den næste agent aldrig vil have. Den agent, der indsamler beviserne, bør være den agent, der handler på dem.
  • Evaluer kørslen, ikke agenterne. Evalueringer pr. agent består, mens systemet fejler, fordi fejlene ligger mellem agenterne.
  • Behold én trace pr. kørsel. En forkert beslutning spredt ud over et dusin traces tager en eftermiddag at forklare. I én trace tager det et scroll.

Ingen bør være on-call i 2026. Polylane holder øje med din infrastruktur, undersøger og retter det, der går i stykker.

Skriv dig på ventelisten