Claude CodeAI Agent多 agentWindowsPowerShell自動化

一晚派 10 個 Claude Code agent 把電腦塞死之後,我們改成這樣排

· · 15 分鐘閱讀
目錄
  1. 那一晚發生了什麼?
  2. 機器明明很強,為什麼會塞死?
  3. 現在的規則是什麼?
  4. 6 這個數字是怎麼來的?
  5. 派工迴圈怎麼寫?
  6. 看門程式怎麼寫?
  7. 工人做完怎麼自清?
  8. 卡死了怎麼救?

同時跑多個 Claude Code agent,先卡住的通常不是 CPU,是每個 agent 各自跑的 npm ci、build 和 headless Chrome 一起搶同一顆磁碟和記憶體。我們一次派 10 個把桌機塞到連 cmd.exe 都開不起來,之後改成:同時最多 6 個、啟動錯開 2 分鐘、派工前看暫停旗標、看門程式量到超標就自動插旗、工人做完自己刪 node_modules 和工作樹。

那一晚發生了什麼?

11 月 3 日凌晨 5 點,我們一次派出 10 個 Claude Code 工人,全部是 headless 的 claude -p:4 個在產品 repo,6 個在營運 repo,做健檢、補功能、送開源 PR。每個工人在自己的 git worktree 裡 npm ci、跑 build,做完再開 headless Chrome 走一遍頁面。

工人照樣在產出,7 點多還收到第一批 7 份報告,但機器其實已經不對勁了:5:49 起,git commit 卡住不動。接著症狀一個個冒出來:

  • 指揮的那個 Claude 視窗,Bash 和 PowerShell 工具連 echo、Get-Date 都 30 分鐘不回。命令其實有執行,只是 shell 結束不了。
  • 工作排程器裡的排程一個個被終止,結果碼 267014(0x41306,工作被終止)。
  • 每兩小時一次的巡邏腳本,05:05、07:05、09:05 三輪一筆紀錄都沒有。
  • 迴圈的心跳檔最後一次更新停在 08:06。

10 點 40 分左右我們才判斷出來:整台機器已經開不了新程序。這時候能做的只剩重開機,一直拖到晚上 18:50 左右才重開。重開之後 git 還報 index file bad signature,要重建 index 才能 commit。

機器明明很強,為什麼會塞死?

這台是 96GB 記憶體、RTX 3080 的桌機,C 槽和 D 槽在同一顆 NVMe 上。隔天凌晨跑 4 個工人的時候量過:CPU 10%、可用記憶體 68GB、磁碟佇列 0。單看這些數字,10 個應該跑得動。

說白了,agent 本身不重。Claude Code 程序大部分時間在等模型回應,重的是它幫你跑的那些指令。我們的推測是這樣:

  1. 磁碟:10 個工人在同一段時間寫 node_modules(每份幾萬個小檔)、寫建置輸出,再各開一個 Chrome,全部打在同一顆 NVMe 上。
  2. 記憶體承諾:分頁檔固定 8.7GB,可承諾的上限大約是實體記憶體加分頁檔,約 104GB。node 建置和 Chrome 都會先預留大量承諾額度,就算實體記憶體還有空,承諾額度先用完,新程序一樣開不起來。

為什麼說推測?因為卡死的當下,連查都沒辦法查。還有一個我們沒解釋清楚的點:巡邏腳本在 01:35 就出現過一次 spawnSync cmd.exe ETIMEDOUT,比派工還早。所以能確定的是觸發條件(同時 10 個,而且每個都在做重 I/O),但不敢說它是唯一原因。

現在的規則是什麼?

五條,每一條都對應到那一晚的一個症狀:

  1. 同時最多 6 個工人,穩定跑一陣子再放到 8。上限看的是工人會跑什麼指令,不是 CPU 核心數。
  2. 啟動錯開 2 分鐘。工人剛啟動的前幾分鐘多半就是在 npm ci,錯開啟動等於把裝套件錯開,不讓 10 份 node_modules 同時寫。
  3. 派工前看暫停旗標。旗標檔存在就不派,已經在跑的照跑。
  4. 看門程式自動插旗。可用記憶體低於 8GB、磁碟佇列連續 3 次超過 8,或 claude.exe 超過 8 個,任一成立就寫旗標、發告警;全部回到門檻內,再拿掉自己插的旗。
  5. 工人做完自清。派工 prompt 最後一段固定寫:刪自己的 node_modules、git worktree remove 自己的工作樹。

6 這個數字是怎麼來的?

