DAY 28 / PHASE 3 · FRONTIER

Local & Edge LLM

Quantization · Memory Math · Runtime 差异 · Hybrid Routing

2026-06-11 · BigCat

本地模型不是「更便宜的 Claude」,它是一类有自己工程约束的部件。

// WHY THIS MATTERS

2026 年,一台 32GB 的 Mac 已经能跑得动 Qwen / Llama / gpt-oss 这一档的开放权重模型。于是很多人把本地模型当成「免费版 Claude」直接塞进 agent——然后撞上一堆莫名其妙的退化:长 context 突然失忆、tool call 格式崩、同一个模型在 Ollama 里和 MLX 里表现不一样。问题不在模型,在于本地推理是一套和调云端 API 完全不同的工程:你要自己背负量化决策、显存预算、运行时选择、约束解码。这一期不讲「量化是什么」或「怎么装 Ollama」(那是入门),而是讲四个决定本地方案成败的工程判断:量化到底损失了什么、显存够不够用怎么算、四个运行时差在哪、以及小模型做 agent 的现实边界与 hybrid 路由。目标是让你在「这个任务该本地还是上云」这个问题上,给出有数字支撑的答案。

// 01

量化是工程决策,不是免费午餐

论断:Perplexity 几乎不变 ≠ 没损失。量化对 reasoning / code / 长 context 的伤害远大于 PPL 显示的。

背景与原理

显存占用第一性公式:权重字节数 = 参数量 × 每参数字节。FP16 是 2 bytes/param,所以 7B 模型裸权重 14GB;量化到 4-bit(约 0.5 byte/param 含 scale 开销)压到 ~4GB——这就是本地能跑起来的前提。GGUF 的 K-quants(Q4_K_MQ5_K_M)是现在的实际标准:它们对不同 tensor 用不同精度(attention 和 feed_forward 的关键层给更高 bit),Q4_K_M 被公认为体积/质量的甜点。再叠加 imatrix(importance matrix):用一份校准语料统计「哪些权重一动输出就大变」,量化时优先保护它们,能进一步压低困惑度损失。

但真正的陷阱是评估指标。社区习惯用 perplexity 衡量量化损失,而 PPL 是 next-token 的平均对数似然——它对「偶尔一个关键 token 选错」极不敏感。可恰恰是这种偶发错误,会让一段代码编译不过、让一条 reasoning 链断掉。所以你会看到「Q4 的 PPL 只涨了 1%」但实际跑 coding / 数学任务掉了一个档。多步推理和精确生成对量化的敏感度,被平均化的 PPL 系统性低估了。

实战示例

# 别只信 PPL——用你自己任务的样本做 A/B
# 同一个模型拉两个量化档,跑你真实的 20 条 eval
ollama pull qwen3:8b              # 默认通常是 Q4_K_M
ollama pull qwen3:8b-q8_0         # 高精度对照组

# 自己带 imatrix 量化(最大化保真):
# 1) 用贴近你领域的语料算 importance matrix
./llama-imatrix -m model-f16.gguf -f calib.txt -o imat.dat
# 2) 量化时挂上 --imatrix,关键权重被优先保护
./llama-quantize --imatrix imat.dat model-f16.gguf model-Q4_K_M.gguf Q4_K_M

核心纪律:量化档位是按任务选的,不是按「能跑就行」选的。抽取 / 分类这类宽容任务,Q4 甚至更低都行;代码 / 数学 / 长链推理,宁可上 Q5_K_M / Q8 或换更小但满血的模型。

失败模式:(1)小模型上激进量化——3B 模型本身冗余就少,Q4 的相对伤害远大于同档量化的 70B;小模型要质量就别低于 Q5。(2)顺手把 KV cache 也量化到 Q4 省显存——长 context 检索会明显退化,因为 KV cache 的精度直接决定模型「回看」早期 token 的能力。KV cache 一般 Q8 是安全线,Q4 慎用。
进阶资源 · llama.cpp quantize README, github.com/ggml-org/llama.cpp/.../quantize · imatrix 讨论, llama.cpp discussion #5006
// 02

显存内存数学,与「本地够用」的判定

