斷網也能寫程式:在 macOS 上用 Qwen3.6 + llama.cpp + Pi 建立完全離線的 AI Coding Agent

本篇文章更新時間:2026/06/14
如有資訊過時或語誤之處,歡迎使用 Contact 功能通知或向一介資男的 LINE 社群反應。
如果本站內容對你有幫助,歡迎贊助支持


前言:當網路斷了,你的 Coding Agent 還在嗎?

現代開發者越來越依賴 AI Coding Agent — Claude Code、Cursor、Kiro、Copilot。但這些工具有一個共同的致命弱點:它們全部依賴網路

網路斷了、API 額度用完了、飛機上想寫 code、或者你單純不想把私人專案的程式碼送到別人的伺服器 — 這些情境都會讓你的 AI 助手瞬間消失。

這篇文章記錄的是:如何在你的 Mac 上建立一個完全離線、速度可用、免費的 AI Coding Agent。不是玩具等級的「跑個模型回答問題」,而是一個真的能讀寫檔案、執行命令、處理多檔案任務的完整 coding agent。

最終成果:在 Apple M5 Pro 64GB 上,達到 84.9 tokens/sec 的生成速度,搭配一個支援工具呼叫、截圖輸入、128K context 的本地 coding agent。


技術堆疊總覽

層級 選擇 角色
推論引擎 llama.cpp 在 Mac 上跑 GGUF 模型,提供 OpenAI 相容 API
硬體加速 Metal + Accelerate Apple GPU + 矩陣運算加速
主模型 Qwen3.6-35B-A3B (Q5_K_XL) 35B 參數 MoE,每次只啟用 3B,coding 能力極強
加速技術 MTP (Multi-Token Prediction) 模型內建的投機解碼,加速 1.3-1.7x
多模態 mmproj-BF16.gguf 支援圖片/截圖輸入
Coding Agent Pi 終端機 coding agent,支援工具呼叫、檔案讀寫

為什麼選 Qwen3.6 而不是 Gemma 4?

2026 年中,能在 Mac 上本地跑的 coding-capable 模型主要有兩個選擇:Google 的 Gemma 4 26B-A4B 和阿里巴巴的 Qwen3.6 35B-A3B。兩者都是 Mixture-of-Experts 架構,都能在 64GB Mac 上順跑。

但 coding 能力差距巨大:

Benchmark Qwen3.6 35B-A3B Gemma 4 26B-A4B
SWE-bench Verified(真實 GitHub issue) 73.4% 17.4%*
SWE-bench Multilingual 78.9% 74.3%
每次推論啟用參數 3B 4B

* Gemma 4 26B-A4B 在 SWE-bench Verified 上僅 17.4%,而同系列 dense 架構的 Gemma 4 31B 為 52.0%。數據來源為 Qwen 官方 benchmark

Qwen3.6 在真實軟體工程任務上碾壓 Gemma 4。啟用的參數還更少,理論上推論更快。唯一的缺點是:因為總參數量較大(35B vs 26B),模型檔案稍大,在記憶體頻寬受限的情境下會稍慢一些。

Gemma 4 的優勢在哪?速度。在同一台 M1 Max 上,Gemma 4 搭配 MTP 可以跑到 72 tok/s,而 Qwen3.6 是 55 tok/s。如果你追求極致回應速度且 coding 品質要求不那麼高,Gemma 4 是合理的選擇。但如果你要的是一個真正能解決問題的 coding agent,Qwen3.6 是目前的最佳本地選擇。


為什麼用 llama.cpp 而不是 MLX?

這個問題在 2026 年中的答案比你想像的複雜。簡單版:因為 MTP 加速在 llama.cpp 上最成熟穩定,且 llama-server 原生提供 coding agent 需要的 OpenAI 相容 API。但完整的技術脈絡值得講清楚。

先講結論不對的部分:MLX 不慢

