儀表板
2026年9月14日

Sub-agents are just wrong

Explore with AI

Polylane 的 autofix 代理原本是由多個子代理組成的工作流程建構而成,每個子代理各自負責一項任務:

  • 一個分流代理,用來從數千個警示與訊號中篩選
  • 一個協調代理,用來操控子代理
  • 最多十五個子代理,用來調查問題所有可能的假設
  • 以及一個程式碼代理,最終提交 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 function、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 年 3 月首次設計時,前沿模型並非總能端到端完成一次調查,包含寫出修復方案,而且坦白說,我們當時的執行機制也還沒那麼成熟。

在這個基礎上繼續建構,變得越來越困難,因為:

  • **除錯需要查看許多追蹤紀錄。**要推敲一次端到端的調查,往往需要查看多個,有時甚至是數十個追蹤紀錄
  • **評估是逐階段進行的。**每個子代理可以個別通過評估,但最終結果卻很差,因為問題就出在交接環節。

我們針對這個架構迭代了好幾個月,逐一改進每個代理,並調整提示與交接方式,但成果始終配不上所付出的努力。它成本高昂、難以除錯,pull request 的品質也不夠好。

單一代理

9 月 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

我們刪除了分流代理、協調代理、假設子代理,以及 autofix 代理,連同在它們之間傳遞結果的工作流程,還有擋在 pull request 前面的關卡也一併移除。營運上的效益立即浮現:只需評估一條追蹤紀錄,子代理之間也不再有摘要。

更重要的是,pull request 的品質提升了,從偵測到問題到開出修復用的 pull request 之間的時間也大幅縮短。在轉換前後那個月,中位數從 2.2 小時降到 35 分鐘,p90 則從九天降到不到兩小時。在舊管線下,一項發現通常會在一連串彼此等待的工作流程中卡上數小時。在單一代理下,pull request 在偵測到問題後幾分鐘內就會開出。

15 分鐘 1 小時 4 小時 1 天 4 天 2 週 14 Aug 18 Aug 22 Aug 26 Aug 30 Aug 3 Sep 7 Sep 11 Sep 9 月 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 9 月 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 9 月 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 監看你的基礎設施、進行調查,並修復壞掉的東西。

加入候補名單