论断:能不能跑 = 权重 + KV cache + overhead 是否 ≤ 你的显存。大多数 OOM 死在被忽略的 KV cache 上。

背景与原理

「这个模型能不能在我机器上跑」不是查表,是算账。预算由三块组成:

把这三块加起来对比你的显存(或 Apple Silicon 的统一内存),才知道「装得下吗」。更重要的是第二个判定:装得下 ≠ 该用。本地模型有明确的「够用区」和「别碰区」:

本地够用区 ──────────────▶ 上云区 分类 / 抽取 / 路由 / PII 脱敏 前沿 reasoning / 复杂数学 固定格式改写 / 摘要 长 context(>本地能稳跑的) 离线 / 隐私强约束场景 多步 agentic 任务(要高可靠) 高并发低成本的「脏活」 一次性、低频、要最强质量 判据:任务宽容度 × 隐私要求 × 调用量 ↑容错 ↑隐私 ↑高频 → 本地划算 ↑要最强 ↑低频 → 直接上云更省心

实战示例

# 30 秒估算:模型装得下吗?
weights_GB = params_B * bytes_per_param   # Q4≈0.5, Q8≈1.0, FP16≈2.0
kv_GB      = 2 * layers * kv_heads * head_dim * ctx * 2 / 1e9  # FP16 KV
need_GB    = weights_GB + kv_GB + 1.5     # +overhead 余量

# 例:Llama-70B Q4, 32K context, GQA(kv_heads=8)
#  weights ≈ 40GB, kv ≈ 10GB+ → need ≈ 52GB
#  → 48GB 机器「号称跑得动 70B」其实开不到 32K context
失败模式:只按权重大小选机器,忘了 KV cache。「48GB 能跑 70B Q4(40GB)」在 2K context 下成立,一开长 context 就 OOM 或被框架悄悄截断。买机器 / 选模型时,按你真实的 context 长度把 KV cache 一起算进预算,别只看模型文件大小。
进阶资源 · Simon Willison How to run an LLM on your laptop, simonwillison.net/2025/Jul/18 · Apple Silicon 运行时对比研究, arXiv:2511.05502
// 03

运行时不是都一样:llama.cpp / Ollama / MLX / LM Studio

论断:同一个权重在不同运行时表现不同。最坑的不是性能差,是 Ollama 默认 context 静默截断。

背景与原理

「本地跑模型」其实是一个分层的栈,每层解决不同的事:

┌─────────────────────────────────────────────┐ │ LM Studio GUI + 模型市场(后端挂 llama.cpp / MLX)│ ├─────────────────────────────────────────────┤ │ Ollama 模型 registry + Modelfile │ │ + OpenAI 兼容 server(包 llama.cpp) │ ├──────────────────────┬──────────────────────┤ │ llama.cpp │ MLX / mlx-lm │ │ 跨平台 C++ 引擎 │ Apple Silicon 原生 │ │ GGUF · GBNF 语法 │ 统一内存零拷贝 · 高吞吐 │ └──────────────────────┴──────────────────────┘

实战示例

Ollama 最隐蔽的坑:默认 num_ctx 很小(历史上常是 2048/4096)。你以为在用模型的 128K 窗口,实际它只看到几千 token,超出部分被静默丢弃——长 context 任务「无故失忆」十有八九是这个。用 Modelfile 显式拉大:

# Modelfile:显式设置 context,别吃默认值的暗亏
FROM qwen3:8b
PARAMETER num_ctx 32768

# 创建并起服务
ollama create qwen3-32k -f Modelfile

# 然后用 OpenAI SDK 直连本地——代码几乎不用改
from openai import OpenAI
client = OpenAI(base_url="http://localhost:11434/v1", api_key="ollama")
r = client.chat.completions.create(model="qwen3-32k",
        messages=[{"role":"user","content":"..."}])
失败模式:(1)拿云端的 benchmark 数字推断本地表现——量化档、运行时、KV 精度全不同,必须在你的运行时上重测。(2)在 Mac 上用通用 GGUF 路线却抱怨慢——同机器换 MLX 往往直接提吞吐,运行时选错比模型选错更亏。(3)忘了 num_ctx,把锅甩给「模型长 context 不行」。
进阶资源 · Ollama OpenAI 兼容文档, docs.ollama.com/api/openai-compatibility · Apple MLX, github.com/ml-explore/mlx
// 04

