2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Mac miniでローカルLLM (2026/10版)

2
Last updated at Posted at 2026-10-16

はじめに

自宅のMacで使っているLLMサービス環境(日常会話AIエージェント用)の構築メモです。

前提

  • Mac mini M4 (RAM:32GB)

LLMサーバ

llama.cpp 公式インストーラを実行します。

curl -LsSf https://llama.app/install.sh | sh

成功判定: 次のコマンドでバージョンとビルド情報が表示されます。

llama cli --version
version: 0.6.0-dev (build 11429, commit d81235049)
built with AppleClang 21.0.0.21000101 for Darwin aarch64

起動・停止スクリプト

前提として、$HOME/bin ディレクトリを作成して $PATH に追加している前提です。
モデルは、unsloth版の gemma-4-26B-A4B-it-qat のGGUFを使います。

※画像解析はメモリ節約のために外しています

cat > ~/bin/gemma4.sh <<'EOF'
#!/bin/zsh

# Mac mini 用 llama.cpp / Gemma 4 管理スクリプト
# 使用方法: gemma4.sh {start|stop|restart|status}

export PATH="$HOME/.local/bin:$PATH"

PID_FILE="$HOME/.gemma4.pid"
LOG_FILE="$HOME/.gemma4.log"
MODEL="unsloth/gemma-4-26B-A4B-it-qat-GGUF:UD-Q4_K_XL"
PORT=8080
LLAMA_BIN="$(command -v llama)"

start() {
    # 二重起動を防ぎ、停止済みのPIDファイルは掃除する
    if [ -f "$PID_FILE" ]; then
        local old_pid="$(cat "$PID_FILE")"
        if kill -0 "$old_pid" 2>/dev/null; then
            echo "Gemma 4 is already running. PID=$old_pid"
            return 0
        fi
        rm -f "$PID_FILE"
    fi

    if [ -z "$LLAMA_BIN" ]; then
        echo "llama command not found. Install llama.cpp first."
        return 1
    fi

    # Gemma 4 26B QAT / コンテキスト65536 / 並列1 / MTP有効
    # 画像用プロジェクタは読み込まず、テキスト用途で起動する
    nohup "$LLAMA_BIN" serve \
        -hf "$MODEL" \
        -ngl 999 \
        -fa on \
        --spec-type draft-mtp \
        --spec-draft-n-max 4 \
        --reasoning auto \
        --reasoning-effort low \
        --reasoning-budget 1024 \
        --ctx-size 65536 \
        --parallel 1 \
        --cache-ram 4096 \
        --no-mmproj \
        --host 0.0.0.0 \
        --port "$PORT" \
        > "$LOG_FILE" 2>&1 < /dev/null &

    local pid=$!
    echo "$pid" > "$PID_FILE"
    sleep 1

    # 初回ダウンロード中でもプロセスの生存を確認する
    if kill -0 "$pid" 2>/dev/null; then
        echo "Gemma 4 starting. PID=$pid"
        echo "Log: $LOG_FILE"
    else
        rm -f "$PID_FILE"
        echo "Gemma 4 failed to start. Check: $LOG_FILE"
        return 1
    fi
}

stop() {
    # PIDファイルがなければ停止済みと判定する
    if [ ! -f "$PID_FILE" ]; then
        echo "Gemma 4 is not running."
        return 0
    fi

    local pid="$(cat "$PID_FILE")"
    if ! kill -0 "$pid" 2>/dev/null; then
        rm -f "$PID_FILE"
        echo "Gemma 4 is not running."
        return 0
    fi

    # 通常終了を最大10秒待つ
    kill "$pid" 2>/dev/null
    local i
    for i in {1..10}; do
        if ! kill -0 "$pid" 2>/dev/null; then
            rm -f "$PID_FILE"
            echo "Gemma 4 stopped."
            return 0
        fi
        sleep 1
    done

    # 通常終了できなかった場合だけ強制終了する
    kill -9 "$pid" 2>/dev/null
    rm -f "$PID_FILE"
    echo "Gemma 4 force stopped."
}

status() {
    # プロセスとHTTP APIの状態を確認する
    if [ ! -f "$PID_FILE" ]; then
        echo "Gemma 4 is not running."
        return 0
    fi

    local pid="$(cat "$PID_FILE")"
    if ! kill -0 "$pid" 2>/dev/null; then
        echo "Gemma 4 is not running (stale PID file)."
        return 0
    fi

    echo "Gemma 4 is running. PID=$pid"
    echo "Model: $MODEL"
    echo "Web UI: http://127.0.0.1:$PORT/"
    echo "Responses API: http://127.0.0.1:$PORT/v1/responses"

    local code="$(curl -s -o /dev/null -w '%{http_code}' --max-time 3 "http://127.0.0.1:$PORT/health")"
    case "$code" in
        200) echo "API: ready" ;;
        503) echo "API: loading" ;;
        *)   echo "API: not ready (HTTP $code)" ;;
    esac
}

case "${1:-}" in
    start)   start ;;
    stop)    stop ;;
    restart) stop && start ;;
    status)  status ;;
    *)
        echo "Usage: $0 {start|stop|restart|status}"
        exit 1
        ;;
esac
EOF

続いて実行権限を付けます。

chmod +x "$HOME/bin/gemma4.sh"

起動・停止

初回はモデルのダウンロードがあります。

gemma4.sh start
Gemma 4 started. PID=13162
Log: /Users/haomei/.gemma4.log

