Resource management 章 講的是 Ollama / ComfyUI 等推論伺服器的 lifecycle。但RAG / MCP 應用比單純 chat 多吃幾倍資源——embedding model、chat model、index 檔、subprocess、tool 邏輯——而且不同階段(ingest vs query)的瓶頸不一樣。

本篇紀錄 RAG demoMCP demo 跑起來的實測資源 footprint、提供本地多模型並存的 baseline、給寫 production 應用前的 sanity check。

驗證日期:2026-05-12 環境:M4 Pro 32 GB、Ollama 0.23.2、Python 3.14 Corpus:本 blog 的 content/llm/、71 個 markdown 檔、463 chunks

各階段資源 footprint

RAG / MCP 工作流通常分三階段、各自吃不同資源:

階段主要資源消耗持續時間是否常駐
RAG ingestembedding model RAM + CPU + 磁碟寫one-shot(corpus 更動時跑)
RAG queryindex 載入 RAM + chat model RAM + GPUper-requestretrieval index 常駐
MCP serversubprocess 永久跑、tool 呼叫時動態載資源session 內常駐

不同階段的瓶頸不一樣、優化目標也不同。

RAG Ingest 階段:one-shot 但批次密集

python3 scripts/rag-demo/ingest.py 時:

1Found 71 markdown files under content/llm
2  [10/71] 86 chunks in 4.5s
3  [20/71] 181 chunks in 8.6s
4  ...
5  [70/71] 461 chunks in 22.2s
6Wrote 463 records to scripts/rag-demo/index.pkl (22.3s)

實測資源消耗:

資源數字為什麼
RAM(峰值)~600 MBnomic-embed-text 模型 (274 MB) + Python runtime + 累積 records (~200 MB)
磁碟寫index.pkl ~3.7 MB463 records、每筆含 chunk text + 768-dim float embedding
CPU + GPUOllama 推 embedding、Apple Silicon Metal backend22 秒處理 463 個 chunk、平均 ~21 chunk/sec
網路0完全本地推論

Ingest 階段的特性

  • One-shot:corpus 不變不用重跑、index 寫一次永久用。
  • 吃 CPU 多於 RAM:產生 embedding 是 forward pass、瓶頸在 GPU 算力、RAM 沒太大壓力。
  • 磁碟寫小:每 chunk 約 8 KB(text 部分 ~5 KB + embedding 768 floats × 4 bytes = ~3 KB)、463 chunks 總共 ~3.7 MB。
  • 可平行:sequential embed(chunk) 是最慢實作、用 batching API(如果 Ollama 支援)或多 worker、能快 5-10x。

規模 extrapolation

Corpus 大小預估 ingest 時間index.pkl 大小
71 docs / 463 chunks(本 blog)22 秒3.7 MB
1000 docs / ~7000 chunks(中型 codebase)~5 分鐘~55 MB
10000 docs / ~70000 chunks(大型 codebase)~50 分鐘~550 MB
100K docs / ~700K chunks(公司 wiki)~8 小時~5.5 GB

10K docs 以上就應該考慮:

  • Batching embedding(單次 request 送 50 個 chunks)
  • 並行 worker(Python multiprocessing、4-8 worker)
  • vector database(避免把全部資料用 pickle 塞 RAM)

RAG Query 階段:retrieval 加 generation

python3 scripts/rag-demo/query.py --show-retrieved "問題" 時:

1Loaded 463 chunks from scripts/rag-demo/index.pkl
2=== Retrieved chunks ===
3  0.870  llm/knowledge-cards/transformer.md#chunk2
4  ...
5(LLM 生成 response)

實測資源消耗(單次 query):

階段RAM 增量時間
載 index.pkl 到 RAM3.7 MB(小 corpus)/ MB 級(大 corpus)< 1 秒
embed query0(已載入的 nomic-embed-text)200 ms
cosine over 463 chunks純 Python 計算、暫時用 ~10 MB50 ms
載 chat model(gemma3:1b)~1 GB(首次)/ 0(已 cached)5-10 秒(首次)/ 0(cached)
生成 response0 額外5-30 秒(看 model + prompt 長度)

Query 階段的特性

  • 第一次 cold start:要載 chat model 進 RAM、5-10 秒首字延遲。
  • 後續 query 都快:embedding model + chat model 都在 RAM、retrieval 毫秒級、只剩 generation 時間。
  • RAM 占用 = embedding model + chat model + index
    • 463 chunks: 274 MB + chat model + 3.7 MB ≈ chat model + 280 MB
    • 100K chunks: 274 MB + chat model + ~800 MB 進 RAM、加上 mmap pickle 額外開銷
  • 瓶頸是 chat model:retrieval 部分快、瓶頸完全在 generation。