Apple 的 MLX 框架在 Apple Silicon 上的基礎推論速度其實比 llama.cpp 快。多份獨立測試都證實了這點:

  • vllm-mlx 論文顯示 MLX 在文字模型上比 llama.cpp 快 21-87%
  • Codersera 2026 對比中,Mac mini M4 Pro 跑 Qwen3-Coder-30B-A3B(MoE 模型),MLX 達 ~130 tok/s,raw llama.cpp Metal 約 ~89 tok/s
  • Ollama 自 0.19 版(2026 年 3 月)起,在 Apple Silicon 上已改用 MLX 作為底層引擎,decode 速度從 58 提升到 112 tok/s(93% 加速)

MLX 是 Apple 專為自家晶片設計的機器學習框架,在 Apple Silicon 上的推論速度優於 llama.cpp 的 Metal 後端。具體原因可參考 vllm-mlx 論文的分析。

那為什麼不用 MLX?三個具體原因

1. MTP 支援成熟度

MTP 是這套方案的速度關鍵(後面會詳細解釋)。到 2026 年 6 月的狀態是:

  • llama.cpp:MTP 支援穩定,--spec-type draft-mtp 直接讀取 GGUF 內建的 MTP heads,無需額外設定。社群已有大量實測數據和最佳化參數。
  • MLXmlx-lm 0.21(2026 年 5 月)加入了 speculative decoding 支援mlx-community/Qwen3.6-35B-A3B-MTP-4bit 也已上架 HuggingFace。但相較 llama.cpp 的 MTP 實作,這是較新的功能,可參考的實測數據和設定範例明顯較少。

一份 Kyle Howells 的實測(M1 Max 64GB, Gemma 4 26B-A4B)顯示,llama.cpp + MTP 達到 72.2 tok/s,而同期的 MLX(無 MTP)僅 43-46 tok/s:

Runtime 生成速度 (tok/s) 備註
llama.cpp + MTP 72.2 MTP 接受率帶來 1.24x 加速
llama.cpp(無 MTP) 58.2 純 Metal 推論
MLX (Unsloth UD 4-bit) 45.8 無 MTP
MLX (mlx-community 4-bit) 43.9 無 MTP

注意這個對比的前提:MLX 端沒有啟用 MTP。如果 MLX 也啟用 MTP,理論上應該比 llama.cpp + MTP 更快(因為 MLX 的基礎速度更高)。但在撰文時,MLX + MTP + Qwen3.6 這個具體組合尚未有可靠的第三方 benchmark 數據佐證其穩定性。

2. Serving 架構

Coding agent 需要一個 OpenAI 相容的 HTTP API endpoint。llama.cpp 的 llama-server 原生提供 /v1/chat/completions,支援 function calling、multimodal(圖片輸入)、streaming — 這些都是 Pi 等 agent 工具的硬需求。

MLX 端的 mlx_lm.server 也有 OpenAI 相容 API,但在工具呼叫格式(tool_choice、parallel tool calls)和多模態(mmproj 載入方式)上,與 llama-server 的成熟度仍有差距。Kyle Howells 的教學和 HuggingFace 上的 Pi 整合範例(如 DuoNeural)均使用 llama-server 作為 Pi 的後端。

3. GGUF 生態系的量化選擇

GGUF 格式提供從 Q2 到 Q8 的完整量化光譜,Unsloth 的 UD 動態量化更能精細地對每層 tensor 分配位元數。MLX 格式的量化選項較少(常見為 3-bit、4-bit、8-bit),且不支援 Unsloth UD 這種 per-tensor 動態位元分配。Q5_K_XL 這種介於 Q4 和 Q6 之間的「甜蜜點」量化,目前僅存在於 GGUF 生態系。

對 64 GB Mac 來說,Q5_K_XL 是最佳平衡點。這個量化等級只在 GGUF 生態系中有。

什麼時候該改用 MLX?

這個技術決策不是永恆的。以下情況出現時,值得重新評估:

  • MLX + MTP + Qwen3.6 組合有穩定的第三方 benchmark 證實其速度和穩定性
  • mlx_lm.server 的 tool calling 和 multimodal 支援追上 llama-server
  • Pi 或你使用的 coding agent 原生支援 MLX serving
  • 你需要在同一台 Mac 上做 LoRA 微調(MLX 的訓練工具鏈遠勝 llama.cpp)

技術選擇的核心原則:選穩定可用的方案,而不是理論最快的方案。 2026 年中,llama.cpp + MTP 是 coding agent 場景下經過最多實戰驗證的路線。


