iT邦幫忙

2026 iThome 鐵人賽

DAY 21
0
AI Engineering

128GB 統一記憶體的三十天:DGX Spark 地端 LLM 與生成式 AI 部署實戰系列 第 21

Day 21|herdr 是地端 AI 同時跑多個 AI agent 的最佳解

  • 分享至 

  • xImage
  •  

https://ithelp.ithome.com.tw/upload/images/20260903/20141816MZmna0saFK.png

agent 時代的新型浪費

三部曲前兩天各讓一個 harness 開了工,今天先講一個比較少人警告過的問題。

agent 工作的節奏跟寫程式完全不同,你交代一件事,它跑三到十分鐘,中間可能停下來等你確認一個高風險動作,然後繼續。單一 agent 時這叫喝咖啡時間,三個 agent 同時跑的時候是災難,A 在等你審查已經等了八分鐘你不知道,B 早就跑完了你也不知道,C 卡在錯誤裡空轉,而你正盯著 D 終端機看它慢慢吐字。agent 時代的浪費不是算力閒置,是人這個最重要的審查節點呢,其注意力排程失敗。

tmux 解決「很多終端機」的問題,但它不知道每個窗格裡的 agent 現在是什麼狀態。今天的主角 herdr 就是衝著這個缺口來的,一個 agent 感知的終端機多工器。

herdr 是什麼 ?

Rust 寫的單一執行檔,定位很簡單,一個知道你的 agent 們在幹嘛的 tmux。它認得 22 種 agent CLI,自動偵測每個窗格裡的 agent 處於工作中、等待輸入、還是已完成,把狀態彙整成一個看板,該你出手的時候通知你。

先修正一件事,免得你照著清單去設定。herdr 0.8.2 的實際支援清單是:pi, claude, codex, gemini, cursor, devin, agy, cline, omp, mastracode, opencode, copilot, kimi, kiro, droid, amp, grok, hermes, kilo, qodercli, qwen, maki。Claude Code、opencode、Hermes Agent 都在,dsh 不在。這件事等一下會變成今天最有用的對照組。

放回 Day 19 的三層地圖,它站在指揮層,MCP 是插座、harness 是電器,herdr 是那塊讓你一眼看見哪台電器在跳電的配電盤。

安裝 herdr

安裝是一行,官方腳本抓對應平台的單一執行檔放進 ~/.local/bin

curl -fsSL https://herdr.dev/install.sh | sh

沒有 Rust 工具鏈的依賴,這台機器上連 cargo 都沒有,照樣跑。更新走 herdr update,它讀 herdr.dev/latest.json 比對版本;今天實測 0.8.2 就是最新版,herdr update 回「already up to date (0.8.2)」。另有 preview channel 可以用 herdr channel set preview 切過去。

啟動後的第一印象是它不像 tmux。左側一條常駐側邊欄,上半是工作區(spaces),下半是 agent 清單,每個 agent 前面一個狀態記號;右邊才是窗格。快捷鍵沿用 tmux 的肌肉記憶,prefix 一樣是 ctrl+bprefix+v 垂直分割、prefix+minus 水平分割、prefix+b 收合側邊欄、prefix+w 開工作區選單、prefix+z 放大窗格。設定檔在 ~/.config/herdr/config.toml,寫錯鍵不會靜默吃掉,herdr config check 會明講「unknown config key ...; ignoring key」,介面上也會跳一條提示。

真正讓今天這篇能全自動測試的,是它三個對 agent 友善的設計,這在同類工具裡很少見:

  • herdr --skill 直接吐出一份給 agent 用的技能檔,講清楚什麼時候該用哪個子指令。
  • herdr api snapshot 給出整個 session 的 JSON 狀態,herdr api schema 是 255 KB 的完整 schema。
  • herdr workspace|tab|pane|agent|notification 一整組 socket API,開窗格、起 agent、送提示、等狀態、讀輸出全部可腳本化。

底下的三窗格與二十分鐘實驗,沒有一個步驟是手動點的,均為全自動腳本。

