跳到主要內容

【2026 最新】Agent Substrate 教學:用 kind 看懂 Actor、休眠續跑與 Sandbox 密度

最後更新: ·
Agent Substrate 教學首圖:Actor、Worker 與 Snapshot 的休眠續跑機制

你可能已經會用 Kubernetes 跑一個 AI Agent:一個 Pod、一份容器、一個工作目錄。但當 100 個 Agent 大部分時間都在等人、等模型或等工具時,難道真的要留 100 個 Pod 在原地發呆?這正是 Agent Substrate 教學 要解開的問題。

這篇專為第一次接觸 Agent 基礎設施、但已經知道 Docker 與 Kubernetes 基本概念的讀者而寫。我們會用官方 kind 路徑建立 counter lab,親手觀察 Actor/Worker 分工、記憶與檔案如何跨 suspend/resume 保存,再用「Actor 多於 Worker」的實驗看排隊、延遲與隔離。這是一份可重跑的 lab,不是一張宣傳成績單;數字要由你的機器、版本與設定自己量。

先說結論:一個 Actor 不是一個 Pod

一句話記住:Actor 是「可休眠、可搬家的工作身份」;Worker 是「已暖機、暫時借住的沙盒空房」。

  • Actor 保存邏輯身份、生命週期與最近 snapshot,不必永遠綁在同一個 Pod。
  • Worker 是預先啟動的 Kubernetes Pod;同一時間最多承載一個 Actor,Actor 休眠後就能接下一個。
  • ActorTemplate 像房型與設備清單,固定映像、sandbox 類型、volume 與 snapshot 規則。
  • WorkerPool 決定到底準備幾間空房;Actor 比 Worker 多時,系統靠 suspend、resume 與 request parking 周轉。
  • Snapshot 才是搬家箱。Full 可包含程序記憶、rootfs 差異與 DurableDir;Data 只保存支援 snapshot 的資料 volume。

所以它不是另一套教模型思考的 Agent framework,也不是把 Kubernetes 換掉。它把「高頻、短暫的 Actor 狀態切換」移到專門控制面,仍讓 Kubernetes 負責低頻的 Worker Pod 與基礎設施。想先補上 Agent 本身的迴圈,可先讀 AI Agent Harness 是什麼;想比較上層控制面,則搭配 Google AX 教學

Agent Substrate 教學架構圖:Actor、WorkerPool、Router 與 Snapshot 的責任邊界
Actor 是可搬家的狀態;Worker 是被重複使用的執行槽。Router 在流量抵達時喚醒 Actor,snapshot storage 保存休眠狀態。

Agent Substrate 教學:先拆開 5 個零件

① Actor:「住客」而不是房間

Actor 是一個 ActorTemplate 的實例,以 (atespace, name) 識別。它可在 SUSPENDEDRESUMINGRUNNINGSUSPENDING 等狀態間移動;實際所在的 Worker 會改變,身份則不變。白話說,你追蹤的是「這位住客」,不是某一間房號。

② Worker 與 WorkerPool:「暖機空房」與房間總數

WorkerPool 是 Kubernetes CRD,會被 controller 轉成一組預先啟動的 Worker Pod。Worker 是控制面資料庫裡對應某個 Pod 的動態紀錄,狀態是 FREEASSIGNED。不要輸入 kubectl get actor;Actor、Worker 與 ActorTemplate 不在 Kubernetes etcd,要用 kubectl ate get ... 查詢。官方 CLI 文件把這個邊界寫得很清楚。

③ ActorTemplate:「可重建的房型」

ActorTemplate 定義容器、volume、snapshot policy、sandbox class 與 Worker 選擇規則。建立時,系統會先產生 golden snapshot,之後的新 Actor 可從這份共同起點快速啟動。Template 被視為不可變;要升版就建立新 template,而不是偷偷修改舊房型。

④ Snapshot:「記憶與檔案要裝進哪種搬家箱」

Full snapshot 的語意最接近「把現場一起搬走」:程序記憶、rootfs 差異與 DurableDir 都可納入;Data snapshot 只保留支援 snapshot 的 volume,程序會重新啟動。這兩者不能混成一句「狀態一定都會保留」。本篇 counter demo 使用 FullonCommit,所以 RAM counter 與 file counter 都應延續。