小模型做 Agent 的边界,与 Hybrid 路由

论断:本地小模型 tool-use 不稳,是工程问题不是能力问题——约束解码保语法,hybrid 路由保质量。

背景与原理

把 8B 本地模型塞进 agentic loop,最常见的崩法是 tool call 格式不稳:少个括号、JSON 截断、字段名拼错——一个解析错误整个 loop 就死。云端大模型把这件事做得很好让你忘了它有多难,小模型没这福气。解法不是「再求模型守规矩」,而是 约束解码(constrained decoding):llama.cpp 的 GBNF 语法 / JSON Schema 在采样层直接把不合语法的 token 概率清零,物理保证输出是合法 JSON。这比 prompt 里写「请输出 JSON」可靠一个量级。

但要记住约束解码的边界:它只保语法,不保语义。模型仍可能填进一个格式合法但内容错误的值。而且 llama.cpp 的实现里,schema 只用于约束输出、不会被注入进 prompt——模型「看不到」schema,你想让它理解字段含义,仍要在 prompt 里描述清楚。

真正的工程答案往往是 hybrid:本地模型干高频、宽容、隐私敏感的「脏活」(路由分类、PII 脱敏、初筛、draft),把难的、低频的、要最强质量的步骤交给云端前沿模型。本地作为 router 和 fallback,而不是全程主力。

┌── 简单/高频/隐私 ──▶ 本地 8B(约束解码) 请求 ─▶ 本地分类器 ┤ └── 复杂/低频/要最强 ─▶ 云端前沿模型 (本地还可做 draft,再让云端 verify)

实战示例

# 用 JSON Schema 约束本地输出(llama.cpp server / llama-cpp-python)
# 采样层强制合法 JSON——小模型也不会再吐坏格式
schema = {"type":"object",
  "properties":{"intent":{"enum":["search","refund","other"]},
                "urgent":{"type":"boolean"}},
  "required":["intent","urgent"]}

r = client.chat.completions.create(model="qwen3-8k",
      messages=[{"role":"user","content": ticket}],
      extra_body={"response_format":{"type":"json_schema",
                  "json_schema":{"schema": schema}}})
# intent=search → 本地直接处理;intent=refund 且 urgent → 升级云端
失败模式:(1)以为约束解码=正确——它保证 {"urgent":true} 是合法 JSON,不保证 true 是对的判断;语义错误照样发生。(2)指望本地 8B 跑完整多 tool 的 agentic loop——步数一多,累积错误率把成功率拖到不可用,这种就该上云。(3)hybrid 里本地分类器自己就不准——路由层用错模型,下游全错,分类器要么够准要么也得是云端便宜档。
进阶资源 · llama.cpp GBNF grammars, github.com/ggml-org/llama.cpp/.../grammars · Simon Willison llama.cpp grammars 生成 JSON, til.simonwillison.net

// 综合实战 · 给自己造一个本地优先的「分诊」层

把这四点串成一个能省钱又护隐私的周末项目:在你现有的某个云端调用前面,加一层本地分诊。

  1. 选量化与模型(§1):分类任务宽容,拉一个 8B 的 Q4_K_M 起步;先用你 20 条真实样本 A/B 一下 Q4 vs Q8,确认 Q4 没掉点再定。
  2. 算显存预算(§2):按你需要的 context 把 KV cache 一起算进去,确认能稳跑——别等上线才 OOM。
  3. 选运行时(§3):Mac 上优先试 MLX,其余用 Ollama 起 OpenAI 兼容 server,Modelfile 里显式写够 num_ctx
  4. 约束 + 路由(§4):本地分类器用 JSON Schema 约束输出 {intent, urgent};简单/隐私的就地处理,复杂/高价值的才升级云端前沿模型。
  5. 量化收益:跑一周,记录「本地处理占比 × 单次省下的 token 成本」和「升级到云端的准确率」。绝大多数分类/路由场景会发现:60%+ 的请求根本不需要上云,成本直接砍半还顺带护了隐私。

