DwarfStar 4 深度实战:当 Redis 之父手写 AI 推理引擎——从 284B MoE 模型塞进 MacBook 到生产级本地 Agent 的完全指南(2026)
引子:一个程序员的偏执
2026 年 5 月,GitHub 上一个项目在一周内突破 10,000 Star。没有花哨的 Web UI,没有云部署方案,甚至连 README 都写得像技术白皮书。它的核心代码是纯 C——25,000 行,外加 1.1 MB 的 Metal 内核和 512 KB 的 CUDA 实现。
作者栏写着 antirez。
对,就是那个 antirez——Salvatore Sanfilippo,Redis 之父。那个用 30,000 行 C 代码改变了全球数据基础设施的男人,这次把目光投向了本地 LLM 推理。
项目名叫 DwarfStar 4(简称 ds4),官方定位极其克制:"DeepSeek V4 Flash 的本地推理引擎"。但读懂它的人都知道,这是 2026 年本地推理领域最具颠覆性的工程实践之一。
为什么?因为它用一种近乎偏执的方式,解决了一个所有人都觉得"不可能"的问题——把一个 284B 参数的 MoE 模型,塞进一台 MacBook,并且让它"真的能用"。
不是概念验证,不是跑个 demo 就发推特。是 26 tokens/s 的生成速度,是完整的 OpenAI/Anthropic 兼容 API,是内置的 Coding Agent,是把 KV 缓存当磁盘一等公民的架构设计。
这篇文章,我们从源码级深度拆解 ds4 的每一个核心设计决策,从非对称量化到磁盘 KV,从 Metal 图执行到分布式推理,从 Session 抽象到方向引导。不管你是想本地跑大模型的 AI 工程师,还是对高性能系统设计感兴趣的后端开发者,这篇文章都会给你远超预期的收获。
一、背景:为什么本地推理在 2026 年依然是个硬骨头
1.1 大模型的"本地悖论"
2026 年的大模型生态有一个很荒谬的现象:模型越来越大,但开发者对本地运行的需求也越来越强。
云 API 确实方便,但痛点也越来越明显:
- 数据隐私:代码、文档、商业逻辑——你真的愿意把公司的核心代码发给别人的 API 吗?
- 成本失控:重度使用场景下,API 费用可以轻松超过硬件折旧成本
- 延迟敏感:Coding Agent 的交互循环里,网络 RTT 是最大的体验杀手
- 供应商锁定:API 格式变更、模型版本切换、定价策略调整——你毫无控制权
理想方案很明确:在自己机器上跑大模型,数据不出本机,成本只有电费。
但现实呢?
DeepSeek V4 Flash 有 284B 参数(MoE 架构),全精度需要 500GB+ 内存。即便是最激进的 4-bit 量化,也需要约 140GB。这已经不是"消费级硬件"的范畴了。
1.2 通用推理引擎的困境
llama.cpp 是这个领域的先驱,功不可没。但它的设计哲学是"广度优先"——支持尽可能多的模型架构。这导致:
- 对特定模型的优化永远是"通用方案的特例",而不是"针对这个模型重新设计"
- KV 缓存管理是通用方案,不会为某个模型的特性做特殊优化
- 量化方案是通用方案,不会利用模型结构的不对称性
这就像用一个"万能序列化框架"去处理一个结构完全已知的固定格式——你可以做到,但你永远做不到极致。
1.3 antirez 的判断
antirez 在 README 里写了一段很直白的话:
"This project deliberately takes a very narrow bet: do one model at a time, with official logits vectors for correctness regression, long context testing, and enough agent integration to confirm it actually works."
翻译过来就是:我不做通用,我只做一个模型,做到极致。
这个判断的背后是对 MoE 架构的深刻理解:DeepSeek V4 Flash 虽然有 284B 参数,但每次推理只激活约 30B——这意味着大部分权重在单次推理中根本不需要,而"不需要"的权重可以被极端量化而不显著影响质量。
这是 ds4 存在的物理基础。
二、核心概念:ds4 的设计哲学
2.1 "窄而深"策略
ds4 的每一个设计决策都遵循一个原则:只做一件事,做到极致。
| 维度 | llama.cpp / 通用方案 | ds4 |
|---|---|---|
| 模型支持 | 所有 GGUF 模型 | 仅 DeepSeek V4 Flash/PRO |
| 量化方案 | 通用 Q2/Q4/Q8 | 非对称 MoE 量化(不同组件不同精度) |
| KV 缓存 | RAM 中 | RAM + SSD 一等公民 |
| GPU 内核 | 通用 kernel | 针对模型张量形态专门调优 |
| 正确性验证 | 社区测试 | 官方 logits 逐 token 回归 |
| API 层 | 基本兼容 | OpenAI + Anthropic + Agent + 工具调用 |
这不是"比 llama.cpp 更好"——这是完全不同的物种。llama.cpp 是瑞士军刀,ds4 是一把手术刀。
2.2 三件套哲学
ds4 不是单独一个推理引擎,而是一个"三件套":
- 推理引擎(ds4.c):纯 C,25K 行,负责从 GGUF 加载到 token 生成的完整路径
- 特制 GGUF 量化文件:非对称量化方案,为这个引擎量身定制
- HTTP API + Agent(ds4_server.c + ds4_agent.c):OpenAI/Anthropic 兼容,内置 Coding Agent
三者配合开箱即用。你不需要在推理引擎和 API 之间做任何集成工作。
2.3 AI 辅助开发的工程实践
一个有趣的细节:antirez 在开发 ds4 时大量使用 GPT 5.5 辅助编码。但他对此的描述非常精准:
"AI assisted coding: GPT 5.5 was used extensively for writing code, but antirez did all the design, testing, and debugging."
这恰恰是 2026 年最成熟的 AI 编程方式——AI 写代码,人类做设计。不是让 AI 从零开始"创造",而是把人的架构决策翻译成高质量实现,再由人来验证和调试。
三、架构深度分析:从源码到运行时
3.1 代码架构总览
ds4 的代码组织非常清晰,遵循了"核心窄,上层宽"的设计:
ds4/
├── ds4.c # 🔴 核心推理引擎(~25K 行 C)
│ # GGUF 解析、模型加载、tokenizer、
│ # CPU 参考实现、Metal 图调度、
│ # session 管理、磁盘 KV payload 序列化
│
├── ds4.h # 🔴 公开 API 边界(~400 行)
│ # 只暴露 engine/session/generate/sync/sample
│
├── ds4_cli.c # 🟢 命令行 REPL
├── ds4_server.c # 🟢 HTTP 服务器(~598 KB)
├── ds4_agent.c # 🟢 内置 Coding Agent(~387 KB)
├── ds4_web.c # 🟡 Web UI
│
├── ds4_metal.m # 🔵 Metal 后端(~1.1 MB Objective-C)
├── metal/ # 🔵 Metal compute kernels
├── ds4_cuda.cu # 🔵 CUDA 后端(~512 KB)
│
├── ds4_kvstore.c/.h # 🟡 磁盘 KV 缓存层
├── ds4_distributed.c/.h # 🟡 分布式推理
├── ds4_bench.c # 🧪 基准测试
├── ds4_eval.c # 🧪 评估与回归测试
├── ds4_ssd.c/.h # 🔧 SSD 读写工具
│
├── gguf-tools/ # ⚙️ 量化与校准工具链
├── dir-steering/ # 🧭 方向引导(实验性)
└── Makefile
3.2 分层架构
┌─────────────────────────────────────────────────────────┐
│ 应用层 │
│ ┌──────────┐ ┌──────────┐ ┌─────────────────────┐ │
│ │ ds4_cli │ │ ds4_web │ │ ds4_server+agent │ │
│ │ (REPL) │ │ (Web UI) │ │ (HTTP API + Agent) │ │
│ └────┬─────┘ └────┬─────┘ └──────────┬──────────┘ │
│ └──────────────┼─────────────────┘ │
│ │ │
│ ┌───────────────────▼──────────────────┐ │
│ │ 公开 API 层(ds4.h, ~400 行) │ │
│ │ ds4_engine : 已加载模型(线程安全) │ │
│ │ ds4_session: 推理会话(每个用户一个) │ │
│ │ sync / generate / sample / save/load │ │
│ └──────┬────────────────────┬───────────┘ │
│ │ │ │
│ ┌──────▼──────────┐ ┌─────▼──────────────┐ │
│ │ ds4.c 核心引擎 │ │ ds4_distributed.c │ │
│ │ · GGUF mmap │ │ · Coordinator/Worker│ │
│ │ · tokenizer │ │ · TCP 激活流 │ │
│ │ · CPU 参考 │ │ · Pipeline prefill │ │
│ │ · Metal 调度 │ └─────────────────────┘ │
│ └──────┬──────────┘ │
│ │ │
│ ┌──────▼──────────┐ ┌──────────────────┐ │
│ │ Metal 后端 │ │ CUDA 后端 │ │
│ │ ds4_metal.m │ │ ds4_cuda.cu │ │
│ │ + metal/*.metal │ │ · CUBLAS GEMM │ │
│ │ · RoPE/RMSNorm │ │ · FlashAttn │ │
│ │ · Attention │ │ · 量化 GEMV │ │
│ │ · MoE Router │ └──────────────────┘ │
│ └─────────────────┘ │
└─────────────────────────────────────────────────────────┘
3.3 公开 API 层:ds4.h 的优雅设计
这是整个项目我最欣赏的设计之一。ds4.h 只有 400 行,但它定义了引擎的完整对外契约。
antirez 在文件开头写了一句关键注释:
"The CLI and server should treat ds4_engine as the loaded model and ds4_session as one mutable inference timeline. Keep this header narrow so HTTP/CLI code does not depend on tensor internals."
核心抽象只有两个:
typedef struct ds4_engine ds4_engine; // 已加载的模型(不可变,线程安全)
typedef struct ds4_session ds4_session; // 一个可变的推理时间线(每个用户一个)
engine 是只读的、可多 session 共享的——包含了模型权重 mmap、tokenizer、Metal/CUDA 图模板等"重"资源。
session 是可变的、属于单个用户的——包含了活跃 KV 缓存、当前 logits、采样状态、磁盘 KV 句柄等"轻"资源。
这种划分的妙处在于:加载一个 90GB 的模型只需要一次,之后创建 100 个 session 的成本极低——每个 session 只需要自己的 KV 缓存空间。
核心 API 流程:
// 1. 加载模型(耗时,只做一次)
ds4_engine *engine = ds4_engine_open("./ds4flash.gguf", ...);
// 2. 创建会话(轻量)
ds4_session *session = ds4_session_create(engine, ...);
// 3. 同步到 prompt(关键:自动 diff + 磁盘 KV 恢复)
ds4_session_sync(session, prompt_tokens, ...);
// 4. 循环生成
while (generating) {
ds4_session_sample(session, ...); // 采样下一个 token
ds4_session_eval(session, token); // 前向传播一个 token
}
// 5. 持久化到磁盘
ds4_session_save_payload(session, fp, ...);
// 6. 下次恢复
ds4_session_load_payload(session, fp, ...);
session_sync 是整个 API 最精妙的设计。它接收"完整的 prompt token 前缀",然后:
- 如果当前 session 的 checkpoint 是新 prompt 的前缀 → 只跑后缀部分(增量推理)
- 如果不是前缀 → 找最长公共前缀,从公共前缀之后重建(部分重建)
- 如果磁盘上有匹配的 KV checkpoint → 直接从磁盘恢复(跳过整个 prefill)
上层代码(server/agent)不需要关心任何增量逻辑——只要传完整 prompt,engine 自己做 diff。
四、核心技术:六大颠覆性设计
4.1 磁盘 KV 缓存:让 SSD 成为推理的"一等公民"
这是 ds4 最具颠覆性的设计决策。
传统推理引擎的信条是:KV 缓存必须在显存/内存里,磁盘太慢了。
antirez 的反直觉洞察是:在 MacBook 上,SSD 读速 7-10 GB/s,而 DeepSeek V4 的 KV 缓存经过了极端压缩——磁盘完全够用。
具体实现(ds4_kvstore.h):
// KV checkpoint 文件格式(48 字节固定头)
typedef struct {
char magic[4]; // "DSV4"
uint32_t version;
uint32_t model_id; // 模型标识(用于兼容性检查)
uint32_t quant_bits; // 量化位数
char reason[8]; // 保存原因("auto" / "session-end" / "manual")
uint64_t token_count; // token 数量
uint64_t hits; // 命中次数
uint64_t ctx_size; // 上下文窗口大小
uint64_t created_at; // 创建时间
uint64_t last_hit; // 最后命中时间
uint64_t payload_bytes; // KV payload 大小
uint64_t text_bytes; // 文本内容大小
} ds4_kvstore_header;
文件名用 SHA1(prompt_prefix_bytes) 生成,支持快速查询"这个 prompt 我之前跑过没"。
淘汰策略是 LRU + 半生命周期衰减:默认半生命周期 6 小时。一个 checkpoint 如果 6 小时内没被再次命中,它的"存活分"减半。超过预算(默认 4096 MB)时,淘汰分最低的。
这个设计对 Agent 场景的改变是根本性的:
场景:Coding Agent 已读了 10 个文件,做了 5 轮工具调用
传统方式:
- 下次对话需要重新跑这 10 个文件的 prefill → 几十秒
- 如果上下文超过 RAM 容量 → 直接 OOM
ds4 方式:
- 磁盘上保存了完整的 KV checkpoint
- 下次启动直接从磁盘恢复 → 几秒
- 新增的对话只需要增量推理
- RAM 装不下的部分自动放磁盘,SSD 带宽足够
4.2 非对称 2-bit 量化:只量化"可以被牺牲"的部分
2-bit 量化在传统认知里是"毁灭性"的——把 float16 压到 2-bit,信息损失极大。但 ds4 用了一个极其聪明的策略:不同模型组件用不同量化精度。
| 模型组件 | 量化方式 | 理由 |
|---|---|---|
| Routed MoE Experts | IQ2_XXS / Q2_K(2-bit) | 占参数最多(~70-80%),每 token 只激活少数,imatrix 可修正 |
| Up / Gate 投影 | IQ2_XXS | MoE 内部线性变换 |
| Down 投影 | Q2_K | — |
| Shared Experts | 4-bit+ | 所有 token 都执行,质量影响大 |
| Attention / Projections / Router | 不量化 | "骨架"部分,不能省 |
配合 imatrix(重要性矩阵)——在模型校准集上测量每层权重对输出 logits 的敏感度——对"更重要"的权重分配更多比特,对"不重要"的分配更少。
结果:q2-imatrix 版本在 96-128 GB 机器上跑,在 Coding Agent 场景下质量几乎不退化。
这个策略的物理基础是 MoE 架构本身:284B 参数中只有 ~30B 被激活,大量"沉睡"的专家权重可以承受极端量化——它们在单次推理中根本不参与计算,量化误差的影响被路由器"筛掉"了。
量化工具链在 gguf-tools/ 目录下:
# 构建校准数据集(~3000 行 Python,生成 prompts.jsonl ~21MB)
python3 gguf-tools/imatrix/dataset/build_ds4_imatrix_dataset.py
# 收集 imatrix
./ds4 --imatrix gguf-tools/imatrix/dataset/prompts.jsonl \
-m ds4flash.gguf \
--imatrix-out ds4flash.imatrix.dat
# 执行非对称量化
./gguf-tools/deepseek4-quantize \
ds4flash-f16.gguf ds4flash-q2-imatrix.gguf \
Q2_K-ASYMMETRIC \
--imatrix ds4flash.imatrix.dat
4.3 纯 mmap 模型加载:零拷贝启动
ds4.c 的内存管理策略极简但极有效:
// 核心思路(伪代码)
int fd = open("ds4flash.gguf", O_RDONLY);
void *base = mmap(NULL, file_size, PROT_READ, MAP_PRIVATE, fd, 0);
// 就这么简单。不拷贝,不解压。
关键设计点:
- 用 mmap() 把 GGUF 文件直接映射进进程虚拟地址空间——不做任何大规模内存拷贝
- 不 eager load——只有 Metal/CUDA 实际需要的部分才会被设备读取
- OS 做 page cache——常用的 layer 留在物理内存,不常用的被 OS 自动换出
实际效果:
启动 90GB 模型:
- 传统方式(read + 拷贝到 GPU):90 秒+
- ds4 mmap 方式:3-5 秒就绪
实际 RSS(物理内存占用):
- 不是 90GB,而是"工作集"大小
- 随上下文增长逐渐增加
- Mac Studio 128GB 可以同时跑 ds4 + 日常开发
多进程共享更是一个隐藏福利:如果两个 worker 加载同一个 GGUF 文件,mmap 的 MAP_PRIVATE 语义保证它们共享物理页面(直到某个 worker 修改了某个 page,但模型权重是只读的,所以永远不会触发 COW)。
4.4 Session 抽象:推理变成可回放的时间线
ds4 的 session 抽象是我见过的最优雅的推理引擎 API 设计之一。
核心思想:推理不是一个"跑一次丢一次"的过程,而是一条可回放、可分叉、可持久化的时间线。
// 时间线操作示例
// 1. 用户发送第一条消息
ds4_session_sync(s, "Hello", ...);
ds4_session_sample(s, ...); // → "Hi there!"
// 2. 用户追加了第二条消息
ds4_session_sync(s, "Hello\nUser: What's Rust?\n", ...);
// session_sync 自动发现 "Hello" 是已有前缀
// 只增量推理 "User: What's Rust?\n" 部分
// 3. 用户修改了第一条消息("时间线分叉")
ds4_session_rewrite_from_common(s, "Hi!\nUser: What's Rust?\n", ...);
// 自动找最长公共前缀,只重建变更后的部分
// 4. 保存时间线到磁盘
ds4_session_save_payload(s, fp, ...);
// 5. 下次从磁盘恢复
ds4_session_load_payload(s, fp, ...);
// 继续从上次暂停的位置推理
这种设计让 server 和 agent 的实现变得异常简洁——它们不需要自己管理 KV 缓存的增量更新、前缀匹配、上下文重建,全部由 engine 自动处理。
4.5 方向引导(Directional Steering):在激活层操控模型行为
这是 ds4 的一个实验性但极具想象力的功能。
传统方式让模型"简洁回答":在 prompt 里写"请简洁回答"。
方向引导的方式:在模型的中间层注入一个数学向量,物理上"拉动"模型的生成倾向。
原理:
- 收集两组 prompt 回答:一组"简洁回答",一组"啰嗦回答"
- 在相同的中间层(attention 和 FFN)收集两组的激活向量
- 计算差值向量(direction vector),存为
.f32文件(~700 KB) - 推理时,在指定层输出上加
steering_attn × direction_vector
使用方式:
./ds4 --directional-steering-file dir-steering/out/verbosity.f32 \
--directional-steering-attn 0.5 \
--directional-steering-ffn 0.2 \
-m ds4flash.gguf --ctx 32768
这比 prompt engineering 更根本——你不是在"说服"模型,而是在激活空间里物理地推它。对某些"对齐过度"的模型(过度礼貌、过度谨慎),这种机制可以有效地反转行为倾向。
目前提供预置方向向量:
verbosity.f32:控制简洁/啰嗦- 社区贡献了更多:
safety-off.f32、creative.f32等
4.6 分布式推理:两台 MacBook 拼成一台推理机
ds4 的分布式推理实现(ds4_distributed.c)走了一条简洁但有效的路径:
# 机器 A(协调者)
./ds4-server -m ds4flash.gguf \
--distributed-coordinator \
--workers 2
# 机器 B(工作者)
./ds4 -m ds4flash.gguf \
--distributed-worker \
--layers 32:61 \ # 只加载 layer 32-61
--coordinator 192.168.1.100:9123
架构:
┌──────────┐ TCP ┌──────────┐
│ Worker A │◄────────────►│ Worker B │
│ layers │ 激活张量流 │ layers │
│ 0-31 │ │ 32-61 │
└──────────┘ └──────────┘
│ │
└─────► Coordinator ┄┄┄┄┘
(用户请求入口)
激活张量通过 TCP 直接在 worker 之间流动(A → B → 回到 A),不经过 coordinator 转发。长 prefill 可以流水线化:worker N 处理 chunk K 的同时,worker N+1 处理 chunk K-1。
实测数据(两台 M5 Max 128GB,Thunderbolt 5 连接,Q4 Flash):
| Prompt 长度 | 单机 | 双机分布式 | 加速比 |
|---|---|---|---|
| 9,421 tokens | 421.70 t/s | 582.22 t/s | 1.38× |
| 28,684 tokens | 405.30 t/s | 674.16 t/s | 1.66× |
| 63,819 tokens | 353.62 t/s | 654.79 t/s | 1.85× |
注意:生成阶段反而比单机慢约 20%——因为自回归生成是逐 token 的,每步都要跨机器通信。分布式主要用于:(a) 跑单机放不下的大模型;(b) 加速长 prefill。
五、代码实战:从编译到生产级部署
5.1 环境准备与编译
# macOS Metal 构建(推荐)
git clone https://github.com/antirez/ds4.git
cd ds4
make # 默认编译 Metal 后端
# CUDA 构建(Linux)
make cuda-spark # DGX Spark / GB10 专用
make cuda-generic # 通用 CUDA GPU
# 检查编译产物
ls -la ds4 ds4-server
# ds4 — CLI 交互式推理
# ds4-server — HTTP API 服务器
Makefile 核心目标约 24 KB,支持以下编译模式:
# 精简版(去掉 agent 和 web UI)
make minimal
# 带分布式支持
make with-distributed
# 纯 CPU 调试(仅 Linux,macOS 勿用!会触发内核 bug)
make cpu
# 运行测试
make test
5.2 模型下载与选择
# q2-imatrix:~90GB,96-128GB 机器可跑(推荐入门)
./download_model.sh q2-imatrix
# q2-q4-imatrix:~100GB,最后 6 层 q4(推荐平衡)
./download_model.sh q2-q4-imatrix
# q4-imatrix:~200GB,256GB+ 机器(高质量推理)
./download_model.sh q4-imatrix
# pro-q2-imatrix:~450GB,512GB 机器(DeepSeek V4 PRO)
./download_model.sh pro-q2-imatrix
量化版本选择指南:
| 量化版本 | 模型大小 | 最低内存 | 质量 | 适用场景 |
|---|---|---|---|---|
| q2-imatrix | ~90 GB | 96 GB | ★★★☆ | 日常对话、快速问答 |
| q2-q4-imatrix | ~100 GB | 128 GB | ★★★★ | Coding Agent、长上下文 |
| q4-imatrix | ~200 GB | 256 GB | ★★★★★ | 高质量推理、生产环境 |
| pro-q2-imatrix | ~450 GB | 512 GB | ★★★★★ | 最强模型,需高端硬件 |
5.3 CLI 交互式推理
# 基础用法
./ds4 -m ds4flash.gguf --ctx 32768
# 关闭思考模式(更快,适合简单问答)
./ds4 -m ds4flash.gguf --ctx 32768 --nothink
# 启用 MAX 思考模式(更长推理链,需 ≥32K 上下文)
./ds4 -m ds4flash.gguf --ctx 65536 --think-max
# 调整采样参数
./ds4 -m ds4flash.gguf --ctx 32768 \
--temperature 0.7 \
--top-p 0.95 \
--min-p 0.05
# 方向引导(简洁模式)
./ds4 -m ds4flash.gguf --ctx 32768 \
--directional-steering-file dir-steering/out/verbosity.f32 \
--directional-steering-attn 0.5 \
--directional-steering-ffn 0.2
# 性能调试
./ds4 -m ds4flash.gguf --ctx 32768 \
--trace /tmp/ds4-trace.json \
--power-percent 75
CLI 内置 Linenoise 交互,支持对话历史、工具调用 UI 展示。
5.4 HTTP Server 部署
# 启动 server
./ds4-server -m ds4flash.gguf \
--host 127.0.0.1 \
--port 8080 \
--ctx 32768
# 启用磁盘 KV 缓存(强烈推荐)
./ds4-server -m ds4flash.gguf \
--host 127.0.0.1 \
--port 8080 \
--ctx 32768 \
--kv-cache-dir ~/.ds4/kv-cache \
--kv-cache-budget 4096 # MB
# 限制最大并发 session
./ds4-server -m ds4flash.gguf \
--host 127.0.0.1 \
--port 8080 \
--max-sessions 8
5.5 OpenAI 兼容 API 调用
# 基础对话
curl http://localhost:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "ds4",
"messages": [
{"role": "system", "content": "You are a helpful coding assistant."},
{"role": "user", "content": "Write a Rust function to parse HTTP headers."}
],
"stream": true
}'
# 工具调用
curl http://localhost:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "ds4",
"messages": [
{"role": "user", "content": "Read the file /tmp/config.json and explain its structure."}
],
"tools": [
{
"type": "function",
"function": {
"name": "read_file",
"description": "Read a file from disk",
"parameters": {
"type": "object",
"properties": {
"path": {"type": "string", "description": "File path"}
},
"required": ["path"]
}
}
}
],
"stream": false
}'
# Anthropic 兼容 API(可以直接替换 Anthropic 客户端)
curl http://localhost:8080/v1/messages \
-H "Content-Type: application/json" \
-H "x-api-key: any" \
-d '{
"model": "ds4",
"max_tokens": 4096,
"messages": [
{"role": "user", "content": "Explain async/await in Rust."}
]
}'
5.6 Python 客户端集成
from openai import OpenAI
# 直接用 OpenAI SDK 连接本地 ds4-server
client = OpenAI(
base_url="http://localhost:8080/v1",
api_key="not-needed" # 本地部署不需要 key
)
# 简单对话
response = client.chat.completions.create(
model="ds4",
messages=[
{"role": "user", "content": "Explain Rust's borrow checker in simple terms."}
],
stream=True
)
for chunk in response:
if chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="")
# 工具调用示例
response = client.chat.completions.create(
model="ds4",
messages=[
{"role": "user", "content": "List files in the current directory."}
],
tools=[{
"type": "function",
"function": {
"name": "list_files",
"description": "List files in a directory",
"parameters": {
"type": "object",
"properties": {
"path": {"type": "string", "default": "."}
}
}
}
}]
)
# 解析工具调用
if response.choices[0].message.tool_calls:
for tool_call in response.choices[0].message.tool_calls:
print(f"Tool: {tool_call.function.name}")
print(f"Args: {tool_call.function.arguments}")
5.7 内置 Coding Agent 使用
ds4_agent.c(387 KB)实现了一个完整的 Coding Agent,支持文件读写、Shell 执行、代码编辑等:
# 启动带 Agent 能力的 server
./ds4-server -m ds4flash.gguf \
--host 127.0.0.1 \
--port 8080 \
--enable-agent \
--agent-workdir /path/to/project \
--agent-tools "read_file,write_file,execute_shell,edit_code"
Agent 的工作流程:
用户: "Fix the memory leak in src/cache.rs"
Agent 思考:
1. [read_file] src/cache.rs → 获取源码
2. [read_file] src/cache_test.rs → 理解测试
3. [分析] 发现 Arc<Mutex<HashMap>> 没有 eviction 逻辑
4. [execute_shell] cargo test → 确认现有测试通过
5. [edit_code] 添加 LRU eviction + max_size 参数
6. [execute_shell] cargo test → 验证修复
7. [回复] 解释问题原因和修复方案
关键特性:整个 Agent 会话的 KV 状态可以持久化到磁盘。下次对话不需要重新加载之前读取的文件——直接从磁盘 checkpoint 恢复。
六、性能优化:榨干每一滴硬件性能
6.1 性能基准实测
以下数据来自官方 README(CLI 单次跑数,--ctx 32768 --nothink,greedy 采样,-n 256):
| 机器 | 量化 | Prompt 长度 | Prefill 速度 | 生成速度 |
|---|---|---|---|---|
| M3 Max 128GB | q2 | short | 58.52 t/s | 26.68 t/s |
| M3 Max 128GB | q2 | 11,709 tokens | 250.11 t/s | 21.47 t/s |
| M5 Max 128GB | q2 | short | 87.25 t/s | 34.27 t/s |
| M5 Max 128GB | q2 | 11,707 tokens | 463.44 t/s | 25.90 t/s |
| M3 Ultra 512GB | q2 | 12,018 tokens | 468.03 t/s | 27.39 t/s |
| M3 Ultra 512GB | q4 | 12,018 tokens | 448.82 t/s | 26.62 t/s |
| M3 Ultra 512GB | PRO q2 | 32,768 tokens | 138.82 t/s | 9.56 t/s |
| DGX Spark GB10 128GB | q2 | 7,047 tokens | 343.81 t/s | 13.75 t/s |
几个关键观察:
- M5 Max 几乎是 M3 Max 的两倍——Apple 新架构对 FP16/矩阵运算有实质提升
- 长 prompt 的 prefill 远快于短 prompt——图执行引擎在大批量输入时利用率更高
- q4 相比 q2 生成速度几乎一样——因为生成阶段的瓶颈是激活/路由,不是权重读取
- PRO 版在 512GB 上只能 9.5 t/s——但它是更强的模型
- Metal 路径比 CUDA(DGX Spark)更快——说明 ds4 的 Metal 优化确实到位
6.2 Metal 内核优化策略
ds4 的 Metal 后端(ds4_metal.m + metal/*.metal)不走通用路线,而是为 DeepSeek V4 的每个算子专门调优:
// 示例:MoE Router kernel(简化版,展示核心思路)
kernel void ds4_moe_route(
device const float* input, // [batch, d_model]
device const float* gate_weight, // [num_experts, d_model]
device int* selected_experts, // 输出:选中的 expert ID
device float* expert_weights, // 输出:路由权重
uint gid [[thread_position_in_grid]]
) {
// 对 DeepSeek V4 的 top-k=8 路由做专门优化
// 不用通用 GEMM,而是展开为寄存器内计算
// 避免中间结果的显存写入
float scores[256]; // num_experts = 256 for V4
for (int e = 0; e < 256; e++) {
scores[e] = dot(input[gid], gate_weight[e * d_model]);
}
// Top-8 选择(用 register 排序,不分配额外显存)
// ...
}
每个 Metal kernel 都针对模型的张量形态做了固定假设(如 d_model、n_heads、num_experts 的具体值),省去了通用 kernel 的动态分支开销。
6.3 KV 缓存压缩
DeepSeek V4 的 KV 缓存比同参数量的 Dense 模型大得多(因为 MLA attention 虽然压缩了 key/value,但 MoE 的 context 依然很重)。ds4 在 KV 缓存层面做了多层优化:
- 量化 KV:KV 缓存本身也做了量化(4-bit 或 8-bit),减少内存/磁盘占用
- 分块 prefill:长 prompt 不是一次性跑完,而是分块流水线化,让 GPU 利用率保持高位
- 图执行引擎重用:同一个 Metal/CUDA 计算图只编译一次,后续请求直接复用
6.4 功率管理
# 降低 GPU 功率(减少发热,略微降低速度)
./ds4 -m ds4flash.gguf --ctx 32768 --power-percent 75
# 完全性能模式
./ds4 -m ds4flash.gguf --ctx 32768 --power-percent 100
--power-percent 参数通过控制 GPU 的运行频率来平衡功耗和性能。在 MacBook 上,降低 25% 功率通常只损失 10-15% 的速度,但发热量显著降低(从 100°C 降到 75°C),长时间运行更稳定。
七、高级场景与生产实践
7.1 长上下文推理:1M token 的挑战
ds4 对长上下文的支持是它的核心设计目标之一。以下是逐步增加上下文长度的推荐配置:
# 32K 上下文(日常对话)
./ds4 -m ds4flash.gguf --ctx 32768
# 128K 上下文(文档分析)
./ds4 -m ds4flash-q4.gguf --ctx 131072 \
--kv-cache-dir ~/.ds4/kv-cache
# 1M 上下文(极端场景,需要 512GB+ 内存)
./ds4 -m ds4flash-q4.gguf --ctx 1048576 \
--kv-cache-dir ~/.ds4/kv-cache \
--kv-cache-budget 16384 # 16GB 磁盘 KV 预算
长上下文的关键技术:
- KV 压缩:4-bit 量化 KV 缓存,减少内存占用
- 分块 prefill:长 prompt 分成多个 chunk 流水线执行
- 磁盘 KV 溢出:RAM 装不下的 KV 部分自动写入 SSD
- session_sync 增量:已推理的前缀不需要重跑
7.2 多用户服务部署
# 生产级 server 配置
./ds4-server -m ds4flash-q2-q4.gguf \
--host 0.0.0.0 \
--port 8080 \
--ctx 32768 \
--max-sessions 8 \
--kv-cache-dir /data/ds4/kv-cache \
--kv-cache-budget 8192 \
--power-percent 80 \
--enable-agent \
--agent-workdir /workspace
多用户场景下的注意事项:
- Session 数量控制:每个 session 占用独立 KV 缓存,
--max-sessions限制并发 - KV 缓存预算:所有 session 共享磁盘 KV 预算,超过后自动 LRU 淘汰
- 功率管理:多用户时降低功率可以避免过热降频
- mmap 共享:所有 session 共享同一份模型 mmap,物理内存占用不会线性增长
7.3 正确性验证与回归测试
ds4 的正确性验证策略是它区别于所有通用推理引擎的关键:
# 运行官方 logits 向量回归测试
./ds4-eval \
--model ds4flash.gguf \
--test-vectors tests/test-vectors/ \
--tolerance 0.01
# 长上下文事实召回测试
./ds4-eval \
--model ds4flash.gguf \
--long-context-test tests/long_context_*.txt \
--needle-in-haystack
回归测试的数据来自 DeepSeek 官方推理实现的 logits 输出——逐 token 对比,确保 ds4 的推理结果和官方实现在数值上对齐。这是"窄而深"策略的直接收益:只做一个模型,所以可以为这个模型提供最严格的质量保证。
7.4 与现有工具链集成
# 替换 LangChain 的 LLM 后端
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(
base_url="http://localhost:8080/v1",
model="ds4",
api_key="not-needed",
temperature=0.7
)
# 替换 Cursor / Continue 的后端
# 在 ~/.cursor/config.json 或 ~/.continue/config.json 中:
{
"models": [
{
"title": "ds4 Local",
"provider": "openai",
"model": "ds4",
"apiBase": "http://localhost:8080/v1",
"apiKey": "not-needed"
}
]
}
八、与其他方案的对比分析
8.1 ds4 vs llama.cpp
| 维度 | ds4 | llama.cpp |
|---|---|---|
| 模型支持 | DeepSeek V4 Flash/PRO | 100+ GGUF 模型 |
| 量化 | 非对称 MoE 专用 | 通用 Q2-Q8 |
| KV 缓存 | RAM + SSD 一等公民 | RAM only |
| GPU 内核 | 模型专用 | 通用 |
| 正确性 | 官方 logits 回归 | 社区测试 |
| Agent | 内置 | 外部工具 |
| 性能(同一模型) | 更优 | 较低 |
| 灵活性 | 低 | 高 |
| 维护者 | antirez + 社区 | Georgi Gerganov + 社区 |
结论:如果你只需要跑 DeepSeek V4 Flash,ds4 在性能和功能上都更优。如果你需要跑多个模型,llama.cpp 仍然是唯一选择。
8.2 ds4 vs Ollama
| 维度 | ds4 | Ollama |
|---|---|---|
| 本质 | 推理引擎 + Agent | 模型管理器 + llama.cpp |
| API 兼容 | OpenAI + Anthropic | OpenAI 部分 |
| 模型管理 | 手动下载 GGUF | 自动拉取 |
| Agent | 内置 Coding Agent | 无 |
| KV 持久化 | 磁盘一等公民 | 无 |
| 量化 | 非对称专用 | 通用 |
| 适用场景 | 深度使用单模型 | 多模型快速切换 |
8.3 ds4 vs vLLM / SGLang
| 维度 | ds4 | vLLM / SGLang |
|---|---|---|
| 目标 | 本地/边缘推理 | 数据中心推理 |
| 硬件 | MacBook / 单 GPU | 多 GPU / A100 集群 |
| 量化 | 2-bit MoE 专用 | FP8/INT8 |
| 并发 | 单用户/少量用户 | 高并发 |
| 部署 | make && ./ds4 | Docker + 复杂配置 |
九、局限性与未来展望
9.1 当前局限
- 只支持一个模型族:如果你想跑 Llama 4 或 Mistral,ds4 帮不了你
- 硬件门槛依然不低:最低 96GB 内存(128GB 推荐),不是所有开发者都能负担
- CUDA 路径不如 Metal 成熟:NVIDIA 后端能用但优化程度不如 Metal
- 2-bit 量化有质量损失:在数学推理、代码生成等需要高精度的场景,q4 版本更可靠
- macOS CPU 路径不可用:内核 bug 会导致系统崩溃,只能用 Metal
- 生成速度的上限:26 t/s(M3 Max q2)对于流式对话够用,但对于批量推理场景偏慢
9.2 未来方向
根据 AGENT.md 和社区讨论,ds4 的未来计划包括:
- 新模型支持:当 DeepSeek V5 发布时,ds4 可能迁移到新模型(保持"窄而深"策略不变)
- MoE 动态加载:利用 MoE 的稀疏激活特性,只把活跃的 Expert 加载到 GPU,其余留在磁盘
- Speculative Decoding 优化:当前 MTP 路径提速有限,未来可能用更大的 draft 模型
- AMD ROCm 正式支持:等待 antirez 获得 AMD 硬件
- 多模态扩展:DeepSeek V4 Flash 的视觉能力目前未在 ds4 中实现
9.3 对行业的启示
ds4 的存在证明了几个重要的事情:
- 专用优化 > 通用方案:在特定场景下,为一个模型专门优化的引擎可以比通用方案快得多
- MoE 改变了量化策略:传统量化是"均匀压缩",MoE 允许"不对称压缩"——只压缩不活跃的部分
- SSD 是推理的一部分:在带宽足够的情况下(MacBook SSD 7-10 GB/s),磁盘不应该被排除在推理架构之外
- API 兼容比功能更重要:ds4 选择兼容 OpenAI/Anthropic API,让它可以零成本接入现有工具链
- "一个人"的项目可以改变行业:antirez 一个人(+ AI 辅助)在几个月内做出的引擎,在某些维度超越了大型团队的通用方案
十、总结
DwarfStar 4 是 2026 年本地推理领域最重要的工程实践之一。它不是一个"更好的 llama.cpp"——它是对本地推理范式的重新思考:
- 当所有人都在做"通用"时,antirez 选择做"专用"——只做一个模型,做到极致
- 当所有人都在把 KV 缓存往 RAM 里塞时,antirez 把它放到了 SSD 上——因为 MoE 的 KV 压缩率足够高
- 当所有人都在用 prompt engineering 控制模型时,antirez 直接在激活层注入方向向量——物理操控比语言说服更可靠
- 当所有人都在让推理和 Agent 分裂时,antirez 把它们合成了一个系统——ds4_server + ds4_agent 是一个整体
它不是完美方案——硬件门槛高、只支持一个模型、CUDA 不如 Metal 成熟。但对于那些"只想在 MacBook 上跑 DeepSeek V4 Flash"的开发者来说,ds4 是 2026 年唯一的选择,也是最好的选择。
最后,用一个程序员视角的总结:ds4 是 antirez 写过的最像 Redis 的项目——不是因为用了相同的技术,而是因为相同的哲学:做一件事,做到极致,用最少的代码创造最大的价值。
项目地址:https://github.com/antirez/ds4
Star:13,000+
协议:MIT License
主要语言:C / Objective-C / CUDA C++