⑤ Router 與 request parking:「櫃台負責叫醒,也負責排隊」

流量帶著 ate-target-actor: <atespace>/<actor> 進入 Router。Actor 若休眠,Router 會要求控制面分配 Worker 並恢復 snapshot;WorkerPool 暫時全滿時,預設 request parking 會在有限 budget 內重試,而不是無限排隊。這個 commit 的預設 park budget 是 5 秒、最多同時停放 1,024 個請求;這是背壓上限,不是「永遠不會回 503」的保證。

開始前:這不是三行指令的輕量工具

截至 2026 年 9 月 23 日,官方最新 GitHub release 是 v0.1.0,但 main 已有大量後續變動。官方 README 同時明示:專案仍在 early development、尚未準備好投入 production,API 可能改動,而且它不是 Google 正式支援產品。因此 lab 要固定 commit,跑完也要刪掉,不要直接接公司 secret 或真實客戶資料。

以下流程固定到 commit 6f45aeb2。你的電腦至少要有可用的 Docker daemon、Go 與 kubectl;官方腳本會透過 Go 管理 kind 等相依工具。microVM 路徑另需要 KVM 或支援 nested virtualization 的環境,官方在本機 microVM 指南中把 8 CPU/16 GiB 視為控制面加一個 microVM Worker 的舒適下限;第一次請先跑預設 gVisor。

git clone https://github.com/agent-substrate/substrate.git
cd substrate
git checkout 6f45aeb2dd656ec65fcdd9dff360e8397052cf42

docker info
go version
kubectl version --client

如果 docker info 連不到 daemon,先停在這裡。不要為了保住「實測」標籤,臨時把 host 權限、KVM 或付費雲端資源擴大;先把操作理解透,再選一台真正適合做 disposable cluster 的機器。

Lab 1:用 kind 跑 counter,證明記憶與檔案都續得上

先照官方 development quickstart 建立本機 cluster、安裝控制面與 counter demo。這一步會建 local registry、PostgreSQL、RustFS 與多個系統元件,不是只啟動一個 app container。注意:create-kind-cluster.sh 會先刪除同名 kind cluster;以下另取專用名稱,避免覆蓋預設的 kind

export KIND_CLUSTER_NAME=substrate-tutorial
hack/create-kind-cluster.sh
hack/install-ate-kind.sh --deploy-ate-system
hack/install-ate-kind.sh --deploy-demo-counter
go install ./cmd/kubectl-ate

kubectl ate create actor my-counter-1 \
  -a ate-demo-counter --template counter
kubectl port-forward -n ate-system svc/atenet-router 8000:80

另開終端送兩次請求,先記錄回應中的 RAM counter、file counter 與 worker 資訊,再查 Actor 與 Worker。不要只看 HTTP 200;你要保存「狀態、數值、Worker Pod、開始與結束時間」四種證據。

for i in 1 2; do
  curl -sS -X POST \
    -H 'ate-target-actor: ate-demo-counter/my-counter-1' \
    http://localhost:8000/
done

kubectl ate get actor my-counter-1 -a ate-demo-counter
kubectl ate get workers

接著量 suspend 與下一次 request-triggered resume。macOS 可用 time 看整條命令的牆鐘時間;Linux 想留下格式化秒數可用 /usr/bin/time -f '%e'。這不是純 runtime latency:CLI、API、snapshot upload、Router 與本機排程都可能混在裡面,所以要把測量邊界寫在紀錄旁。

time kubectl ate suspend actor my-counter-1 -a ate-demo-counter
kubectl ate get actor my-counter-1 -a ate-demo-counter

time curl -sS -X POST \
  -H 'ate-target-actor: ate-demo-counter/my-counter-1' \
  http://localhost:8000/

kubectl ate get actor my-counter-1 -a ate-demo-counter
kubectl ate get workers

驗收不是「counter 有回應」而已:兩個 counter 都應從先前數值繼續,Actor 應從 SUSPENDED 回到 RUNNING,而 Worker Pod 可能與前一次不同。若 RAM counter 歸零但 file counter 延續,先檢查 template 是否真的使用 Full scope;這正是 FullData 的可觀察差別。