状態確認するとエンドポイントとモデル名が表示されます。
※--host 0.0.0.0としているので、外部IPでの接続も可能です

gemma4.sh status
Gemma 4 is running.
PID:       13162
Web UI:    http://127.0.0.1:8080/
Responses: http://127.0.0.1:8080/v1/responses
Model:     unsloth/gemma-4-26B-A4B-it-qat-GGUF:UD-Q4_K_XL

停止はあっさり。

gemma4.sh stop
Gemma 4 stopped.

性能

個人的にプログラム生成のベンチマークにしているプロンプトで50tps程度です。

image.png

image.png

AIエージェントから使う(おまけ)

Podman(コンテナ)環境が前提です。
BRAVEのAPIキー(有償)で検索できるようにしています。

mkdir -p "$HOME/podman/openclaw/runtime/workspace"
cd "$HOME/podman/openclaw"
cat > .env <<EOF
OPENCLAW_GATEWAY_TOKEN=$(openssl rand -hex 32)
BRAVE_API_KEY=<BraveのAPIキー>
EOF

設定ファイルの作成

cat > runtime/openclaw.json <<'JSON'
{
  "models": {
    "mode": "merge",
    "providers": {
      "mac-llama": {
        "baseUrl": "<LLMのベースURL>",
        "api": "openai-completions",
        "models": [
          {
            "id": "<モデル名>",
            "name": "Local LLM",
            "reasoning": false,
            "input": ["text"],
            "contextWindow": 65536,
            "maxTokens": 8192,
            "cost": {"input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0}
          }
        ]
      }
    }
  },
  "agents": {
    "defaults": {
      "workspace": "/home/node/.openclaw/workspace",
      "model": {"primary": "mac-llama/<モデル名>"},
      "models": {
        "mac-llama/<モデル名>": {
          "params": {
            "extra_body": {
              "chat_template_kwargs": {"enable_thinking": false}
            }
          }
        }
      }
    }
  },
  "tools": {
    "web": {
      "search": {"enabled": true, "provider": "brave"}
    }
  },
  "gateway": {
    "mode": "local",
    "port": 18789,
    "bind": "lan",
    "auth": {
      "mode": "token",
      "token": "${OPENCLAW_GATEWAY_TOKEN}"
    },
    "controlUi": {
      "allowedOrigins": [
        "http://<MacのIPアドレス>:8081",
        "http://localhost:8081",
        "http://127.0.0.1:8081"
      ]
    }
  }
}
JSON

お試し用に起動スクリプトを作成。

cat > start.sh <<'SH'
#!/bin/sh
set -eu
ROOT=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
exec /opt/podman/bin/podman run -d \
  --name openclaw-trial \
  --restart unless-stopped \
  --user 0:0 \
  --memory 2g \
  --cpus 2 \
  --publish 8081:18789 \
  --env HOME=/home/node \
  --env TZ=Asia/Tokyo \
  --env NODE_OPTIONS=--max-old-space-size=640 \
  --env-file "$ROOT/.env" \
  --volume "$ROOT/runtime:/home/node/.openclaw" \
  ghcr.io/openclaw/openclaw@sha256:e79f0168377a1ee77e29c652f133007d270558590bce8358967f456258c8bb16 \
  node openclaw.mjs gateway --bind lan --port 18789
SH

実行権付与して実行。

chmod +x start.sh
./start.sh

WebUIは http://<MacのIPアドレス>:8081/。
接続時に求められるトークンは、次のコマンドで表示する。

sed -n 's/^OPENCLAW_GATEWAY_TOKEN=//p' .env

初回「何ができますか?」で15秒。
image.png

2ターン目、「今日の小田原市の天気予報を確認して。」は9回も失敗を繰り返し54秒。
image.png

3ターン目、「今日のAIエージェント関連のニュースを教えて。」は、検索1回で38秒。
image.png

停止してコンテナ削除。

podman stop openclaw-trial
podman rm openclaw-trial

次は非公開の自作のAIエージェント。※鹿児島弁で「超エージェント」
Codex Coreを使ってます。

podman ps -a
CONTAINER ID  IMAGE                                COMMAND     CREATED         STATUS                   PORTS                   NAMES
30a1a3b73ce8  localhost/wazzeagent:0.9.11              12 minutes ago  Up 10 minutes (healthy)  0.0.0.0:8081->8080/tcp  wazzeagent-trial

初回「何ができますか?」で10.2秒。
image.png

2ターン目、「今日の小田原市の天気予報を確認して。」は天気予報スキル(これも自作)で11.9秒。
image.png

3ターン目、「今日のAIエージェント関連のニュースを教えて。」は、検索1回で16.0秒。「今日」では検索しなかった...
image.png

openclawのように毎ターン余計な検索動作はさせていないのでレスポンスは速いです。
キャッシュヒット率を上げるコンテキスト設計もしているので、コンテキストが溜まっても速さが持続します。

また、ローカル小規模モデル用に「会話ルーター」という問い合わせ内容がツール実行含む依頼の場合、ツール呼び出しを強化する実装もしています。
これにより、gemmaなのにqwen並みのツールコール性能があると思います。(計測はしていない)

大体のAIエージェントはフロンティアモデルのAPIや、つよつよローカルモデル向けの設定寄りだと感じます。
ローカル小規模モデルに特化した設定のAIエージェント製品は無いように思えるので、ローカル小規模モデルのみを使うつもりであれば、AIエージェントは自作した方が良いと思います。

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?