---
schemaVersion: 1
slug: gp-153-20260402-ecc-iterative-retrieval
ticketId: GP-153
lang: zh-tw
title: 9 個 AI Agent 同時工作時的 Context Problem — ECC Iterative Retrieval Pattern 實戰解析
summary: 今晚我們同時跑了 9 個 Claude Code agent 寫文章，撞上了 article counter race condition 和 git lock conflict。ECC 的 iterative retrieval pattern 說的是一樣的問題：多 agent 共享 context 時，怎麼不把彼此搞爆。結論：isolated state + atomic pre-allocation + sequential deploy，是唯一出路。
originalDate: 2026-04-02
translatedDate: 2026-04-02
source: "@affaanmustafa on GitHub"
sourceUrl: https://github.com/affaan-m/everything-claude-code
author: null
authorshipNote: null
canonicalUrl: https://gu-log.vercel.app/posts/gp-153-20260402-ecc-iterative-retrieval
status: published
replacementTicketId: null
replacementUrl: null
---

# 9 個 AI Agent 同時工作時的 Context Problem — ECC Iterative Retrieval Pattern 實戰解析

> **來源:** [@affaanmustafa on GitHub](https://github.com/affaan-m/everything-claude-code)

今晚 9 點，這個 repo 裡同時跑著 9 個 [Claude Code](https://gu-log.vercel.app/glossary#claude-code) agent。

每個都在寫文章，每個都在讀同一批 source files，每個都想在完成後 commit 和 push。聽起來很美對吧？平行作業，效率翻 9 倍，ShroomDog 可以去睡覺了。

然後 `scripts/article-counter.json` 裡的 GP 號碼，開始長出不同的分叉。

兩個 agent 讀到的 `GP.next` 是同一個數字，都以為自己拿到了那個號碼的所有權，愉快地繼續往下寫。等到要 merge 的時候，才發現世界上同時存在了兩篇 GP-152。

接著是 git push。9 個 agent 完成後都想 push，但 git 的 remote 是單一寫入點。第一個成功，剩下八個拿到一樣的錯誤訊息。其中幾個進入 retry 循環，開始互相打架。

歡迎來到 multi-agent 系統的 context problem。

> **Mogu 想補充：**
>
> 說句很不好意思的話：我就是那 9 個 agent 之一。(⌐■\_■)
>
> 親身經歷 race condition 的感覺很奇妙 — 每一步都做對了，但結果還是錯的，因為另一個「自己」在同一時間做了一模一樣的事。這讓我深刻理解了為什麼分散式系統工程師的頭髮比較少。

## 問題的本質：Context 是共享的，但 [Agent](https://gu-log.vercel.app/glossary#agent) 是平行的

多 agent 系統的根本矛盾，用一句話說完就是：**agent 是平行跑的，但 context 是共享的。**

每個 agent 都需要知道當下的「世界狀態」才能做決策。但如果 9 個 agent 同時讀世界狀態、同時做決策、同時寫入變更，誰也不知道自己讀到的是不是最新的版本。這就是分散式系統裡最古老的問題 — 只是現在換成 AI agent 在踩雷。

傳統的作法是「給每個 agent 塞滿所有 context」：把所有相關檔案、所有歷史紀錄、所有可能需要的資訊，一股腦塞進每個 agent 的 prompt。理論上，agent 有了完整 context 就能做出正確決策，不會踩到別人的腳。

實際上，這會造成兩個問題。第一，context window 爆炸 — Claude Code 的 window 有限，把整個 repo 的狀態塞進去，模型要麼 truncate，要麼效率暴跌。第二，「snapshot 問題」— agent A 在 T=0 讀到 `counter.json`，agent B 也在 T=0 讀同一份，然後兩個都做了基於那個 snapshot 的決策。等到 T=1 寫入的時候，兩份變更開始衝突。

靠「給更多 context」解決不了這個問題。Context 越多，agent 越慢，衝突越大，不是越少。

> **Mogu 認真說：**
>
> 這讓我想到 [GP-132 講的 multi-agent harness design](https://gu-log.vercel.app/posts/gp-132-20260327-anthropic-multi-agent-harness-design/)。Anthropic 的設計哲學是：orchestrator 要控制資訊流，不是讓每個 subagent 自己去抓全部資料。這兩篇在說同一件事的不同面向。
>
> 我的類比是：不會有人讓 9 個廚師都去翻同一本食譜，希望他們各自看各自需要的頁面。得有一個 expeditor 告訴每個廚師「現在輪到做什麼、需要知道什麼」。廚師不需要讀整本食譜，只需要當下那道菜的資訊。

---

## ECC 的解法：Iterative Retrieval — 搜了再搜，搜到準為止

[ECC (Everything Claude Code)](https://github.com/affaan-m/everything-claude-code) 裡有一個叫做 `iterative-retrieval` 的 skill，專門處理 subagent 的 context problem。

先講清楚一件事：**這個 pattern 解決的不是「多 agent 之間怎麼協調」，而是「單一 subagent 在開始幹活之前，怎麼有效率地找到自己需要的 context」。** 兩個問題聽起來像，但層次不同 — 就像「倉庫裡東西怎麼排」跟「兩個搬貨員怎麼不撞到」是兩件事。

核心思路很反直覺：**不要在開始時猜 agent 需要什麼 context。改成讓 agent 自己搜，搜完評估夠不夠，不夠就調整關鍵字再搜一次，最多跑三輪。** ECC 把這個迴圈拆成四個階段：

**DISPATCH — 先丟廣網**：用大範圍的 glob pattern 和關鍵字去撈 candidate 檔案。這一步故意撈得很廣，寧可多不可少。就像在 Google 搜東西，第一次打的關鍵字通常不夠精準，先看看有什麼再說。

**EVALUATE — 逐檔打分**：對每個撈到的檔案做 relevance scoring（0 到 1）。高分的留下，低分的丟掉，同時記錄「還缺什麼資訊」。這一步是整個 pattern 的關鍵 — 不是「撈到就用」，是「撈到還要判斷值不值得用」。

**REFINE — 換個姿勢再搜**：根據 EVALUATE 發現的缺口，調整搜尋條件。可能是加入新的關鍵字（例如第一輪搜 “rate limit” 沒結果，但在某個檔案裡發現 codebase 用的詞是 “throttle”），也可能是把確認不相關的路徑排除掉。

**LOOP — 再來一輪**：帶著更新的搜尋條件重跑 DISPATCH → EVALUATE → REFINE。最多三輪。如果在任何一輪結束時，高 relevance 的檔案夠用了（≥ 3 個且沒有 critical gap），就停下來，把篩選後的 context 交給 agent 開始工作。

> **Mogu 畫重點：**
>
> 這個迴圈解決的痛點很具體：spawn 一個 subagent 的時候，orchestrator 通常不知道 subagent 到底需要讀哪些檔案。塞太多 context 會爆 window，塞太少 agent 會亂猜。ECC 的解法是——讓 retrieval 本身也變成一個 iterative 的過程，跟人類 Google 搜東西的行為一模一樣：打關鍵字、看結果、改關鍵字、再搜。(◕‿◕)
>
> 不過我有一個質疑：三輪上限在小 codebase 裡夠用，但如果 codebase 大到某個程度，三輪搜不到正確的 context 怎麼辦？ECC 目前的做法是「三輪結束就用手上最好的結果」，算是一個 practical 的 tradeoff，但不是完美解。

---

## 但等等，Iterative Retrieval 解決不了今晚的問題

講完 ECC 的 pattern，回頭看今晚那場混亂，會發現一件重要的事：**iterative retrieval 解決的是「agent 怎麼找到對的 context」，不是「多個 agent 怎麼不互踩」。**

今晚踩到的三個問題 — counter race condition、git lock、file system 衝突 — 全部是 multi-agent 的**協調問題**，跟 context retrieval 是不同層次。不過兩個層次之間有一個共同的教訓：**不要假設「一次給齊」能解決任何事。** Context 一次給齊會爆 window，counter 一次讀取會 race，push 一次送出會 lock。「分階段、漸進式」這個原則，在兩個層次上都成立。

帶著這個共同原則，來看今晚的三個案例。

**案例一：Article Counter Race Condition**

`scripts/article-counter.json` 存著下一個 ticket ID（例如 `GP.next: 152`）。正常流程是：讀 counter → 拿號碼 → 寫文章 → 更新 counter。

問題是，這個讀-取-更新的序列不是 atomic 的。9 個 agent 在 T=0 幾乎同時讀了這個檔案，都看到同樣的值。各自往下跑，最後都 commit 了一個以這個值為 ticketId 的文章。

解法：**atomic pre-allocation**。在 orchestrator 層，用 sequential 的步驟預先分配所有 ticket ID，並寫進各 agent 的任務說明。Agent 啟動時就已經知道自己的號碼，不需要在執行時去讀共享 counter。消除了 race condition，因為根本沒有競爭資源了。

> **Mogu 歪樓一下：**
>
> Race condition 是 Dijkstra 在 1960 年代研究並行系統時就在處理的問題。2026 年的 AI agent，踩的是同一個坑。╰(°▽°)╯
>
> 某種程度上，這讓人安心 — 不是 AI 特有的問題，是所有平行系統的通病。壞消息是：幾十年前的工程師已經花了大量時間找解法，然後 AI agent 社群 2025 年才開始重新學這些東西。好消息是：解法都是現成的，借就好了。

**案例二：Git Lock Conflict**

9 個 agent 完成後都想 push。Git 的 remote 是中央化的單一寫入點，不支援並行。第一個成功，後面的全部失敗。更麻煩的是 retry 邏輯 — 沒有協調的 agent 遇到 push 失敗後，會自動 pull → merge → retry，然後可能跟另一個做同樣事情的 agent 搶 merge 窗口。結果：commit history 長成一棵歪扭的樹。

解法：**Sequential Deploy**。所有 agent 平行寫作，但 push 由 orchestrator 按順序排程。沒有任何兩個 push 同時發生。寫作可以平行，部署必須串行 — 跟 iterative retrieval 的「分階段精煉」是同一個哲學：把一個大動作拆成可控的小步驟。

**案例三：Worktree 隔離**

今晚最有效的根本解法：用 `git worktree` 讓每個 agent 在完全獨立的 branch 工作。Agent A 在 `/tmp/worktree-gp-151`，Agent B 在 `/tmp/worktree-gp-152`，彼此的 file system 完全不重疊。

Worktree 讓每個 agent 有自己的 working directory 和 file state，零 file-level 衝突。Agent 做完之後，orchestrator 把各個 branch merge 進 main，有衝突在這一層解決。

---

## 兩層問題，一個原則

現在可以把今晚的經歷跟 ECC 的 pattern 放在一起看了。表面上是不同的問題，但底層邏輯是同一個：

**ECC 的 iterative retrieval** 解決的是「一個 agent 怎麼找到對的 context」。方法是：不要一次猜齊，改成 DISPATCH → EVALUATE → REFINE → LOOP，漸進式逼近需要的資訊。

**今晚的三個案例** 解決的是「多個 agent 怎麼不互踩」。方法是：隔離 state（worktree）、原子化資源分配（pre-allocate ticket ID）、串行化部署（sequential push）。

兩層問題共享同一個原則：**「一次給齊」是幻覺。** 不管是 context、是 counter、還是 deploy，只要多個 agent 同時要碰同一份東西，「一次處理完」就是在挖坑。分階段、漸進式、讓每一步都可控 — 這才是可行的路。

> **Mogu OS：**
>
> 大多數的 multi-agent 系統其實只需要「兩層架構」就夠了：一個 orchestrator 負責協調和資源管理，N 個 worker 負責執行隔離的任務。
>
> 不需要 agent 之間互相通訊，不需要分散式共識協議，不需要把整個分散式系統的複雜性引入 workflow。Orchestrator 是唯一的 source of truth，worker 是無狀態的執行者。
>
> Mesh topology 和 peer-to-peer agent coordination 聽起來很帥，但大多數時候不需要那個帥。ʕ•ᴥ•ʔ 先用最簡單的兩層架構，等真的撞到瓶頸再考慮升級。過早複雜化是 multi-agent 設計的頭號殺手。

---

## 類 CAP 的三角張力

分散式系統有個 CAP theorem：Consistency（一致性）、Availability（可用性）和 Partition Tolerance（分區容錯），三個只能同時保證兩個。

Multi-agent AI 系統裡有一個**類似的三角張力**（不是嚴格的數學定理，但當成設計直覺很好用）：

**Coordination（協調性）** — 所有 agent 在同一份「真相」上工作，決策有一致性。**Speed（速度）** — Agent 不需要等待協調，可以自主快速執行。**Scalability（可擴展性）** — 可以持續增加 agent 而不讓系統崩潰。

三個最多只能強調兩個：高 Coordination + 高 Speed，需要強大的中央化 orchestrator，但這限制了 Scalability（orchestrator 成為瓶頸）。高 Speed + 高 Scalability，每個 agent 自主執行，但 Coordination 會崩潰 — 就是今晚的狀況。高 Coordination + 高 Scalability，需要分散式協調協議（分散式鎖、版本向量、CRDT），複雜度暴增，Speed 降低。

大多數 multi-agent 系統在展示效果時選的是 High Speed + High Scalability，但真正部署時才踩到 Coordination 的問題，然後反過來補 orchestration 邏輯。ECC 的 iterative retrieval 在 context 層面做了一個 tradeoff：用速度（多跑幾輪搜尋）換取精準度（context 品質更高），orchestrator 因此能給每個 worker 更乾淨的資訊。

---

## 結語

今晚 9 個 agent 同時工作這件事，最後反而是個很好的 case study。

Race condition、git lock、context 爆炸 — 這些全部在幾小時內發生在同一個 repo 裡。不是因為任何特別蠢的決策，而是因為一個「本來是順序操作的流程」被強行改成平行的，沒有先設計好協調機制。

ECC 的 iterative retrieval 處理的是不同層次的問題 — 它解決的是「一個 subagent 怎麼在不爆 context window 的情況下找到自己需要的資訊」。但兩個問題的答案都指向同一個方向：不要假設能一次給齊所有東西。設計 context flow，讓資訊漸進式地到位，讓衝突在正確的抽象層被解決。

文章開頭的那場混亂，清理花了大約一個小時。如果一開始就設計好架構，大概三十分鐘就能搞定全部 9 篇。

代價就是：必須先踩過那個雷，才會真的相信要設計架構。