Lab 2:4 個 Actor 壓 2 個 Worker,看見密度的代價

密度不是「同一個 Worker 同時塞進很多 Actor」。目前一個 Worker 同時最多承載一個 Actor;密度來自大量休眠 Actor 共享少量可重複使用的 Worker。官方 parking demo 已把比例做成 4 個 Actor 對 2 個 Worker:

hack/install-ate-kind.sh --deploy-demo-parking

for id in p1 p2 p3 p4; do
  kubectl ate create actor "$id" \
    --atespace ate-demo-parking --template parking
done

kubectl port-forward -n ate-system svc/atenet-router 8000:80

先請求 p1p2 把兩個 Worker 佔滿,再用第三個終端請求 p3。它會進入有限時間的 parking;在 5 秒 budget 內 suspend p1p3 才有機會取得空位。用 curl -w 留下狀態碼與總等待時間:

curl -s -H 'ate-target-actor: ate-demo-parking/p1' http://localhost:8000
curl -s -H 'ate-target-actor: ate-demo-parking/p2' http://localhost:8000
kubectl ate get workers

curl -s -w '\nHTTP %{http_code} / %{time_total}s\n' \
  -H 'ate-target-actor: ate-demo-parking/p3' http://localhost:8000

# 在另一個終端、5 秒內執行
kubectl ate suspend actor p1 --atespace ate-demo-parking

你真正要量的是:Worker 數、同時執行 Actor 數、休眠 Actor 數、parking wait 的 median/p95、budget_exhaustedparking.rejected。官方提供 atenet.router.parking.activeatenet.router.parking.wait.durationatenet.router.parking.rejected,也能從 Router 的 /statusz?format=json 看目前 parking 狀態。宣傳中的 10×、30× 或 sub-500ms 只能當專案自述與特定 demo;你的 production 決策必須以自己的 snapshot 大小、儲存距離、Actor 工作集與併發分布重測。

Lab 3:三個故障注入,別只測 happy path

故障 A:Worker Pod 消失

先在 disposable kind cluster 找到承載測試 Actor 的 Worker Pod,再刪除它,持續觀察 Actor 狀態、Worker replacement 與最後一次 snapshot 之後的資料。這是一個破壞性演練,不要在共用或 production cluster 執行。

# 先 suspend,留下已知可恢復的 external snapshot
kubectl ate suspend actor my-counter-1 -a ate-demo-counter
kubectl ate resume actor my-counter-1 -a ate-demo-counter

# 再送一次請求,製造「snapshot 之後」的新狀態
curl -sS -X POST \
  -H 'ate-target-actor: ate-demo-counter/my-counter-1' \
  http://localhost:8000/

# 從 kubectl ate 的 JSON 找到目前 Worker Pod;確認後才刪除
actor_json=$(kubectl ate get actor my-counter-1 \
  -a ate-demo-counter -o json)
worker_ns=$(printf '%s' "$actor_json" | \
  jq -er '.status.workerAssignment.workerNamespace')
worker_pod=$(printf '%s' "$actor_json" | \
  jq -er '.status.workerAssignment.workerPod')
printf 'delete %s/%s\n' "$worker_ns" "$worker_pod"
kubectl -n "$worker_ns" delete pod "$worker_pod"

kubectl ate get actor my-counter-1 -a ate-demo-counter
kubectl ate revert actor my-counter-1 -a ate-demo-counter
kubectl ate resume actor my-counter-1 -a ate-demo-counter

這個 commit 的 E2E 測試預期:刪除正在承載 Actor 的 Worker Pod 後,Actor 會進入 CRASHEDkubectl ate revert actor 可讓它回到 SUSPENDED,再從最後一份 external snapshot 恢復,因此 snapshot 之後的新變更會遺失。要特別誠實的是,同一 commit 的舊 upgrade runbook 仍寫著 CRASHED 沒有 recover verb,文件與實作尚未同步;所以把 revert 視為實驗性救援路徑,而不是穩定復原承諾。驗收要記錄「何時停止服務、何時進入 CRASHED、revert 是否成功、資料回到哪一版」,而不是只等 Pod 重新 Ready。

