Dashboard
14. September 2026

Sub-Agents sind einfach falsch

Explore with AI

Polylanes Autofix-Agent war ursprünglich als Workflow aus mehreren Sub-Agents aufgebaut, jeder zuständig für eine einzelne Aufgabe:

  • eine Triage, um Tausende von Alerts und Signalen zu sichten
  • ein Coordinator, der die Sub-Agents steuert
  • bis zu fünfzehn Sub-Agents, um alle möglichen Hypothesen zum Problem zu untersuchen
  • und ein Coding-Agent, der schließlich einen Pull Request einreicht

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

Ein bestätigtes Issue konnte bis zu 18 Agents und Sub-Agents für die Untersuchung und den Fix nach sich ziehen. Heute erledigt dieselbe Arbeit ein einziger Agent.

Der Workflow war ein sinnvolles Design auf Basis dessen, was wir zu der Zeit wussten: kleine Prompts, kleine Tool-Sets, ein Orchestrator, der Ergebnisse zwischen Spezialisten trägt. Er war jedoch extrem teuer und schwer zu betreiben und nachzuvollziehen.

Was ist ein Issue?

Polylane scannt Logs, Metriken und Traces von jeder Cloud-Ressource (Lambda-Funktion, Cloudflare Worker, Vercel-Projekt usw.) bei jedem verbundenen Provider. Für jede Ressource speichern wir Baselines über mehrere Zeithorizonte, sodass wir Saisonalität in den Daten erfassen können. Anschließend bewerten wir den aktuellen Zustand der Cloud-Ressource und vergleichen ihn mit historischen Daten. Werte, die die Baseline durchbrechen, werden als Issues erfasst.

Issues können auch durch Alerts entstehen, die wir von Observability- und Error-Tracking-Lösungen erhalten.

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

Wie lösen wir Issues?

Um ein Issue zu lösen, müssen wir:

  1. Triage: Daten sammeln, um das Issue zu bestätigen oder zu verwerfen.
  2. Untersuchen: Mehrere Hypothesen bewerten und den Root Cause bestimmen.
  3. Einen Pull Request öffnen oder eskalieren. Entweder einen Pull Request schreiben, der das Issue löst, an einen Engineer eskalieren, wenn der Fix keine Codeänderung ist, oder einen Bericht mit den Erkenntnissen schreiben und stoppen.

Die überwiegende Mehrheit der Runs endet vor einem Pull Request oder einer Eskalation, wenn Polylane zu dem Schluss kommt, dass das Issue entweder ein False Positive oder harmlos ist.

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

Sub-Agents-Architektur

Unsere ursprüngliche Architektur basierte auf mehreren Sub-Agents.

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

Wir nutzten einen Orchestrator-Agent, um die Arbeit zwischen mehreren Sub-Agents zu koordinieren. Sub-Agents wurden instanziiert, um Hypothesen zu untersuchen und sie mit unterschiedlichen Ausgangspunkten zu beweisen oder zu widerlegen (zum Beispiel beginnend mit den Logs oder beginnend mit der Codebase usw.).

Die Erkenntnisse der Sub-Agents wurden mit einfacher Arithmetik abgestimmt und von einem weiteren Agent zusammengefasst, bevor sie an den Coordinator-Agent weitergereicht wurden.

Das Urteil jeder Hypothese war eine konfidenzgewichtete Abstimmung über ihre Durchläufe.

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

Sobald alle Hypothesen untersucht waren, hat der Coordinator, falls ein Root Cause identifiziert wurde, entweder an einen Engineer eskaliert oder einen Plan für den Fix geschrieben. Dieser Plan wurde an einen Coding-Sub-Agent übergeben, um den Fix umzusetzen und den Pull Request einzureichen.

Das Hauptproblem dieser Architektur ist der Kontextverlust zwischen den Sub-Agents. Jeder Agent gab nur Bruchteile seines Kontexts nach oben oder unten weiter, durch Zusammenfassung, und sowohl vor- als auch nachgelagerte Agents mussten regelmäßig dieselbe Arbeit wiederholen.

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

Zudem hatte der Agent, der den Fix schrieb, nur den Plan zur Verfügung, ohne Kontext zum ursprünglichen Issue oder den in der Untersuchung gesammelten Beweisen. Das führte zu mittelmäßigen Pull Requests, die sich häufig auf Symptome statt auf den Root Cause konzentrierten.

Wir haben uns für diese Architektur vor allem deshalb entschieden, weil die führenden Modelle, als wir sie im März 2026 entworfen haben, nicht immer in der Lage waren, eine Untersuchung von Anfang bis Ende durchzuführen, einschließlich des Schreibens des Fixes, und ganz einfach gesagt, unser Harness war auch noch nicht so gut.

Es wurde zunehmend schwerer, auf dieser Grundlage weiterzubauen, weil:

  • Debugging viele Traces erforderte. Um eine End-to-End-Untersuchung nachzuvollziehen, mussten mehrere, teils Dutzende von Traces durchgesehen werden.
  • Evaluation pro Stufe erfolgte. Jeder Sub-Agent konnte einzeln evaluiert werden und bestehen, während das Endergebnis schlecht war, weil die Fehler in den Übergaben lagen.