多模型並存(embedding + chat):

1# 看當前 RAM 占用
2ollama ps
3# NAME                       SIZE      UNTIL
4# nomic-embed-text:latest    274 MB    4 minutes from now
5# gemma3:4b                  5.5 GB    4 minutes from now

兩個 model 都載入時、Ollama RAM 占用約 6 GB。Ollama 的 OLLAMA_KEEP_ALIVE(預設 5 分鐘)會 idle 後分別 unload 兩個 model。

規模 sanity check

場景RAM 需求
純 chat(gemma3:1b)~1 GB
RAG with gemma3:1b + nomic-embed-text + 小 index~1.5 GB
RAG with gemma3:4b + nomic-embed-text + 中型 index~6 GB
RAG with gemma4:31b + nomic-embed-text + 大 index~20 GB

跑 RAG 比 chat 額外要 ~300-1000 MB(embedding model + index)、不會太重。

MCP Server 階段:subprocess 常駐

python3 scripts/mcp-demo/test_client.py 時、client 會 spawn blog_mcp_server.py 當 child process。

實測:

資源數字備註
Subprocess RAM~50 MBPython runtime + index.pkl mmap
stdio pipe 數量3(stdin、stdout、stderr)每 spawn 一個 server 都要 3 FD
持續時間client 在跑就在跑client 結束時 SIGPIPE 自動結束 server

MCP server 的特性

  • 每個 client spawn 一個 server:Claude Desktop 開 5 個 MCP server、就有 5 個 Python subprocess。
  • Index lazy load:本 demo load_index() 第一次 call 才 read pickle、之後 cached。Cold start 第一次 tool call 稍慢。
  • Process lifecycle 在 client 端:client 死了、stdin EOF、server 自然結束。Client 沒清乾淨 spawn 多次就 leak process。
1# 看當前所有 MCP server
2ps aux | grep blog_mcp_server | grep -v grep
3
4# 如果 client crash 留下 zombie:
5pkill -f "blog_mcp_server.py"

多 MCP server 並存(如 Claude Desktop 接 git server + filesystem server + custom server):

ServerRAM主要負載
git MCP server~30 MBshell 呼叫
filesystem MCP server~30 MBfs 操作
blog_mcp_server(本 demo)~50 MB(含 index)embedding + retrieval
5 個 server 同時~200 MB累積

200 MB 在 32 GB Mac 上不顯眼、但 16 GB Mac + 多 MCP server + 大 chat model 就可能擠到。

RAG + MCP 整合:完整應用 stack

實際應用會疊起來:

1User 在 Claude Desktop 打字
23Claude Desktop (~200 MB)
4  ↓ MCP stdio
5blog_mcp_server.py (~50 MB)
6  ↓ HTTP /api/embeddings + /v1/chat/completions
7Ollama daemon (~200 MB)
8  ↓ load
9nomic-embed-text 模型 (~274 MB) + 主 chat model (~6 GB)

整體 RAM 占用範圍:

配置估算
Minimal(gemma3:1b + 小 index)~1.7 GB
Standard(gemma3:4b + 中 index)~6.5 GB
Heavy(gemma4:31b + 大 index + 多 MCP server)~22 GB

resource-management 章 比、RAG / MCP 加 ~500 MB-1 GB overhead 在 chat 之上、是合理的 tradeoff(換來 retrieval + tool use 能力)。

各資源類型的關鍵指標

整理三 dimension 的關鍵指標跟監控方式:

RAM

1# 看 Ollama 載了哪些 model
2ollama ps
3
4# 看所有 LLM-related process
5ps aux | grep -E "ollama|comfyui|mcp" | grep -v grep | awk '{print $4, $11, $12, $13}' | sort -rn
6
7# 系統整體
8vm_stat | head -3

告警閾值

  • RAM 占用 > 80% 系統總量:開始考慮 unload model 或關掉 ComfyUI
  • 看到 swap 增加(vm_stat | grep "Swapouts"):已經 swap、要立刻減少 model

磁碟