故障 B:沒有 egress policy 就必須拒絕

官方 egress demo 的設計是 policy default-deny:沒有 EgressPolicy 的 Actor 不會取得 tunnel。先部署 demo,建立 Actor 後在「尚未建立 allow rule」時送一次外連請求,保存拒絕結果;接著建立只允許測試目標的 policy,再做正向與負向探針。最小自動測試如下:

hack/install-ate-kind.sh --deploy-demo-egress
./demos/egress/test-egress.sh --cleanup

不要只測「允許的站能連」。還要測未列入的 host/port、非 Actor 身份的 Pod,以及 suspend/resume 前後政策是否一致。更完整的 Agent 網路邊界可搭配 Tailscale Agent fleet 教學Agent runtime controls

故障 C:Worker 全滿時,排隊不能無上限

在 parking demo 先故意讓所有 Worker 忙碌,再觀察請求超過 park budget 後是否得到可辨識的 503;接著把 --parked-request-max=0 暫時加入 Router args,比較 fail-fast 與有限排隊。這個實驗回答的是「你的 API 要等多久才應該明確失敗」,不是追求 0 個 503。

Agent Substrate 教學:怎麼做一張可信的量測表

每一輪至少固定:commit SHA、Docker/kind/Kubernetes 版本、CPU/RAM、sandbox class、Worker 數、Actor 數、snapshot scope、snapshot bytes、物件儲存位置與併發數。然後把「控制面成功」和「使用者真的收到回應」分開計時。

  1. 生命週期:suspend、resume、first response 各自記 median 與 p95。
  2. 狀態正確性:RAM counter、DurableDir file counter、Actor VERSION 是否符合預期。
  3. 密度:Actors/Workers 比例、Worker busy ratio、snapshot storage bytes。
  4. 背壓:parking active、wait duration、rejected、budget exhausted。
  5. 復原:Worker 中斷後的不可用時間、資料回復點與人工介入。
  6. 隔離:允許與拒絕 egress、跨 Actor 殘留、secret 是否進入 snapshot。
Agent Substrate 教學生產決策卡:生命週期、密度、故障與安全四道閘門
先驗證正確性,再談密度;先證明故障可復原,再談 production。單一成功 demo 不能取代四道閘門。

gVisor、microVM、一般容器池怎麼選?

  • 一般容器池:最容易上手,適合可信 workload 與低隔離風險;但「一個長駐容器對一個 Agent」會讓大量 idle 實例持續佔資源。
  • gVisor WorkerPool:是官方 kind quickstart 的預設路徑,能練習 Full snapshot、Actor 搬移與 request parking;它仍需要你驗證 syscall 相容性、snapshot 大小與實際延遲。
  • microVM WorkerPool:隔離邊界與 guest RAM snapshot 更接近 VM,但本機需要 KVM/nested virtualization,設定與資源門檻較高。Apple Silicon 的 nested virtualization 還受硬體世代限制。

選擇順序不是「microVM 一定最好」,而是先回答:你的 code 是否不可信、需要保留多少程序狀態、可接受多少恢復延遲、主機是否支援所需虛擬化。想先理解 microVM 與容器差異,可讀 Docker Sandboxes 與 microVM 教學

能不能進 production?用 6 道硬閘門回答

  1. 版本可重現:映像 digest、Substrate commit、SandboxConfig 與 WorkerPool manifest 都能重建。
  2. 狀態語意清楚:每個目錄、RAM 狀態與 secret 都知道是否會進 Full/Data snapshot。
  3. 容量會失敗:parking budget、queue 上限與 503 行為已在尖峰併發下驗過。
  4. 故障有 recovery objective:Worker、node、Router、database、object storage 任一中斷,都有實際 RTO/RPO 紀錄。
  5. 隔離是負向證據:未授權 egress、跨 Actor 殘留、Kubernetes API、metadata service 與其他 snapshot 都確實被拒絕。
  6. 升級與清理可演練:Template 不可變、Actor 可刪除、snapshot 有 retention,舊 WorkerPool 能安全退場。