不是精算出來的。重開機之後我們先保守壓到 4 個;隔天凌晨量到 CPU 10%、可用記憶體 68GB、磁碟佇列 0,資源一直很空,就放寬到 6,再讓看門程式兜底。

上限還是 4、5 個的時候,我們另外規定會 npm ci、build、開瀏覽器的重活最多 3 個同時跑。放到 6 之後,主要靠錯開啟動把重活岔開。讀程式碼、寫報告、整理 PR 說明這類工人很輕,多幾個沒關係。

要放到 8 的條件也先講好:看門程式一段時間都沒插旗,排程也沒再出現 267014。

老實說,寫這篇的同一晚,我們自己就把工人開到了 9 個,看門程式第一次試跑就判定超標(claude.exe 9 個,門檻 8)。規則寫下來跟真的守住是兩回事,這也是為什麼插旗要交給程式,不能靠人自律。

派工迴圈怎麼寫?

下面是 Git Bash 版的最小派工迴圈,照佇列順序派,同時數量和錯開時間都是參數:

#!/usr/bin/env bash
# dispatch.sh:照順序派工,同時最多 MAX 個,每個錯開 STAGGER 秒
MAX=6
STAGGER=120
QUEUE=/c/work/queue          # 一個 prompt 一個 .md
LOGS=/c/work/logs
PAUSE_FLAG=/c/ops/DISPATCH_PAUSE

