跳转至

本地 LLM 推理架構:Ollama、llama.cpp、GGUF 與效能測試

這次用 Qwen3:4B 跑本地推理,最重要的收穫是看懂整套系統:上層有 Agent 和 API,中間是 Ollama 與推理 runtime,底下則是 GGUF 模型、量化設定和實際硬體。幾次簡單測試也讓我看到,回答時間除了受 generation 速度影響,也和輸出長度及 thinking 內容的多寡有關。

我有一臺 2024 年的電腦,配備 AMD Ryzen 8040 系列處理器和 64GB RAM。因為一些原因,它現在處於半退休狀態。我一直在想,這臺規格仍然不差的電腦,還能拿來做什麼?

我平常會使用 ChatGPT,也用過它的 API,卻從來沒有親手在本機跑過 LLM。Ollama、GGUF、量化與 GPU offloading 這些名詞看過不少,真正連在一起時,我仍然說不清楚它們各自負責哪一層。

於是我決定用這臺電腦跑本地模型。ChatGPT 建議先試 Qwen3:4B,我就從這個模型開始。

實際動手之後,我想弄懂的問題逐漸變得具體:

當我在本地輸入一句 prompt 之後,整個 AI 系統到底經過了哪些層,最後才把答案送回來?

Ollama:本地模型執行平台

最初我對 Ollama 的理解很簡單。

我以為它就像是一個模型管理器:

  1. 執行 ollama pull
  2. 把模型下載下來
  3. 使用 ollama run 指令啟動模型並進入互動模式
  4. 模型就可以在本地回答問題

從使用者角度來看,這個理解並沒有完全錯。

Ollama 的確幫我們處理了許多事情,包括:

  • 下載和管理模型
  • 啟動本地推理服務
  • 選擇模型設定
  • 提供 HTTP API
  • 管理模型載入與記憶體
  • 將底層推理細節包裝成比較簡單的使用方式

Ollama 像是一個位於上層的本地模型執行平台。

它把模型檔案、推理引擎、參數設定、API 服務和模型生命週期包裝在一起,讓使用者不需要直接面對底層的複雜度。

llama.cpp:常用的底層推理技術之一

llama.cpp 是一個用 C/C++ 實作的本地大型語言模型推理專案。

llama.cpp 是 Ollama 常用的底層推理技術之一,也是理解 GGUF 本地推理生態的重要入口。

它負責的是更底層的工作,例如:

  • 載入模型權重
  • 將文字轉換成 token
  • 建立與管理 KV cache
  • 執行 Transformer 的 forward pass
  • 使用 CPU 或 GPU 進行矩陣運算
  • 逐一產生新的 token
  • 控制 sampling,例如 temperature、top-p 和 top-k

換句話說,Ollama 比較像是一個已經整合好的播放器,而 llama.cpp 則是可能在播放器內部負責解碼與處理資料的核心技術之一。

使用 Ollama 時,我通常不需要直接操作 llama.cpp。

但理解 llama.cpp 的存在,讓我第一次看見了 Ollama 包裝之下的那一層。

我也開始明白,當模型速度慢、GPU 使用率低,或者記憶體不足時,問題不一定只是「模型不好」。

它可能發生在不同地方:

  • 模型太大
  • 量化格式不適合
  • GPU VRAM 不足
  • 只有部分 layer 被 offload 到 GPU
  • 其餘計算仍然由 CPU 執行
  • context 太長,KV cache 佔用太多記憶體
  • prompt processing 和 token generation 的瓶頸不同
  • 推理引擎沒有充分支援目前的硬體

這使我開始從「選模型」轉向「理解系統」。

GGUF:模型的檔案格式

另一個我這週經常看到的詞是 GGUF。

一開始看到像這樣的名稱:

Qwen3-4B-Q4_K_M.gguf

我很容易把整串名稱都當成模型名稱。

但後來我才拆解出來:

  • Qwen3 是模型家族
  • 4B 大約代表參數規模
  • Q4_K_M 是一種量化方式
  • .gguf 是模型檔案格式

GGUF 是一種用來儲存模型權重與相關 metadata 的檔案格式,常被 llama.cpp 生態系統使用。

它可以包含:

  • 模型權重
  • tokenizer 資訊
  • 模型架構資訊
  • context 相關設定
  • 特殊 token
  • 量化後的 tensor 資料

所以可以把這幾個概念分開:

Qwen3 是模型,GGUF 是儲存和發布模型的格式,llama.cpp 是可載入並執行它的推理技術之一,而 Ollama 則是在更上層管理和提供服務的平台。

量化的目的,是讓大型模型能在有限硬體上實際執行

為了讓大型模型可以在一般電腦上執行,模型通常會經過量化。

模型原始權重可能使用 FP16 或 BF16 儲存,而量化會使用較少的 bit 表示權重,例如 8-bit、6-bit、5-bit 或 4-bit。