做完这一套,你对「本地 vs 云」就不再是感觉,而是一条有数字的判据线——这正是超级个体该有的成本直觉。

// ENGLISH GLOSSARY

GGUF
llama.cpp 的模型权重格式,本地推理的事实标准容器。
Quantization
把权重从 FP16 压到 4/5/8-bit 以省显存,本地能跑的前提。
K-quants (Q4_K_M)
对不同 tensor 用不同精度的混合量化;Q4_K_M 是体积/质量甜点。
imatrix
importance matrix,用校准语料统计关键权重,量化时优先保护以降损失。
KV Cache
缓存历史 token 的 K/V,随 context 线性增长;常被低估的显存大头。
GQA
分组查询注意力,减少 kv_heads,是长 context 显存可控的关键。
Unified Memory
Apple Silicon 的 CPU/GPU 共享物理内存,MLX 借此零拷贝。
MLX / mlx-lm
Apple 为自家芯片的 ML 框架及其 LLM 推理库。
Constrained Decoding
采样层屏蔽非法 token,物理保证输出合法 JSON/语法(GBNF)。
Hybrid Routing
本地干高频脏活、云端干难活的混合架构,省钱护隐私。

// 深入思考

既然 perplexity 系统性低估量化对 reasoning 的伤害,为什么社区还在普遍用 PPL 比较量化档?
因为 PPL 便宜、确定、可复现——一份固定语料跑一遍就有数,不需要设计任务、不引入评判噪声。而任务级 eval 贵且主观。这是「能测的 vs 想测的」之间的经典妥协。正确姿势:把 PPL 当粗筛(PPL 暴涨直接淘汰),但定档必须用你自己任务的样本做 A/B。指标的便利性不该决定你的工程决策——这是所有 eval 工程的通则。
统一内存让 Mac 能用「大内存」跑大模型,但为什么它在重负载下仍打不过同等显存的独显?
瓶颈在内存带宽与算力,不只是容量。独显的 GDDR/HBM 带宽远高于 Mac 统一内存,且有更强的并行算力。统一内存的胜场是「容量大、零拷贝、能效高」——能装下别人装不下的模型、做单流低延迟很爽;但高并发 batch 吞吐、超大模型的算力密集场景,独显仍占优。选机器要看你是「单人低延迟」还是「多路高吞吐」。
约束解码能 100% 保证合法 JSON,那它会不会反而降低输出质量?
会,而且这是真实 trade-off。强行屏蔽 token 可能把模型「本想走的高概率路径」掐断,逼它进入次优分支,某些任务上质量略降。更隐蔽的是:如果 schema 和模型自然倾向冲突(比如它想先解释再给结论,你却只允许纯 JSON),约束会放大这种别扭。缓解:schema 尽量贴合模型自然输出、必要时留一个 reasoning 字段先让它「想」、复杂结构分两步(先自由生成再抽取)。
Hybrid 路由听起来很美,但多引入一个本地分类器就多一个故障点和延迟。什么时候 hybrid 反而是过度工程?
当调用量不够大时。Hybrid 的收益来自「高频请求里很大比例其实简单」——量小时省下的成本覆盖不了多维护一套本地栈的复杂度(部署、监控、分类器自身的错误)。低频场景直接全上云更省心。判据和 Day 3「workflow vs agent」同源:先用最简单的(全云),只在成本/隐私真的成为瓶颈时才加 hybrid。别为了「用上本地模型」而 hybrid。
「本地够用区」会随模型变强不断扩大,最终是否会把云端前沿模型挤到很窄的角落?
够用区确实在扩大——今天 8B 能干的,三年前要 GPT-3.5。但前沿能力也在涨,二者是赛跑而非收敛。更可能的稳态:本地吃掉「成熟、可标准化、对最强质量不敏感」的长尾任务(分类、抽取、改写),云端守住「需要最新知识、最强推理、最长 context」的前沿。边界持续右移,但不会消失——就像本地数据库没消灭云数据库,只是各占其位。真正的杠杆是判断力:知道哪类任务此刻落在哪边。

// 延伸阅读