跳到主要內容

【2026 最新】Agent KV Cache 擴充實戰:跨 Worker 路由、分層快取與故障演練

最後更新: ·
Agent KV Cache 跨 Worker 擴充教學首圖,呈現 router 將請求分流到 GPU、CPU 與遠端快取層

Agent KV Cache 在單機 warm run 命中,不代表擴成多個 worker 後還會快。只要下一輪被送到沒有那段前綴的 worker,或熱門 session 把同一張 GPU 的 queue 塞滿,省下的 prefill 就可能被重算、排隊與 KV 搬運吃掉。真正要解的不是「開不開 cache」,而是哪個 worker 有可重用前綴、它現在多忙,以及把 KV 搬過去是否比重算划算

這篇接在既有的 Agentic Workload 單機 trace 教學之後,刻意只處理跨 worker 維運:先用不需要 GPU 的 mock topology 接通 router,再用單 GPU 建立 Prefix Cache 基準,最後比較 round robin、sticky session、cache-and-load-aware routing,逐層加入 CPU/remote tier,並注入 worker loss、hot-prefix skew、router state gap 與 stale session。即使你第一次接觸推論維運,也可以先做前兩層;本文交付的是可落地的驗收設計與 runbook 骨架,不會把模擬器輸出冒充真實 GPU 叢集成績。

先說結論:Agent KV Cache 的有效命中要過四道門

🧭 記憶把手:有效命中=前綴相同 × 路由找對 × 快取仍在 − 搬運代價。

  • 前綴相同:模型、tokenizer、chat template、tool schema、adapter 與 token 順序都必須相容。
  • 路由找對:router 得知道哪些 block 大概在哪個健康 worker,而不只是記住 session ID。
  • 快取仍在:router 的索引可能落後;worker 重啟、eviction 或 namespace 旋轉後,舊紀錄不能算命中。
  • 搬運值得:CPU/remote tier 找得到 KV 還不夠,完整讀取與 promotion 路徑必須比本地重新 prefill 更划算。

白話比喻:router 像值班主管,要把「同一本長筆記的下一頁」交給讀過前文、而且現在不塞車的同事;若筆記已移到倉庫,還要比較取件和從頭重讀哪個快。

vLLM 0.29 的 Automatic Prefix Caching 文件把邊界說得很清楚:它重用共同前綴的 KV,省的是共享部分的 prefill,不能加速新 token 的 decode。因此,Agent KV Cache 的 hit rate 很高,長答案、工具等待或 router queue 仍可能讓整個任務變慢。

Agent KV Cache 跨 Worker 架構圖,顯示 router 依前綴位置與負載選擇 worker,並在 GPU、CPU 與遠端儲存間取用或重算
圖中用 GPU/CPU/remote 表示實體位置,避免把產品各自的 L1/L2/L3 名稱混在一起。Router 索引只是「可能在哪裡」的估計。

先把三件事分開:路由、分層、Prefill/Decode 分離

三種設計常被一起畫在架構圖上,卻回答不同問題:

  1. KV-aware routing:在多個相同模型的 worker 中,選一個已持有最多共同前綴、同時又沒有塞車的 worker。
  2. Hierarchical cache:把近期 block 留在 GPU,較冷資料降到 host memory,再把更冷或需跨節點分享的資料放到外部儲存;L1/L2/L3 的名字依產品而異。
  3. Disaggregated prefill/decode:讓 prefill worker 算 prompt、decode worker 產生新 token,中間傳 KV。它能隔離兩種負載,但也新增一次傳輸與更多故障面。

不要一次全開。NVIDIA Dynamo 的分離式服務文件也提醒,小模型、短 prompt、低併發或傳輸很慢時,aggregated serving 可能更簡單甚至更快。本文的順序是 router → CPU tier → remote tier → prefill/decode 分離;每次只多一個因果變數。

第 0 步:沿用單機基準,只新增跨 Worker 變因

先完成前篇的 cold、warm、restart 與共同前綴驗收;本篇不重跑那套單機教學,而是在同一組 prompt、模型與量測口徑上,新增 router commit、worker 數與 UID、KV tier、cache-index/event version、網路拓撲。模型要 pin 完整 commit SHA;tokenizer、chat template、GPU、trace SHA-256 與 seed 也固定,另記每套引擎實際 block/page size。跨引擎報告換算成 tokens 或 bytes,不直接比較「幾個 blocks」。

  • A|無 GPU topology smoke:用 Dynamo Mocker 接通 frontend、discovery、router log,不拿模擬 TTFT 當成績。
  • B|單 GPU baseline:沿用 vLLM cold/warm counters,建立這個模型、硬體與負載的 smoke baseline。
  • C|多 GPU routing:至少兩個真 worker 比較 round robin、session-only sticky 與 cache+load,才回答目標部署的跨 GPU locality 與 latency。

