Skip to main content

在 Mac 上运行本地 LLM

本指南将带你在 macOS 上运行一个提供 OpenAI 兼容 API 的本地 LLM 服务。这样做可以获得完整隐私、零 API 成本,以及在 Apple Silicon 上相当不错的性能表现。

我们会介绍两种后端:

后端安装方式最适合格式
llama.cppbrew install llama.cpp首 token 延迟最低,量化 KV 缓存更省内存GGUF
omlxomlx.aitoken 生成速度最快,原生 Metal 优化MLX (safetensors)

两者都会暴露兼容 OpenAI 的 /v1/chat/completions 接口。Hermes 能直接对接任意一种,只需把地址指向 http://localhost:8080http://localhost:8000

仅适用于 Apple Silicon

本指南面向 Apple Silicon Mac(M1 及更新机型)。Intel Mac 也能运行 llama.cpp,但无法使用 GPU 加速,性能会明显下降。


选择模型

入门推荐使用 Qwen3.5-9B。这是一个推理能力很强的模型,在经过量化后,可以比较轻松地运行在 8GB 以上统一内存的机器上。

变体磁盘占用所需内存(128K 上下文)后端
Qwen3.5-9B-Q4_K_M (GGUF)5.3 GB约 10 GB,含量化 KV 缓存llama.cpp
Qwen3.5-9B-mlx-lm-mxfp4 (MLX)约 5 GB约 12 GBomlx

经验法则: 总内存占用约等于模型体积 + KV 缓存。一个 9B 的 Q4 模型大约为 5 GB;在 128K 上下文下,Q4 量化 KV 缓存还会再增加约 4 到 5 GB。若使用默认的 f16 KV 缓存,则会膨胀到约 16 GB。llama.cpp 的量化 KV 缓存参数,是内存受限机器上最关键的优化手段。

如果要运行更大的模型,如 27B、35B,通常需要 32 GB 以上统一内存。对 8 至 16 GB 的机器来说,9B 是甜点位。


方案 A:llama.cpp

llama.cpp 是最通用的本地 LLM 运行时。在 macOS 上,它开箱即用支持 Metal GPU 加速。

安装

brew install llama.cpp

安装后你就可以直接使用全局命令 llama-server

下载模型

你需要一个 GGUF 格式模型。最简单的来源是通过 huggingface-cli 从 Hugging Face 下载:

brew install huggingface-cli

然后执行:

huggingface-cli download unsloth/Qwen3.5-9B-GGUF Qwen3.5-9B-Q4_K_M.gguf --local-dir ~/models
受限模型

Hugging Face 上部分模型需要登录后才能下载。如果遇到 401 或 404,请先执行 huggingface-cli login

启动服务

llama-server -m ~/models/Qwen3.5-9B-Q4_K_M.gguf \
-ngl 99 \
-c 131072 \
-np 1 \
-fa on \
--cache-type-k q4_0 \
--cache-type-v q4_0 \
--host 0.0.0.0

各参数含义如下:

参数作用
-ngl 99将所有层尽量卸载到 GPU(Metal)上。给一个足够大的数值,确保不要残留在 CPU。
-c 131072上下文窗口大小(128K tokens)。如果内存吃紧,可以先降低这个值。
-np 1并行槽位数。单用户场景建议保持为 1,槽位更多会拆分你的内存预算。
-fa on打开 Flash Attention,降低长上下文推理的内存占用并提升速度。
--cache-type-k q4_0将 key cache 量化为 4-bit。这是最重要的省内存选项之一。
--cache-type-v q4_0将 value cache 量化为 4-bit。与上一项结合后,KV 缓存相比 f16 可降低约 75% 内存占用。
--host 0.0.0.0监听所有网卡。如果不需要局域网访问,可改成 127.0.0.1

出现以下输出后,就表示服务已经就绪:

main: server is listening on http://0.0.0.0:8080
srv update_slots: all slots are idle

面向内存受限机器的优化

对内存紧张的机器来说,--cache-type-k q4_0 --cache-type-v q4_0 是最关键的优化。以 128K 上下文为例:

KV 缓存类型KV 缓存内存占用(128K 上下文,9B 模型)
f16(默认)约 16 GB
q8_0约 8 GB
q4_0约 4 GB

如果你用的是 8GB Mac,建议启用 q4_0 KV 缓存,并把上下文降到 -c 32768(32K)。16GB 机器通常可以较轻松地跑 128K。32GB 及以上则可以尝试更大的模型,或更多并行槽位。

