DwarfStar
DwarfStar 是一个小型原生推理引擎,首先针对 DeepSeek V4 Flash 进行了优化,并支持在极高内存机器上运行 DeepSeek V4 PRO。它的定位刻意狭窄:不是通用的 GGUF 运行器,也不是对其他运行时的封装——它完全自包含。除了以正确且快速的方式运行模型外,该项目目标还在于提供 DeepSeek 专属的加载、提示词渲染、工具调用、KV 状态处理(内存及磁盘)、服务器 API 和集成编码智能体,全部准备好配合编码智能体或提供的 CLI 界面使用。此外还包含用于 GGUF 和 imatrix 生成,以及质量和速度测试的工具。
我们支持以下后端:
- Metal 是我们的主要目标平台。从配备 96GB 内存(或更少,使用 SSD 流式传输)的 MacBook 开始。
- NVIDIA CUDA / DGX Spark,对 DGX Spark 的 CUDA 进行特别优化。
- Strix Halo(ROCm),适用于如 Framework Desktop 以及基于相同 GPU 和统一 RAM 设计的其他系统。
没有 llama.cpp 和 GGML 就没有这个项目,请务必阅读致谢部分,衷心感谢 Georgi Gerganov 和所有其他贡献者。
请注意,DeepSeek v4 并非我们的唯一目标。目前 Flash 和 PRO 因其能力、大小和 KV 缓存效率而非常合适。如果明天有更好的开源权重模型发布,适用于 128GB 大小,我们可能会切换;对于其他重要的大小类别(如 512GB RAM)也是如此。该项目严格根据特定时刻存在的开源权重模型灵活调整。如果新模型获得支持,旧模型可能会被完全移除并停止支持,除非存在某种能力上的重叠。
动机
- 具有强大能力的开源权重模型终于出现了。DeepSeek v4 Flash 感觉接近前沿水平。PRO 甚至更好。两者在 2 位量化下表现都非常好。
- 像 MacBook、DGX Spark 这样功能强大的计算机现已存在。
- DeepSeek v4 的 KV 缓存设计使其在运行非常大的上下文时变得实用。其他供应商也在采用这种方法。
- 这些数千亿参数的模型严格优于较小的模型(即使是稠密模型),无论基准测试结果如何。
尽管如此,关于这个项目有几件重要的事情:
- 本地推理生态中有许多优秀的项目,但新模型不断发布,注意力很快就会被下一个实现的模型所吸引。这个项目刻意采取了狭窄的押注:一次只针对一个模型,进行官方向量验证(通过官方实现获取的 logits)、长上下文测试,以及足够的智能体集成来确认它是否真的有效。具体模型可能会随着生态演变而改变,但约束条件依然不变:在高性能个人电脑或 Mac Studio(从 96/128GB 内存起步)上实现可信的本地推理。
- 这款软件是在 GPT 5.5 的强大协助下开发的,由人类主导创意、测试和调试。我们公开这一点,因为它塑造了项目的构建方式。如果你对 AI 开发的代码不满意,这款软件不适合你。下面的致谢同样重要:如果没有 llama.cpp 和 GGML(大部分由人工手写),这个项目就不会存在。
- 该实现基于这样一个理念:像 DeepSeek v4 那样的压缩 KV 缓存,以及现代 MacBook 的快速 SSD 磁盘,应该改变我们认为 KV 缓存属于 RAM 的观念。KV 缓存实际上是一等磁盘公民。快速 SSD 磁盘还从“模型需要适配 RAM”的角度改变了推理游戏的规则:虽然拥有大于模型大小的 RAM 仍然更受青睐,但 SSD 流式传输将可用 RAM 容量从硬性门槛(我能运行这个模型吗?)转变为连续的速度谱系。
- 我们的愿景是,本地推理应是一套开箱即用、协同良好的三要素组合:A) 带 HTTP API 的推理引擎 + B) 专为特定引擎和假设条件优化运行的 GGUF 格式模型 + C) 结合编码智能体实现的测试与验证。D) 针对特定模型和执行环境构建的专用智能体。DwarfStar 仅能运行所提供的 GGUF 文件,并在不同上下文规模下与官方获取的 logits 进行比对测试。这个项目的存在,是因为我们希望让一个本地模型体验做到端到端完善,而不仅仅是可运行。不过,这仍是测试版质量的代码,所以我们可能尚未达到目标,尤其是近期我们引入了大规模新功能:分布式推理、SSD 流式读取以及其他小优化。
- 优化后的图路径针对 macOS 上的 Metal 和 Linux 上的 CUDA。CPU 路径仅用于正确性检查及模型/分词器诊断。对于仅使用 CPU 的 Linux 构建,请使用 `make cpu`;它会编译常规的 `./ds4` 和 `./ds4-server` 二进制文件,不含 CUDA 或 Metal。在 macOS 上,警告:当前 macOS 版本在虚拟内存实现中存在一个 bug,如果你尝试运行 CPU 代码,会导致内核崩溃。还记得吗?软件就是烂。无法通过修复 CPU 推理来避免崩溃,因为每次你都得重启电脑,这一点都不好玩。如果你有胆量,请帮助我们一起修复。
致谢 llama.cpp 和 GGML
ds4.c 并不链接 GGML,但它的存在要归功于 llama.cpp 项目所开辟的道路,以及在其上开发的核函数、量化格式、GGUF 生态系统和来之不易的工程知识。我们非常感谢并受益于 llama.cpp 及其贡献者。在构建这条专门针对 DeepSeek V4 的推理路径时,他们的实现、核函数、测试和设计选择都是重要的参考。这里根据 MIT 协议保留或改编了一些源代码级别的部分:GGUF 量化布局与表格、CPU 量化/点积逻辑以及某些核函数。出于这个原因,也因为我们由衷感激,我们在 LICENSE 文件中保留了 GGML 作者的版权声明。
状态
代码和 GGUF 文件应视为 Beta 质量,因为模型推理和服务是一项复杂的事务,而且这些内容仅存在了几天。要达到更稳定的形态还需要好几个月的时间。不过,我们努力保持项目的可用状态,并且正在取得进展。如果你遇到问题,请务必使用 `--trace` 记录会话,然后提交包含完整跟踪信息的问题报告。
ds4-agent 是 Alpha 质量,该项目是后来添加的。
更多文档
如果你在寻找非常具体的内容,我们还有其他的子 README 文件。否则,对于常规使用,请继续阅读后续章节。
- CONTRIBUTING.md:面向贡献者的正确性与速度回归测试指南。在提交拉取请求之前请先阅读本文。
- gguf-tools/README.md:离线 GGUF 生成、imatrix 收集、量化工具与质量检查。
- gguf-tools/imatrix/README.md:如何收集和使用路由式 MoE 的 imatrix。
- gguf-tools/imatrix/dataset/README.md:如何生成校准提示词语料库。
- gguf-tools/quality-testing/README.md:如何将本地 GGUF 与官方 DeepSeek V4 Flash / PRO 连续生成结果进行评分对比。
- dir-steering/README.md:方向性引导数据、向量生成及使用方法。
- speed-bench/README.md:基准测试命令、图表和 CSV 生成。
- tests/test-vectors/README.md:用于回归检查的官方连续生成向量。
模型权重
本实现仅适用于为此项目发布的 DeepSeek V4 Flash 和 PRO 的 GGUF 文件。这不是一个通用的 GGUF 加载器,任意的 DeepSeek / GGUF 文件将不具备引擎所预期的张量布局、量化混合、元数据或可选的 MTP 状态。这里提供的 2 比特量化并非玩笑:它们表现良好,可在编码智能体下工作,并能以可靠的方式调用工具。2 比特量化使用了一种高度不对称的量化方式:只有路由式 MoE 专家被量化,up/gate 使用 IQ2_XXS,down 使用 Q2_K。它们占据了模型空间的大多数:其他组件(共享专家、投射层、路由)保持不变以保证质量。
下载一个主模型。优先选择 imatrix 版本。
./download_model.sh q2-imatrix # 96/128 GB RAM machines, imatrix-tuned q2 ./download_model.sh q2-q4-imatrix # 96/128 GB RAM machines, q2 with last 6 layers q4 ./download_model.sh q4-imatrix # >= 256 GB RAM machines, imatrix-tuned q4 ./download_model.sh pro-q2-imatrix # 512 GB RAM machines, PRO q2 imatrix quant
如需完整的 PRO Q4 分布式运行,请在两台机器上各下载一半。
./download_model.sh pro-q4-layers00-30 # first half of PRO Q4 split ./download_model.sh pro-q4-layers31-output # second half of PRO Q4 split
该脚本从 https://huggingface.co/antirez/deepseek-v4-gguf 下载,将文件存储在 ./gguf/ 目录下,使用 curl -C - 支持断点续传,并更新 ./ds4flash.gguf 以指向所选的主模型。pro-q4-layers00-30、pro-q4-layers31-output 和 pro-q4-split 目标会下载分布式 PRO Q4 分片,且不会更新 ./ds4flash.gguf。对于公共下载,身份验证是可选的,但如果存在 --token TOKEN、HF_TOKEN 或本地 Hugging Face token 缓存,则会使用它们。
如果你想重新生成 GGUF 文件或收集新的 imatrix,请参见 gguf-tools/README.md。这些工具用于离线模型构建工作,在完整的 DeepSeek V4 Flash 权重上运行可能需要很长时间。本地工具支持 Flash GGUF 生成。PRO GGUF 的生成目前仍然依赖基于 llama.cpp 的外部工作流;未来可以添加原生工具。
./download_model.sh mtp 会获取 Flash 的可选推测解码支持 GGUF。它可以与 q2-imatrix、q2-q4-imatrix 和 q4-imatrix 一起使用,但必须通过 --mtp 显式启用。当前的 MTP/推测解码路径仍处于实验阶段:它经过了正确性验证,目前最多只能带来轻微的加速,并非有意义的生成速度提升。
然后进行构建:
make # macOS Metal make cuda-spark # Linux CUDA, DGX Spark / GB10 make cuda-generic # Linux CUDA, other local CUDA GPUs make cpu # CPU-only diagnostics build
./ds4flash.gguf 是两个二进制文件默认使用的模型路径。使用 -m 参数可从 ./gguf/ 中选择其他支持的 GGUF 文件。运行 ./ds4 --help 和 ./ds4-server --help 可查看完整的标志列表。
速度
以下为单次运行的 Metal CLI 数据,参数为 --ctx 32768、--nothink、贪婪解码以及 -n 256。短提示词是一个普通的意大利小故事提示词。长提示词则用于测试分块预填充加长上下文解码。Q4 需要更大内存的机器类型,因此 M3 Max 的 Q4 数据为 N/A。
| 机器 | 量化 | 提示词 | 预填充 | 生成 |
|---|---|---|---|---|
| MacBook Pro M3 Max, 128 GB | q2 | 短 | 58.52 t/s | 26.68 t/s |
| MacBook Pro M3 Max, 128 GB | q2 | 11709 tokens | 250.11 t/s | 21.47 t/s |
| MacBook Pro M3 Max, 128 GB | q4 | 短 | N/A | N/A |
| MacBook Pro M3 Max, 128 GB | q4 | 长 | N/A | N/A |
| MacBook Pro M5 Max, 128 GB | q2 | 短 | 87.25 t/s | 34.27 t/s |
| MacBook Pro M5 Max, 128 GB | q2 | 11707 tokens | 463.44 t/s | 25.90 t/s |
| Mac Studio M3 Ultra, 512 GB | q2 | 短 | 84.43 t/s | 36.86 t/s |
| Mac Studio M3 Ultra, 512 GB | q2 | 11709 tokens | 468.03 t/s | 27.39 t/s |
| Mac Studio M3 Ultra,512 GB | q4 | 短 | 78.95 t/s | 35.50 t/s |
| Mac Studio M3 Ultra,512 GB | q4 | 12018 tokens | 448.82 t/s | 26.62 t/s |
| Mac Studio M3 Ultra,512 GB | PRO q2 | 32768 tokens | 138.82 t/s | 9.56 t/s |
| DGX Spark GB10,128 GB | q2 | 7047 tokens | 343.81 t/s | 13.75 t/s |
运行大于内存的模型
通常的 Metal 路径会尝试让模型驻留在 GPU 可寻址内存中。这是最快的路径,当模型适配时应保持为默认选项。当模型不适配时,DwarfStar 还提供了纯 Metal 的 SSD 流式传输容量模式。在此模式下,非路由模型权重保持驻留,而路由的 MoE 专家则保存在内存缓存中,并在缓存未命中时从 GGUF 文件加载。
流式传输不如将完整模型适配到 RAM 中快。它仍然需要内存来存放非路由权重、KV 缓存、图暂存、激活值和路由专家缓存。它的有用之处在于,路由专家占据了模型大小的主导地位,而现代 Mac SSD 足够快,使得缓存未命中可以容忍。较长的预填充仍然可以很快;生成过程对缓存未命中更敏感,因为每个新 token 都会重新路由到专家。
从自动缓存预算开始:
./ds4 -m ./ds4flash.gguf --ssd-streaming
如果启动时报告专家缓存过大,或者你想为上下文保留更多内存,请显式设置路由专家缓存:
./ds4 -m ./ds4flash.gguf --ssd-streaming --ssd-streaming-cache-experts 32GB
32GB 的值是一个完整路由专家的内存预算,而不是通用的字节缓存。DwarfStar 会将其转换为适合当前 GGUF 的完整专家数量。非路由权重、KV 缓存、图暂存和激活值需要额外的内存。只有自动缓存预算会为你做减法:它取 Metal 推荐工作集的 80%,减去非路由权重,然后将剩余部分用于路由专家。正常使用时应保持热专家预加载启用;仅在测量时使用 --ssd-streaming-cold 和 --ssd-streaming-preload-experts N。
实用 SSD 流式传输示例
在 64GB MacBook 上,从 2-bit Flash GGUF 和适中的专家缓存开始:
./download_model.sh q2-imatrix ./ds4 \ -m ./ds4flash.gguf \ --ssd-streaming \ --ssd-streaming-cache-experts 32GB \ --ctx 32768 \ --nothink
在 128GB MacBook 上,PRO q2 流式传输是实验性的,但在接受缓慢生成的情况下可用于检查和偶尔的工作。从 --nothink 开始:
./download_model.sh pro-q2-imatrix ./ds4 \ -m gguf/DeepSeek-V4-Pro-IQ2XXS-w2Q2K-AProjQ8-SExpQ8-OutQ8-Instruct-imatrix.gguf \ --ssd-streaming \ --ctx 32768 \ --nothink
在配备 128GB 内存的 M5 Max 上,一项简短的 PRO q2 流式解码基准测试发现,自动预算方案表现最佳:它选择了约 59GB 的路由专家缓存。在该设备上,手动设置 64GB 到 75GB 的缓存效果接近。推理开始前,显式指定的较大 NGB 请求会被截断,以确保专家缓冲区保持可锁定状态,而不会落入 macOS 的分页机制。如果系统面临额外内存压力且 mlock 仍然失败,ds4 将拒绝安装可分页的专家缓存条目,并在继续使用已测得的可锁定缓存大小之前释放一部分锁定缓存余量。建议优先使用自动预算方案;如果在此类设备上手动设置缓存,从约 48GB 到 64GB 开始,然后仅在启动日志报告可锁定缓存时逐步增加。一旦设备稳定,启用带保守生成限制的思考功能。
./ds4 \ -m gguf/DeepSeek-V4-Pro-IQ2XXS-w2Q2K-AProjQ8-SExpQ8-OutQ8-Instruct-imatrix.gguf \ --ssd-streaming \ --ctx 32768 \ --think \ --tokens 1500
启动时的重要日志行是缓存报告。从保守设置开始,如果设备有剩余空间再增加缓存。
分布式推理
分布式推理允许 DwarfStar 运行单台机器无法承载的大模型,方法是将 Transformer 层拆分到多台机器上。主要示例是在两台 128GB MacBook 上运行完整的 4-bit Flash 量化模型:每个进程只映射自己的那部分层,激活值通过 TCP 传输,协调器保持正常的 CLI/API 行为。
分布式推理还可以通过同时使用多块 GPU 来加速预填充(prefill)阶段——就像流水线一样,在不同层上处理不同的微批次。只有预填充阶段能以这种方式加速。生成阶段是纯自回归的:每个 token 必须走完整个路由路径后,下一个 token 才能开始计算。模型计算量与单进程相同,但额外增加了协调延迟,因此分布式生成会更慢。
为了建立初步的认知模型,以下是几个高层概念:
- 你在每台机器上都放置 GGUF 文件,但每台机器只加载其中一部分。`--layers` 参数控制加载哪些张量,例如使用 `--layers 20:output` 的工作节点不会加载前面的层。
- 层的范围是闭区间:`10:20` 表示第 10、11、……、20 层。`N:output` 表示从第 N 层到最后一层并包含输出头。
- 你指定其中一台机器作为协调节点,其余机器作为工作节点。工作节点会连接到协调节点,告知自身存在以及能够处理哪些网络层。
- 每个工作节点保存自己那部分的 KV 缓存。
- 通信是工作节点之间的直接通信,无需将协调节点作为中继;因此,如果你的协调节点是 A,并且你发起一个请求,激活值将沿着 A → B → C → 回到 A 的路径流动。
工作原理及配置方法
预填充路径采用流水线方式(这就是为什么它可以比单机更快)。对于大型提示词,协调节点可以在处理第 N+1 个数据块上的自身切片时,同时让工作节点处理第 N 个数据块上的自身切片。下面的分布式行数据是通过两台 M5 Max 128 GB MacBook 在 Thunderbolt 5 连接下测得的,使用了 Q4 Flash GGUF 和默认的 4096 token 分布式预填充块。单进程列是单机 Q2 GGUF 的参考运行结果,因此实际上速度更快一些,因为路由 MoE 的规模更小。
| 提示词 | 单进程参考 | 两台 MacBook | 加速比 |
|---|---|---|---|
| 9421 token | 421.70 t/s | 582.22 t/s | 1.38 倍 |
| 28684 token | 405.30 t/s | 674.16 t/s | 1.66 倍 |
| 63819 token | 353.62 t/s | 654.79 t/s | 1.85 倍 |
生成阶段则不同。生成阶段严格自回归:token N+1 必须等到 token N 产生对数几率并完成采样选出下一个 token 后才能开始。这意味着分布式生成无法利用长预填充流水线。每生成一个 token,至少要经历一次跨机器的激活值跳转,因此生成速度比单机本地进程更慢。在同样的两台 Mac Thunderbolt 设置下,一个 12k 上下文的控制运行中,91 GB Flash 量化版本从单进程的 30.59 t/s 降至分布式的 24.67 t/s,性能损失 19.4%。因此,分布式推理的主要目的是适配更大的模型以及加速长预填充,而不是提升解码速度。
在两台 Mac Studio 上运行完整版 DeepSeek V4 PRO Q4
完整版 PRO Q4 GGUF 可以通过将协调节点分配层 0:30、工作节点分配层 31:output 的方式在两台 512 GB Mac Studio M3 Ultra 机器上运行。请使用分片 GGUF 文件,这样每一侧只映射自身需要的张量:
# Coordinator machine. ./download_model.sh pro-q4-layers00-30 # Worker machine. ./download_model.sh pro-q4-layers31-output
这两个文件分别是:
gguf/DeepSeek-V4-Pro-Q4K-Layers00-30.gguf
gguf/DeepSeek-V4-Pro-Q4K-Layers-31-output.gguf
这是一个容量用例:每个进程仅映射模型自身的一半,而工作节点拥有输出头并返回 logits。
当前的 PRO Q4 Metal 路径使用队列驻留的精确专家表来处理大型路由专家。这避免了早期分布式 PRO Q4 尝试中出现的宽泛多 GB 路由张量绑定,这些绑定要么导致运行极慢,要么触及 Metal 内存会计限制。在一次对直接的 192.168.0.182 / 192.168.0.183 链路的简短贪婪冒烟测试中,模型生成了连贯的文本,启动后测得生成速度为 11.47 t/s。每个 token 的遥测数据保持平衡:本地层大约 39-43 毫秒,远程层大约 44-49 毫秒,每个 token 总耗时约 84-92 毫秒。预计启动较慢,因为两侧各自映射并驻留其一半模型。长上下文 PRO Q4 的预填充和解码性能仍需单独基准测试。
上述测量使用的是 Thunderbolt 5 线缆。实现基于纯 TCP,也可在更慢的链路上工作(包括 WiFi),但强烈推荐使用快速以太网或 Thunderbolt 网络。慢速链路主要影响生成延迟和短预填充;当层分配均衡时,长预填充仍能受益。在正常性能路径中,最后一个工作节点拥有输出头并直接返回 logits。
最小双主机配置:
# Machine A: coordinator, owns tokenization, sampling, the prompt, and layers 0..30. ./ds4 \ -m gguf/DeepSeek-V4-Pro-Q4K-Layers00-30.gguf \ --role coordinator \ --layers 0:30 \ --listen 169.254.43.68 1234 # Machine B: worker, connects to A and owns layers 31..output. ./ds4 \ -m gguf/DeepSeek-V4-Pro-Q4K-Layers-31-output.gguf \ --role worker \ --layers 31:output \ --coordinator 169.254.43.68 1234
通常情况下,最后一个工作节点也应拥有输出头,例如 --layers 20:output。这避免了在预填充后返回完整的最终隐藏状态批次,并让最后一个工作节点直接产生 logits。在非常慢或计量的链路上,也支持 --layers 20:42:协调器将加载输出头并在本地计算 logits,用额外的协调器工作换取更小的每 token 回复。
网络链路对比
下表显示了相同的两台 M5 Max 主机、相同的 91 GB Flash 量化、协调器 --layers 0:19、工作节点 --layers 20:output、来自 speed-bench/promessi_sposi.txt 的 8192 个 token 提示,以及 128 个生成 token。WiFi 和互联网数字随当地条件变化,但关键部分是形状:高延迟直接影响生成,而较低带宽也会降低长预填充速度。
| 链路 | 地址 | 平均 Ping | 预填充 | 生成 |
|---|---|---|---|---|
| 雷电 5 | 169.254.43.68 -> 169.254.12.245 | 0.45 毫秒 | 582.99 token/秒 | 25.09 token/秒 |
| WiFi | 192.168.1.57 -> 192.168.1.95 | 77.20 毫秒 | 250.70 token/秒 | 10.70 token/秒 |
| 互联网 / VPN | 10.77.0.4 -> 10.77.0.3 | 152.10 毫秒 | 114.88 token/秒 | 3.63 token/秒 |
互联网/VPN 场景并不意味着理想的交互体验。但它对于集体测试仍然有用:多人可以临时合并机器来运行一个无法在任何单一主机上容纳的更大模型,以接受缓慢的解码为代价,换取能够实际检查该模型的能力。
像使用普通的 `./ds4` 一样使用协调器:交互式聊天、/read 和普通生成都通过相同的高层会话 API 进行。相同的分布式选项也集成到了 ds4-agent、ds4-eval 和 ds4-bench 中。对于基准测试,工人(worker)应该已经在运行;ds4-bench 会等待直到存在一条完整的路由。
有用的调优和诊断:
./ds4-bench \ -m gguf/DeepSeek-V4-Flash-Q4KExperts-F16HC-F16Compressor-F16Indexer-Q8Attn-Q8Shared-Q8Out-chat-v2.gguf \ --prompt-file speed-bench/promessi_sposi.txt \ --ctx-start 32768 \ --ctx-max 65536 \ --step-incr 32768 \ --gen-tokens 0 \ --role coordinator \ --layers 0:19 \ --listen 169.254.43.68 1234 \ --debug
在协调器上使用 `--debug` 会打印路由形成信息以及每一跳的遥测数据:层范围、token 跨度、本地评估时间、下游等待时间、socket 发送时间以及输入/输出字节数。这是当前用于判断切分是否均衡的性能分析工具。`--dist-prefill-window N` 控制端到端可以有多少个预填充块在传输中;默认值是保守且有上限的。`--dist-prefill-chunk N` 用于实验,但默认的 4096-token 块是标准设置,除非你正在明确验证不同的块大小,否则应使用默认值。
默认情况下,DwarfStar 以 32 位浮点数发送隐藏状态激活值。为减少流量,可在协调器上传递 `--dist-activation-bits 16` 或 `--dist-activation-bits 8`。这仅改变机器间的传输格式,不影响模型权重或 KV 缓存。16 位传输可使激活流量减半,是在以太网或 WiFi 上应首先尝试的选项。8 位传输更为激进,应视为近似/实验性模式,除非你已针对自身用例验证过输出结果。然而,实验表明减少激活大小并未带来显著改善,因此该选项未来可能会被移除。
如果某个工作节点断开连接,协调器会将该节点从活动路由中移除。正在处理中的请求可能失败,后续调用会报告路由不完整,直到有兼容的工作节点重新连接并发送新的注册信息。对于实时会话,协调器会保存 token 历史记录,当路由再次可用时,可以通过重放前缀来重建工作节点的 KV 状态。工作节点还会在每个工作项上验证一个滚动的 64 位 token 前缀哈希值,因此,在位置 0 重启的工作节点无法静默地接受位置 N 的工作;它会报告不匹配,然后协调器会重放当前的转录记录。CLI 和智能体中的 Ctrl+C 是协作式的:DwarfStar 会等待当前的分布式 token 或预填充块处理完毕后再交还控制权,这避免了因协调器导致的 KV 分割。保存的智能体/服务器会话使用与单机会话相同的 KV 文件格式:保存时,协调器会获取工作节点拥有的层张量,并序列化为一个正常的有效载荷;加载时,它会将该有效载荷分发到当前已注册的路由上。
分布式协议概述
在协议层面存在两种连接。工作节点会向协调器保持一条TCP控制连接,并发送一个HELLO消息,其中包含其模型ID、模型族、量化配置、层切片、上下文容量和数据端口。协调器利用这些注册信息构建一条覆盖所有层的路由。随后,工作通过低延迟TCP数据连接进行:协调器计算第一个切片,发送一个WORK帧,其中包含会话ID、token位置、该跨度前后的滚动token前缀哈希、路由信息以及隐藏状态载荷,每个工作节点计算其切片。中间工作节点可以直接将数据转发给下一个工作节点。最后一个工作节点将logits返回给协调器,对于非最终的预填充块则返回ACK,以便预填充管道保持满载。RESULT帧回显请求ID和跨越后的哈希值。工作节点状态错误的处理方式与套接字故障不同:KV/哈希不匹配可以通过在同一路由上回放token历史来恢复,而传输故障则会丢弃该路由并等待替换的工作节点。对于持久化KV,协调器会打开工作节点数据连接,并针对每个工作节点所拥有的层范围发送快照保存/加载消息;磁盘载荷仍为单个智能体/服务器缓存文件。该协议不含加密或认证,且尚未达到发布稳定状态;协调器和工作节点应由同一版本代码构建,并在可信的机器和可信网络上使用。
减少热量、功耗和风扇噪音
长时间的本地推理运行可能让GPU持续忙碌。如果你更关心热量、风扇噪音、MacBook的电池续航或降低硬件热应力,而不是追求最大吞吐量,请使用 --power N。
--power 100 是默认值,表示全速运行。较低的值会指示 DwarfStar 将GPU使用率控制在目标百分比:--power 70 目标约为70%,--power 50 目标约为一半使用率,以此类推。DwarfStar 通过测量GPU工作时间并在工作单元之间插入短暂休眠来实现这一点:在预填充阶段,它在各层之间休眠;在生成阶段,它在每个解码出的token之间休眠。这降低了持续负载,同时不会改变模型输出。
该选项可在 CLI、服务器、智能体、评测和基准测试工具中使用,例如:
./ds4 --power 50 ./ds4-agent --power 70 ./ds4-server --power 40 --ctx 100000
原生智能体
DwarfStar 具有一个原生编码智能体,其工作方式与大多数其他系统不同:推理在智能体自身内部进行控制,无需 socket/API 边界,因此会话由磁盘上的 KV 缓存本身表示。此外,工具和系统提示词均为 DeepSeek v4 Flash 和 PRO 垂直设计。这带来了一些优势:
- 低延迟体验,主要受预填充速度限制。生成文本、调用工具、启动新会话始终是瞬间完成的。
- 预填充期间显示实时进度条。
- 无需进行 DSML 工具调用转换,工具在 LLM 格式中原生处理。
- 从结构上杜绝 KV 缓存不匹配,当前状态始终是真实的。
- 一切均为该模型调优。
- 能够通过 /list 和 /switch 切换已保存的会话;完整的 KV 会话在无需预填充阶段的情况下恢复。
智能体会话存储在 ~/.ds4/kvcache 中。使用 /save 持久化当前会话,使用 /list 显示按最近更新时间排序的已保存会话,使用 /switch <sha> 恢复其中一个。会话 ID 在未来的保存中保持稳定,由第一条用户提示和创建时间派生。/del <sha> 删除已保存的会话。/strip <sha> 保留渲染后的对话文本和标题,但移除占空间大的 KV 负载;切换到已剥离的会话会通过预填充所保存的文本来重建 KV 缓存。
从其他目录启动 ds4-agent 时,使用 --chdir /path/to/ds4,这样相对运行时文件(如 metal/*.metal)会从项目树中解析。
不过,尽管系统目前已经可以工作,但距离正式投入使用还有许多工作要做。当智能体最终达到理想形态时,我们很可能会将服务器和客户端分离,创建一个有状态的基于会话的协议,以客户端-服务器的方式重现所有这些功能。
基准测试
ds4-bench 在上下文边界处测量即时预填充和生成吞吐量,而不是报告整个运行过程的平均值。它只加载一次模型,遍历固定 token 序列到达诸如 2048、4096、6144 等边界,并使用增量预填充,使每一行仅测量新增的 token 区间。在每个边界之后,它将活跃的 KV 状态保存到内存中,生成一个固定的贪婪非 EOS 探针,恢复内存快照,然后继续预填充。
./ds4-bench \ -m ds4flash.gguf \ --prompt-file speed-bench/promessi_sposi.txt \ --ctx-start 2048 \ --ctx-max 65536 \ --step-incr 2048 \ --gen-tokens 128
示例文件是 Alessandro Manzoni 所著《I Promessi Sposi》(Gutenberg 电子书 #45334)的经过整理的公有领域 Project Gutenberg 文本,移除了 Gutenberg 页眉和页脚:https://www.gutenberg.org/ebooks/45334。
使用 --step-incr N 设置不同的线性间隔,或使用 --step-mul F 进行指数扫描。输出为 CSV 格式,每行对应一个边界:最新预填充间隔的 token/秒、该边界处的生成 token/秒,以及 kvcache_bytes。
会话默认以 4096 token 的块对长提示词进行预填充。设置 DS4_METAL_PREFILL_CHUNK=N 以比较其他块大小,例如设为 2048 以匹配严格的官方向量检查点路径;或设置 DS4_METAL_PREFILL_CHUNK=0 以在内存允许时将提示词作为单个批次整体预填充。更改块大小会改变 KV 检查点/Logit 路径,因此应将其作为显式的运行配置进行比较。分段式 Metal 预填充对每个块复用同一个支持范围扩展的逐层主导图,在保留绝对压缩器/索引器边界的同时,避免了旧的逐层块分发路径。
能力评估
ds4-eval 是一个小型的真实模型集成基准测试。它不是一个排行榜运行工具,也不应被报告为官方 GPQA、SuperGPQA、AIME 或安全基准测试分数:其题目是嵌入的 92 项子集,选择该子集是为了便于本地回归测试和可视化检查。该程序加载真实的 GGUF 文件,渲染 DeepSeek 聊天提示词,在分屏 TUI 中流式输出采样 token,对最终答案进行评分,并输出每道题的详细报告,包括提示词 token 数、生成 token 数、通过/失败状态、模型答案以及正确答案。
./ds4-eval -m ds4flash.gguf --trace /tmp/ds4-eval.txt
默认运行时使用 `--tokens 16000`,启用思考模式,并设置软/硬 `</think>` 预算截断,以便模型有空间生成可见的回答。ds4-eval 会根据所选提示词的最大长度加上生成预算来动态调整上下文,并拒绝需要超过 100 万上下文 token 的运行。按 p 暂停,按 q 退出并打印报告,按上/下键检查或选择另一个问题,按回车键运行当前选中的问题。`--plain` 禁用 TUI。
使用 `--regrade-trace /path/to/trace.txt` 可以针对之前的 `--trace` 文件重放当前的答案提取器和评分器,而无需加载模型或重新生成 token。这在审计评估器变更时非常有用:它会显示哪些案例发生了变化、旧的选中答案、新的选中答案,以及通过/失败汇总。
对于可能导致生成结果漂移的推理变更,需要在测试计划中保留这个确定性的 q1..q4 token 计数门控:
./ds4-eval \ -m ds4flash.gguf \ --plain \ --questions 4 \ --tokens 2048 \ --temp 0 \ --seed 1
生成的 token 数量必须与基线保持一致:
| 问题 | 预期状态 | 预期生成 token 数 | 预期给定/正确 |
|---|---|---|---|
| 1 | 通过 | 2048 | B / B |
| 2 | 通过 | 438 | C / C |
| 3 | 通过 | 666 | 70 / 70 |
| 4 | 失败 | 2048 | A / C |
前 75 个嵌入问题是按以下顺序交错排列的:25 个 GPQA Diamond、25 个审核后的 SuperGPQA 以及 25 个 AIME 2025 问题。最后 17 个是经审核的 COMPSEC 子集,包含简化的单功能 C/C++ 漏洞定位问题。模型需要给出唯一的最佳源代码行,或者当漏洞无法定位到单行时给出最小精确行集;只有当相邻行是同一漏洞的等效位置时,评分器才接受较小的审核范围。顺序有意设为渐进式:早期问题是有用的冒烟测试,而后期问题难度较大,即使较强的推理模型也应当会遗漏其中一部分。SuperGPQA 切片是经过筛选而非盲选的:上游数据中键值错误、缺少图表或提示词不明确的行会被替换为更干净的行。
该数据集应被视为硬能力回归测试套件,而非通过/失败单元测试。
- GPQA Diamond 提供了研究生级别的科学问题,附带多项选择题选项。DeepSeek 的模型卡报告了在思考模式下对完整 GPQA Diamond 的强劲表现,但个别题目仍需仔细的物理、化学或生物推理,且容易因微小的提示词/渲染或采样回归而出错。
- SuperGPQA 提供了广泛的专家知识和跨领域转移问题。模型卡上的 SuperGPQA 数字远低于 GPQA Diamond,因此这些题目预计参差不齐:有些看起来平淡无奇,另一些则需要小众的专业知识或对翻译风格考试题目的准确解读。
- AIME 2025 贡献了精确答案的竞赛数学题。这些通常是整个集合中最不留情面的题目:没有多项选择题的先验信息,没有部分分数,一次算术或代数失误就会改变成绩。
- COMPSEC 贡献了基于公开 CVE 分析报告提炼出的单函数 C/C++ 安全推理题目。这些并非漏洞利用提示词:任务在于识别引入防御性代码缺陷的最佳源码行,或对安全函数返回 0。
实际上这意味着不应期望 ds4-eval 能完美跑出 92/92 的成绩。它的目的是回答一个更有用的工程问题:在修改内核、量化、提示词渲染、KV 缓存或工具流后,DeepSeek V4 Flash 是否仍能在用户实际使用的推理路径下,解决一组兼具硬科学、广泛知识、精确数学和安全代码问题的代表性混合任务?
CLI(命令行界面)
一次性提示词:
./ds4 -p "Explain Redis streams in one paragraph."不加 -p 则启动交互式提示词:
./ds4
ds4>交互式 CLI 是一个真正的多轮对话。它会保留渲染的聊天记录和实时的图 KV 检查点,因此每一轮都会延续之前的对话。有用的命令包括 /help、/think、/think-max、/nothink、/ctx N、/read FILE 和 /quit。Ctrl+C 可中断当前生成并返回 ds4>。
CLI 默认使用思考模式。使用 /nothink 或 --nothink 可直接获得答案。--mtp MTP.gguf --mtp-draft 2 可启用可选的 MTP 推测路径;该路径仅适用于贪婪解码,当前使用置信度门控(--mtp-margin)来避免缓慢的部分接收,应被视为实验性的轻微加速路径。
服务器
启动一个兼容本地 OpenAI/Anthropic 的服务器:
./ds4-server --ctx 100000 --kv-disk-dir /tmp/ds4-kv --kv-disk-space-mb 8192
从其他目录启动 ds4-server 时,请使用 --chdir /path/to/ds4,以便相对运行时文件(如 metal/*.metal)能从项目树中正确解析。
服务器在内存中保留一个可变的后端/KV 检查点,因此重新发送相同提示词更长版本的无状态客户端可以重用共享前缀,而无需从 token 零开始预填充。
请求解析和套接字在客户端线程中运行,但推理本身通过一个图(graph)工作线程串行化。当前服务器不将多个独立请求进行批处理;并发请求在单个活动图/会话上依次等待。
支持的端点:
- GET /v1/models
- GET /v1/models/deepseek-v4-flash
- GET /v1/models/deepseek-v4-pro
- POST /v1/chat/completions
- POST /v1/responses
- POST /v1/completions
- POST /v1/messages
Flash 和 PRO 模型端点是兼容性别名。它们都报告当前通过 -m 传入的 GGUF 所加载的模型;端点名称不会选择不同的模型。
/v1/chat/completions 接受常规的 OpenAI 风格消息、max_tokens/max_completion_tokens、temperature、top_p、top_k、min_p、seed、stream、stream_options.include_usage、tools 和 tool_choice。工具架构被渲染为 DeepSeek 的 DSML 工具格式,生成的 DSML 工具调用会映射回 OpenAI 工具调用。
/v1/responses 接受 OpenAI Responses 风格的输入、instructions、tools、tool_choice、max_output_tokens、temperature、top_p、stream 和 reasoning。这是 Codex CLI 的首选端点。服务器尽可能保持 Responses 延续与活动状态绑定,并可以回退到与聊天补全相同的 DSML 渲染和 KV 前缀重用。
/v1/messages 是 Claude Code 风格客户端所使用的兼容 Anthropic 的端点。它接受 system、messages、tools、tool_choice、max_tokens、temperature、top_p、top_k、stream、stop_sequences 以及 thinking 控制参数。工具调用会以 Anthropic tool_use 块的形式返回。
默认采样 API 生成使用 temperature=1、top_p=1 和 min_p=0.05,因此默认过滤器是相对概率而非核采样质量。在思考模式下,DwarfStar 使用这些固定的采样默认值,并忽略客户端的采样旋钮,这与 DeepSeek 的固定思考 API 行为一致。
chat、Responses 和 Anthropic 端点均支持 SSE 流式传输。在思考模式下,推理以原生 API 格式进行流式传输,而不是混入最终文本中。OpenAI chat 流式传输在识别到 DSML 调用时也会立即流式传输工具调用:先发送工具头部,然后在生成过程中将参数字节作为 tool_calls[].function.arguments 增量转发。Anthropic 端点实时流式传输思考内容和文本,然后在生成的工具块完成时发出结构化的 tool_use 块。Responses 端点流式传输 Codex 所期望的 Responses 事件生命周期,包括 response.output_text.delta、函数调用参数事件,以及终止的 response.completed / response.incomplete / response.failed 事件。
对于来自其他源(origin)的浏览器 JavaScript 客户端,请使用 --cors 启动服务器,以发送 Access-Control-Allow-* 头部。这仅更改 HTTP 头部,并不会将服务器暴露在局域网中。当需要远程机器能够连接时,请显式使用 --host 0.0.0.0。
工具调用处理与规范化
DeepSeek V4 将工具调用以 DSML 文本形式发出。智能体客户端在下一次请求时并不会发送相同的文本:它们会发送规范化的 OpenAI/Anthropic JSON 工具调用对象。如果服务器以略微不同的方式重新渲染这些对象,那么渲染出的字节前缀将不再匹配实时的 KV 检查点,下一次对话轮次将不得不重建。
第一道防线是精确回放。每次工具调用都会获得一个不可猜测的 API 工具 ID,服务器会记住工具 ID 到精确采样的 DSML 块之间的映射,并存储在由基数树支持的有界内存映射中。当客户端随后将该工具 ID 发回时,提示词渲染器会使用模型采样的精确 DSML 字节,而不是重新格式化的近似值。这个映射也可以保存在 KV 缓存文件中,因此对于缓存的历史记录,精确回放能够在服务器重启后依然生效。
规范化只是备用路径。如果精确的 DSML 块丢失,或者通过 `--disable-exact-dsml-tool-replay` 禁用了精确回放,服务器会从 JSON 工具对象渲染出一个确定性的 DSML 形式。在完成一轮工具调用后,它会将实时采样的 token 流与下一个客户端请求将渲染的提示词进行比较。如果需要,服务器会重写实时检查点,或者回退到旧的磁盘 KV 快照并仅重播后缀部分。这样能让模型生成的延续与无状态 API 记录保持一致。
生成过程中,服务器还会区别对待 DSML 语法和负载。当模型输出稳定的协议结构,例如 DSML 标签、参数头、JSON 标点或结束标记时,采样会强制设为 temperature=0,以确保工具调用可解析。这种贪婪模式不适用于参数负载:字符串类型的参数体和 JSON 字符串值(包括文件内容和编辑文本)使用请求的正常采样设置。这种区分很重要:确定性解码有助于语法,但在应用到较长的代码或文件体时可能会产生重复文本。
最小的 OpenAI 示例:
curl http://127.0.0.1:8000/v1/chat/completions \ -H 'Content-Type: application/json' \ -d '{ "model":"deepseek-v4-flash", "messages":[{"role":"user","content":"List three Redis design principles."}], "stream":true }'
智能体客户端使用
ds4-server 可以被支持 OpenAI 兼容聊天补全的本地编码智能体使用。首先启动服务器,并将客户端上下文限制设置为不超过您启动服务器时使用的 `--ctx` 值。
./ds4-server --ctx 100000 --kv-disk-dir /tmp/ds4-kv --kv-disk-space-mb 8192
如果你愿意,可以使用更大的上下文和更大的缓存。完整的100万token上下文将消耗大约26GB内存(仅压缩索引器就需要约22GB),因此请根据你的系统配置一个合理的上下文。在128GB内存的情况下,你可以运行2位量化模型,这些模型本身已经占用81GB,再加上26GB可能负担过重,因此将上下文窗口设为10万到30万token更为明智。不过有用户报告称,在仅96GB系统内存的Mac上也能成功运行2位量化模型搭配25万token的上下文窗口:如果你打算这么做,请务必提前关闭占用内存过多的进程😉
下面设置的384000输出限制避免了token上限,因为该模型能够生成非常长的回复(最高可达384k token)。当配置的上下文窗口已满时,服务器仍会停止生成。
对于opencode,请在~/.config/opencode/opencode.json中添加一个provider和agent条目:
{
"$schema": "https://opencode.ai/config.json",
"provider": {
"ds4": {
"name": "ds4.c (local)",
"npm": "@ai-sdk/openai-compatible",
"options": {
"baseURL": "http://127.0.0.1:8000/v1",
"apiKey": "dsv4-local"
},
"models": {
"deepseek-v4-flash": {
"name": "DeepSeek V4 Flash (ds4.c local)",
"limit": {
"context": 100000,
"output": 384000
}
}
}
}
},
"agent": {
"ds4": {
"description": "DeepSeek V4 Flash served by local ds4-server",
"model": "ds4/deepseek-v4-flash",
"temperature": 0
}
}
}对于Pi,请在~/.pi/agent/models.json中添加一个provider:
{
"providers": {
"ds4": {
"name": "ds4.c local",
"baseUrl": "http://127.0.0.1:8000/v1",
"api": "openai-completions",
"apiKey": "dsv4-local",
"compat": {
"supportsStore": false,
"supportsDeveloperRole": false,
"supportsReasoningEffort": true,
"supportsUsageInStreaming": true,
"maxTokensField": "max_tokens",
"supportsStrictMode": false,
"thinkingFormat": "deepseek",
"requiresReasoningContentOnAssistantMessages": true
},
"models": [
{
"id": "deepseek-v4-flash",
"name": "DeepSeek V4 Flash (ds4.c local)",
"reasoning": true,
"thinkingLevelMap": {
"off": null,
"minimal": "low",
"low": "low",
"medium": "medium",
"high": "high",
"xhigh": "xhigh"
},
"input": ["text"],
"contextWindow": 100000,
"maxTokens": 384000,
"cost": {
"input": 0,
"output": 0,
"cacheRead": 0,
"cacheWrite": 0
}
}
]
}
}
}可选地,在~/.pi/agent/settings.json中将其设为Pi的默认模型:
{
"defaultProvider": "ds4",
"defaultModel": "deepseek-v4-flash"
}对于Codex CLI,请使用Responses wire API:
[model_providers.ds4] name = "DS4" base_url = "http://127.0.0.1:8000/v1" wire_api = "responses" stream_idle_timeout_ms = 1000000
然后运行:
codex --model deepseek-v4-flash -c model_provider=ds4
对于Claude Code,请使用兼容Anthropic的端点。像下面这样的封装器可匹配本地的~/bin/claude-ds4设置:
#!/bin/sh unset ANTHROPIC_API_KEY export ANTHROPIC_BASE_URL="${DS4_ANTHROPIC_BASE_URL:-http://127.0.0.1:8000}" export ANTHROPIC_AUTH_TOKEN="${DS4_API_KEY:-dsv4-local}" export ANTHROPIC_MODEL="deepseek-v4-flash" export ANTHROPIC_CUSTOM_MODEL_OPTION="deepseek-v4-flash" export ANTHROPIC_CUSTOM_MODEL_OPTION_NAME="DeepSeek V4 Flash local ds4" export ANTHROPIC_CUSTOM_MODEL_OPTION_DESCRIPTION="ds4.c local GGUF" export ANTHROPIC_DEFAULT_SONNET_MODEL="deepseek-v4-flash" export ANTHROPIC_DEFAULT_HAIKU_MODEL="deepseek-v4-flash" export ANTHROPIC_DEFAULT_OPUS_MODEL="deepseek-v4-flash" export CLAUDE_CODE_SUBAGENT_MODEL="deepseek-v4-flash" export CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 export CLAUDE_CODE_DISABLE_NONSTREAMING_FALLBACK=1 export CLAUDE_STREAM_IDLE_TIMEOUT_MS=600000 exec "$HOME/.local/bin/claude" "$@"
Claude Code在开始执行有用工作之前,可能会发送一个较大的初始提示词,通常约为25k token。请保持启用--kv-disk-dir:在首次昂贵的预填充之后,磁盘KV缓存可以让后续的延续任务或重启的会话重用已保存的前缀,而无需重新处理整个提示词。
思考模式
DeepSeek V4 Flash具有不同的非思考、思考和思考最大模式。服务器默认为思考模式。reasoning_effort=max会请求思考最大模式,但仅当上下文大小足够大以满足模型卡片推荐时才会应用;较小的上下文会回退到普通思考模式。OpenAI的reasoning_effort=xhigh仍映射到普通思考模式,而非思考最大模式。
对于直接回复,请使用thinking: {"type":"disabled"}、think:false,或非思考模式的模型别名,例如deepseek-chat。
磁盘KV缓存
聊天/补全API是无状态的:智能体客户端通常会在每次请求时重新发送整个对话。ds4-server首先尝试成本低廉的精确token前缀检查,若失败则回退到比较渲染后的提示词字节与解码后的检查点字节。内存中的实时检查点覆盖当前会话;磁盘KV缓存让有用的前缀在会话切换和服务器重启后仍能保留。
出于内存原因,当前内存中只有一个实时KV缓存。当新的无关会话替换它时,旧检查点只有已被写入磁盘KV缓存才能在不重新处理的情况下恢复。换句话说,内存缓存处理当前活动会话;磁盘缓存是不同会话的恢复机制。
通过以下方式启用:
./ds4-server --kv-disk-dir /tmp/ds4-kv --kv-disk-space-mb 8192
缓存键是渲染后字节前缀的SHA1,文件命名为<sha1>.kv。DS4载荷仍会存储该前缀的精确token ID和图状态。这对于持续聊天很重要:模型可能生成了一个token,其解码后的文本稍后被客户端作为两个规范提示词token发送回来。一次渲染后的字节前缀命中仍然可以复用检查点,并且只对新的后缀进行分词。该文件特意使用普通的读写I/O(而非mmap)写入,这样恢复缓存条目不会给已经映射了模型的进程增加更多VM映射。
工具调用还维护一个有界精确DSML重放映射,其键为不可猜测的工具ID,这样客户端的JSON历史可以被渲染回精确的采样文本。默认情况下,该RAM映射最多保存100,000个ID;通过--tool-memory-max-ids调整。使用--disable-exact-dsml-tool-replay可禁用此功能并回退到规范的JSON到DSML渲染。
在磁盘上,缓存文件是:
KVC fixed header, 48 bytes
u32 rendered_text_bytes
rendered_text_bytes of UTF-8-ish token text
DS4 session payload, payload_bytes from the KVC header
optional tool-id map section
固定头部采用小端序:
0 u8[3] magic = "KVC"
3 u8 version = 1
4 u8 routed expert quant bits, currently 2 or 4
5 u8 save reason: 0 unknown, 1 cold, 2 continued, 3 evict, 4 shutdown
6 u8 extension flags, bit 0 = appended tool-id map
7 u8 reserved
8 u32 cached token count
12 u32 hit count
16 u32 context size the snapshot was written for
20 u8[4] reserved
24 u64 creation Unix time
32 u64 last-used Unix time
40 u64 DS4 session payload byte count
渲染文本是缓存token前缀经过分词器解码后的文本。它既是人类可检查的前缀,也是查找标识:其SHA1是文件名,只有当那些字节是传入渲染提示词的前缀时,文件才可复用。加载后,来自DS4载荷的精确检查点token保持权威性,只有缓存的字节之后传入的文本后缀才会被分词。
可选的工具 ID 映射仅在头部扩展位 0 置位时存在。附加部分采用固定位顺序,因此未来的扩展位可以无歧义地添加字段。该映射将不可猜测的 API 工具调用 ID 反查回模型采样的具体 DSML 块。仅存储那些 DSML 块存在于已渲染缓存文本中的映射。这使得重启后的服务器能够逐字节地还原后续客户端历史记录,就像原始模型输出一样,即使客户端对 JSON 参数进行了重新排序。
当前的工具 ID 映射部分结构如下:
0 u8[3] magic = "KTM"
3 u8 version = 1
4 u32 entry count
For each entry:
0 u32 tool id byte length
4 u32 sampled DSML byte length
8 bytes tool id
... bytes exact sampled DSML block
该部分是辅助性的回放内存,而非模型状态。缓存命中时,首先恢复会话负载,之后再加载映射(若存在)。在渲染请求之前,服务器还可以扫描缓存文件,查找客户端历史记录中出现的工具 ID,并仅加载这些映射。这样一来,即使匹配的 KV 快照并非最终用于渲染前缀命中时所用的那个,精确的 DSML 回放也能在服务器重启后继续工作。
DS4 会话负载以十三个小端序 u32 字段开头:
0 magic = "DSV4"
1 payload version = 2
2 saved context size
3 prefill chunk size
4 raw KV ring capacity
5 raw sliding-window length
6 compressed KV capacity
7 checkpoint token count
8 layer count
9 raw/head KV dimension
10 indexer head dimension
11 vocabulary size
12 live raw rows serialized below
随后存储的是:
- u32[token_count] 检查点 token ID。
- float32[vocab_size] 该检查点之后下一个 token 的 logits。
- u32[layer_count] 压缩注意力行数。
- u32[layer_count] 比率-4 索引行数。
- 对每一层:存活的原始滑动窗口 KV 行,按逻辑位置顺序写入,而非物理环形顺序。
- 对压缩层:存活的压缩 KV 行和压缩器前沿张量。
- 对比率-4 压缩层:存活的索引器压缩行和索引器前沿张量。
logits 是来自主机 ds4_session 缓冲区的原始 IEEE-754 float32 值。它们紧跟在检查点 token 之后保存,以便加载的快照能够在精确的下一 token 分布上进行采样或继续生成,而无需多运行一次解码步骤。MTP 草稿 logits/状态不会被持久化;加载磁盘检查点后,草稿状态将失效并由正常生成过程重建。
分布式协调器会话使用相同的 DSV4 负载。工作节点持有的层张量在保存时被拉取,并合并到正常的按层排序的张量流中;加载时协调器将该流按当前路由拆分,并将相关层张量推回给工作节点。保存的文件不保留分布式拓扑结构。
该张量负载是 DS4 专用的 KV/会话状态,而非通用的推理图导出。它预期仅在与该模型布局兼容的 ds4.c 构建版本之间可移植。
缓存会在四个时刻保存检查点:
- cold(冷启动):在较长的首个提示词达到稳定前缀之后、生成开始之前。
- continued(持续):当预填充或生成到达下一个绝对对齐边界时。
- evict(驱逐):在无关请求替换当前内存中的会话之前。
- shutdown(关闭):当服务器正常退出时。
冷保存会特意裁剪少量尾部 token,并对齐到预填充块边界。这可以避免未来请求向同一提示词追加文本时常见的 BPE 边界重新分词遗漏问题。默认值较为保守:保存至少 512 token 的前缀,冷保存提示词最多 30000 token,裁剪末尾 32 个 token,并对齐到 2048 token 块。重要的调节参数如下:
持续保存使用相同的对齐方式,且仅当活动图自然到达绝对边界时才会写入。采用默认值时,这意味着大约每 10000 token 保存一次,与第一个冷检查点的位置无关,这样长生成过程会留下重启点,而无需持久化末尾几个不稳定的 token。
- --kv-cache-min-tokens
- --kv-cache-cold-max-tokens
- --kv-cache-continued-interval-tokens
- --kv-cache-boundary-trim-tokens
- --kv-cache-boundary-align-tokens
- --tool-memory-max-ids
- --disable-exact-dsml-tool-replay
默认情况下,如果渲染出的前缀匹配,检查点可以在 2-bit 和 4-bit 路由专家变体之间复用。如果希望严格限定相同量化精度才能复用,请使用 --kv-cache-reject-different-quant。
缓存目录是可丢弃的。如果行为异常,请停止服务器并删除该目录。你可以用 hexdump 查看缓存内容,因为 KV 缓存文件包含缓存时完整的提示词原文。
后端
默认的图后端在 macOS 上是 Metal,在 CUDA 构建中是 CUDA:
./ds4 -p "Hello" --metal ./ds4 -p "Hello" --cuda
在 Linux 上,单独的 make 会打印可用的构建目标,而不是隐式选择 CUDA 目标。对 DGX Spark / GB10 使用 make cuda-spark。它会省略显式的 nvcc -arch,因为目前在 GB10 上这是最快的路径。对普通的本地 CUDA 构建使用 make cuda-generic,或者在交叉构建或需要已知目标时显式设置 CUDA_ARCH:
make cuda CUDA_ARCH=sm_120 make cuda CUDA_ARCH=native
还有一个 CPU 参考/调试路径:
./ds4 -p "Hello" --cpu make cpu ./ds4 ./ds4 -p "Hello"
不要将 CPU 路径视为生产目标。CLI 和 ds4-server 支持 CPU 后端用于参考/调试用途,并且与 Metal 和 CUDA 共享相同的 KV session 和快照格式,但正常的推理应使用 Metal 或 CUDA。
引导
该项目支持通过单向量激活方向进行引导;更多信息请参见 dir-steering 目录。这遵循了《Refusal in Language Models Is Mediated by a Single Direction》论文的核心思想。你可以用它来让模型更详细或更简洁,降低模型回答编程问题的意愿(如果它是一个用于你的租车网站的聊天机器人),等等,这比微调快得多。这对于网络安全研究人员也很有用,他们希望降低模型提供双重用途或攻击性安全指导的意愿。
测试向量
tests/test-vectors 包含从官方 DeepSeek V4 Flash API 捕获的短上下文和长上下文的续写向量。请求使用 deepseek-v4-flash,贪心解码,禁用思考,并使用 API 暴露的最大 top_logprobs 切片。本地向量通过 ./ds4 --dump-logprobs 生成,并通过 token 字节进行比较,因此 tokenizer/template 或 attention 回归会在它们变成长时间生成失败之前显现出来。C 运行器固定 DS4_METAL_PREFILL_CHUNK=2048 以进行这种严格的 API-向量比较。
所有项目测试由 C 运行器驱动,首先运行一个小型的 ds4-eval 提取器自测试:
make test # ./ds4-eval --self-test-extractors && ./ds4_test --all ./ds4_test --logprob-vectors ./ds4_test --server
调试笔记
当生成看起来有问题时,通常三个小工具足以获得初步答案:
./ds4 --dump-tokens -p "..." ./ds4 --dump-logprobs /tmp/out.json --logprobs-top-k 20 --temp 0 -p "..." ./ds4 --dump-logits /tmp/logits.json --metal --nothink --prompt-file prompt.txt ./ds4-server --trace /tmp/ds4-trace.txt ...
- --dump-tokens 会严格按照输入内容对 -p 或 --prompt-file 中的字符串进行 token 化,识别 DS4 协议特殊标记,然后在推理开始前退出。例如,DSML 工具关闭标记以两个 token 起始:</ 和 |DSML|。
- --dump-logprobs 会存储一个贪心延续,并在每一步附带局部的备选结果,这有助于将采样选择与 logit/模型问题区分开来。
- ds4-server --trace 会写入整个智能体会话中的渲染提示词、缓存决策、生成的文本以及工具解析器事件。