https://ithelp.ithome.com.tw/upload/images/20260903/20141816tsM01Pkx7N.png

狀態偵測準不準

在三個窗格分別跑 opencode、dsh 與 qwen-code(原本想用 Hermes Agent,但它的 CLI 這台機器上沒有,只有 NAS 上的服務端,qwen 同樣在支援清單裡,而且已經接著地端閘道),交代三個不同的任務,每兩秒抓一次 herdr agent list,同時在閘道側記錄每一個請求的起訖時間。這樣就有兩份獨立的紀錄可以對,一份是 herdr 說的,一份是實際發生的。

595 個取樣點的結果:

agent 與閘道實況一致 不一致的部分
opencode 99.1% working 但沒請求 4 點、idle 但有請求 1 點
qwen-code 99.0% working 但沒請求 3 點、idle 但有請求 3 點
dsh 不適用 595 點全部 unknown,從不出現在 herdr agent list

不一致的全部是單點,來自兩秒取樣對不上狀態轉換的邊界。原本擔心的「agent 在長輸出時被判成等待」完全沒有發生。這個準確度還是在沒有裝任何整合外掛的情況下測到的,這點稍微講一下,herdr integration status 顯示這台機器上 claude、codex、antigravity-cli 的整合已安裝,opencode 與 qwen 沒有。也就是說上面那兩個 99% 是純終端機輸出的啟發式判讀,不是 agent 主動回報。

https://ithelp.ithome.com.tw/upload/images/20260903/20141816tLTq196j2j.png

但它有一個真正的漏判,而且代價不小。opencode 讀專案目錄外的檔案時會跳一個 external_directory 確認框,herdr 不認得這個框。問它為什麼,顯示出來這樣:

$ herdr agent explain a1
agent: opencode
state: idle
rule: none
fallback_reason: default_known_agent_idle_fallback

沒有規則命中,於是退回「已知的 agent,那就當作 idle」。看板顯示 idle,實際上在等人按 Enter。我第一次跑這個實驗就這樣空轉了四分鐘,直到去翻 opencode 自己的日誌才看到 message=asking permission=external_directory。最後是寫了一段監看 opencode 日誌的腳本去回答那個框,因為看板這條路走不通。

順帶更正一個容易誤會的地方。herdr 預設的狀態指示器是 dots,只有顏色:灰色空心是 idle、琥珀色是 working、粉紅色是 blocked。所以「黃燈」在 herdr 裡代表的是正在工作,不是在等你審查,等審查是粉紅色。要讓狀態不靠顏色也讀得出來,設 ui.status_indicators = "symbols",就會變成 idle、 working、× blocked,本篇主圖用的就是這個。

通知有沒有用

這部分比想像中安靜。[ui.toast] delivery 預設是 off,也就是預設不跳任何彈出通知;可選 herdr(介面內浮出)、terminal(請外層終端機發桌面通知)、system(直接叫作業系統的通知服務)。[ui.sound] enabled 預設是 true,但條件是「背景工作區的 agent 狀態改變」,你正在看的那個工作區不響。另外有 herdr notification show <title> --sound done|request 可以自己送一則。

實際體感

由於這台是無人值守的機器,我從 SSH 進去,聲音響在機器自己的喇叭上,沒有人聽得到。所以我最後靠的是側邊欄那一排記號,而不是通知。這是這類工具的通則,通知的價值取決於你人在哪裡,如果你是本機開著終端機工作,delivery = "terminal" 加上聲音應該就夠,遠端就只能靠看板。

herdr認不認得地端的組合 ?

支援清單上的 agent 都是知名 CLI,我們的 opencode 與 qwen 接的是 127.0.0.1:8100 的地端閘道而不是雲端。結論是完全不受影響,兩個都被正確分類,狀態偵測的 99% 也是在這個組態下量到的。它認的是 agent 程式本身的行為,跟後端是誰無關。