這樣做的主要目的,是降低:

  • 模型檔案大小
  • RAM 使用量
  • VRAM 使用量
  • 記憶體頻寬需求

例如,同樣是一個 4B 模型,FP16 和 4-bit 量化版本所需要的記憶體可能差異很大。

但是量化並不是免費的。

量化程度越高,通常越有可能損失一部分模型精度。不同量化方法在速度、記憶體和品質之間,也會有不同的平衡。

因此,Q4_K_M 代表了一種工程上的選擇:

我願意犧牲多少精度,來換取更低的記憶體需求和更高的本機執行可行性?

這也是本地模型和雲端模型很不一樣的地方。

使用雲端 API 時,我通常只需要選擇模型名稱。

但在本地環境中,我還必須思考:

  • 使用哪個模型大小
  • 使用哪種量化
  • context window 設定多大
  • 有多少 layer 放到 GPU
  • CPU 與 GPU 如何分工
  • 系統記憶體是否足夠
  • 速度和品質之間如何取捨

完整的本地推理流程

下面這張圖先把整個本地 AI 系統分成幾個主要層次:

flowchart TB
    U[使用者] --> A[聊天介面或 Agent<br/>Hermes / OpenClaw]
    A --> API[OpenAI-compatible API]
    API --> O[Ollama<br/>模型管理與服務層]
    O --> R[推理 Runtime<br/>例如 llama.cpp]
    R --> M[Qwen3 模型<br/>GGUF / Quantized Weights]

    subgraph H[本地硬體]
        CPU[CPU]
        GPU[GPU]
        RAM[RAM]
        VRAM[VRAM]
    end

    M --> R
    CPU --> R
    GPU --> R
    RAM --> R
    VRAM --> R

從上層的 Agent 到底層的 CPU 和 GPU,每一層負責的工作都不同。Ollama 並不是模型本身,而是連接 API、推理引擎、模型檔案與硬體資源的服務層。

現在,我對本地推理流程的理解大致可以分成幾層。

第一層:應用程式或 Agent

最上層是使用模型的應用程式,例如聊天介面、程式設計 Agent,或者 OpenClaw

這一層通常負責:

  • 組合 system prompt
  • 保留對話歷史
  • 呼叫工具
  • 管理 Agent loop
  • 將請求送到模型 API
  • 解析模型回傳的內容

Agent 不負責實際的神經網路推理,它只需要知道應該向哪一個 endpoint 發送請求。

第二層:API 相容層

下一層可能是一個 OpenAI-compatible API,例如:

http://localhost:11434/v1/chat/completions

這層的作用,是讓原本連接 OpenAI 或 Codex 的應用程式,可以用類似的請求格式連接本地模型。

例如請求中可能包含:

  • model
  • messages
  • temperature
  • tools
  • stream
  • max_tokens

這也是為什麼我可以嘗試把 Hermes Agent 原本連接的遠端模型,改成連接本機上的 Ollama。

不過,「API 格式相容」不代表所有能力都完全相同。

一個系統可能支援 Chat Completions API,但不一定完整支援 Responses API、tool calling、reasoning 欄位或其他 Codex 行為。

所以除了 endpoint 能不能連通之外,還要確認上層 Agent 所期待的協定和功能。

第三層:模型管理與服務層

這一層可以是 Ollama。

它接收 HTTP 請求,找到指定的模型;如果模型尚未載入記憶體,Ollama 會先完成載入,再管理推理工作,最後把結果以 API 回應的形式傳回去。已經駐留記憶體的模型則可以直接處理新的請求。

它將底層複雜度隱藏起來,讓上層應用程式不需要知道模型檔案實際放在哪裡,也不需要自己管理推理程序。

第四層:推理引擎

再往下,是負責執行模型計算的推理引擎,例如採用 llama.cpp 技術的 runtime。

sequenceDiagram
    participant User as 使用者
    participant Agent as Agent / 聊天介面
    participant Ollama as Ollama API
    participant Runtime as 推理 Runtime(例如 llama.cpp)
    participant Model as Qwen3 / GGUF
    participant Hardware as CPU / GPU

    User->>Agent: 輸入 Prompt
    Agent->>Agent: 組合 System Prompt 與對話歷史
    Agent->>Ollama: 發送 Chat Completions 請求

    alt 模型尚未駐留記憶體
        Ollama->>Runtime: 載入模型
        Runtime->>Model: 讀取模型權重與 Tokenizer
    end

    Ollama->>Runtime: 傳送 Prompt 與推理設定
    Runtime->>Runtime: 將 Prompt Tokenize

    rect rgb(235, 235, 235)
        Note over Runtime,Hardware: Prompt Evaluation
        Runtime->>Hardware: 處理所有輸入 Token
        Hardware-->>Runtime: 回傳模型計算結果
        Runtime->>Runtime: 建立 KV Cache
    end

    loop 每次產生一個新 Token
        Runtime->>Hardware: 執行 Transformer Forward Pass
        Hardware-->>Runtime: 回傳 Logits
        Runtime->>Runtime: Sampling 選擇下一個 Token
        Runtime->>Runtime: 更新 Context 與 KV Cache
        Runtime-->>Ollama: 串流輸出 Token
        Ollama-->>Agent: 回傳部分內容
        Agent-->>User: 顯示產生中的答案
    end