量化等級怎麼選?

什麼是量化?

原始模型用 16-bit 浮點數儲存權重,一個 35B 參數的模型需要約 70GB。量化是把精度降低(例如 4-bit、5-bit),用更少的空間存放模型,換取可以在消費級硬體上跑。

Unsloth 的 UD(Unsloth Dynamic)量化特別之處在於:它不是全部用同一個精度,而是對每層的每個 tensor 分析重要度,動態分配不同的位元數。重要的 tensor 保留高精度,不重要的壓更低。這讓同樣的檔案大小能保留更多品質。

選擇建議

量化等級 檔案大小 適合硬體 品質
Q4_K_XL ~21 GB 24 GB VRAM / 32 GB Mac 日常夠用
Q5_K_XL ~25 GB 64 GB Mac(推薦) 品質甜蜜點
Q6_K_XL ~33 GB 64 GB+ Mac 接近無損但速度降
Q8_K_XL ~39 GB 雙 GPU / 96 GB Mac 近乎無損

如果你有 64 GB 統一記憶體,Q5_K_XL 是最佳選擇。比 Q4 在長推理鏈和複雜程式碼生成上更穩定,但只多佔 4 GB。Q6 以上的品質提升在 coding 任務中已不明顯,卻要付出更多的速度代價。


MTP 是什麼?為什麼重要?

傳統的語言模型生成是「一次一個 token」— 生成一個、確認一個、再生成下一個。瓶頸不在計算,而在記憶體頻寬:每生成一個 token 都要把整個模型的權重從記憶體讀一遍。

MTP(Multi-Token Prediction)的做法是:在模型中額外訓練幾個小型「draft head」(草稿頭),它們會在主模型生成一個 token 的同時,預測接下來的 2-3 個 token。然後主模型驗證這些預測是否正確 — 正確的直接接受,不正確的從第一個錯誤處重新生成。

因為驗證比生成快(可以平行化),只要預測的接受率夠高,就能顯著加速。在我們的實測中:

  • 接受率:72.1%(每 366 個 draft token,264 個被接受)
  • 實際加速:依硬體而異,第三方測試報告 1.24x(Gemma 4, M1 Max)至 1.49x(Qwen3.6, A100)。我們的 M5 Pro 實測 84.9 tok/s,相較無 MTP 的預估基線約有 30-50% 提升。

而且 MTP 不影響輸出品質 — 被接受的 token 和主模型自己生成的完全一致。


Context Window 與記憶體的關係

Context window 決定模型一次能「看到」多少文字。對 coding agent 來說,這包括:系統提示詞 + 對話歷史 + 程式碼檔案內容 + 工具呼叫結果。

更大的 context 需要更多的 KV cache(Key-Value cache)記憶體。不過 Qwen3.6 的混合架構讓這個成本出奇地低:

Qwen3.6 的 40 層中,只有 10 層使用傳統的注意力機制(需要 KV cache),其餘 30 層使用 DeltaNet(線性遞歸架構,其 state 大小固定,不隨 context 長度增長)。因此,隨 context 線性增長的 KV cache 記憶體僅來自 10/40 = 1/4 的層。

實測數據(M5 Pro 64GB):

Context 大小 KV cache 佔用 Server 總記憶體 剩餘給系統
65K ~2.7 GB ~28 GB ~36 GB
128K(推薦) ~5.4 GB ~30 GB ~34 GB
262K(模型上限) ~11 GB ~36 GB ~28 GB

128K context 涵蓋了 99% 的 coding agent 使用情境(大型 repo 上下文、長對話歷史),且在 64 GB Mac 上完全不構成壓力。


完整安裝步驟

前置需求

  • Apple Silicon Mac(M1 以上,建議 32 GB 以上統一記憶體)
  • macOS 14 以上
  • Homebrew 已安裝
  • 約 30 GB 磁碟空間

Step 1:安裝依賴

brew install cmake tmux

cmake 用來編譯 llama.cpp,tmux 用來讓伺服器在背景跑。

Step 2:編譯 llama.cpp

mkdir -p ~/Developer/ML-Models/Qwen36/repos
cd ~/Developer/ML-Models/Qwen36