反過來,dsh 這格就是對照組。它不在支援清單裡,結果不是被標成 unknown 然後列在旁邊,而是根本不進 agent 清單herdr agent list 裡沒有它,只有 herdr pane get w5:p2 會回一個 agent_status: "unknown"。所以那一格在看板上就是一個普通終端機,跑什麼、跑到哪,herdr 一無所知。要送任務進去也只能走 herdr pane run,用不了 herdr agent prompt 那套等待與狀態語意。

三個 agent 的真實負載:一台 Spark 確實能服務一個小團隊的 agent 工作

這一節是今天的正題,也是本系列一直欠的一個驗收。Day 11 用跑分推導出「這台機器服務甜蜜點是並發 8」,但跑分的並發是假人。昨天已經修正過一次認知,平行工具呼叫不會打到引擎,開 subagent 才會,所以今天的實驗把兩種併發都放進來。

先講常態那一半。三個 agent 各領一份真實工作同時跑二十分鐘,全部打向 :8100 這個閘道與同一顆 DeepSeek-V4-Flash-0731(雙機 vLLM、TP=2、--max-num-seqs 12、開 prefix caching、投機解碼 k=5)。為了讓閘道分得出誰是誰,在 LiteLLM 上開了三個名字不同但指向同一個後端的別名,逐請求記錄因此可以歸戶。

任務是三份真的文件工作,A(opencode)讀閘道量測鏈路上的三支程式寫技術文件,B(dsh)逐檔審閱 ~/aiops-runbook/ 並寫修訂建議,C(qwen-code)讀 vLLM 的啟動指令寫調校指南。第一輪三份各約四分鐘就寫完,所以後面是排隊餵下一件。這才是我們工作中的真實情況,你不會只交代一件事就走開。二十分鐘總共產出 20 份文件、2,703 行。

指標 數值
尖峰同時在途請求數 4(等待佇列尖峰 3)
平均併發 2.61(600 個取樣點的平均)
期間總吞吐 2752.86 t/s(其中解碼 69.98 t/s)
prefix cache 命中率 91.8%
最差 TTFT 引擎 10 到 20 秒那一格;閘道逐請求量到 25.51 秒
三個任務全部完成耗時 7 分 54 秒

https://ithelp.ithome.com.tw/upload/images/20260903/20141816EuHitqcZ49.png

同一件事從引擎自己的計數器看是這樣,這是併發期間直接抓 :8008/metrics 的畫面,num_requests_running 就停在 3:
https://ithelp.ithome.com.tw/upload/images/20260903/201418163Y9hZpT2DV.png

結果與原本的預期相反。

我本來預期會看到三個 agent 的請求在時間軸上錯開,實際併發遠低於「三個 agent」聽起來的壓力。實測不是這樣,600 個取樣點裡有 431 點引擎手上正好有三個請求,只有 3 點是空的。三條線上幾乎是連續的實心色塊,不是零星的短棒。

原因不難懂,這三份任務都是「讀幾個檔案然後寫一份長文件」,agent 的一輪裡工具執行只佔幾秒,剩下全部是解碼,一旦三個都在寫長文件,引擎就沒有喘息。陣發不陣發,取決於你交代的是什麼工作,不是取決於它是 agent。

那 agent 的閒置到哪裡去了?在圖的中間,第 4 到 6 分鐘那塊灰底。那 112 秒引擎掉到只剩一個請求,原因不是引擎、不是排程、不是任何技術問題,是 A 和 C 都把第一份任務寫完了,而人還沒把下一件交出去。這才是 agent 時代真正的浪費,而且它在引擎的圖上長這個樣子,一段沒有理由的平坦畫面。

還有一個把它講得更透的數字。第二次嘗試我沒有察覺 opencode 卡在那個 herdr 看不見的權限對話框上,231 個取樣點裡有 89 點引擎完全是空的,38.5% 的時間這台機器在等一個沒人知道要按的 Enter。空轉的不是算力,是人的注意力,而且它會偽裝成「系統很閒」。

尖峰那一半:subagent 分身撞在同一秒

跑完常態負載測試,把昨天那個問題正面補上。二十分鐘的窗結束後,我把 Day 20 那個 subagent 任務原封不動再跑一次,同一句提示、同一個工作目錄、同樣三個分身加母 agent,四個請求撞在同一秒。唯一換掉的是引擎,Day 20 是 ds4-server 的兩個常駐 session,今天是雙機 vLLM 的 12 個 slot。