1# Ollama models 累積
2du -sh ~/.ollama/models
3
4# RAG index 累積(多個 corpus)
5du -sh scripts/rag-demo/index*.pkl 2>/dev/null
6
7# ComfyUI checkpoints / VAE / LoRA / etc
8du -sh ~/Projects/ComfyUI/models/*

累積評估

  • Ollama: 每 model 1-20 GB、半年累積容易破 50 GB
  • RAG index: 每 100K chunks ~800 MB、多 corpus 累積要管
  • ComfyUI: 每 checkpoint 4-7 GB、加 LoRA / VAE / ControlNet 等可達 50+ GB

Process / Port

1# 一鍵 audit 所有 LLM service
2for p in 11434 1234 8080 8188 8000; do
3  echo "=== port $p ==="
4  lsof -i :$p 2>/dev/null | head -2
5done
6
7# 找 zombie subprocess(沒 parent 的 mcp server)
8ps aux | grep "mcp_server" | grep -v grep

告警訊號

  • 同 port 兩個 process listen:明顯有 zombie、要 kill
  • 多個 mcp_server PPID = 1(被 reparent 到 init):原 client 死了沒清乾淨

RAG 應用的長期累積管理

跑超過幾週、會累積:

累積物為什麼累積怎麼清
Multiple index.pkl跑不同 corpus 各建 index、舊的沒刪find scripts -name 'index*.pkl' -mtime +30 -delete
Ollama models試了不同 model 沒清ollama list modified 欄、ollama rm 不用的
Python __pycache__每次跑 script 累積.gitignore 已包、本地 find . -name __pycache__ -exec rm -rf {} +
Embedding cache如果你寫了 embedding cache 機制各自清理策略

清理 idiom:

1# 每月跑一次的 cleanup
2llm-rag-cleanup() {
3  echo "[*] Old indexes (>30 days):"
4  find scripts -name 'index*.pkl' -mtime +30 -ls
5  echo "[*] Ollama models (review):"
6  ollama list
7  echo "[*] Python caches:"
8  find ~/Projects -name __pycache__ -type d | head -10
9}

跟 production 的差距預告

本篇紀錄的數字、是「single-user、single-machine、no concurrency」的 baseline。Production 場景多了幾個維度:

維度本地Production
並發 user110-10000
Index 大小< 100 MBTB 級
Model servingOllama 1 processvLLM / TGI / Triton 多 worker
Vector storagepicklePinecone / Weaviate / pgvector
Latency 要求秒級 OKp50 < 500ms、p99 < 2s
Cost model一次性硬體$/request、$/token
Observabilitytail logmetrics / traces / dashboards
失敗模式crash → 自己重啟99.9% uptime SLA

Production 視角詳細展開見 4.9 Production 部署的資源評估原理

何時這篇會過時

不會過時的部分

  • 三階段 footprint 分類(ingest / query / server)
  • RAM / 磁碟 / process 三 dimension 的監控指令
  • 多模型並存的 RAM 預估方法
  • 長期累積管理 idiom

會變的部分

  • 具體 RAM / 磁碟數字(隨模型架構、量化方法演化)
  • OLLAMA_KEEP_ALIVE 等具體環境變數名
  • 哪些 vector DB 主流(會持續演化)

讀的時候若 RAM 占用跟本篇對不上、可能是新 model 架構效率改變、用同樣方法量自己環境的 baseline 即可。

跟其他 hands-on 章節的關係:完整 hands-on 系列見 Hands-on 章節索引、實作配對見 RAG demoMCP demo、Ollama / ComfyUI 共用的 lifecycle 管理見 Resource management、Apple Silicon 統一記憶體預算原理見 0.5 記憶體預算

跑這篇實測的指令總結

 1# 1. RAG ingest 階段 RAM 量
 2ollama ps  # 先看 baseline
 3python3 scripts/rag-demo/ingest.py &
 4INGEST_PID=$!
 5ollama ps  # 看 embedding model 載入後
 6vm_stat | head -3
 7wait $INGEST_PID
 8
 9# 2. RAG query 階段 RAM 量
10ollama ps  # 看 idle 後 unload
11python3 scripts/rag-demo/query.py --show-retrieved "test query"
12ollama ps  # 看 chat model 載入
13
14# 3. MCP server 階段 process / RAM
15python3 scripts/mcp-demo/test_client.py &
16CLIENT_PID=$!
17sleep 2
18ps aux | grep blog_mcp_server | grep -v grep
19wait $CLIENT_PID
20
21# 4. 完成釋放
22ollama list | tail -n +2 | awk '{print $1}' | xargs -I {} \
23  curl -s http://localhost:11434/api/generate -d "{\"model\":\"{}\",\"keep_alive\":0}"