需要快速建立 B 時,依 vLLM 0.29 APCEngine ArgumentsBenchmark CLIHugging Face CLI,先把模型與 tokenizer 下載到同一個完整 SHA 的本機 snapshot,再跑 100 requests 的有限速 plumbing smoke。兩個終端都要從同一目錄設定相同的 AGENT_KV_MODEL;server 留在終端 A,benchmark 在終端 B 執行:

export AGENT_KV_MODEL="$PWD/.agent-kv-models/qwen3-0.6b-c1899de"
hf download Qwen/Qwen3-0.6B \
  --revision c1899de289a04d12100db370d81485cdf75e47ca \
  --local-dir "$AGENT_KV_MODEL"

vllm serve "$AGENT_KV_MODEL" \
  --tokenizer "$AGENT_KV_MODEL" \
  --served-model-name Qwen/Qwen3-0.6B \
  --host 127.0.0.1 \
  --port 8000 \
  --enable-prefix-caching

vllm bench serve --backend openai \
  --model Qwen/Qwen3-0.6B \
  --tokenizer "$AGENT_KV_MODEL" \
  --dataset-name prefix_repetition \
  --num-prompts 100 --seed 42 \
  --request-rate 2 --max-concurrency 4 \
  --prefix-repetition-prefix-len 512 \
  --prefix-repetition-suffix-len 128 \
  --prefix-repetition-num-prefixes 5 \
  --prefix-repetition-output-len 128

prefix_repetition在同一 run 內本來就會產生共享 prefix,所以這一輪是 initial-fill/mixed smoke,不是「每個 request 都 cold」,也不是 production lower bound。保存 run 前後的 /metrics;若要做 cold/warm 對照,沿用前篇的獨立 reset 與兩輪流程。正式路由結論仍以第 1 步的 filtered Agent trace 與多 worker run 為準。

第 1 步:沿用公開 Agent Trace,新增 Route Receipt

沿用 AgentX 公開方法的去識別 trace:它用 session-local 64-token block hashes 保存長度、時間與前綴關係,不保存 prompt、程式碼或工具內容。這類資料適合重播 serving 形狀,不適合評模型答案品質。

不要直接拿「前 20 個 sessions」送進小模型。使用 AgentX 方法頁指向的 AIPerf/WEKA 合成流程,或建立可重現的 block-ID→token-ID 映射,先檢查每筆 input_length + max_tokens沒有超過 server 實際 max_model_len;超長 request 排除並記下 retained/excluded 數量,再把保留集合上限設為 100 requests。結果只能稱 filtered synthetic subset,不能稱 AgentX 性能。它的 hash 又是 session-local,無法量到不同 session 之間共用 system/tool prefix 的命中。

每個 request 額外記一張 route receipt;prefix_fingerprint用受控的每租戶 HMAC 或資料集既有匿名 ID,不要直接對可猜出的原始 prompt 做一般 hash:

{
  "request_id":"s17-t2",
  "session_id":"s17",
  "prefix_fingerprint":"tenant-hmac:7f…",
  "eligible_prefix_blocks":100,
  "chosen_worker":"worker-b",
  "matched_blocks_at_route":80,
  "worker_queue_at_route":1,
  "route_reason":"cache_plus_load",
  "actual_local_hits":80,
  "remote_bytes_read":0,
  "status":"ok"
}

接著對同一份排序、seed 與 arrival timing 跑三種 policy:

  1. Round robin:不看 session、cache 或 load,當控制組。
  2. Sticky session:首個 request 先由 round robin 選出健康 worker;成功 dispatch 後建立 session binding,後續同 session request 才精準回到該 worker。它能保住線性對話,卻可能把熱門 session 全黏到同一台。
  3. Cache+load aware:先排除不健康 worker,再以可重用 block 加分、active prefill/decode 與 queue 扣分,最後才處理平手。

Dynamo 的 KV-aware routing 概念頁同樣把 prefix overlap 與預估負載一起評分。下面是策略的概念偽碼,不是 Dynamo API;權重必須用你的 SLO sweep 校正:

candidates = workers.filter(healthy)

for w in candidates:
    score[w] = (
        alpha * reusable_blocks[w]
        - beta  * active_prefill_tokens[w]
        - gamma * active_decode_blocks[w]
        - delta * estimated_transfer_ms[w]
    )

return argmax(score)

不要替三種 policy 各挑一段「最適合它」的 trace。每個 arm 都用隔離 workers/新 namespace,或明確重置 worker cache 與 router index,之後套用相同 primer/warm-up,再重播相同 requests。至少做 3 次獨立重複並輪替 arm 順序,報分布與所有失敗;單做 ABBA 不能清掉前一個 policy 留下的 cache placement。

第 2 步:先用 Mocker 接通,再上兩個真 Worker

在 Dynamo 官方開發容器或依安裝頁完成的環境中,可以先照 v1.4.2 Mocker live simulation開三個終端。它不需要 GPU,只適合檢查 file discovery、round-robin request routing 與 client。v1.4.2 安裝頁明列 file discovery 沒有 KV events/routing/planner,因此這一層不要加 --router-mode kv

# 終端 A:frontend
python -m dynamo.frontend \
  --http-host 127.0.0.1 \
  --http-port 8000 \
  --discovery-backend file

# 終端 B:四個 mock workers
python -m dynamo.mocker \
  --model-path Qwen/Qwen3-0.6B \
  --discovery-backend file \
  --num-workers 4

# 終端 C:送出官方 smoke request
curl localhost:8000/v1/chat/completions \
  -H 'Content-Type: application/json' \
  -d '{
    "model": "Qwen/Qwen3-0.6B",
    "messages": [{"role": "user", "content": "Hello!"}],
    "max_tokens": 32
  }'

這個 Mocker smoke 固定使用預設 round robin;不要在 file backend 上假造 KV 或 sticky 對照。成功後才把三種 policy 移到下方 NATS+etcd 與獨立真 workers 的隔離 arms。

成功回應只證明本機 discovery 與 request routing 已接通。v1.4.2 的 Mocker worker 建立流程顯示,--num-workers 4會在同一個 Mocker OS process 內建立四個各自具有獨立 DistributedRuntime 的 workers,但它們共用同一個 event loop 與 Tokio runtime。這段指令只有一個可送 TERM 的 OS PID,不能靠 process signal 選擇性終止其中一個 worker;若未另加 pinned-version 支援的故障注入或獨立 worker 部署,本 smoke 只驗 routing 與整個 Mocker process 的停止/重啟。它的 latency 也不能代表 CUDA kernel、PCIe、NVLink 或真實 KV tensor 搬運;simulation model 邊界明列不執行真實 GPU inference,也不建模 KV tensor payload。

控制面通過後,固定 NVIDIA Dynamo v1.4.2。這個 stable release 的官方測試組合是 vLLM 0.26.0/SGLang 0.5.16,不能與本文獨立的 vLLM 0.29/SGLang 0.5.19 smoke 合併成同一組結果。官方 dev/docker-compose.yml會把 NATS 三個與 etcd 兩個服務埠(4222、6222、8222、2379、2380)映射到所有介面,且測試用 etcd 沒有驗證;只能在隔離主機使用,絕不能直接暴露到網際網路。

本機實驗先用 Docker Compose 2.24.4 以上支援的 !override建立 dev/docker-compose.loopback.yml;若 docker compose config不認得此標記就停止,不要退回原始 port mapping:

services:
  nats-server:
    ports: !override
      - "127.0.0.1:4222:4222"
      - "127.0.0.1:6222:6222"
      - "127.0.0.1:8222:8222"
  etcd-server:
    ports: !override
      - "127.0.0.1:2379:2379"
      - "127.0.0.1:2380:2380"

接著在多 GPU 主機重做第 0 步的 SHA download,並把 AGENT_KV_MODEL重新設成該 snapshot 的絕對路徑。先檢視合併後設定,確認五條 published ports 全是 127.0.0.1,再啟動 NATS+etcd;DYN_HTTP_HOST把 frontend 限在 loopback,HF_HUB_OFFLINE=1則讓正式 arms 不再解析遠端 model HEAD。遠端主機要改用有驗證、TLS 與防火牆的部署,不把這份開發 Compose 當 production manifest:

git clone --branch v1.4.2 --depth 1 \
  https://github.com/ai-dynamo/dynamo.git
cd dynamo

# 未設定、不是絕對路徑或 snapshot 不完整時立即停止
: "${AGENT_KV_MODEL:?請先設定 SHA-pinned snapshot 的絕對路徑}"
case "$AGENT_KV_MODEL" in /*) ;; *) exit 1 ;; esac
test -f "$AGENT_KV_MODEL/config.json" || exit 1
test -f "$AGENT_KV_MODEL/tokenizer_config.json" || exit 1
find "$AGENT_KV_MODEL" -maxdepth 1 -name '*.safetensors' \
  -print -quit | grep -q . || exit 1

docker compose version
docker compose -f dev/docker-compose.yml \
  -f dev/docker-compose.loopback.yml config
docker compose -f dev/docker-compose.yml \
  -f dev/docker-compose.loopback.yml up -d

export DYN_HTTP_HOST=127.0.0.1
export DYN_SYSTEM_HOST=127.0.0.1
export PYTHONHASHSEED=0
export HF_HUB_OFFLINE=1

v1.4.2 routing quickstart的 vLLM/SGLang aggregated preset 是兩個 workers、2 GPUs;所有 vLLM processes 使用相同 PYTHONHASHSEED=0。但官方 agg_router.shMODEL="Qwen/Qwen3-0.6B"寫死,不能原封不動拿來做可比較的正式 arms。先複製成 agg_rr.shagg_sticky.shagg_kv.sh;三份都把 MODEL設為 "${AGENT_KV_MODEL:?}",並在兩個 python3 -m dynamo.vllm worker commands 都加入 --tokenizer "$MODEL" --served-model-name Qwen/Qwen3-0.6B,再把 KV event endpoints 從 tcp://*:20080tcp://*:20081改成 tcp://127.0.0.1:20080tcp://127.0.0.1:20081Dynamo runtime 的 system host 預設0.0.0.0,所以前面的 DYN_SYSTEM_HOST也不能省。其餘 worker flags 必須相同,啟動前用 diff 確認三份只剩 frontend policy 不同。

三個 arms 的每個同-session request 都帶相同的 X-Dynamo-Session-ID: s17;未設定 affinity TTL 時,這個 header 只提供 session identity,不會啟用 affinity。round-robin control 使用 --router-mode round-robinsession-only sticky只多加 --router-session-affinity-ttl-secs 300;KV+load arm 使用 --router-mode kv且不加 affinity TTL。這個 sticky 對照假設未設定 DYN_LORA_ENABLED;若啟用 LoRA,v1.4.2 不支援 round-robin+affinity,便不能把 KV+affinity 冒充純 sticky。300 秒只是本次 manifest 的測試值,不是推薦預設;一次只啟動一份 launcher。

逐一記下 frontend、router、worker PID/pod UID、GPU UUID、model revision、port 與 log path;三個 arms 之間停止整組 processes、使用新 namespace/清空測試狀態,再重播第 1 步的同一 filtered subset。分離式 preset 是 bash launch/disagg_router.sh,vLLM/SGLang 範例需要 4 GPUs;只有 aggregated routing 已通過、且 prefill queue 確實是瓶頸時才進那一層。完成後從 repo root 執行下列清理,不能讓無驗證的開發服務留在背景:

docker compose -f dev/docker-compose.yml \
  -f dev/docker-compose.loopback.yml down
Round robin、sticky session 與 cache 加 load aware 三種 Agent KV Cache 路由策略的比較與通過條件
三種 policy 使用同一 trace。命中率只是一欄;最後要讓 TTFT、queue、錯誤與 task completion 一起過關。

第 3 步:GPU → CPU → Remote 一層一層加(層級名稱依產品)

本文用 GPU/CPU/remote 描述實體媒介,不把 L1/L2/L3 當成跨產品標準。SGLang HiCache稱 GPU=L1、instance-private host memory=L2、storage backend=L3;L3 只有在 backend 實際共享時才跨 instance。LMCache MP則稱 CPU DRAM 或 GDS NVMe slab=L1、persistent backend=L2。

分層快取不是容量越大越好。每多一層,就多了 serialization、copy、網路、版本相容與故障處理。最小實驗矩陣固定 router policy,依序比較 GPU only、GPU+CPU、GPU+CPU+remote storage;不同 arm 使用新 namespace/重置 cache 與 router index,再跑相同 primer、cold/warm/worker-restart block。

  • GPU tier:最快,但容量最稀缺。記 usage、eviction、local hit 與被 active requests 占用的 tokens/bytes。
  • CPU tier:容量較便宜,需記 GPU↔host bytes、讀寫延遲、promotion/demotion 與 pinned-memory 壓力;同一節點的多個 instance 也不必然自動 pooled。
  • Remote-storage tier:可跨程序或節點,但必須記 get/put 成功率、network bytes、完整 retrieval-path p95、timeout、namespace 與已配置的 failure policy。

如果 backend 是 SGLang,可依 rolling 的 HiCache best practices先只開 host tier;顯示的 flags 另以 v0.5.19 server arguments交叉核對。以下是起點,不是通用最佳值;執行前仍看該版 --help並確認主機 RAM 足夠:

python -m sglang.launch_server \
  --model-path Qwen/Qwen3-0.6B \
  --revision c1899de289a04d12100db370d81485cdf75e47ca \
  --host 127.0.0.1 \
  --port 8000 \
  --page-size 64 \
  --enable-hierarchical-cache \
  --hicache-ratio 2 \
  --hicache-io-backend kernel \
  --hicache-write-policy write_through

若你使用 vLLM,vLLM 0.29 的 LMCache integration 範例把單一 process 的 CPU/disk offload,與可跨 process 分享的 standalone server 分開。LMCache MP 文件是 rolling contract,需再以安裝版本的 lmcache server --help核對。一次只選一套 tier 實作;同時切 SGLang HiCache、LMCache 與 router,無法知道改善或退步來自哪裡。

第 4 步:觀測不能只看 Hit Rate

把 dashboard 分成五排,所有延遲欄位都保留 p50、p95、p99 或原始 histogram;低流量時不要用太短窗口硬算 percentile:

  1. 使用者等待:TTFT、inter-token latency、E2E latency、完整 session wall time。
  2. 快取效果:eligible tokens、route-time matched tokens、actual GPU-local hits、CPU/remote hits、重新 prefill tokens、KV usage/eviction。
  3. 路由負載:chosen worker、queue、active prefill tokens、active decode blocks、routing decision latency、spill 次數。
  4. 搬運可靠度:bytes、fetch/store latency、timeout、success/failure、fallback reason。
  5. 任務結果:request error、retry、完整任務成功率、cost/GPU-seconds per successful task。

vLLM 0.29 metrics中,vllm:prefix_cache_hitsvllm:prefix_cache_queries計的是 cached/queried tokens,不是 blocks,也不是「有命中的 requests」。Dashboard 可用 rate(vllm:prefix_cache_hits[5m]) / rate(vllm:prefix_cache_queries[5m])看窗口比率,但仍要與 TTFT、queue 和 route receipt 對讀。

Dynamo 的 engine metrics 比較頁顯示,不同 backend 暴露的 KV transfer 指標並不完全相同;但該表是 2026 年 4 月 10 日、Dynamo v1.0.0 的 live scrape,測的是 vLLM 0.19.0、SGLang 0.5.9、TensorRT-LLM 1.3.0rc9,只能當「metric surface 不可攜」的例子。先讀實際 /metrics再建立 canonical dashboard;沒有對應 counter 就標 unavailable,不從總網卡流量猜成 KV bytes。

正式 gate 要在 run 前寫好。例如:「p95 TTFT 不得突破現有 SLO、task success 不下降、錯誤與 retry 在預算內。」Remote 路徑要把 lookup、storage read、deserialize、network、host→GPU promotion、timeout/fallback 與 queue 都算進 remote_path_e2e_ms;在相同 prefix-length 與 load bucket 做配對比較,只有完整 TTFT/task 結果和可靠度都勝過本地重算才保留。門檻由你的 baseline 決定,不能從別人的 GPU 或模擬結果抄百分比。

一段完整 Route Trace:命中最多的 Worker 不一定勝出

以下是教你讀 receipt 的算術例子,不是實測速度。Session S17 的第二輪有 100 個 eligible prefix blocks:

  • Worker A:100 blocks 都在 GPU-local cache,但 active prefill 很高;代入本次測試權重後,reuse 加分 100、load 扣分 45,總分 55。
  • Worker B:80 blocks 在 GPU-local cache,queue 只有 1;reuse 加分 80、load 扣分 5,總分 75。
  • 決策:router 選 B。實際執行若只命中 76 blocks,receipt 要同時留下 route-time 80 與 actual 76;差額是索引新鮮度問題,不能偷偷仍算 80。

在兩個獨立真 workers 的 staging 中,第三輪開始前對 Worker B 送出 TERM。健康檢查先把 B 移出 candidates;若 A 仍健康,後續 request 再依已配置 policy 重用、重算或回錯,並記 failure_reason=worker_lost。已在 B 執行中的 generation 不會因此自動無縫遷移;必須另外啟用並驗收 request migration,否則把它計入失敗或重試。當 B 重新加入時,router 必須以新 worker incarnation/pod UID 看待它,不能只因名稱仍叫 worker-b就沿用舊 cache residency。

第 5 步:跑 5 場失效演練,再談上線

以下是故障 runbook 骨架,只在隔離的 staging/benchmark namespace 執行;每場先確認目標 PID、pod UID 或 test endpoint,保留 baseline,並準備一鍵回到 round robin+GPU-only。故障期間的失敗 request 不能從報表刪除。

  1. Worker loss:在長 session 的第二、三輪之間終止一個已確認的獨立 worker。驗收 health 優先於 affinity、有限 retry、無限重送不發生,恢復後不沿用舊 incarnation 的 cache index。
  2. Hot-prefix skew:讓 80% 合成 requests 共用一個 prefix、20% 分散到其他 prefixes。比較 sticky 是否塞車,以及 cache+load policy 是否在「多重算一點」與「少排隊」間守住 TTFT。
  3. Router restart/event gap:保留 workers,丟掉一個非尾端 KV event,接著送出更高 ID 的 event。驗收 router 在看到序號缺口後才要求 snapshot/reset,再恢復正常決策;只丟最後一個 event 並不能證明 gap detection。
  4. Remote tier unavailable:關閉測試用 remote endpoint 或注入 timeout。先明確配置並驗收 failure policy:它可能是有界錯誤、opt-in recompute 或 tier fallback;不能假設 transfer 失敗一定自動降到 CPU/重新 prefill。恢復後還要檢查 thundering herd。
  5. Stale session/namespace rotation:每次不相容的 model revision、tokenizer、chat template、adapter 或 tenant policy 變更,都原子旋轉 remote namespace/key epoch 並 drain 舊 workers;只改 revision 字串不會自動隔離所有共享 KV。驗收舊 prefix 不再跨 epoch 命中,closed session 不因 sticky key 復活。

Dynamo router 的 event design 文件說明 KV events 是 fire-and-forget,事件帶單調序號;較高 ID 抵達後才會暴露中間 gap,router 再向仍可用的 worker 要 snapshot,而 worker removal 會移除其 blocks。vLLM 0.29 NIXL的 transfer failure 預設是 failrecompute需明確選擇。兩者都要親自驗證:worker 本身不在時,snapshot 並不是魔法備份;未配置的 fallback 也不會憑空出現。

Agent KV Cache 五場故障演練的注入方式、必看訊號與通過條件
每場故障都要有注入點、可觀測訊號、驗收條件與回滾;「服務最後回來了」不是完整的通過證據。

題源 Simulator 為何不能證明跨 Worker?兩個證據陷阱

這次題源的 agentic-kv-cache repository很適合閱讀 trace schema、policy skeleton 與產生假說;它的 README 也明說模擬不含 GPU execution,不能當 throughput、latency 或 SLO benchmark。本文沒有把它的模擬數字當成 AlphaLab 實測。

進一步檢查 2026 年 9 月 10 日的 commit 73e7e46807d0,還有兩個必須先擋下來的問題:

  • TTL 沒有到期路徑:TTL class繼承 LRU 後只保存 ttl欄位,沒有覆寫查詢、淘汰或 expiry。TTL 與 LRU 輸出相同,不能證明「TTL 從未觸發」或「容量一定是主因」。
  • Miss 不等於 eviction 重算:該 repo 的 gap 分析把共同鏈中未命中的 block 都叫 recompute,其中也會包含首次出現的新 suffix 與 cold request;沒有 seen-before counterfactual,就不能把所有 miss 歸因給 eviction。

還要避免把一種 cache 結構說成所有引擎的 production baseline。vLLM 0.29 prefix caching 設計使用獨立 block hash 與 free queue 的 LRU eviction,並非一律採 radix-leaf。安全的用法是:把 repo 當作可修改骨架,先補 TTL expiry 與 seen-before 分類測試,再用本篇的真實 worker、metrics 與 failure gates 驗證。

Agent KV Cache FAQ

1. 開了 Prefix Cache,多個 Worker 就會自動共享嗎?

不能從「每台都開了 Prefix Cache」推定已共享。本文採用的 vLLM 本地 APC 與 LMCache standalone server 是兩種不同配置;只有另行配置並驗收跨程序/節點的 KV tier,才把其他 worker 的 KV 列為可用來源。

2. Sticky Session 和 Prefix-aware Routing 哪個比較好?

沒有固定答案。Sticky 簡單,適合線性 session;熱門 session 或 fan-out 可能造成單點 queue。Prefix-aware 要維護索引,應再加入 load 與 health,並以同 trace A/B。

3. Cache Hit Ratio 越高就一定越快嗎?

不一定。Hit 只省部分 prefill;queue、decode、KV transfer、工具等待與錯誤重試仍在。最後看 p95 TTFT、session wall time 與成功任務吞吐。

4. 什麼時候才該分離 Prefill 與 Decode?

當兩種負載互相干擾,而且 KV transfer 的代價可接受時。先讓 aggregated routing 通過;再用長 prompt、高併發的真實分布驗證,短 prompt 不要預設會受益。

5. CPU/Remote Tier 應該配置多大?

從 reuse-distance 與 break-even 分布反推。先掃小、中、大三個容量;若較大 tier 只增加寫入與網路,卻沒有降低重新 prefill 或 TTFT,就停在較小設定。

6. 模擬器可以取代 GPU 壓測嗎?

不能。它很適合比較事件順序、容量假說與 router 邏輯;GPU kernel、記憶體 copy、真實網路、contention 與 tail latency 要在目標硬體重跑。

7. Router 的 Cache Index 過期,會直接產生錯答案嗎?

Router 索引只能當估計,不能當正確性證明。把 route-time matched blocks 與 worker 實際 local hits 分開記;不一致時回報 miss,再交給已明確配置的 policy 處理成有界錯誤、recompute 或 fallback。若跨 tier 的 namespace、模型版本或租戶隔離做錯,才會升高為 correctness/security 風險,因此 rotation test 不能省。

8. 多租戶 Prefix Cache 還要注意什麼?

不要只靠相同文字做全域共享。分開 tenant namespace、限制可觀測 timing,並使用 backend 支援的 salt。vLLM 0.29 的 security 文件提供 cache_salt來降低跨租戶 prefix timing side channel;這不取代身分、授權與資料保留政策。

給新手的上線檢查單

  1. 先用 Mocker 接通 topology,再用單 GPU 證明 trace 有共同前綴。
  2. Round robin、sticky、cache+load 使用隔離但相同的 trace 與固定 manifest。
  3. 每次只新增 CPU tier、remote tier 或 disaggregated serving 其中一項。
  4. 同時讀 cache、queue、TTFT、transfer、error 與 task completion。
  5. 完成 worker loss、hot skew、event gap、remote outage、namespace rotation 五場演練。
  6. 只有真實 GPU 結果通過預先寫好的 SLO,才把簡單 policy 換成複雜 policy。

若你還在單機階段,可先用 oMLX 長 session 驗收練習 cold、warm 與 restart 的判讀;想把 Agent、prompt 與自動化打成完整學習路線,也可從 AlphaLab 線上課程選一條實作路徑。

接著閱讀

左右滑動查看更多推薦

結語:今晚先證明 Router 知道自己不知道

Agent KV Cache 擴充的第一個里程碑,不是漂亮的全局 hit rate,而是每次決策都有 receipt:router 當時以為哪些 blocks 在哪裡、worker 實際命中多少、排隊多久、搬了多少 bytes,失敗後又走哪條已配置 policy。今晚先做一份通過 context preflight、上限 100 requests 的 filtered synthetic subset,接通 round-robin Mocker 與單 GPU smoke;下一次有兩張 GPU 時,再讓 round robin、session-only sticky、cache+load 跑隔離但相同的 trace。回到最前面的把手:只有前綴相同、路由找對、快取仍在,而且完整搬運路徑真的划算,跨 worker cache 才不只是 warm demo 裡成立的架構圖。

ALPHALAB 社群

有問題?來 Telegram 聊

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

加入 Telegram 討論

📩 訂閱 AlphaLab 電子報

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

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