git clone https://github.com/ggml-org/llama.cpp repos/llama.cpp

cd repos/llama.cpp
cmake -B build \
  -DCMAKE_BUILD_TYPE=Release \
  -DGGML_METAL=ON \
  -DGGML_ACCELERATE=ON

cmake --build build --config Release -j

關鍵 flag 說明:

  • DGGML_METAL=ON — 啟用 Apple GPU 加速
  • DGGML_ACCELERATE=ON — 啟用 Apple Accelerate 框架(BLAS 矩陣運算)
  • -j — 平行編譯,用滿所有 CPU 核心

編譯完成後,build/bin/llama-server 就是我們需要的推論伺服器。

Step 3:下載模型

cd ~/Developer/ML-Models/Qwen36
python3 -m venv .venv
source .venv/bin/activate
pip install -U huggingface_hub hf_xet

mkdir -p models/unsloth-Qwen3.6-35B-A3B-MTP-GGUF

hf download unsloth/Qwen3.6-35B-A3B-MTP-GGUF \
  Qwen3.6-35B-A3B-UD-Q5_K_XL.gguf \
  mmproj-BF16.gguf \
  --local-dir models/unsloth-Qwen3.6-35B-A3B-MTP-GGUF

會下載兩個檔案:

  • Qwen3.6-35B-A3B-UD-Q5_K_XL.gguf(25 GB)— 主模型,內建 MTP heads
  • mmproj-BF16.gguf(861 MB)— 多模態投影器,讓模型能理解圖片

注意: repo 名稱中的 -MTP-GGUF 表示 MTP 已經嫁接在 GGUF 檔案中,不需要額外下載 draft model。

Step 4:建立啟動腳本

建立 ~/Developer/ML-Models/Qwen36/start_server.sh

#!/usr/bin/env bash
set -euo pipefail

ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SESSION_NAME="${SESSION_NAME:-qwen36-server}"
HOST="${HOST:-127.0.0.1}"
PORT="${PORT:-8081}"
CTX_SIZE="${CTX_SIZE:-131072}"
PARALLEL="${PARALLEL:-1}"

LLAMA_SERVER="$ROOT_DIR/repos/llama.cpp/build/bin/llama-server"
MODEL="$ROOT_DIR/models/unsloth-Qwen3.6-35B-A3B-MTP-GGUF/Qwen3.6-35B-A3B-UD-Q5_K_XL.gguf"
MMPROJ="$ROOT_DIR/models/unsloth-Qwen3.6-35B-A3B-MTP-GGUF/mmproj-BF16.gguf"
LOG_FILE="$ROOT_DIR/logs/llama-server.log"

mkdir -p "$ROOT_DIR/logs"

if tmux has-session -t "$SESSION_NAME" 2>/dev/null; then
  echo "Session '$SESSION_NAME' already running. Attach with: tmux attach -t $SESSION_NAME"
  exit 0
fi

tmux new-session -d -s "$SESSION_NAME" -c "$ROOT_DIR" \
  "$LLAMA_SERVER \
    -m '$MODEL' \
    --mmproj '$MMPROJ' \
    --spec-type draft-mtp \
    --spec-draft-n-max 3 \
    -ngl 999 \
    -fa on \
    -c $CTX_SIZE \
    --parallel $PARALLEL \
    --host $HOST \
    --port $PORT \
    2>&1 | tee -a '$LOG_FILE'"

echo "Server started in tmux session '$SESSION_NAME'"
echo "  Endpoint: http://$HOST:$PORT/v1"
echo "  Attach:   tmux attach -t $SESSION_NAME"
echo "  Stop:     tmux kill-session -t $SESSION_NAME"
chmod +x ~/Developer/ML-Models/Qwen36/start_server.sh

關鍵參數說明:

  • --spec-type draft-mtp — 啟用 MTP 投機解碼(使用 GGUF 內建的 draft heads)
  • --spec-draft-n-max 3 — 每步最多預測 3 個 draft token(需要自己 benchmark,2-3 通常最佳)
  • -ngl 999 — 把所有模型層放到 GPU(Metal)上
  • -fa on — 啟用 Flash Attention
  • -c 131072 — 128K context window
  • --parallel 1 — 單一 slot(個人使用不需要多人並行)