https://ithelp.ithome.com.tw/upload/images/20260903/20141816jZji42xXlo.png

Day 20 · ds4-server Day 21 · vLLM 雙機
三個分身的首 token 51.2 / 69.6 / 81.8 秒 5.71 / 8.27 / 10.85 秒
沒排隊時的首 token 0.7 到 1.1 秒 0.48 秒
引擎側等待佇列尖峰 兩個 session 全滿 2(瞬間)
整個任務 沒有計時 36.0 秒

最慢的那個分身從 81.8 秒變成 10.85 秒,7.5 倍。 兩邊都沒有任何請求失敗,差別純粹在排隊:ds4-server 只有兩個常駐 session,四個請求進來就有兩個得等前面做完;vLLM 把四個一起收進批次,等待佇列只有瞬間的 2。

這一格就是 Day 11 那句「代理人平台要開 subagent 就直上 vLLM」的正面完整實證。而且要注意它跟前半段的關係:常態負載那半段,兩顆引擎的差別其實不會這麼大,因為三個 agent 慢慢寫文件,兩個 session 也接得住。差別全部集中在分身那一秒。你會不會需要 vLLM,取決於你的 agent 會不會開分身,不取決於你有幾個 agent。

順便觀察一下大家的記憶體耗用狀態,三個 agent 平行處理時 KV 池水位最高只到 6.72%,平均 2.76%,subagent 那一波更只有 3.69%。這顆模型的池子是 2,478,130 tokens(順帶更正,Day 15 那個 335 萬是 Ornith 的數字,換模型就換一個不同大小的 KV 池),6.72% 大約是 16.6 萬 token。長對話歷史吃 KV 的速度,比想像中慢得多,因為每一輪送回去的歷史絕大部分打在 prefix cache 上,期間 321 萬個 prompt token 裡有 300 萬是快取命中,真正要重算的只有 27 萬。在 agent 這種「每輪重送整段歷史」的用法下,KV 不是牆,prefill 才是,而 prefix caching 正好把 prefill 的九成拿掉。

至於容量,這台機器離極限還很遠。等待佇列尖峰是 3,--max-num-seqs 12 的位子從來沒用滿過,最擠的一秒也只吃掉 4 個。所以「一台 Spark 服務一個小團隊的 agent 工作」這句話,今天從推論變成實證,這句話值多少錢,做 MSP 的讀者自己知道。

最後兩個順手撿到的數字。投機解碼在真實 agent 流量下的接受率是 46.1%(12.7 萬個草稿 token 接受 5.8 萬),落在 Day 8 那份 fixture 量到的 28% 到 93% 區間中段,比我預期的高。另外兩個獨立量測對 TTFT 的說法差了一截,引擎自己的直方圖說最差落在 10 到 20 秒那一格,閘道逐請求說 25.51 秒。這不是誰錯,是引擎只從它接手那一刻開始算,中間那五秒以上發生在 vLLM 的帳本之外。要引用 TTFT,先問清楚是採用哪一個表來測量。

指揮層的治理紅利

昨天用四個檢查點驗了單一 harness 的治理邊界,herdr 在上面疊了一層對稽核者友善的東西:集中的可觀測性。所有 agent 的活動收在同一個介面,等待審查的動作排成一個佇列,這對「人在迴圈 Human in the loop」的落實是結構性的幫助,需要人類的審查工作不再散落在五個視窗裡靠你輪巡。

它管不到的也要說清楚,而且今天實測到的邊界比預期更顯著:

herdr看得見狀態,看不見內容的風險等級。
它不知道那個等待中的確認是「建一個目錄」還是「刪整個專案」。今天 qwen 那格跳出來的是「寫一個檔案到 /tmp」,看板上跟「刪掉整個工作區」會長得一模一樣。

