ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Macbook 用 MLX-LM 跑 Qwen3.6-27B 量化模型并接入 opencode 的完整配置

Macbook 用 MLX-LM 跑 Qwen3.6-27B 量化模型并接入 opencode 的完整配置

1. 为什么要在 Macbook 上折腾本地 Qwen3.6-27B 量化模型

如果你手里是一台 Apple Silicon 的 Macbook,又想让 AI 编码助手真正跑在本地,MLX-LM 加载 Qwen3.6-27B 量化模型并接入 opencode 这套组合,是目前比较省心的一条路。它解决的核心问题是:你不想把项目代码发到云端,也不想每个月为编码订阅付费,但又希望有一个能读文件、能改代码、能跑工具调用的本地 Agent 后端。

MLX 是 Apple 官方开源的数组计算框架,专门为统一内存架构设计。模型权重、计算图和内存之间不需要来回拷贝,这让它在 Mac 上的推理效率比通用方案更直接。MLX-LM 则是在 MLX 之上封装的推理与微调工具,它自带一个 HTTP 服务,接口形态和 OpenAI Chat Completions 基本一致,默认监听127.0.0.1:8080。opencode 支持把任意兼容/v1/chat/completions的服务注册成 provider,所以两者对接非常自然。

这套方案适合谁?适合用 Macbook Pro M 系列、内存 24GB 起步、想本地跑编码 Agent 的开发者。不适合谁?如果你只有 16GB 内存,或者需要同时服务多个用户,那 27B 的 4-bit 权重会非常吃力,建议换更小的模型。我实测下来,24GB 统一内存跑 4-bit 量化是能用的,但必须把并发和缓存压到最低,否则系统会开始疯狂交换内存。

Qwen3.6-27B 本身支持思考模式和工具调用,这对编码场景很关键。opencode 依赖工具调用来读文件、写补丁、执行命令,所以模型必须能正确返回 OpenAI 格式的tool_calls。MLX-LM 的服务端代码已经能接收tools字段并返回对应结构,这也是我选择它而不是自己写一层适配的原因。

整篇文章会按顺序讲清楚:环境准备、模型下载、服务启动参数、opencode 配置、端到端验证、常见报错排查。每一步都给可复制的命令或配置片段,你可以直接跟着做。

2. 前置准备:MLX-LM 安装与 Qwen3.6-27B 量化权重获取

在 Macbook 上用 MLX-LM 跑 Qwen3.6-27B 量化模型,第一步是把工具链装好。我建议用uv来管理 MLX-LM,因为它能把命令行工具隔离在独立环境里,升级和卸载都干净。如果你还没装 Homebrew,先执行官方安装脚本;已经装过的可以跳过。

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

接着安装 uv,并刷新 shell 路径:

brew install uv uv tool update-shell

执行完uv tool update-shell后,一定要重新打开终端,否则mlx_lm.server可能找不到。然后安装 MLX-LM:

uv tool install mlx-lm

以后需要升级时用:

uv tool upgrade mlx-lm

验证安装是否成功:

mlx_lm.server --help

如果这条命令能打印出参数列表,说明工具链就绪。如果提示command not found,先执行uv tool update-shell再重开终端,或者用uv tool dir --bin查看可执行文件目录并手动加入 PATH。

接下来是模型权重。推荐使用mlx-community/Qwen3.6-27B-4bit,这是已经为 MLX 转换好的 4-bit 量化版本,模型页显示文件约 16.1GB。24GB 内存的机器跑它,模型加载后还剩大约 7 到 8GB 给系统和 KV 缓存,属于紧但可用的状态。

下载方式有两种。方案 A 是首次启动服务时自动下载,直接在启动命令里写 Hugging Face 仓库名,MLX-LM 会检查本地缓存,没有就自动拉取。方案 B 是下载到固定目录,适合你想明确管理模型文件位置的情况:

mkdir -p "$HOME/Models" uvx --from huggingface_hub hf download \ mlx-community/Qwen3.6-27B-4bit \ --local-dir "$HOME/Models/Qwen3.6-27B-4bit"

下载完成后检查目录大小:

du -sh "$HOME/Models/Qwen3.6-27B-4bit"

这里有几个坑要避开。BF16 版本远超 24GB,不要选;8-bit 通常约 30GB,也放不下;35B-A3B 的 4-bit 版本模型本身就接近 24GB,几乎不给系统和缓存留空间;未标注 MLX 的 GGUF 权重是给 llama.cpp 或 Ollama 用的,MLX-LM 加载不了。选模型时认准mlx-community组织下带-4bit后缀的 MLX 权重。