若任何一項只能回答「理論上應該」,目前就把它留在 lab。官方自己的 README 已把 production readiness 判為未完成,threat model 也明列 worker reuse、snapshot 權限、policy 同步、API authorization 與 lateral movement 等風險;把專案名稱前的 Google 版權誤讀成產品支援,是最危險的捷徑。

清理:lab 成功的最後一步

先刪 demo,再刪 kind cluster。不要只關掉終端;確認 container、network、local registry 與 volume 都沒有殘留。

hack/install-ate-kind.sh --delete-demo-egress
hack/install-ate-kind.sh --delete-demo-parking
hack/install-ate-kind.sh --delete-demo-counter
hack/delete-kind-cluster.sh

docker ps -a
docker volume ls
docker network ls

常見問題

1. Agent Substrate 是 Agent framework 嗎?

不是。它管理執行環境、Actor 生命週期、snapshot、Worker 分配與 routing;Agent 如何推理、呼叫模型與工具,仍由你的 harness 或上層 framework 決定。

2. 一個 Worker 可以同時跑很多 Actor 嗎?

同一時刻不行。目前一個 Worker 最多承載一個 Actor;高密度來自時間上的 multiplexing,也就是大量休眠 Actor 輪流使用少量 Worker。

3. Suspend 後 RAM 一定會保留嗎?

不一定。要看 snapshot scope。Full 可保存程序記憶;Data 只保存支援 snapshot 的 volume,程序會依 resume source 冷啟動或從 golden 恢復。

4. 30× oversubscription 代表我的 cluster 也能省 30 倍嗎?

不能這樣推論。那是官方 demo 的 Actor/Pod 比例,不是你的成本保證。snapshot 大小、idle ratio、併發、儲存距離與恢復頻率都會改變答案。

5. 用 kind 跑通就能上 production 嗎?

不能。kind 適合理解拓撲與重現生命週期;production 還要補高可用 database、object storage、權限、升級、節點故障、容量與安全驗證。

6. gVisor 和 microVM 哪個比較安全?

要看威脅模型與設定。兩者提供不同隔離邊界與相容性取捨;不要把 runtime 名稱當成完整安全證明。真正要驗的是 node、network、snapshot、identity、secret 與 worker reuse 的整條邊界。

7. Request parking 會不會讓請求永遠排隊?

不會無限排隊。它有 park budget 與最大容量;超時或 parking lot 已滿時仍會失敗。你要把 503 視為容量訊號,而不是把 queue 調到無限大。

8. 現在最適合的使用方式是什麼?

先做 disposable lab。固定版本,跑 counter、parking、egress 與 worker 故障注入,留下可重跑紀錄;等六道 production 閘門都有證據,再評估小流量 pilot。

給新手的 5 個重點

  1. Actor 是身份與狀態,Worker 是暫時執行槽;不要把 Actor 等同 Pod。
  2. Full 與 Data snapshot 決定「醒來時還記得什麼」,必須用 counter 實驗驗收。
  3. 密度來自休眠輪替,不是把無限 Actor 同時塞進一個 Worker。
  4. Parking 有時間與容量上限;503 是系統誠實回報飽和的方式。
  5. 官方目前仍標示未準備好 production;先累積故障、安全與升級證據。

結語:先證明「醒來還是同一個它」

Agent Substrate 最值得帶走的,不是 10× 或 30×,而是新的資源心智模型:Actor 是可休眠、可搬家的工作身份;Worker 是暖機好的沙盒空房。當你把兩者拆開,才有機會讓大量 idle Agent 不再各佔一個 Pod。

你的下一步很具體:在 disposable kind cluster 跑完一次 counter suspend/resume,保存前後數值、Actor VERSION、Worker Pod 與時間,再做 4 Actor/2 Worker 的 parking 實驗。想把這套基礎設施思維接回完整 AI 工作流,可到 AlphaLab 課程繼續建立從 Agent Harness 到部署驗收的完整能力。

ALPHALAB 社群

有問題?來 Telegram 聊

和 Terry、編輯、其他網友一起討論這篇文章。提問、分享觀點,回覆更即時。

加入 Telegram 討論

📩 訂閱 AlphaLab 電子報

每週最多三封:一封 Weekly 週報與最多兩封關鍵 Alpha Signal。

我們不會 spam,隨時可退訂。