如何在macOS上设置本地编码代理

Hacker News Top 工具

摘要

一份关于在macOS上使用Gemma 4与MTP草稿模型及llama.cpp设置本地编码代理的详细教程,通过投机解码实现了约24%的速度提升。

暂无内容
查看原文
查看缓存全文

缓存时间: 2026/06/12 20:56

# 如何在 macOS 上搭建本地编码代理 来源:https://ikyle.me/blog/2026/how-to-setup-a-local-coding-agent-on-macos 最近几次网络出现问题,让我在没有编码代理的情况下束手无策,所以当我看到关于 Gemma 4 的“MTP 让 Gemma 4 运行速度提升 2 倍” (https://x.com/UnslothAI/status/2065107734916432189) 更新时,我决定尝试让它在本地跑起来。 我希望本地的编码代理设置能满足以下几点: - 在我的 Mac 上足够快,真正可用 - 通过 OpenAI 兼容的 API 工作(这样我就可以在其他工具中使用它) - 最好能在需要时处理截图/图像,这样我可以将制作好的截图喂给它。 我做到了!这个视频是实时拍摄的,展示了代理以完全可用的速度响应。 经过一番测试,我最终使用的配置是: - llama.cpp (https://github.com/ggml-org/llama.cpp) 在 macOS 上使用 Metal 构建 - Gemma 4 26B-A4B GGUF 格式 - 用于推测解码的 Q8 MTP draft 模型 - Gemma 4 多模态投影器 - Pi (https://github.com/earendil-works/pi) 作为终端编码代理 测试环境:Apple M1 Max,64 GB 统一内存,运行 macOS 15.7.7。 ## 模型 主模型为:`gemma-4-26B-A4B-it-UD-Q4_K_XL.gguf`。 Huggingface 链接:models/unsloth-gemma-4-26B-A4B-it-GGUF/gemma-4-26B-A4B-it-UD-Q4_K_XL.gguf (https://huggingface.co/unsloth/gemma-4-26B-A4B-it-GGUF/blob/main/gemma-4-26B-A4B-it-UD-Q4_K_XL.gguf) 该文件约 16 GB。加上 MTP draft 头部和多模态投影器,模型文件夹总共约 17 GB。 基准测试提示词: ``` Write a compact Python function that parses a unified diff and returns the changed file paths. Then explain two edge cases. ``` 每次基准测试生成约 128 个 token。 ## 基准线:llama.cpp + Metal 首先我直接在 llama.cpp 中通过 Metal 加速运行主模型: ``` repos/llama.cpp/build/bin/llama-cli \ -m models/unsloth-gemma-4-26B-A4B-it-GGUF/gemma-4-26B-A4B-it-UD-Q4_K_XL.gguf \ -ngl 999 \ -fa on \ -c 4096 \ -n 128 ``` 结果: | 设置 | Prompt tok/s | Generation tok/s | |------|--------------|------------------| | Gemma 4 26B-A4B Q4, llama.cpp Metal | 298.0 | 58.2 | 58.2 tokens/秒并不算快,但还算可用。不过对于编码代理的工作来说,尤其是在代理进行多次工具调用时,我们希望速度尽可能快。 ## 添加 MTP Draft 模型 Gemma 4 现在提供了 MTP draft 模型 (https://huggingface.co/unsloth/gemma-4-26B-A4B-it-GGUF/blob/main/MTP/gemma-4-26B-A4B-it-Q8_0-MTP.gguf): ``` MTP/gemma-4-26B-A4B-it-Q8_0-MTP.gguf ``` 它可以作为推测 draft 模型加载到 llama.cpp 中: ``` repos/llama.cpp/build/bin/llama-cli \ -m models/unsloth-gemma-4-26B-A4B-it-GGUF/gemma-4-26B-A4B-it-UD-Q4_K_XL.gguf \ --model-draft models/unsloth-gemma-4-26B-A4B-it-GGUF/MTP/gemma-4-26B-A4B-it-Q8_0-MTP.gguf \ --spec-type draft-mtp \ --spec-draft-n-max 3 \ -ngl 999 \ -fa on \ -c 4096 \ -n 128 ``` 首次运行 MTP 时,使用 4 个 draft token 达到了 69.2 tokens/秒。不过,Unsloth 的《如何运行 MTP 模型》指南 (https://unsloth.ai/docs/models/mtp) 包含以下说明: > “我们发现 `--spec-draft-n-max 2` 是最佳起点,但不要假定 2 是最优值,因为性能取决于硬件。可以尝试 1 到 6 之间的任何值,选择对您的系统最快的那个。” 经过对 `--spec-draft-n-max` 的扫描,最佳结果是 72.2 tokens/秒(使用 3 个 draft token)。 | 设置 | Prompt tok/s | Generation tok/s | Speedup | |------|--------------|------------------|---------| | 仅主模型 | 298.0 | 58.2 | 1.00x | | 主模型 + Q8 MTP draft | 295.6 | 72.2 | 1.24x | 有用的一点是,提示处理速度基本保持不变,而生成速度提升了约 24%。 ## 调整 MTP 我测试了 `--spec-draft-n-max` 从 1 到 6 的值。 | `--spec-draft-n-max` | Prompt tok/s | Generation tok/s | |----------------------|--------------|------------------| | 1 | 295.5 | 68.4 | | 2 | 299.1 | 72.0 | | 3 | 295.6 | 72.2 | | 4 | 297.3 | 70.7 | | 5 | 297.9 | 63.7 | | 6 | 296.3 | 61.2 | 在我的 M1 Max 机器上,`3` 是最快的,`2` 也非常接近,两者都可以。更高的值会变慢。 ## MLX 对比 我还通过 `mlx-lm` 测试了 MLX 模型,以了解在 Mac 上运行该模型更快的方式是 llama.cpp 还是 mlx。 | 运行时 | 模型 | Generation tok/s | |--------|------|------------------| | llama.cpp Metal + MTP | Unsloth GGUF Q4 + Q8 MTP | 72.2 | | llama.cpp Metal | Unsloth GGUF Q4 | 58.2 | | MLX-LM | Unsloth UD MLX 4-bit | 45.8 | | MLX-LM | mlx-community 4-bit | 43.9 | | MLX-LM | mlx-community OptiQ 4-bit | 38.1 | 我原以为 MLX(为 Mac 优化)会最快。但在这个特定设置中,llama.cpp 比 MLX 更快,而带有 MTP 的 llama.cpp 显然是最佳选择。 我想,长期以来对 llama.cpp 进行的所有优化和调整,使得它在 macOS 上得到了相当不错的优化,尽管它是跨平台的。 我还尝试了通过 gemma-4-swift-mlx (https://github.com/VincentGourbin/gemma-4-swift-mlx) 运行 Gemma 4 MTP,但测试的 26B 4-bit MLX 检查点与加载器预期的权重键不匹配,而且我已经有了之前的 MLX 测试结果,所以继续前进,没有再重新下载新模型并尝试调整匹配。 ## 添加图像支持 对于 Pi,我还希望能够附加截图。我最初为其设置的本地模型条目声明为纯文本: 这意味着 Pi 没有正确地将图像工具输出发送给模型。 llama.cpp 服务器还需要 Gemma 4 多模态投影器才能让多模态部分工作(只有 12B 版本原生支持多模态 (https://blog.google/innovation-and-ai/technology/developers-tools/introducing-gemma-4-12b/)): 当加载 `--mmproj` 时,llama.cpp 会宣传多模态支持,Pi 就可以发送图像了。 我重新运行了文本基准测试(加载投影器),以检查是否影响速度: | 设置 | 投影器 | Prompt tok/s | Generation tok/s | |------|--------|--------------|------------------| | llama.cpp Metal + MTP | 无 | 120.3 | 71.4 | | llama.cpp Metal + MTP | `mmproj-BF16.gguf` | 297.4 | 72.2 | 最终运行投影器并没有显示出文本生成速度的下降。 --- 现在来看设置说明: ## 安装 llama.cpp 安装依赖: ``` brew install cmake git tmux [email protected] ``` 克隆并构建 llama.cpp: ``` mkdir -p ~/Developer/ML-Models/Gemma4/repos cd ~/Developer/ML-Models/Gemma4 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 ``` 我测试的构建包含: ``` GGML_METAL=ON GGML_ACCELERATE=ON GGML_BLAS=ON GGML_BLAS_VENDOR=Apple ``` ## 下载模型文件 创建 Python 环境: ``` cd ~/Developer/ML-Models/Gemma4 python3.11 -m venv .venv source .venv/bin/activate pip install -U huggingface_hub hf_xet ``` 下载文件: ``` mkdir -p models/unsloth-gemma-4-26B-A4B-it-GGUF huggingface-cli 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 models/unsloth-gemma-4-26B-A4B-it-GGUF ``` 最终你会得到: ``` models/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 ``` ## 启动本地服务器 最终服务器命令: ``` repos/llama.cpp/build/bin/llama-server \ -m models/unsloth-gemma-4-26B-A4B-it-GGUF/gemma-4-26B-A4B-it-UD-Q4_K_XL.gguf \ --model-draft models/unsloth-gemma-4-26B-A4B-it-GGUF/MTP/gemma-4-26B-A4B-it-Q8_0-MTP.gguf \ --mmproj models/unsloth-gemma-4-26B-A4B-it-GGUF/mmproj-BF16.gguf \ --spec-type draft-mtp \ --spec-draft-n-max 3 \ -ngl 999 \ -fa on \ -c 65536 \ --parallel 1 \ --host 127.0.0.1 \ --port 8080 ``` OpenAI 兼容的端点是: 我使用了一个小的 `start_server.sh` 包装脚本,使之在 tmux 中运行: ``` #!/usr/bin/env bash set -euo pipefail ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" SESSION_NAME="${SESSION_NAME:-gemma4-server}" HOST="${HOST:-127.0.0.1}" PORT="${PORT:-8080}" CTX_SIZE="${CTX_SIZE:-65536}" PARALLEL="${PARALLEL:-1}" LLAMA_SERVER="$ROOT_DIR/repos/llama.cpp/build/bin/llama-server" MODEL="$ROOT_DIR/models/unsloth-gemma-4-26B-A4B-it-GGUF/gemma-4-26B-A4B-it-UD-Q4_K_XL.gguf" DRAFT_MODEL="$ROOT_DIR/models/unsloth-gemma-4-26B-A4B-it-GGUF/MTP/gemma-4-26B-A4B-it-Q8_0-MTP.gguf" MMPROJ="$ROOT_DIR/models/unsloth-gemma-4-26B-A4B-it-GGUF/mmproj-BF16.gguf" LOG_FILE="$ROOT_DIR/logs/llama-server-mtp.log" mkdir -p "$ROOT_DIR/logs" tmux new-session -d -s "$SESSION_NAME" -c "$ROOT_DIR" \ "$LLAMA_SERVER \ -m '$MODEL' \ --model-draft '$DRAFT_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'" ``` 启动它: ``` chmod +x start_server.sh ./start_server.sh ``` 检查服务器是否正在运行: ``` curl http://127.0.0.1:8080/v1/models ``` ## 配置 Pi Pi 从以下位置读取模型提供商: 添加本地提供商: ``` { "providers": { "gemma4-local": { "name": "Gemma 4 Local", "baseUrl": "http://127.0.0.1:8080/v1", "api": "openai-completions", "apiKey": "local", "authHeader": false, "compat": { "supportsDeveloperRole": false, "supportsReasoningEffort": false }, "models": [ { "id": "gemma-4-26B-A4B-it-UD-Q4_K_XL.gguf", "name": "Gemma 4 26B-A4B Q4 + MTP", "reasoning": false, "input": ["text", "image"], "contextWindow": 65536, "maxTokens": 8192, "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } } ] } } } ``` 重要部分: - `baseUrl` 指向 llama.cpp OpenAI 兼容服务器。 - `api` 是 `openai-completions`。 - `authHeader` 为 `false`,因为这是本地服务器。 - `input` 包含 `text` 和 `image`,否则 Pi 会将其视为纯文本。 可选地将其设置为默认值,在: ``` ~/.pi/agent/settings.json ``` ``` { "defaultProvider": "gemma4-local", "defaultModel": "gemma-4-26B-A4B-it-UD-Q4_K_XL.gguf", "defaultThinkingLevel": "minimal" } ``` 然后检查 Pi 是否能看到它: ``` pi --offline --list-models gemma ``` 预期输出: ``` provider model context max-out thinking images gemma4-local gemma-4-26B-A4B-it-UD-Q4_K_XL.gguf 65.5K 8.2K no yes ``` 使用本地模型运行 Pi: ``` pi --provider gemma4-local --model gemma-4-26B-A4B-it-UD-Q4_K_XL.gguf ``` 或使用非交互模式: ``` pi -p --provider gemma4-local --model gemma-4-26B-A4B-it-UD-Q4_K_XL.gguf \ "Explain what this repository does" ``` 对于截图: ``` pi -p @"/path/to/screenshot.png" "Describe this image and point out anything relevant to the UI" ``` ## 最终设置 最终的本地编码代理栈: | 层 | 选择 | |----|------| | 推理运行时 | llama.cpp | | macOS 加速 | Metal + Accelerate | | 主模型 | `gemma-4-26B-A4B-it-UD-Q4_K_XL.gguf` | | Draft 模型 | `gemma-4-26B-A4B-it-Q8_0-MTP.gguf` | | MTP 设置 | `--spec-draft-n-max 3` | | 多模态投影器 | `mmproj-BF16.gguf` | | 服务器 | `llama-server` 运行在 `127.0.0.1:8080` | | API | OpenAI 兼容 `/v1` | | 编码代理 | Pi | | Pi 模型输入 | `["text", "image"]` | 主要结论是,MTP draft 模型值得使用。在这台机器上,它将 Gemma 4 从 58.2 tokens/秒提升到 72.2 tokens/秒,同时保持了足够简单的设置,可以作为本地 OpenAI 兼容服务器运行。 --- **附注:** 有建议说可以用 `Qwen3.6 35B-A3B` 代替 `Gemma 4 26B-A4B`。根据我能找到的基准测试,Qwen 作为编码代理**远比** Gemma 4 优秀。但它的速度也更慢。`Qwen3.6-35B-A3B-UD-Q4_K_XL.gguf` + `unsloth-Qwen3.6-35B-A3B-MTP-GGUF` + `mmproj-BF16.gguf` 的结果是 55 tk/s,而不是 72 tk/s。当你坐着等待时,这个差异非常显著。 下载模型: ``` mkdir -p models/unsloth-Qwen3.6-35B-A3B-MTP-GGUF huggingface-cli download unsloth/Qwen3.6-35B-A3B-MTP-GGUF \ Qwen3.6-35B-A3B-UD-Q4_K_XL.gguf \ mmproj-BF16.gguf \ --local-dir models/unsloth-Qwen3.6-35B-A3B-MTP-GGUF ``` 启动服务器: ``` LLAMA_SERVER=/Users/kylehowells/Developer/ML-Models/Gemma4/repos/llama.cpp/build/bin/llama-server $LLAMA_SERVER \ -m models/unsloth-Qwen3.6-35B-A3B-MTP-GGUF/Qwen3.6-35B-A3B-UD-Q4_K_XL.gguf \ --mmproj models/unsloth-Qwen3.6-35B-A3B-MTP-GGUF/mmproj-BF16.gguf \ --spec-type draft-mtp \ --spec-draft-n-max 3 \ -ngl 999 \ -fa on \ -c 65536 \ --parallel 1 \ --host 127.0.0.1 \ --port 8081 ``` Pi 配置: ``` { "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-Q4_K_XL.gguf", "name": "Qwen3.6 35B-A3B Q4 + MTP", "reasoning": true, "input": ["text", "image"], "contextWindow": 65536, "maxTokens": 8192, "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } } ] } } } ``` ## 参考文献 - unsloth.ai/docs/models/qwen3.6 (https://unsloth.ai/docs/models/qwen3.6) - unsloth.ai/docs/models/gemma-4 (https://unsloth.ai/docs/models/gemma-4) - unsloth.ai/docs/models/mtp (https://unsloth.ai/docs/models/mtp) - github.com/ggml-org/llama.cpp (https://github.com/ggml-org/llama.cpp) - github.com/earendil-works/pi (https://github.com/earendil-works/pi) - Introducing Gemma 4 12B: a unified, encoder-free multimodal model (https://blog.google/innovation-and-ai/technology/developers-tools/introducing-gemma-4-12b/) - "MTP enables Google Gemma 4 run ~1.4–2.2× faster with no accuracy loss" (https://x.com/UnslothAI/status/2065107734916432189) - unsloth/gemma-4-26B-A4B-it-GGUF (https://huggingface.co/unsloth/gemma-4-26B-A4B-it-GGUF) - unsloth/Qwen3.6-35B-A3B-MTP-GGUF (https://huggingface.co/unsloth/Qwen3.6-35B-A3B-MTP-GGUF)

相似文章

@port_dev: https://x.com/port_dev/status/2054259445732110408

X AI KOLs Timeline

本文提供了一份详细教程,介绍如何通过 Unsloth Studio 和 Pi 编码框架配置基于 Qwen3.6-27B 的本地编码智能体。文章强调了使用 GGUF 量化模型在消费级硬件(如搭载 Apple Silicon 芯片的 Mac 电脑)上实现高效推理的优势。