Step 5:安裝並設定 Pi Coding Agent

Pi 是一個終端機 coding agent,支援 OpenAI 相容 API、工具呼叫、檔案讀寫和截圖輸入。

npm install -g @earendil-works/pi-coding-agent

建立 ~/.pi/agent/models.json

{
  "providers": {
    "qwen36-local": {
      "name": "Qwen3.6 Local",
      "baseUrl": "http://127.0.0.1:8081/v1",
      "api": "openai-completions",
      "apiKey": "local",
      "authHeader": false,
      "compat": {
        "supportsDeveloperRole": false,
        "supportsReasoningEffort": false
      },
      "models": [
        {
          "id": "Qwen3.6-35B-A3B-UD-Q5_K_XL.gguf",
          "name": "Qwen3.6 35B-A3B Q5 + MTP",
          "reasoning": true,
          "input": ["text", "image"],
          "contextWindow": 131072,
          "maxTokens": 8192,
          "cost": {
            "input": 0,
            "output": 0,
            "cacheRead": 0,
            "cacheWrite": 0
          }
        }
      ]
    }
  }
}

重要設定說明:

  • "input": ["text", "image"] — 告訴 Pi 這個模型支援圖片,否則 Pi 不會傳送截圖
  • "reasoning": true — 啟用 thinking mode,模型會先在 <think> 區塊推理再回答
  • "authHeader": false — 本地伺服器不需要認證
  • "cost" 全設 0 — 本地推論免費

Step 6:驗證

# 啟動伺服器
~/Developer/ML-Models/Qwen36/start_server.sh

# 等待載入完成(約 15 秒)
curl http://127.0.0.1:8081/v1/models

# 確認 Pi 看到本地模型
pi --list-models qwen

# 測試推論
pi -p --provider qwen36-local "Write a Python function to reverse a string"

實測效能

測試環境:Apple M5 Pro, 18 CPU cores, 20 GPU cores, 64 GB 統一記憶體, macOS 26.4

指標 數據
Token 生成速度 84.9 tok/s
Prompt 處理速度 92.5 tok/s
MTP 接受率 72.1%(264/366)
模型記憶體佔用 ~30 GB(含 128K KV cache)
首次載入時間 ~15 秒

84.9 tok/s 意味著每秒輸出約 60-70 個字元,大約半行程式碼。在實際使用中,回應速度與使用雲端 API 的體感差異不大。

對比 Kyle Howells 在 M1 Max 64GB 上的 55 tok/s(同模型 Q4 量化),M5 Pro 的記憶體頻寬提升相當明顯。


日常使用方式

基本工作流程

# 開機後啟動伺服器(只需一次)
~/Developer/ML-Models/Qwen36/start_server.sh

# 進入專案目錄,啟動本地 agent
cd ~/Projects/my-app
pi --provider qwen36-local

模型切換

Pi 支援多個 provider 共存。如果你同時設定了雲端 API,可以隨時切換:

# 用本地 Qwen(離線可用、免費)
pi --provider qwen36-local

# 用雲端 OpenAI(需網路、更強但收費)
pi --provider openai-codex

# 不加 --provider 就用預設
pi

截圖輸入

# 讓 agent 看截圖來調整 UI
pi -p --provider qwen36-local @"~/Desktop/screenshot.png" "這個按鈕的對齊有問題,幫我修正 CSS"

伺服器管理

# 查看伺服器狀態
curl -s http://127.0.0.1:8081/v1/models | head -3

# 查看即時 log
tmux attach -t qwen36-server
# (Ctrl+B, D 脫離)

# 關閉伺服器(釋放記憶體)
tmux kill-session -t qwen36-server

實戰示範:用本地 Agent 修一個 Bug

以下是一個真實的操作紀錄,展示 Pi + 本地 Qwen3.6 如何在完全離線的情況下完成一個開發任務。

情境

你有一個 utils.py,裡面的 parse_csv function 在遇到欄位不齊的 CSV 時會 crash:

def parse_csv(filepath):
    results = []
    with open(filepath, 'r') as f:
        headers = f.readline().strip().split(',')
        for line in f:
            values = line.strip().split(',')
            row = {}
            for i in range(len(headers)):
                row[headers[i]] = values[i]  # IndexError if row is short
            results.append(row)
    return results

