<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Resource-Planning on Tarragon</title><link>https://tarrragon.github.io/blog/tags/resource-planning/</link><description>Recent content in Resource-Planning on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Tue, 12 May 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/tags/resource-planning/index.xml" rel="self" type="application/rss+xml"/><item><title>4.9 Production 部署的資源評估原理</title><link>https://tarrragon.github.io/blog/llm/04-applications/production-resource-planning/</link><pubDate>Tue, 12 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/llm/04-applications/production-resource-planning/</guid><description>&lt;p>LLM 應用從本地實驗跨到 production 是個 phase transition、不是線性放大。本地 single-user 場景的「跑得起來」變 production 場景就要回答全新一組問題：100 個 user 同時打進來怎麼辦、每個 &lt;a href="https://tarrragon.github.io/blog/llm/knowledge-cards/token/" data-link-title="Token" data-link-desc="LLM 處理文字時的最小單位：介於字元與單字之間">token&lt;/a> 要多少錢、p99 latency 怎麼控、model service down 了怎麼處理。&lt;/p>
&lt;p>本章寫的是「&lt;strong>從本地實驗 → production 該想清楚的維度&lt;/strong>」、focus 在跨工具世代不變的原理。具體 framework（vLLM、TGI、Triton、SGLang）跟雲端服務（OpenAI / Anthropic / Bedrock）的選型不展開——這些半年一個世代、寫了會過時。本章建立的是「無論用哪套工具、都該回答」的設計取捨清單。&lt;/p>
&lt;p>跟 &lt;a href="https://tarrragon.github.io/blog/llm/04-applications/rag-principles/" data-link-title="4.1 RAG 原理：retrieval &amp;#43; augmentation 模式" data-link-desc="為什麼模型需要外掛知識、語意相似 vs 字面相似、chunking 的本質取捨、retrieval 失敗的根本原因">4.1 RAG&lt;/a> / &lt;a href="https://tarrragon.github.io/blog/llm/04-applications/tool-use-principles/" data-link-title="4.3 Tool use 原理：LLM 跟外部世界互動" data-link-desc="Structured output 是 LLM 跨入工程系統的橋、function calling 取捨、為什麼本地小模型 tool use 表現崩潰">4.3 Tool use&lt;/a> / &lt;a href="https://tarrragon.github.io/blog/llm/04-applications/agent-architecture/" data-link-title="4.4 Agent 架構原理" data-link-desc="Agent loop 結構、失敗模式、什麼任務適合 vs 不適合、跟人類審查的協作模型">4.4 Agent&lt;/a> 對應「應用怎麼設計」、本章對應「應用怎麼跑」。&lt;/p>
&lt;h2 id="本章目標">本章目標&lt;/h2>
&lt;p>讀完本章後你能：&lt;/p>
&lt;ol>
&lt;li>列出 production LLM 部署該評估的 6 個 dimension。&lt;/li>
&lt;li>解釋 single-user benchmark 為什麼不能直接 extrapolate 到 multi-user 場景。&lt;/li>
&lt;li>區分 latency-sensitive 跟 throughput-sensitive 應用的設計差別。&lt;/li>
&lt;li>對成本模型（$/request、$/token、$/month）做合理估算。&lt;/li>
&lt;/ol>
&lt;h2 id="從本地到-production-的-phase-transition">從本地到 production 的 phase transition&lt;/h2>
&lt;p>本地 LLM 跑 &lt;a href="https://tarrragon.github.io/blog/llm/knowledge-cards/rag/" data-link-title="RAG" data-link-desc="Retrieval-Augmented Generation：動態外掛知識給 LLM、繞開模型參數記憶的靜態限制">RAG&lt;/a> / &lt;a href="https://tarrragon.github.io/blog/llm/knowledge-cards/mcp/" data-link-title="MCP（Model Context Protocol）" data-link-desc="LLM application ↔ 外部 tool server 之間的標準化協議、複用 OpenAI 相容 API 的成功模式">MCP&lt;/a> 的 baseline（&lt;a href="https://tarrragon.github.io/blog/llm/01-local-llm-services/hands-on/rag-mcp-resources/" data-link-title="Hands-on：RAG / MCP 的資源 footprint" data-link-desc="RAG ingest / query / MCP server 三階段的 RAM / 磁碟 / process 實測、多模型並存的 RAM 衝突、本地 LLM 跑 RAG 跟單純 chat 的差異">hands-on 章節&lt;/a>）：&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>維度&lt;/th>
 &lt;th>本地（single-user）&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>並發 user&lt;/td>
 &lt;td>1&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Latency 要求&lt;/td>
 &lt;td>秒級 OK&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Index 大小&lt;/td>
 &lt;td>&amp;lt; 100 MB&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Cost&lt;/td>
 &lt;td>一次性硬體&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Uptime&lt;/td>
 &lt;td>自己重啟&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>觀測&lt;/td>
 &lt;td>&lt;code>tail log&lt;/code>&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>Production 場景每個維度都跳一個量級：&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>維度&lt;/th>
 &lt;th>Production（multi-tenant）&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>並發 user&lt;/td>
 &lt;td>10 - 10000&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Latency 要求&lt;/td>
 &lt;td>p50 &amp;lt; 500 ms、p99 &amp;lt; 2 s&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Index 大小&lt;/td>
 &lt;td>GB - TB&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Cost&lt;/td>
 &lt;td>$ / request、$ / token、$ / month&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Uptime&lt;/td>
 &lt;td>99.9% SLA&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>觀測&lt;/td>
 &lt;td>metrics、traces、dashboards&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>每個維度跳一個量級的 implication 不是「資源 × 10」、是「全新的失敗模式 + 新的設計取捨」。&lt;/p></description><content:encoded><![CDATA[<p>LLM 應用從本地實驗跨到 production 是個 phase transition、不是線性放大。本地 single-user 場景的「跑得起來」變 production 場景就要回答全新一組問題：100 個 user 同時打進來怎麼辦、每個 <a href="/blog/llm/knowledge-cards/token/" data-link-title="Token" data-link-desc="LLM 處理文字時的最小單位：介於字元與單字之間">token</a> 要多少錢、p99 latency 怎麼控、model service down 了怎麼處理。</p>
<p>本章寫的是「<strong>從本地實驗 → production 該想清楚的維度</strong>」、focus 在跨工具世代不變的原理。具體 framework（vLLM、TGI、Triton、SGLang）跟雲端服務（OpenAI / Anthropic / Bedrock）的選型不展開——這些半年一個世代、寫了會過時。本章建立的是「無論用哪套工具、都該回答」的設計取捨清單。</p>
<p>跟 <a href="/blog/llm/04-applications/rag-principles/" data-link-title="4.1 RAG 原理：retrieval &#43; augmentation 模式" data-link-desc="為什麼模型需要外掛知識、語意相似 vs 字面相似、chunking 的本質取捨、retrieval 失敗的根本原因">4.1 RAG</a> / <a href="/blog/llm/04-applications/tool-use-principles/" data-link-title="4.3 Tool use 原理：LLM 跟外部世界互動" data-link-desc="Structured output 是 LLM 跨入工程系統的橋、function calling 取捨、為什麼本地小模型 tool use 表現崩潰">4.3 Tool use</a> / <a href="/blog/llm/04-applications/agent-architecture/" data-link-title="4.4 Agent 架構原理" data-link-desc="Agent loop 結構、失敗模式、什麼任務適合 vs 不適合、跟人類審查的協作模型">4.4 Agent</a> 對應「應用怎麼設計」、本章對應「應用怎麼跑」。</p>
<h2 id="本章目標">本章目標</h2>
<p>讀完本章後你能：</p>
<ol>
<li>列出 production LLM 部署該評估的 6 個 dimension。</li>
<li>解釋 single-user benchmark 為什麼不能直接 extrapolate 到 multi-user 場景。</li>
<li>區分 latency-sensitive 跟 throughput-sensitive 應用的設計差別。</li>
<li>對成本模型（$/request、$/token、$/month）做合理估算。</li>
</ol>
<h2 id="從本地到-production-的-phase-transition">從本地到 production 的 phase transition</h2>
<p>本地 LLM 跑 <a href="/blog/llm/knowledge-cards/rag/" data-link-title="RAG" data-link-desc="Retrieval-Augmented Generation：動態外掛知識給 LLM、繞開模型參數記憶的靜態限制">RAG</a> / <a href="/blog/llm/knowledge-cards/mcp/" data-link-title="MCP（Model Context Protocol）" data-link-desc="LLM application ↔ 外部 tool server 之間的標準化協議、複用 OpenAI 相容 API 的成功模式">MCP</a> 的 baseline（<a href="/blog/llm/01-local-llm-services/hands-on/rag-mcp-resources/" data-link-title="Hands-on：RAG / MCP 的資源 footprint" data-link-desc="RAG ingest / query / MCP server 三階段的 RAM / 磁碟 / process 實測、多模型並存的 RAM 衝突、本地 LLM 跑 RAG 跟單純 chat 的差異">hands-on 章節</a>）：</p>
<table>
  <thead>
      <tr>
          <th>維度</th>
          <th>本地（single-user）</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>並發 user</td>
          <td>1</td>
      </tr>
      <tr>
          <td>Latency 要求</td>
          <td>秒級 OK</td>
      </tr>
      <tr>
          <td>Index 大小</td>
          <td>&lt; 100 MB</td>
      </tr>
      <tr>
          <td>Cost</td>
          <td>一次性硬體</td>
      </tr>
      <tr>
          <td>Uptime</td>
          <td>自己重啟</td>
      </tr>
      <tr>
          <td>觀測</td>
          <td><code>tail log</code></td>
      </tr>
  </tbody>
</table>
<p>Production 場景每個維度都跳一個量級：</p>
<table>
  <thead>
      <tr>
          <th>維度</th>
          <th>Production（multi-tenant）</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>並發 user</td>
          <td>10 - 10000</td>
      </tr>
      <tr>
          <td>Latency 要求</td>
          <td>p50 &lt; 500 ms、p99 &lt; 2 s</td>
      </tr>
      <tr>
          <td>Index 大小</td>
          <td>GB - TB</td>
      </tr>
      <tr>
          <td>Cost</td>
          <td>$ / request、$ / token、$ / month</td>
      </tr>
      <tr>
          <td>Uptime</td>
          <td>99.9% SLA</td>
      </tr>
      <tr>
          <td>觀測</td>
          <td>metrics、traces、dashboards</td>
      </tr>
  </tbody>
</table>
<p>每個維度跳一個量級的 implication 不是「資源 × 10」、是「全新的失敗模式 + 新的設計取捨」。</p>
<h2 id="維度-1concurrent-users--throughput">維度 1：Concurrent users / Throughput</h2>
<h3 id="為什麼這個維度最關鍵">為什麼這個維度最關鍵</h3>
<p>本地 single-user 的 baseline 數字（<a href="/blog/llm/01-local-llm-services/hands-on/rag-mcp-resources/" data-link-title="Hands-on：RAG / MCP 的資源 footprint" data-link-desc="RAG ingest / query / MCP server 三階段的 RAM / 磁碟 / process 實測、多模型並存的 RAM 衝突、本地 LLM 跑 RAG 跟單純 chat 的差異">hands-on</a> 紀錄的 RAM / latency）<strong>在 multi-user 場景下幾乎無法 extrapolate</strong>、根因是資源爭用會放大原本看不到的成本：</p>
<ul>
<li>100 個 user 同時送 request → 不是「同樣 latency × 100」、是「queueing + memory contention + GPU 排隊」、單個 user 的 latency 可能漲 10×</li>
<li>同樣 model 服務 N 個 user → KV cache 占用要乘以 N、單卡 GPU 在容量限制下可能裝不下</li>
<li>Single-user 「200 ms latency」可能 production 變「p99 5 秒」</li>
</ul>
<h3 id="key-conceptbatching">Key concept：batching</h3>
<p><a href="/blog/llm/knowledge-cards/batching/" data-link-title="Batching" data-link-desc="多 request 一起跑、攤平 model load 成本：production LLM inference 的核心優化、決定 throughput vs latency 取捨">Batching</a> 跟 <a href="/blog/llm/knowledge-cards/kv-cache/" data-link-title="KV Cache" data-link-desc="已處理 token 的 attention 中間結果暫存：避免重算、加速後續生成">KV cache</a> 設計讓 GPU 能多 user 的 request 一次 forward pass、是 production <a href="/blog/llm/knowledge-cards/inference-server/" data-link-title="Inference Server" data-link-desc="載入模型權重、處理 prompt、產生 token 的常駐 process">inference server</a> 的核心優化。但 batching 也帶取捨：</p>
<ul>
<li><strong>靜態 batching</strong>：等湊滿 N 個 request 才跑、提高 throughput、犧牲首字延遲</li>
<li><strong>連續 batching（continuous batching）</strong>：vLLM / TGI 等用、新 request 動態加入正在跑的 batch、平衡 throughput + latency</li>
<li><strong>No batching</strong>：每 request 獨立跑、latency 低、GPU 利用率差</li>
</ul>
<p>選 batching 策略主要取決於 latency 跟 throughput 哪個重要：</p>
<table>
  <thead>
      <tr>
          <th>應用場景</th>
          <th>適合 batching 策略</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>互動式對話（IDE plugin、chatbot UI）</td>
          <td>continuous batching、低 latency 優先</td>
      </tr>
      <tr>
          <td>批次處理（document summarization、code review）</td>
          <td>static batching、throughput 優先</td>
      </tr>
      <tr>
          <td>Embedding 服務</td>
          <td>batching 越大越好、embedding 是純 forward pass、batch 16-128 都 OK</td>
      </tr>
  </tbody>
</table>
<h3 id="評估-concurrent-throughput">評估 concurrent throughput</h3>
<p>要做的測試（不在本章 hands-on、是 framework）：</p>
<ol>
<li><strong>Single-user baseline</strong>：measure single request 在 idle server 上的 latency</li>
<li><strong>N-user load test</strong>：用 <a href="https://k6.io">k6</a> / <a href="https://github.com/tsenart/vegeta">vegeta</a> / 自寫 async client 跑 1、10、100 個並發 request</li>
<li><strong>觀察 p50 / p95 / p99 latency 隨並發數變化</strong>：通常 &lt; N=batch_size 時平、超過 batch_size 後 latency 線性漲</li>
<li><strong>GPU memory 飽和點</strong>：tokens-in-flight 超過某個量、新 request 開始排隊</li>
</ol>
<p>實務評估公式：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">Max concurrent users (steady state)
</span></span><span class="line"><span class="ln">2</span><span class="cl">    = (GPU memory available - model weights) / (per-user KV cache size)</span></span></code></pre></div><p>例：H100 80 GB - 31B model 60 GB = 20 GB 可用 / 每 user 平均 200 MB KV cache = 100 個並發 user。</p>
<p>公式的失效條件（用這幾個 signal 判讀公式何時不可信）：</p>
<ul>
<li><strong>變長 context</strong>：per-user KV cache 隨 context 長度線性增長、長 context 用戶（10K+ tokens）的 KV cache 是短 context 用戶的 5-10 倍、用平均值會嚴重低估。修法：依 P95 context 長度估、不用 average。</li>
<li><strong>Prefix cache 啟用</strong>：vLLM、TGI 等用 prefix sharing 大幅省 KV cache、實際容量比公式高 2-3 倍。修法：跑實測量 prefix cache hit rate。</li>
<li><strong>Speculative decoding</strong>：drafter 跟 target 的 KV cache 都要算進去、每 user 佔用會比 dense baseline 高 10-20%。修法：用 drafter+target 合計算。</li>
<li><strong>不同 batching 策略</strong>：static batching 上限是「batch_size × 等待時間」、continuous batching 是「平均 in-flight tokens」、不同策略下公式的「per-user」定義不同。</li>
</ul>
<p>但這是上限、實際還要考慮 latency target。</p>
<h2 id="維度-2latency-budget">維度 2：Latency budget</h2>
<h3 id="latency-sensitive-vs-throughput-sensitive">Latency-sensitive vs throughput-sensitive</h3>
<p>兩類應用的設計取捨完全不同：</p>
<table>
  <thead>
      <tr>
          <th>屬性</th>
          <th>Latency-sensitive</th>
          <th>Throughput-sensitive</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>範例</td>
          <td>IDE 補完、chat UI、search assistant</td>
          <td>批次標籤、文件摘要、離線 RAG ingest</td>
      </tr>
      <tr>
          <td>目標 metric</td>
          <td>p99 latency</td>
          <td>tokens / second / GPU</td>
      </tr>
      <tr>
          <td>User 經驗影響</td>
          <td>直接（卡住）</td>
          <td>間接（總時間）</td>
      </tr>
      <tr>
          <td>Batching</td>
          <td>小 batch / continuous</td>
          <td>大 batch</td>
      </tr>
      <tr>
          <td>資源規劃</td>
          <td>預留 headroom 給 spike</td>
          <td>跑滿 GPU 利用率</td>
      </tr>
  </tbody>
</table>
<p>混合應用（如 chat with RAG）有兩段：retrieval（throughput-friendly、可 batch）+ generation（latency-sensitive、要 stream）。兩段獨立優化。</p>
<h3 id="latency-預算分配">Latency 預算分配</h3>
<p>一個 RAG 應用的 p99 latency 是各段加總：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">Total p99 = client → API gateway → retrieval → LLM prefill → LLM decode → response stream
</span></span><span class="line"><span class="ln">2</span><span class="cl">         ≈ 50 ms      20 ms        50 ms        500 ms       1500 ms      100 ms
</span></span><span class="line"><span class="ln">3</span><span class="cl">         ≈ 2.2 seconds</span></span></code></pre></div><p>如果 p99 budget 是 2 秒、要先確認<strong>最大消耗段是哪個</strong>：</p>
<ul>
<li>通常 LLM generation 是最大、是優化重心</li>
<li>Retrieval 在大 corpus 場景可能超過 100 ms、要 index 優化（HNSW、近似 nearest neighbor）</li>
<li>API gateway 通常可忽略、超過 50 ms 就有 SRE 議題</li>
</ul>
<p>各段監控分開、把監控拆到各段才找得到 root cause；只看 total latency 會錯失定位線索。</p>
<h2 id="維度-3cost-model">維度 3：Cost model</h2>
<h3 id="三種計費單位">三種計費單位</h3>
<table>
  <thead>
      <tr>
          <th>單位</th>
          <th>怎麼算</th>
          <th>適合</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>$/request</td>
          <td>每 API call 固定價</td>
          <td>簡單應用、可預測流量</td>
      </tr>
      <tr>
          <td>$/token</td>
          <td>看 input + output token 數</td>
          <td>OpenAI / Anthropic 主流、混合輸入長度應用</td>
      </tr>
      <tr>
          <td>$/server-hour</td>
          <td>自家跑 GPU instance、月租</td>
          <td>高 throughput、可預測 utilization</td>
      </tr>
  </tbody>
</table>
<p>雲端 API（OpenAI / Anthropic）幾乎都 $/token、給定 model 不同 price tier。自家跑（vLLM on Lambda Labs / RunPod）是 $/server-hour。</p>
<h3 id="成本估算-worked-example">成本估算 worked example</h3>
<p>假設應用：</p>
<ul>
<li>1000 active users / day</li>
<li>每 user 平均 10 requests / day</li>
<li>每 request 平均 1000 input tokens + 500 output tokens</li>
<li>用 Claude Sonnet 4.6（假設 $3 input / $15 output per million tokens）</li>
</ul>
<p>每日 cost：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">total_requests = 1000 × 10 = 10000 / day
</span></span><span class="line"><span class="ln">2</span><span class="cl">input_tokens = 10000 × 1000 = 10M
</span></span><span class="line"><span class="ln">3</span><span class="cl">output_tokens = 10000 × 500 = 5M
</span></span><span class="line"><span class="ln">4</span><span class="cl">daily_cost = 10M × $3/M + 5M × $15/M = $30 + $75 = $105 / day
</span></span><span class="line"><span class="ln">5</span><span class="cl">monthly_cost ≈ $3150</span></span></code></pre></div><p>跑自家 GPU 比較：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">H100 instance: ~$2/hour（以 2026 年 spot price 為例、實際隨雲廠商與當期報價變動）
</span></span><span class="line"><span class="ln">2</span><span class="cl">H100 monthly = $2 × 24 × 30 = $1440
</span></span><span class="line"><span class="ln">3</span><span class="cl">若 utilization &gt; 50% 且團隊有 SRE 能力維運、自架較划算
</span></span><span class="line"><span class="ln">4</span><span class="cl">若 utilization &lt; 30%、或團隊無 GPU 維運經驗、API 較划算</span></span></code></pre></div><p><strong>Breakeven 點通常在「持續高 utilization + 團隊有維運能力」</strong>——尖峰流量短的應用、或團隊無 GPU 維運經驗、API 更划算（不用養閒置 capacity 跟 SRE 人力）。實際判讀還要加合規 / 資料主權 / vendor lock-in 等非價格因素。</p>
<h3 id="hidden-cost">Hidden cost</h3>
<p>容易漏算的：</p>
<ul>
<li><strong>Egress bandwidth</strong>：cloud GPU instance 出流量、AWS / GCP 都 $/GB</li>
<li><strong>Storage</strong>：vector DB / log retention / metric retention</li>
<li><strong>失敗 retry</strong>：5xx error 自動 retry、token 重算</li>
<li><strong>Cold start</strong>：scale-to-zero 設定、cold start 浪費 5-30 秒 GPU time / 次</li>
</ul>
<h2 id="維度-4storage--vector-db">維度 4：Storage / Vector DB</h2>
<p>本地 <a href="/blog/llm/knowledge-cards/rag/" data-link-title="RAG" data-link-desc="Retrieval-Augmented Generation：動態外掛知識給 LLM、繞開模型參數記憶的靜態限制">RAG</a> demo 用 pickle、production 不行——pickle 不支援並發 read、不支援 update、不支援 partition、必須換 <a href="/blog/llm/knowledge-cards/vector-database/" data-link-title="Vector Database" data-link-desc="為高維向量 (embedding) 設計的儲存 &#43; 近似最近鄰 (ANN) 檢索系統：RAG 從 prototype 跨到 production 的關鍵元件">vector database</a>。</p>
<h3 id="vector-db-的設計取捨">Vector DB 的設計取捨</h3>
<table>
  <thead>
      <tr>
          <th>維度</th>
          <th>取捨</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong>Hosted vs self-host</strong></td>
          <td>Hosted（Pinecone、Weaviate Cloud）省維護、self-host 控制成本</td>
      </tr>
      <tr>
          <td><strong>In-memory vs disk-based</strong></td>
          <td>In-memory 快但記憶體限制、disk-based 大但 latency 高</td>
      </tr>
      <tr>
          <td><strong>HNSW vs flat</strong></td>
          <td>HNSW 近似但 sublinear、flat 精確但 linear</td>
      </tr>
      <tr>
          <td><strong>Update strategy</strong></td>
          <td>Periodic batch index rebuild vs incremental update</td>
      </tr>
  </tbody>
</table>
<p>具體選型半年一變、本章不展開。<strong>設計時要回答的問題</strong>：</p>
<ol>
<li>Corpus 多大？1M 以下 in-memory 就好、1M 以上要 disk-based</li>
<li>Update 頻率？每天一次 vs 即時、影響 architecture</li>
<li>Latency target？&lt; 50 ms 要 in-memory / HNSW、&lt; 200 ms 用 disk-based</li>
<li>並發 query 量？每秒 100 query 跟每秒 10000 query 設計完全不同</li>
</ol>
<h3 id="index-大小成長">Index 大小成長</h3>
<p>從 hands-on 章節 extrapolate：</p>
<table>
  <thead>
      <tr>
          <th>Corpus 規模</th>
          <th>Index 大小（含 chunks + embeddings）</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>1K docs</td>
          <td>~50 MB</td>
      </tr>
      <tr>
          <td>100K docs</td>
          <td>~5 GB</td>
      </tr>
      <tr>
          <td>1M docs</td>
          <td>~50 GB</td>
      </tr>
      <tr>
          <td>10M docs</td>
          <td>~500 GB</td>
      </tr>
      <tr>
          <td>100M docs</td>
          <td>~5 TB</td>
      </tr>
  </tbody>
</table>
<p>10M docs 以上、單機（256GB RAM、商用 SSD）放不進 in-memory index、要 sharding + 分散式 index。</p>
<h2 id="維度-5observability">維度 5：Observability</h2>
<p>Single-user <code>tail log</code> 不夠 production 用。要看的 metric：</p>
<h3 id="latency-metrics">Latency metrics</h3>
<ul>
<li><strong>TTFT (Time to First Token)</strong>：user-perceived「響應時間」、streaming 場景關鍵</li>
<li><strong>TPS (Tokens per second)</strong>：generation 速度</li>
<li><strong>End-to-end latency</strong>：含 retrieval + LLM + post-processing</li>
<li><strong>Per-percentile breakdown</strong>：p50 / p90 / p95 / p99——p99 反映最差 user 體驗</li>
</ul>
<h3 id="throughput-metrics">Throughput metrics</h3>
<ul>
<li><strong>Requests per second</strong>：API 端 RPS</li>
<li><strong>Tokens per second</strong>（aggregate）：GPU 整體 throughput</li>
<li><strong>Queue depth</strong>：等待 batch 的 request 數量、暴漲表示 overload</li>
</ul>
<h3 id="cost-metrics">Cost metrics</h3>
<ul>
<li><strong>$ per active user per day</strong>：產品經濟學基本盤</li>
<li><strong>Cost per session</strong>：互動式應用單位成本</li>
<li><strong>Cache hit rate</strong>：prompt cache / embedding cache 命中率、直接影響 cost</li>
</ul>
<h3 id="quality-metrics">Quality metrics</h3>
<ul>
<li><strong>Refusal rate</strong>：模型 refuse 回應的比例</li>
<li><strong>Hallucination rate</strong>：（要 reviewer 標）</li>
<li><strong>User feedback score</strong>：thumb up / down</li>
</ul>
<h3 id="工具metrics--traces--logs-三層">工具：metrics / traces / logs 三層</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">Metrics（Prometheus / Datadog / CloudWatch）
</span></span><span class="line"><span class="ln">2</span><span class="cl">    → time-series、aggregate、適合 alerting
</span></span><span class="line"><span class="ln">3</span><span class="cl">Traces（OpenTelemetry / Datadog APM）
</span></span><span class="line"><span class="ln">4</span><span class="cl">    → per-request、可追蹤跨服務 latency
</span></span><span class="line"><span class="ln">5</span><span class="cl">Logs（structured JSON、推 ELK / Loki）
</span></span><span class="line"><span class="ln">6</span><span class="cl">    → 詳細 context、debug 用</span></span></code></pre></div><p>三層各司其職、各層保留專屬職責：metric 看到 p99 漲、用 trace 找哪個 request 哪段慢、用 log 看那 request 的具體 prompt / response。</p>
<h2 id="維度-6reliability--sla">維度 6：Reliability / SLA</h2>
<h3 id="可預期的失敗模式">可預期的失敗模式</h3>
<table>
  <thead>
      <tr>
          <th>失敗類型</th>
          <th>處理</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong>Transient GPU OOM</strong></td>
          <td>retry with smaller batch、circuit breaker</td>
      </tr>
      <tr>
          <td><strong>Inference timeout</strong></td>
          <td>切短 max_tokens、拒絕過長 prompt</td>
      </tr>
      <tr>
          <td><strong>Model server crash</strong></td>
          <td>health check + auto-restart（systemd / k8s）</td>
      </tr>
      <tr>
          <td><strong>Vector DB unavailable</strong></td>
          <td>fallback：跳過 RAG、純 chat 答</td>
      </tr>
      <tr>
          <td><strong>Upstream API rate limit</strong></td>
          <td>exponential backoff + jitter</td>
      </tr>
  </tbody>
</table>
<h3 id="graceful-degradation">Graceful degradation</h3>
<p>設計 production LLM 應用、要回答「失敗時降級到什麼」：</p>
<table>
  <thead>
      <tr>
          <th>Component down</th>
          <th>Acceptable degradation</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Vector DB</td>
          <td>用 LLM 內知識回答 + 標明「未查最新文件」</td>
      </tr>
      <tr>
          <td>RAG retrieval 但 LLM 仍跑</td>
          <td>用退役 cache 結果 + retry</td>
      </tr>
      <tr>
          <td>Primary LLM API</td>
          <td>fallback 到 secondary（OpenAI ↔ Anthropic ↔ 本地）</td>
      </tr>
      <tr>
          <td>全部 down</td>
          <td>顯示維護頁、回 503 + Retry-After、避免直接 5xx</td>
      </tr>
  </tbody>
</table>
<p>在 SLA 承諾下、每個 fallback 路徑都要事前設計、避免出事時臨時決策（早期 prototype / 內部工具可接受 reactive 處理、production 階段不行）。</p>
<h3 id="capacity-planning">Capacity planning</h3>
<p>簡單公式：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">Required capacity = peak_concurrent_users × per_user_RAM
</span></span><span class="line"><span class="ln">2</span><span class="cl">                  × overhead_factor (1.3-1.5)
</span></span><span class="line"><span class="ln">3</span><span class="cl">                  × redundancy_factor (2x for HA)</span></span></code></pre></div><p>例：peak 100 並發、每 user ~500 MB KV cache、overhead 1.3、HA 2x → 130 GB GPU memory。一張 H100 不夠、要兩張 A100 80GB 或 H100 + sharding。</p>
<h2 id="跟本地-hands-on-的對照">跟本地 hands-on 的對照</h2>
<table>
  <thead>
      <tr>
          <th>維度</th>
          <th>本地 hands-on 紀錄</th>
          <th>Production 該量什麼</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Single-user latency</td>
          <td>30-60s for SDXL、5-20s for chat</td>
          <td>p50 / p95 / p99 latency</td>
      </tr>
      <tr>
          <td>Index size</td>
          <td>~3.7 MB / 463 chunks</td>
          <td>sharded index、GB-TB 規模</td>
      </tr>
      <tr>
          <td>Process management</td>
          <td><code>pkill -9</code></td>
          <td>systemd / k8s liveness probe</td>
      </tr>
      <tr>
          <td>Disk cleanup</td>
          <td>手動 <code>ollama rm</code></td>
          <td>自動 retention policy</td>
      </tr>
      <tr>
          <td>Cost</td>
          <td>一次性硬體</td>
          <td>$/token / day budget alerts</td>
      </tr>
      <tr>
          <td>Observability</td>
          <td><code>tail log</code></td>
          <td>Prometheus + Grafana / Datadog</td>
      </tr>
      <tr>
          <td>Failure response</td>
          <td>自己重啟</td>
          <td>auto-recover + alert + runbook</td>
      </tr>
  </tbody>
</table>
<p>本地數字是「能跑」的證明、production 數字是「能用」的驗證。本地驗證完 architecture 後、production deployment 該重做 load test、不能 assume 線性 scale。</p>
<h2 id="跨-framework-不變的設計問題">跨 framework 不變的設計問題</h2>
<p>不管你用 vLLM / TGI / Triton / SGLang / OpenAI API、production 設計都要回答：</p>
<ol>
<li><strong>Latency vs throughput</strong>：哪個是主要 metric？</li>
<li><strong>Batch strategy</strong>：static / continuous / per-request？</li>
<li><strong>Cost ceiling</strong>：$/day budget 多少？超過怎麼處理？</li>
<li><strong>Storage</strong>：vector DB 規模？update 頻率？</li>
<li><strong>Observability</strong>：哪些 metric 是 alert worthy？</li>
<li><strong>Reliability</strong>：failure mode + graceful degradation 設計</li>
<li><strong>Capacity</strong>：peak + redundancy 需要多少 GPU memory</li>
</ol>
<p>這 7 個問題回答一致時、framework 選擇通常不是 production 失敗的根因——資源評估跟設計取捨已對齊、framework 多半是配套選項。</p>
<h2 id="何時這篇會過時">何時這篇會過時</h2>
<p><strong>不會過時的部分</strong>：</p>
<ul>
<li>6 個維度（concurrency / latency / cost / storage / observability / reliability）</li>
<li>Latency-sensitive vs throughput-sensitive 應用的設計差異</li>
<li>三類計費單位的取捨</li>
<li>Metrics / traces / logs 三層觀測</li>
<li>Graceful degradation 設計</li>
</ul>
<p><strong>會變的部分</strong>：</p>
<ul>
<li>具體 inference framework（vLLM / TGI / SGLang 等）的 ranking</li>
<li>雲端 API price tier</li>
<li>哪些 vector DB 主流</li>
</ul>
<p>新 framework 出來時、回到 6 維度 framework 問：它在哪個維度有突破？對既有設計問題的答案有沒有改變？通常會發現核心問題沒變、只是工具更熟。</p>
<h2 id="跟其他章節的關係">跟其他章節的關係</h2>
<ul>
<li><a href="/blog/llm/01-local-llm-services/hands-on/rag-mcp-resources/" data-link-title="Hands-on：RAG / MCP 的資源 footprint" data-link-desc="RAG ingest / query / MCP server 三階段的 RAM / 磁碟 / process 實測、多模型並存的 RAM 衝突、本地 LLM 跑 RAG 跟單純 chat 的差異">hands-on RAG/MCP 資源</a>：本地 baseline 數字、本章的 production extrapolation 起點</li>
<li><a href="/blog/llm/04-applications/rag-principles/" data-link-title="4.1 RAG 原理：retrieval &#43; augmentation 模式" data-link-desc="為什麼模型需要外掛知識、語意相似 vs 字面相似、chunking 的本質取捨、retrieval 失敗的根本原因">4.1 RAG</a> / <a href="/blog/llm/04-applications/tool-use-principles/" data-link-title="4.3 Tool use 原理：LLM 跟外部世界互動" data-link-desc="Structured output 是 LLM 跨入工程系統的橋、function calling 取捨、為什麼本地小模型 tool use 表現崩潰">4.3 Tool use</a> / <a href="/blog/llm/04-applications/agent-architecture/" data-link-title="4.4 Agent 架構原理" data-link-desc="Agent loop 結構、失敗模式、什麼任務適合 vs 不適合、跟人類審查的協作模型">4.4 Agent</a>：應用層設計、本章是「應用如何跑」的補完</li>
<li><a href="/blog/llm/00-foundations/hardware-memory-budget/" data-link-title="0.5 Apple Silicon 記憶體預算" data-link-desc="記憶體決定能跑什麼，Q4 量化下的可運作模型對照與系統保留">0.5 硬體記憶體預算</a>：本地單機 perspective、本章對應 multi-machine production</li>
<li><a href="/blog/llm/01-local-llm-services/troubleshooting/" data-link-title="1.7 排錯方法論：用三層架構做故障定位" data-link-desc="故障定位的分層思考、症狀到層級的對應反射、log 在三層的角色差異、最小可重現的縮減策略">1.7 排錯方法論</a>：本地 trouble-shooting、本章是 production observability 的對照</li>
</ul>
]]></content:encoded></item></channel></rss>