herdr看不見的東西,你也看不見。
opencode 那個外部目錄確認框就是最好的例子,偵測規則沒命中,看板不但沒有警示,還積極地顯示成 idle。一個把「沒有紅燈」當作「沒事」的稽核流程,會在這裡漏掉一個實際上停住的 agent。

不在支援清單上的 agent 完全不存在。
dsh 那格跑了 50 次模型呼叫、寫了 5 份檔案,看板上什麼都沒有。

所以風險分級還是得靠 harness 層的審核閘門與閘道層的權限控制,指揮層負責讓你不漏接,不負責替你判斷,而且「不漏接」這件事本身也有它的覆蓋率邊界,值得你花十分鐘用自己的 agent 組合實測一次,而不是相信支援清單。分層治理,每層做自己的事,這是黑客松做完那個題目之後我和閃光彼此都更確定的架構觀。

三部曲小結

三天走完三層,協定層的 MCP 讓工具標準化,執行層的 harness 各有性格(opencode 精實、dsh 外掛萬花筒),指揮層的 herdr 把人的注意力當資源排程。地端的部分自始至終只有一套,Day 18 的閘道與背後那些引擎,這正是解耦的勝利,上面換什麼,下面不用動。今天換上 qwen-code 當第三個 agent,改的只有一個設定檔裡的 baseURL。

明天預告

三部曲累積了三天的真實 agent 流量紀錄,明天拿它當測試資料打一場對決,vLLM 的 prefix cache 對上 FreeToken 的語意錨點快取,Day 11 的「代理人平台選 vLLM」接受挑戰者的正面挑戰,順便驗證 FreeToken 那套 CPU 與 GPU 協同執行在統一記憶體上還剩多少價值。今天那個 91.8% 的命中率就是擂台上的守方成績。引擎週的番外篇,Day 22 開打。

我們Day 22 見。

Day 1|為什麼 2026 年是地端 AI 部署元年:系列規劃與硬體總覽
Day 2|DGX Spark GB10 深度解析:128GB 統一記憶體到底解決了什麼問題
Day 3|網路與儲存規劃:10GbE 骨幹、雙 Spark 直連與 NFS 集中模型庫
Day 4|開箱之後:DGX OS 初始環境建置與 CUDA、Docker 生態確認
Day 5|NFS 模型庫實戰:下載工具、權限設計與版本管理
Day 6|llama.cpp、vLLM、TensorRT-LLM 、Ollama、DS4 與 Unsloth 的定位與取捨
Day 7|第一個模型上線:gpt-oss-120b 從模型庫到 API 的完整流程
Day 8|llama.cpp 實測:先定量尺與 SOP
Day 9|vLLM 實測:官方容器、同時處理吞吐曲線,與剛出爐的新模型 Ornith 1.5
Day 10|TensorRT-LLM 實測:我花了一個下午,跟它的預設值搏感情
Day 11|地端 AI 三大推論引擎綜合評測,同場加映神秘嘉賓
Day 12|量化格式解析:「4-bit」兩個字,古今多少事 ? 都付笑談中
Day 13|Perplexity 把整套 Agent 搬上 DGX Spark:地端元年的官方背書
Day 14|Qwen3.8-27B 與 Flash-Next 加碼實測:地端模型世代對決與落地評估
Day 15|Ornith 1.5 實測:為 AI agent 而生的 35B-A3B
Day 16|模型選型方法論:五個問題幫你跳脫排行榜迷失,找到適合自己任務用的 AI 模型
Day 17|ds4 深度解析:Redis 之父只為一種模型造了一個引擎
Day 18|OpenAI 相容 API 層:地端 AI 多模型常駐好助手
Day 19|地端 agent 框架總覽:它們到底對你的端點做了什麼呢?
Day 20|DeepSeek Harness 實戰:21 萬顆星的外掛式 harness 接上地端
Day 21|herdr 是地端 AI 同時跑多個 AI agent 的最佳解


上一篇
Day 20|DeepSeek Harness 實戰:21 萬顆星的外掛式 harness 接上地端
系列文
128GB 統一記憶體的三十天:DGX Spark 地端 LLM 與生成式 AI 部署實戰21
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言