指令

cd ~/Projects/my-app
pi -p --provider qwen36-local \
  "Look at utils.py. This parse_csv function crashes when a row has fewer columns than the header. Fix it to handle mismatched row lengths gracefully."

結果

Pi 讀取了檔案、定位了問題(values[i] 在 row 較短時觸發 IndexError)、直接修改了程式碼:

def parse_csv(filepath):
    results = []
    with open(filepath, 'r') as f:
        headers = f.readline().strip().split(',')
        for line in f:
            values = line.strip().split(',')
            row = {}
            for i in range(len(headers)):
                row[headers[i]] = values[i] if i < len(values) else None
            results.append(row)
    return results

整個過程約 3 秒完成。Pi 自動讀取檔案、生成修正、寫回磁碟 — 不需要網路,不需要 API key,不產生費用。

這就是本地 coding agent 的核心價值:一個隨時可用、零成本、能直接動你程式碼的 AI 助手。


補充:Gemma 4 的雙模型方案

如果你想同時擁有「快速回應」和「高品質 coding」兩種選擇,可以額外設定 Gemma 4:

# 下載 Gemma 4(額外 17 GB)
mkdir -p ~/Developer/ML-Models/Gemma4/models/unsloth-gemma-4-26B-A4B-it-GGUF
hf download unsloth/gemma-4-26B-A4B-it-GGUF \
  gemma-4-26B-A4B-it-UD-Q4_K_XL.gguf \
  mmproj-BF16.gguf \
  MTP/gemma-4-26B-A4B-it-Q8_0-MTP.gguf \
  --local-dir ~/Developer/ML-Models/Gemma4/models/unsloth-gemma-4-26B-A4B-it-GGUF

Gemma 4 的 MTP 是獨立檔案(不是嫁接在主模型中),所以啟動時需要額外指定 --model-draft

llama-server \
  -m gemma-4-26B-A4B-it-UD-Q4_K_XL.gguf \
  --model-draft MTP/gemma-4-26B-A4B-it-Q8_0-MTP.gguf \
  --mmproj mmproj-BF16.gguf \
  --spec-type draft-mtp \
  --spec-draft-n-max 3 \
  -ngl 999 -fa on -c 65536 \
  --host 127.0.0.1 --port 8080

然後在 models.json 中加入第二個 provider,用不同的 port 區分。根據任務複雜度選擇:簡單問題用 Gemma 4(快),複雜 coding 用 Qwen3.6(強)。

注意:兩個模型不建議同時跑在同一台 64GB Mac 上(記憶體會不夠)。需要時關掉一個再開另一個。


已知限制

  • 品質仍不及頂級雲端模型 — Qwen3.6 35B-A3B 在 SWE-bench 73.4% 很強,但 Claude Opus 4.8 和 GPT-5.5 等閉源模型仍有明顯優勢。本地模型適合中等複雜度的任務。
  • 記憶體限制 — 16 GB Mac 只能跑 Q3/Q4 量化且 context 受限。32 GB 是起步,64 GB 最舒適。
  • 首次 prompt 較慢 — 如果一次餵入大量 context(例如整個大型檔案),prompt processing 會需要幾秒。後續對話因為有 cache 會快很多。
  • 工具呼叫偶有失誤 — 本地模型在複雜的多步驟工具呼叫中,偶爾會格式錯誤。比雲端模型的穩定性稍差。

結語

2026 年中,本地 AI Coding Agent 已經從「堪用」進入「好用」的階段。84.9 tok/s 的速度、128K 的 context、72% 的 MTP 接受率 — 這些數字代表的是一個真正可以日常使用的開發工具,而不只是技術展示。

更重要的是:它完全免費、完全離線、完全在你的控制之下。你的程式碼不會離開你的電腦。

如果你有一台 64 GB 的 Apple Silicon Mac,這套方案值得嘗試。最壞的結果不過是多佔 30 GB 硬碟空間;最好的結果是你多了一個永遠在線、不收費、不斷網的 coding 夥伴。


參考資源


Share:

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *


文章
Filter
Apply Filters
Mastodon