Wir haben diese Architektur monatelang iteriert, jeden Agent einzeln verbessert und an Prompts und Übergaben gefeilt, aber die Ergebnisse haben den Aufwand nie gerechtfertigt. Es war teuer, schwer zu debuggen, und die Pull Requests waren nicht gut genug.

Ein einzelner Agent

Am 3. September haben wir den Sub-Agents-Workflow durch einen einzigen Agent ersetzt, der alles von der Triage bis zum Pull Request übernimmt. Derselbe Agent sammelt die Beweise, untersucht alle Hypothesen und reicht den Pull Request ein.

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

Wir haben den Triage-Agent, den Coordinator, die Hypothesen-Agents und den Autofix-Agent gelöscht, zusammen mit den Workflows, die Ergebnisse zwischen ihnen transportierten, und den Gates, die vor dem Pull Request standen. Die betrieblichen Vorteile zeigten sich sofort: ein Trace zum Auswerten, keine Zusammenfassungen zwischen Sub-Agents mehr.

Wichtiger noch: Die Qualität der Pull Requests hat sich verbessert, und die Zeit zwischen der Erkennung eines Issues und der Eröffnung des Pull Requests, der es behebt, ist deutlich gesunken. Im Monat rund um den Wechsel sank der Median von 2,2 Stunden auf 35 Minuten und das p90 von neun Tagen auf unter zwei Stunden. Unter der Pipeline blieb ein Finding typischerweise mehrere Stunden lang in einer Kette von Workflows liegen, jeder wartend auf den vorherigen. Beim einzelnen Agent öffnet sich der Pull Request nur Minuten nach der Erkennung des Issues.

15 Min 1 Std 4 Std 1 Tag 4 Tage 2 Wo 14 Aug 18 Aug 22 Aug 26 Aug 30 Aug 3 Sep 7 Sep 11 Sep 3. Sep: ein Agent 30 min
Tagesmedian Tagesmedian to p90 Axis not to scale
Abbildung 1
Stunden von der Erkennung eines Issues bis zur Eröffnung seines Pull Requests

Wir eröffnen jetzt auch deutlich mehr Pull Requests. Als wir Sub-Agents einsetzten, endeten 0,6 % der erkannten Issues in einem Pull Request. Beim einzelnen Agent sind es 4,2 %, Tendenz steigend. Der Unterschied liegt in den Fehlermodi. Eine Pipeline mit mehreren Sub-Agents muss jede Übergabe überstehen: Die Triage muss das Finding weiterreichen, der Coordinator muss Hypothesen erzeugen, der Fan-out muss zu einem Urteil kommen usw. Jede Übergabe ist ein potenzieller Fehlermodus.

0% 2% 4% 6% 8% 10% 14 Aug 18 Aug 22 Aug 26 Aug 30 Aug 3 Sep 7 Sep 11 Sep 3. Sep: ein Agent 9.1%
Abbildung 2
Anteil der erkannten Issues, die in einem Pull Request endeten

Auch die Kosten pro Pull Request sind gesunken. Jeder Run startet jetzt mit dem stärkeren Modell, sodass ein verworfenes Finding mehr kostet als unter der Pipeline. Aber die durchschnittlichen Kosten pro Pull Request sind in den ersten neun Tagen des einzelnen Agents von $111 auf etwa $18 gefallen. Dieser Rückgang ist nicht ausschließlich das Ergebnis der Architekturänderung, da wir aktiv an jedem Aspekt von Polylane iterieren. Ein Teil davon ist die gewöhnliche Drift eines Systems unter ständiger Iteration.

$1 $10 $100 $1,000 14 Aug 18 Aug 22 Aug 26 Aug 30 Aug 3 Sep 7 Sep 11 Sep 3. Sep: ein Agent $2.88
Logarithmic scale
Abbildung 3
Modellkosten pro eröffnetem Pull Request
Alle Werte hinter den drei Diagrammen, nach UTC-Tag
TagErkannte Issues mit Pull RequestMedianp90Kosten pro 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 · ein 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 · bis 16:00 8.2% 30 min 55 min $2.88

Baue keine Sub-Agents

  • Übergaben verlieren mehr, als sie sparen. Jede Zusammenfassung, die zwischen Agents weitergegeben wird, ist Kontext, den der nächste Agent nie haben wird. Der Agent, der die Beweise sammelt, sollte der Agent sein, der danach handelt.
  • Bewerte den Run, nicht die Agents. Evaluationen pro Agent bestehen, während das System versagt, weil die Fehler zwischen den Agents liegen.
  • Behalte einen Trace pro Run. Eine falsche Entscheidung, verteilt über ein Dutzend Traces, braucht einen Nachmittag, um erklärt zu werden. In einem Trace reicht ein Scrollen.

2026 sollte niemand mehr On-Call sein. Polylane beobachtet deine Infrastruktur, untersucht und repariert, was kaputtgeht.

Auf die Warteliste