這一層主要會:

  1. 將 prompt tokenize
  2. 處理輸入 token
  3. 建立 attention 所需要的 KV cache
  4. 執行模型每一層的計算
  5. 算出下一個 token 的機率分布
  6. 根據 sampling 規則選出 token
  7. 將新 token 加回輸入
  8. 重複計算,直到完成回答

這裡也可以區分兩種工作。

第一種是 prompt evaluation,也就是處理一開始送入的所有輸入 token。

第二種是 token generation,也就是模型一個 token、一個 token 地產生答案。

兩者的效能特性並不完全一樣。

第五層:模型與權重

最底下才是模型本身。

例如 Qwen3 4B 的架構、參數和 tokenizer。

模型可能以 GGUF 格式儲存,並且已經經過某種量化。

模型決定了它具備什麼能力,而量化與推理設定則影響它在目前硬體上的實際表現。

第六層:硬體

最後,所有計算仍然需要落到實際硬體上:

  • CPU
  • GPU
  • RAM
  • VRAM
  • 記憶體頻寬
  • PCIe 資料傳輸

如果 GPU 無法容納完整模型,推理引擎可能只把部分 layer 放到 GPU,其他 layer 留在 CPU。

這並不代表模型完全不能執行,但速度可能比預期慢很多。

所以「模型是否支援我的 GPU」其實不是一個單純的是非題。

更準確的問題應該是:

這個模型在目前的推理引擎、量化格式、offloading 設定和硬體資源下,能以什麼速度執行,又能維持多少品質?

我在寫這篇文章的時候,剛好是 2026 年 7 月 17 日,也是半導體股票大跌的一天。

可是我發現,以我們目前硬體的狀況,要在一般個人電腦上跑出接近最前沿雲端模型能力的本地模型,其實還有相當長的一段距離。未來無論是晶片運算能力、記憶體頻寬、功耗,還是整體硬體架構,都還需要繼續進步。

繼續 ALL IN 半導體。

簡單實測

理解整套架構後,我們也用 Qwen3:4B 做了幾次簡單測試。模型透過 Ollama 執行,運算以 CPU 為主,context 設為 4096;我們重複送出相同問題,觀察總時間、輸出 token 數量和 generation 速度。這些結果主要用來了解這臺電腦的實際表現,不算完整、可重現的 benchmark。

Ollama API 會回傳幾個重要的時間與 token 統計欄位:

  • total_duration:整個請求花費的時間
  • prompt_eval_count:模型處理的輸入 token 數量
  • prompt_eval_duration:模型處理輸入 prompt 的時間
  • eval_count:模型產生的 token 數量;在這次使用的 Ollama 與 Qwen3 組合中,觀察到的數值包含 thinking 內容與最終回答
  • eval_duration:模型產生輸出 token 的時間

本文的 prompt processing 與 generation 速度,是用各自的 token 數除以時間換算出的 tokens per second。

最明顯的現象是,即使 generation 速度相近,回答時間仍可能從約 7 秒增加到 47.6 秒。差異主要來自輸出長度:較短的一次 eval_count 約為 142,較長的一次則為 918;在這次測試中,這些數值也涵蓋 thinking 內容。因此,判斷本地模型是否夠快,除了 tokens per second,還要看模型最後產生了多少內容。

結語:我開始學的不是某一個工具,而是一套系統

我一開始只是想善用這臺半退休的電腦,找一個適合目前 GPU 的模型。如果 Qwen3 4B 跑得不理想,我就想換一個。實際動手之後,我才發現,現階段更值得學的是整套系統如何運作,至於哪個模型最好,可以晚一點再決定。

現在遇到「模型跑不起來」或「模型很慢」時,我可以沿著架構逐層排查:Agent 組出的請求是否正確、API 協定是否相容、Ollama 有沒有載入模型、推理引擎是否使用 GPU、量化版本是否適合,以及瓶頸究竟來自硬體還是過長的模型輸出。

當我再次輸入一段 prompt 時,我開始能想像,這段文字如何被轉換成 token,如何穿過 API、推理服務和模型層,如何在 CPU 與 GPU 上被計算,最後再一個 token、一個 token 地變成答案。

而當模型花了很長時間回答時,我也開始知道,應該進一步觀察:究竟是硬體真的很慢,還是模型只是想得太多。

這或許才是我真正開始理解本地 AI 系統的第一步。