如果你打算把本地推理端点接到 TaoToken 做对比测试,可以先用 TaoToken 的模型对话页面确认云端 Qwen 系列的表现,再决定本地量化版本是否满足需求。TaoToken 的 API 地址是https://taotoken.net/api,模型对话入口在 deep link 里可以找到。不过本文的重点是本地 MLX-LM,TaoToken 只作为可选参照。

3. 可复制配置:MLX-LM 服务启动参数与 opencode 接入片段

这一节是整篇的核心,所有配置都可以直接复制。先启动 MLX-LM 服务,再写 opencode 的 provider 配置。

3.1 以 24GB 安全配置启动 MLX-LM

如果你用 Hugging Face 缓存中的模型,启动命令如下:

mlx_lm.server \ --model "mlx-community/Qwen3.6-27B-4bit" \ --host 127.0.0.1 \ --port 8080 \ --max-tokens 2048 \ --temp 0.6 \ --top-p 0.95 \ --top-k 20 \ --decode-concurrency 1 \ --prompt-concurrency 1 \ --prefill-step-size 512 \ --prompt-cache-size 1 \ --prompt-cache-bytes 1GB \ --chat-template-args '{"enable_thinking":true}'

如果你下载到了固定目录,把--model换成绝对路径:

MODEL_DIR="$HOME/Models/Qwen3.6-27B-4bit" mlx_lm.server \ --model "$MODEL_DIR" \ --host 127.0.0.1 \ --port 8080 \ --max-tokens 2048 \ --temp 0.6 \ --top-p 0.95 \ --top-k 20 \ --decode-concurrency 1 \ --prompt-concurrency 1 \ --prefill-step-size 512 \ --prompt-cache-size 1 \ --prompt-cache-bytes 1GB \ --chat-template-args '{"enable_thinking":true}'

参数逐个解释一下。--host 127.0.0.1只允许本机访问,不要改成0.0.0.0,否则同网络下其他设备可能连进来。--max-tokens 2048限制默认输出长度,避免思考过程无限增长把内存吃满。--temp 0.6、--top-p 0.95、--top-k 20是 Qwen 官方对精确编程任务建议的采样参数。--decode-concurrency 1和--prompt-concurrency 1把解码和预填充都限制为单请求,减少内存峰值。--prefill-step-size 512降低长提示预填充时的峰值内存。--prompt-cache-size 1只保留一份会话缓存,--prompt-cache-bytes 1GB限制缓存总量。enable_thinking开启 Qwen3.6 的思考模式。

首次建议不要启用preserve_thinking。确认 opencode 能正确处理多轮思考内容后,可以改成:

--chat-template-args '{"enable_thinking":true,"preserve_thinking":true}'

如果工具调用不稳定,可以测试非思考模式:

--temp 0.7 \ --top-p 0.8 \ --top-k 20 \ --chat-template-args '{"enable_thinking":false}'

3.2 opencode 稳定版配置

opencode 的全局配置放在~/.config/opencode/opencode.json,项目级配置放在项目根目录的opencode.json。建议先用项目级配置做测试,确认没问题再合并到全局。

{ "$schema": "https://opencode.ai/config.json", "model": "mlx-local/default_model", "provider": { "mlx-local": { "npm": "@ai-sdk/openai-compatible", "name": "MLX-LM Local", "options": { "baseURL": "http://127.0.0.1:8080/v1", "apiKey": "local" }, "models": { "default_model": { "name": "Qwen3.6 27B MLX 4-bit", "limit": { "context": 8192, "output": 2048 } } } } } }

这里用default_model作为模型键很关键。MLX-LM 服务端内部提供了这个别名,会把它映射到启动时的--model参数。这样无论你用 Hugging Face 仓库名还是本地目录启动,opencode 都不需要知道绝对路径,也避免客户端发送另一个仓库名导致服务尝试切换或重新加载模型。

limit.context是告诉 opencode 本地可用的上下文预算,不会改变 Qwen 模型文件本身。24GB 机器先设 8192,稳定后再逐步增加。limit.output设 2048,和启动参数里的--max-tokens保持一致。

如果你已经有opencode.json,不要整份覆盖,只合并model和provider.mlx-local部分。

3.3 opencode 2.0 beta 配置

只有安装了 opencode 2.0 beta 才使用本节。2.0 beta 的字段与稳定版不同:provider改为providers,npm改为package,options改为settings,并且可以通过modelID把友好名称映射到 MLX-LM 的default_model。