如果还是爆内存,优先先减小上下文大小(-c),其次再考虑更小的量化,比如从 Q4_K_M 降到 Q3_K_M。

测试

curl -s http://localhost:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "Qwen3.5-9B-Q4_K_M.gguf",
"messages": [{"role": "user", "content": "Hello!"}],
"max_tokens": 50
}' | jq .choices[0].message.content

获取模型名

如果你忘了当前服务暴露的模型名,可以查询模型列表接口:

curl -s http://localhost:8080/v1/models | jq '.data[].id'

方案 B:通过 omlx 使用 MLX

omlx 是一个 macOS 原生应用,用于管理和服务 MLX 模型。MLX 是苹果自家的机器学习框架,专门针对 Apple Silicon 的统一内存架构做了优化。

安装

omlx.ai 下载并安装。它自带图形界面用于模型管理,也带内置服务能力。

下载模型

打开 omlx,浏览并下载模型。搜索 Qwen3.5-9B-mlx-lm-mxfp4 即可。模型会保存在本地,通常位于 ~/.omlx/models/

启动服务

omlx 默认在 http://127.0.0.1:8000 提供服务。你可以在应用界面里启动,也可以在有 CLI 时通过命令行启动。

测试

curl -s http://127.0.0.1:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "Qwen3.5-9B-mlx-lm-mxfp4",
"messages": [{"role": "user", "content": "Hello!"}],
"max_tokens": 50
}' | jq .choices[0].message.content

列出可用模型

omlx 可以同时服务多个模型:

curl -s http://127.0.0.1:8000/v1/models | jq '.data[].id'

基准对比:llama.cpp vs MLX

两种后端都在同一台机器上测试:Apple M5 Max,128 GB 统一内存;模型相同,都是 Qwen3.5-9B;量化层级尽量对齐(GGUF 使用 Q4_K_M,MLX 使用 mxfp4)。测试包含五类不同提示,每类跑三次,并按顺序测试后端,避免资源抢占。

结果

指标llama.cpp (Q4_K_M)MLX (mxfp4)胜者
TTFT(平均)67 ms289 msllama.cpp(快 4.3 倍)
TTFT(p50)66 ms286 msllama.cpp(快 4.3 倍)
生成速度(平均)70 tok/s96 tok/sMLX(快 37%)
生成速度(p50)70 tok/s96 tok/sMLX(快 37%)
总耗时(512 tokens)7.3s5.5sMLX(快 25%)

如何理解这些结果

  • llama.cpp 在提示处理阶段表现极强。它配合 Flash Attention 和量化 KV 缓存后,可以在约 66ms 内吐出首个 token。如果你做的是聊天机器人、自动补全这类强调“响应快”的交互应用,这个优势很有价值。
  • MLX 在正式生成阶段更快,token 生成速度大约快 37%。如果你的场景更在乎总完成时间,比如批处理、长文生成、后台任务,那么 MLX 会更占优。
  • 两者的表现都非常稳定,多轮测试的波动很小,可以把这些结果当成有参考意义的数据。

怎么选

场景推荐
交互式聊天、低延迟工具调用llama.cpp
长文生成、批量处理MLX(omlx)
内存较紧(8 至 16 GB)llama.cpp(量化 KV 缓存优势明显)
同时服务多个模型omlx(内置多模型支持)
最大兼容性(未来也想跑在 Linux)llama.cpp

连接到 Hermes

当本地服务已经跑起来以后,执行:

hermes model

然后选择 Custom endpoint,按提示输入基础 URL 和模型名,填写为你刚才配置好的后端值即可。


超时设置

Hermes 会自动识别本地端点(如 localhost 或局域网 IP),并自动放宽流式请求的超时限制。大多数情况下不需要额外配置。

如果你仍然遇到超时,比如在比较慢的硬件上跑超大上下文,可以手动覆盖流式读取超时:

# In your .env — raise from the 120s default to 30 minutes
HERMES_STREAM_READ_TIMEOUT=1800
超时项默认值本地自动调整环境变量覆盖
流式读取(socket 级)120s自动提升到 1800sHERMES_STREAM_READ_TIMEOUT
静默流检测180s完全禁用HERMES_STREAM_STALE_TIMEOUT
API 调用(非流式)1800s无需调整HERMES_API_TIMEOUT

最容易出问题的一般是流式读取超时。它本质上是“等待下一段数据返回”的 socket 级超时。对于超大上下文,本地模型在预填充阶段可能几分钟都没有输出,因为它还在处理提示词。Hermes 的本地端点识别会自动照顾这一点。