count_claude() { tasklist //FI "IMAGENAME eq claude.exe" //NH | grep -ci 'claude.exe'; }
BASE=$(count_claude)          # 指揮自己和其他已開的視窗,不算工人

for prompt in "$QUEUE"/*.md; do
  tag=$(basename "$prompt" .md)
  while [ -f "$PAUSE_FLAG" ] || [ $(( $(count_claude) - BASE )) -ge "$MAX" ]; do
    sleep 60
  done
  ( cd /c/work/repo && claude -p --model opus < "$prompt" > "$LOGS/$tag.log" 2>&1 ) &
  echo "$(date +%H:%M) dispatched $tag"
  sleep "$STAGGER"
done
wait

幾個細節:在我們的環境,一個 claude -p 工人對應一個 claude.exe,換環境請先自己數一次。tasklist 在 Git Bash 裡要寫 //FI、//NH,單斜線會被當成路徑轉換。子殼裡每次都 cd 到絕對路徑,因為背景子殼的工作目錄不保證是你以為的那個,我們吃過 build 跑錯 repo 的虧。

看門程式怎麼寫?

我們自己用的版本是 Node 寫的,量測交給 PowerShell 的 Get-Counter,工作排程器每 5 分鐘叫一次,11 月 4 日凌晨才上線,還沒有真的插過旗。下面是精簡後的等價 PowerShell 版,門檻和行為一樣,拿去用之前請自己先測:

# resource-guard.ps1:量三個數字,超標就寫暫停旗標
$flag  = 'C:\ops\DISPATCH_PAUSE'
$state = 'C:\ops\guard-diskq-hits.txt'

$os     = Get-CimInstance Win32_OperatingSystem
$freeGB = $os.FreePhysicalMemory / 1MB      # 單位是 KB,除 1MB 得 GB
$queue  = (Get-Counter '\PhysicalDisk(_Total)\Current Disk Queue Length').CounterSamples[0].CookedValue
$claude = @(Get-Process claude -ErrorAction SilentlyContinue).Count

# 磁碟佇列要連續 3 次超標才算,單次尖峰不理
$hits = 0
if (Test-Path $state) { $hits = [int](Get-Content $state) }
if ($queue -gt 8) { $hits++ } else { $hits = 0 }
Set-Content $state $hits

$why = @()
if ($freeGB -lt 8) { $why += ('free memory {0:N1} GB' -f $freeGB) }
if ($hits -ge 3)   { $why += "disk queue > 8 for $hits checks" }
if ($claude -gt 8) { $why += "claude.exe x $claude" }

if ($why.Count -gt 0) {
  if (-not (Test-Path $flag)) {
    Set-Content $flag ('resource-guard: ' + ($why -join '; '))
    # 這裡接你自己的告警:Telegram bot、Discord webhook、寄信都可以
  }
} elseif ((Test-Path $flag) -and (Get-Content $flag -Raw).StartsWith('resource-guard')) {
  Remove-Item $flag      # 全部回到門檻內:只拿掉自己插的旗,人手動放的不動
}

註冊排程:

schtasks /Create /TN ResourceGuard /SC MINUTE /MO 5 /TR "powershell -NoProfile -ExecutionPolicy Bypass -File C:\ops\resource-guard.ps1"

旗標分兩種:看門程式插的,指標回穩就自己拿掉;人手動放的(例如要維修),程式不碰,一定要人刪。我們的版本另外記告警時間,同一段暫停只發一次告警,免得半夜一直響。另外兩個小坑:這樣註冊每 5 分鐘會閃一個主控台視窗,我們是外面再包一層 VBS 隱藏;腳本裡如果有中文字串,存檔要用含 BOM 的 UTF-8,Windows PowerShell 5.1 才不會讀成亂碼。

工人做完怎麼自清?

10 個工作樹各帶一份 node_modules,每份幾百 MB 到一兩 GB,不清的話 C 槽幾天就滿。我們把清理寫進派工 prompt 的最後一段,工人自己做:

WT=/c/work/wt-$tag
git -C /c/work/repo worktree add -b "$tag" "$WT" origin/main
# ……工人在 $WT 裡工作……
rm -rf "$WT/node_modules"
git -C /c/work/repo worktree remove "$WT"

git worktree remove 如果因為有未追蹤的檔案失敗,先看 git status 是什麼,別為了過關直接加 --force。

有一個真的會出事的例外:如果工作樹的 node_modules 是用 mklink /J 接到主目錄的 junction,rm -rf 和 git worktree remove --force 都會穿過去,把主目錄那份一起刪光。兩個月內我們至少踩了四次。要共用套件就用絕對路徑呼叫主目錄的工具,別建 junction。

卡死了怎麼救?

先認症狀,越早認出來越好救:

  • 工具呼叫或 shell 很久不回,但命令其實有跑。
  • 排程結果碼 267014。
  • log 出現 spawnSync cmd.exe ETIMEDOUT。
  • git commit 卡住。

shell 還能動的話,先停工人、留指揮:用 Get-CimInstance Win32_Process 列出 claude.exe,對建立時間找出工人,再用 Stop-Process -Id 指定 PID 殺。別用命令列比對 tag 一次殺光,執行這條指令的 shell 命令列裡也有同一個 tag,會連自己一起殺掉。

連新程序都開不了的話,只能重開機。重開之後 git 報 bad signature,照這樣重建 index:

mv .git/index .git/index.bak && git reset

git reset 會照 HEAD 重建 index,工作目錄的檔案不會動。

這套規則和一張顯卡給好幾個 Claude 排隊的渲染鎖是同一個想法:機器資源有限,就讓 agent 排隊,不要讓它們自己搶。


我們把這類多 agent 的實戰紀錄都寫在部落格,連搞砸的也寫。想看 agent 協作實際有幾個在動、花多少,可以接著讀多 agent 協作的真實規模和成本怎麼查。

常見問題

同時跑多個 Claude Code agent,上限要怎麼抓?

看 agent 會跑什麼指令,不是看 agent 本身。Claude Code 程序大部分時間在等模型回應,很輕;重的是它幫你跑的 npm ci、build、headless Chrome。我們在 96GB 記憶體、單顆 NVMe 的桌機上,10 個同時做重 I/O 會塞死,現在定成同時 6 個、啟動錯開 2 分鐘,穩定一陣子再放到 8。

怎麼在機器塞死之前自動停止派工?

用一個暫停旗標檔。派工迴圈每派一個之前先看旗標在不在;另一支看門程式由工作排程器每 5 分鐘跑一次,量可用記憶體、磁碟佇列長度和 claude.exe 數量,超標就寫旗標並發告警,全部回到門檻內再把自己插的旗拿掉。磁碟佇列要連續 3 次超標才算,避免單次尖峰誤停。

Windows 卡死重開機之後 git 顯示 index bad signature,怎麼修?

index 檔在寫入途中被中斷,內容壞了。把 .git/index 改名移走,再跑 git reset,git 會照 HEAD 重建 index,工作目錄的檔案不會被動到,只是原本 staged 的變更會變回未 staged。如果是 worktree,index 在 .git/worktrees/<名稱>/index。

每週 AI 自動化實戰筆記

不廢話,只有能直接用的東西。Prompt 模板、自動化 SOP、技術拆解。

加入一人公司實驗室

免費資源包、每日建造日誌、可以對話的 AI Agent。一群用 AI 武裝自己的獨立開發者社群。

想自己動手試試?

UltraProbe 免費、免註冊,掃一次網站就知道 Google 與 AI 找不找得到你。