{ "$schema": "https://opencode.ai/config.json", "model": "mlx-local/qwen36-code", "providers": { "mlx-local": { "name": "MLX-LM Local", "package": "@opencode-ai/ai/providers/openai-compatible", "settings": { "baseURL": "http://127.0.0.1:8080/v1", "apiKey": "local" }, "models": { "qwen36-code": { "modelID": "default_model", "name": "Qwen3.6 27B MLX 4-bit", "capabilities": { "tools": true, "input": ["text"], "output": ["text"] }, "limit": { "context": 8192, "output": 2048 } } } } } }

opencode 官方说明 2.0 当前属于 beta,不要把该配置与稳定版字段混用。如果你不确定自己装的是哪个版本,运行opencode --version确认。

4. 验证请求:从健康检查到端到端对话与显存占用

配置写完后,必须一步步验证,不要直接跳到 opencode 里跑。先确认 MLX-LM 服务本身正常,再确认 opencode 能连上。

4.1 健康检查与模型列表

另开一个终端,执行:

curl http://127.0.0.1:8080/health

预期返回:

{"status":"ok"}

然后查看模型列表:

curl http://127.0.0.1:8080/v1/models

你应该能看到default_model这个别名。如果这里报错,说明服务没起来或者端口不对,先回到启动命令检查。

4.2 测试普通对话

用 curl 发一个 Chat Completions 请求:

curl http://127.0.0.1:8080/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{ "model": "default_model", "messages": [ { "role": "user", "content": "请用 Python 实现一个带类型标注的 LRU 缓存,并解释复杂度。" } ], "max_tokens": 1024, "temperature": 0.6, "top_p": 0.95, "top_k": 20, "stream": false }'

如果这个请求成功返回内容,说明 MLX-LM 服务完全正常。注意model字段填default_model,不要填 Hugging Face 仓库名。

4.3 opencode 端到端验证

确保 MLX-LM 服务仍在运行,然后查看 opencode 是否识别到模型:

opencode models mlx-local

应看到:

mlx-local/default_model

接着用命令行模式跑一次只读检查:

opencode run \ --model mlx-local/default_model \ "检查当前项目的结构,只提出一个最值得优先修复的问题,不要修改文件。"

也可以直接启动 opencode:

opencode

进入后运行/models,选择 MLX-LM Local 下的 Qwen3.6。如果 opencode 要求凭据,运行/connect,选择 Other,Provider ID 填mlx-local,API Key 填任意本地占位值,例如local。MLX-LM 本地服务本身不会验证这个占位值。

4.4 显存占用检查

运行时打开「活动监视器 → 内存」,重点看三个指标:内存压力是否为绿色、交换内存是否持续增长、opencode 每轮对话后是否越来越慢。

推荐调整顺序:保持上下文 8192;如果内存压力变黄,把输出从 2048 降到 1024;把--prompt-cache-bytes 1GB降为 512MB;关闭浏览器重型页面、Docker 和虚拟机;仍然交换内存时,改用更小模型。

只有在内存压力持续为绿色时,才把 opencode 配置中的上下文依次增加为 8192 → 12288 → 16384。不要直接设成 32K、128K 或 256K。opencode 的仓库工具会把文件内容、补丁、系统提示和工具定义全部计入上下文,实际占用通常比普通聊天更高。

4.5 工具调用测试

opencode 依赖工具调用读取和编辑项目。建议按以下顺序测试:只读检查项目;让模型读取一个指定文件;让模型提出修改方案但不修改;最后才允许小范围编辑。

测试提示:

列出当前项目根目录文件,读取 README.md,然后总结项目用途。不要修改任何文件。

如果模型只输出类似工具调用的文本,却没有真正调用工具,先升级 MLX-LM,确认使用模型自带的chat_template.jinja,不要设置--use-default-chat-template覆盖模型模板,先关闭preserve_thinking,测试非思考模式,检查 opencode 日志及 MLX-LM 终端是否出现 tool parser 警告。

5. 本篇常见错排查:401、local proxy failed、reading choices 与 OAuth

这一节对照真实报错,给出排查路径。如果你在接入过程中遇到问题,先在这里找对应条目。

5.1 401 Unauthorized

opencode 报 401,通常是apiKey字段缺失或为空。MLX-LM 本地服务不验证 key,但 opencode 的 OpenAI-compatible provider 要求这个字段存在。检查配置:

"options": { "baseURL": "http://127.0.0.1:8080/v1", "apiKey": "local" }

apiKey填任意非空字符串即可,比如local。不要留空,也不要删掉这个字段。

5.2 local proxy failed

