若曾考虑将编程工具(coding harness)的 API 调用换成本地模型,结果常令人失望。你跑过拉玛基准(llama-bench),以为可见 X tokens/秒。但该测试无法反映实际使用这些工具的手感,开发体验极不稳定。你可能枯坐数分钟才见回应,或中途停滞不解为何预填充(prefill)耗时良久。这并非你的错,多数编程工具设计之初并未顾及本地模型。
正文
如果你曾想把编码 harness 的 API 调用换成本地模型,结果多半令人失望。你跑个 llama-bench,以为会看到 X token/秒。但那个基准不告诉你实际使用这些 harness 的感觉。开发体验变化极大。你可能坐等几分钟才看到任何响应。也许你正有进展,半路却开始卡住。“这个 prefill 是谁,占了我所有时间?”不是你的错:大多数编码 harness 不是为本地模型设计的。
这不是对其它团队出色工作的攻击。我想可靠测量把数据中心换成 localhost 时会发生什么。一定程度的工具不对口是预期的。请自行加盐:我一直在捣鼓 chad,一个专为 Apple silicon 上 Qwen 3.8 27B 优化的编码 harness。
大多数 harness 在三个方面与 localhost 作对。
大系统提示与工具 schema。假设你的笔记本读 90 token/秒,写约 10。这代表编码 harness 每个循环的 prefill 和 generation 部分。这个速度下,每读 1,000 token 提示,就要盯着光标约 11 秒,模型才开始写。LLM 做任何工作之前会读整个系统提示以及加载的工具 schema。在 pi harness 中,Qwen 3.8 27B 的组合是 2,008 token,而 Opencode 全部加起来 18,046。数据中心 GPU prefill 平均 10k+ token/秒时,你不会注意到多大差别。这压缩到 0.2 对 1.8 秒。在你的笔记本上?这是 22 对 226 秒的实测差别。无法忍受!
更小的上下文窗口。LLM 读完系统提示后,内存中留给工作的上下文窗口有限。比你想象的小,而 harness 已经花掉一部分。你有多少上下文取决于起始内存与模型权重。变量很多,但在相当好的笔记本上、相当好的模型,32,000 token 是合理猜测。pi harness 还剩多少 32,000 token 预算?94%——似乎可控。Opencode?18,046 token 已经出门,只剩 44% 上下文实际工作。侧请求的习惯。传统本地客户端/远程服务器模式中,harness 可以随意向数据中心服务器发侧请求。你的笔记本既是客户端又是服务器。最好情况下侧请求让本地模型排队等待,最坏情况下导致反复长 prefill。24 个任务中 opencode 发了 33 个,crush 51,dsh 24(会话标题和摘要),几乎每个都与代理轮次重叠。模型在 opencode 和 crush 中“忙碌”了 125% 和 114% 的墙钟时间:一个 GPU 上两个请求在飞。
我把 9 个 harness 跑过 8 个 Exercism 练习,各自自动批准模式,一个相同的一句话提示。每个任务用同一台 M4 MacBook Pro(24GB,macOS 26.6.2),3 bit 量化的 Qwen 3.8 27B,通过 llama.cpp(build 10470)服务。同一个 llama-server 被每个 harness 共享,一个公共代理强制执行相同的推荐 Qwen 采样(temperature=1.0,topk=20,topp=0.95,min_p=0.05)。每个会话有相同的 32,768 统一缓存,跨四个槽。下面每个数字都是 llama-server 自己的统计,通过代理解读,从不是 harness 自报,除了标 * 的两行(chad 在其进程内 MLX 引擎,没有服务器可观察,所以来自 chad 自己的 prefill trace,定义相同)。
wait before 1st token 是服务器对系统提示、工具 schema 和第一个请求的 prefill 时间。
wait / later turn 是第一轮之后每轮的同样暂停,侧请求除外。
cache reuse 是每个后续轮次从前缀缓存服务的比例。
experienced tokens/second 是整个任务的生成 token 除以墙钟时间,包括 prefill 和工具时间;不同夜晚有高达 50% 波动,所以精瘦组之间没有发现。
pass 是 Exercism 任务的门槛。不要解读为排名,这些是简单 python 任务。T 计入 1,200 秒上限的超时。
| 编码工具 | 版本 | 工具数 | 负担:第一轮提示(token) | 第一 token 前等待,第一轮 | 后续轮等待(中位数 · p90) | 缓存重用 | 实际 token/秒 | 通过(门槛) | | --- | --- | --- | --- | --- | --- | --- | --- | --- | | mini-swe-agent | 2.4.6 | 1 | 1,171 | 12.2 s | 3.6 s · 21 s | 96% | 8.0 | 11/24 (14 T) | | pi | 0.80.3 | 4 | 2,008 | 21.6 s | 1.3 s · 22 s | 99% | 8.1 | 19/24 (7 T) | | cline | 3.0.60–61 | 26 | 5,876 | 64.1 s | 9.9 s · 52 s | 94% | 7.3 | 17/24 | | codex | 0.151.0 | 10 | 7,804 | 87.8 s | 9.6 s · 28 s | 94% | 6.9 | 19/24 (5 T) | | dsh | 0.1.1-rc.2 | 25 | 8,052 | 94.4 s | 2.2 s · 34 s | 99% | 7.2 | 18/24 | | goose | 1.50.0 | 18 | 9,617 | 110.3 s | 1.0 s · 22 s | 100% | 8.0 | 22/24 (3 T) | | crush | 0.92.0 | 26 | 16,263 | 199.8 s | 1.8 s · 40 s | 100% | 5.8 | 18/24 (8 T) | | opencode | 1.17.12 | 10 | 18,046 | 225.7 s | 4.6 s · 44 s | 99% | 5.7 | 15/24 (13 T) | | chad (llama.cpp) | 2.0.3 | 5 | 2,563 | 25.6 s | 0.8 s · 19 s | 99% | 7.9 | 24/24 | | chad (MLX, serial) * | 2.0.3 | 5 | 2,566 | 4.7 s | 1.0 s · 19 s | 99% | 12.4 | 21/24 (3 T) | | chad (MLX, dflash2) * | 2.0.3 | 5 | 2,562 | 4.6 s | 0.9 s · 36 s | 99% | 17.4 | 22/24 (3 T) |
在我的经验中,harness 分三组。
精瘦稳定(pi、mini-swe-agent、chad)。这些 harness 有精简系统提示和 96–99% 缓存重用。它们基本上开箱即用,无论数据中心还是本地模型后端。唯一警告是 mini-swe-agent 只通过 11/24 的 Exercism 任务,超时最多。
重但有纪律(dsh、cline、codex、goose)。长系统提示或高工具 schema 数量的综合效果。它们的前缀字节稳定,所以一旦开始感觉可忍受。(goose 在 1.50.0 获得此组:早期版本每轮把分钟分辨率时间戳重新渲染到第一条用户消息,缓存重用跌到 78%)
启动重(crush、opencode)。要等 3–4 分钟才看到任何活动。
这些 harness 都不是糟糕工程。Opencode 的 18k 系统提示存在,是因为它帮助部署在 API 后的前沿模型。crush 的 26 个工具 schema 在有 200k 上下文且 prefill 瞬时完成时没问题。所有这些选择都是在 prefill 几乎免费的环境中做出的。本地速度下这一切崩溃。
chad 故意最小。只有 5 个工具可用,都是模型预训练时已学会的形状。磁盘检查点系统提示、持久前缀缓存、拟合 drafter 都是这种稀缺思维的延伸。仅靠纪律不能解释你实际感受到的差距。标准客户端/服务器模型中,chad/llama.cpp 接口甚至不是精瘦三者中最快的。你大概可以用一个下午写出大部分循环。
chad 的真正数字出现在你有自由把服务器和代理循环耦合在同一进程时。harness 现在拥有缓存。相同 harness 和提示,进程内 MLX 引擎经历 7.9 → 17.4 token/秒(或关闭 drafter 时 12.4 token/秒)。MLX 进程的唯一优势是解码,不是 chad 的请求模式:后续轮等待在两个引擎上都是 1 秒。与自己的控制配对在生成中再跑三夜,DFlash2 drafter 读 23.3 对 15.9 token/秒,合并 1.47 倍,八个任务每个都赢。
所有东西从 chad 的 checkout 运行:runner、强制代理、八个任务的记分卡在 benchmarks/matrix。每个臂的原始行(grid.json,每个请求一行 turns.jsonl,采样器审计,来源)与合并表一起提交,意图每个数字可追溯到一行。命令:uv run python benchmarks/matrix/run.py setup|smoke|llama|mlx|table
感谢 llama.cpp 维护者提供服务器端前缀缓存和 timings 对象,足够构建整个仪器;感谢 z-lab 提供 DFlash2。
评论
社区聚焦于本地编程代理的轻量化与推理性能对比,同时普遍反感 AI 代写的技术文档,并吐槽原网页滚动体验糟糕。
反感 AI 生成的技术文档与提交
- 读者难以忍受 AI 写的说明文档和大体量代码提交,认为哪怕项目其余部分靠 AI 辅助,也只接受人类手写的可读文档(少数人)。 > 只要开发者手写一份真实可读的文档,我不介意项目其余部分靠氛围编程,但我实在提不起兴趣看 AI 生成的摘要和文档。(@montyanne)
本地代理框架的推理效率差异巨大
- 同硬件无独显下,不同框架调用同一本地模型延迟悬殊,如 opencode 检查目录需 20 分钟,而 llama.cpp 可秒回;社区因而推荐 hax 等内存仅几 MB 的原生轻量工具,并关注优化缓存复用的新框架(较多人)。
- 不少用户在各类设备上广泛采用 Qwen 27B 做本地推理,推断其质量获社区公认(少数人)。
质疑基准测试的上下文容量设定
- 有开发者分享实际上下文轻易突破数十万 token,加个简单函数也耗 5 万,因此对测试中仅考量 32k 级别 token 的实用性困惑(少数人)。 > 我让 Claude 总结 60 万 token 上下文并开新会话,新会话又快到 50 万了,你拿 32k token 到底能干什么?(@nottorp)
希望建立可复现的公开评测体系
- 框架迭代快致结果易过时,有人呼吁建小巧可本地跑、统一比各项延迟与缓存率的测试仓库;新框架作者也抱怨评测忽视其作,渴求用户反馈(较多人)。 > 我希望有个极简可复现的基准仓库,大家直接在自有硬件上跑,统一比对首字延迟、缓存复用率等指标。(@humbleferret)
批评原文网站交互体验糟糕
- 多名用户指出网页滚动异常、快速滑动有渲染伪影,严重阻碍阅读(较多人)。 > 这网站的滚动到底出了什么问题?快速滚动时还有破碎的渲染伪影。(@asdfsa32)
非本地商业模型的横向对比
- 脱离本地语境,有用户实测某商业模型在速度与 token 效率上均优于另外两款(少数人)。 > 我测了三款商业编码工具,发现某一款既快又省 token,另外一款从未赢过。(@toasty228)
有价值的补充
- 社区顺带分享了其他特定场景工具,如擅长浏览器交互的 jcode、用于同类用例的 maki.sh 等。
- 新框架 Juggler 与 Reasonix 的关注者呼吁评测应涵盖更多新兴工具,后者强调在前缀缓存复用上的调优。