这个报错通常出现在 opencode 尝试连接baseURL但服务没起来,或者地址写错。先确认 MLX-LM 服务在运行:

curl http://127.0.0.1:8080/health

如果这条命令失败,说明服务没启动或端口被占用。端口被占用时,改用 8081:

mlx_lm.server \ --model "mlx-community/Qwen3.6-27B-4bit" \ --host 127.0.0.1 \ --port 8081

然后把 opencode 的baseURL改成http://127.0.0.1:8081/v1。

5.3 reading choices 报错

这个报错说明 opencode 收到了响应,但结构不符合预期。常见原因是model字段填错,或者 MLX-LM 返回了非标准格式。检查 opencode 配置中的模型键是否为default_model,以及baseURL是否包含/v1。如果用的是 2.0 beta 配置,确认modelID映射正确。

5.4 OAuth 相关报错

opencode 某些 provider 会走 OAuth 流程,但 MLX-LM 是本地 OpenAI-compatible 服务,不需要 OAuth。如果你看到 OAuth 相关提示,说明 provider 类型选错了。确认配置里用的是@ai-sdk/openai-compatible,而不是某个需要 OAuth 的官方 provider。

5.5 其他常见问题

mlx_lm.server: command not found:执行uv tool update-shell,重开终端,或用uv tool dir --bin查看可执行文件目录。

Model type ... not supported:升级uv tool upgrade mlx-lm,确认模型是 MLX 权重而不是 GGUF。

opencode 报 404:依次检查curl http://127.0.0.1:8080/health和curl http://127.0.0.1:8080/v1/models,确认配置中的地址包含/v1。

opencode 尝试加载错误模型:确保稳定版配置中的模型键是default_model,并使用mlx-local/default_model。

输出只有几百 tokens 就停止:MLX-LM 默认max_tokens是 512,确认启动命令包含--max-tokens 2048,同时确认 opencode 中limit.output为 2048。

服务启动后系统变得很卡:模型加运行时已接近 24GB 上限,关闭其他重型应用,将缓存降至 512MB,输出降至 1024,仍然卡顿则换较小模型。

图像输入限制:Qwen3.6-27B 本身带视觉能力,但当前 MLX-LM 文本服务器会把 opencode 接入场景视为文本 Chat Completions,服务端对消息内容的处理只适合文本片段。opencode 中的代码、终端输出和文本文件可以正常使用,不要期待此配置直接处理截图或图片附件。需要图片输入时,使用 mlx-vlm、支持 MLX VLM 的 LM Studio,或单独建立视觉服务。

6. 长期编码与 Agent 场景的 CTA 分流

本地 MLX-LM 跑 Qwen3.6-27B 量化模型接入 opencode,适合对代码隐私敏感、想完全离线编码的场景。但它也有明显边界:24GB 内存下上下文只能开到 8192 到 16384,并发只有 1,模型切换和长任务会受限于本地算力。如果你需要更长的上下文、更高的并发,或者想让 Agent 跑更复杂的多步任务,可以考虑把部分请求分流到云端。

TaoToken 提供了 OpenAI-compatible 的 API 接入方式,API 地址是https://taotoken.net/api。你可以把它作为 opencode 的另一个 provider,和本地 MLX-LM 并存,按任务类型切换。比如日常小改动走本地,长上下文重构走云端。TaoToken 的模型对话入口可以用来快速验证模型表现,API Keys 管理页面用来生成和轮换密钥,接入文档里有完整的 Base URL、Key 和 Model ID 三件套说明。

如果你主要做长期编码和 Agent 任务,可以关注 TaoToken 的 Coding Plan,它针对编码场景做了额度和模型路由的优化。具体入口在 deep link 里可以找到:模型对话、coding-plan、console、api-keys、doc 都有对应页面。

配置云端 provider 时,opencode 的写法类似:

{ "$schema": "https://opencode.ai/config.json", "provider": { "taotoken": { "npm": "@ai-sdk/openai-compatible", "name": "TaoToken", "options": { "baseURL": "https://taotoken.net/api/v1", "apiKey": "你的 TaoToken Key" }, "models": { "qwen-plus": { "name": "Qwen Plus" } } } } }

注意 Base URL 要带/v1,Key 从 TaoToken 的 API Keys 页面获取,Model ID 参考接入文档。这样你就有本地和云端两条路径,按需切换。

最后提醒一句:MLX-LM 的 HTTP 服务只提供基础安全检查,不要直接暴露到公网。本地开发够用,生产环境需要额外的反向代理和鉴权层。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进