一句话看懂
Edge0 是一个面向混合专家模型(Mixture-of-Experts,MoE,指模型把参数拆成很多个"专家"子网络,每次推理只激活其中一小部分)的流式推理框架。35B 参数规模的模型,峰值内存只要 2.9 GiB,在 M4 Pro 24GB 的机器上能跑到 14.9 到 17.7 tok/s 的解码速度。当前只支持 macOS 的 Apple Silicon 芯片(M1 到 M4 系列),CUDA 后端还在路线图里。
它解决什么问题
MoE 模型推理时真正用到的专家是稀疏的,一次前向可能只激活几个专家。传统做法要把所有专家权重都加载进内存,哪怕大部分用不上,35B 模型的内存开销会直接劝退绝大多数个人设备。
Edge0 反过来做:专家权重存在 SSD 上,推理时按需用 mmap(内存映射,一种让磁盘文件像内存数组一样被直接读取的机制)读取。峰值内存由当前活跃的专家集合决定,而不是模型总参数量决定。35B 模型的 4-bit 量化检查点约 23 GB,8B 模型约 4.2 GB,但运行时内存只要 2.9 GiB 和 1.0 GiB。
量化会丢精度,Edge0 用 Recover-LoRA 来补。4-bit 量化把模型体积压缩到四分之一左右,LoRA 从全精度教师模型蒸馏训练,推理时叠加到基模型上补回精度损失。README 的 Quality 表格显示,35B 版本的平均基准损失是 3.9 分,8B 版本是 2.8 分。
核心概念速览
Prerouter(预路由器):单独训练的小型 MLP 头,提前一步预测下一层会用到哪些专家。输入是当前隐藏态和已执行过的专家索引,输出是下一层专家的 Top-K 预测。预测结果和实际的专家加载、前向计算重叠执行,加载磁盘数据的等待时间被计算时间掩盖。
SSD expert offload(专家卸载到 SSD):专家权重以 safetensors 格式存成分片文件,运行时通过 mmap 按偏移量读取对应片段。任何时刻内存里只有当前活跃的 Top-K 专家,这是内存占用能压到 GB 级别的直接原因。
Recover-LoRA:把 int4 量化后的基模型冻结,用从全精度教师模型蒸馏训练出来的 LoRA 适配器在推理时叠加到基模型上,把量化造成的精度损失补回来。
Tier(规格等级):每套完整的模型方案,比如 edge0-35b 和 edge0-8b。每个 tier 打包了基模型、LoRA 适配器和 prerouter 权重,三者配套使用,目录结构统一,加载时自动匹配。
架构拆解
代码库横跨多个平台栈。python/src/edge0/server/ 是 OpenAI 兼容的 /v1/chat/completions 服务端,往下调用 python/src/edge0/backends/mlx/,基于 MLX(Apple 的机器学习框架,专为 Apple Silicon 优化)实现,所有 MLX 相关代码都隔离在这一个目录。
后端抽象层通过 AutoEngine 这个 facade 调用具体后端,MLX 是当前唯一实现,backends/cuda/ 目录已预留。按 README 原话,新增后端只需实现这个 facade,不需要改动核心代码。
重计算部分在 macos/crates/engine-native/ 这个 C++ 实现里,包括专家加载(expertio.cpp)、prerouter 推理(prerouter.cpp)和 LoRA 应用(lora.cpp)。调用链是:prerouter 模块根据隐藏态预测下一层专家索引,ExpertIo 模块拿着索引去 mmap 对应的专家分片取回 Bundle,lora.cpp 把 LoRA 的 delta 叠加到专家权重上,最后参与实际前向计算。
移动端是独立技术栈。iOS 用 Swift 加 MLX Swift 实现(ios/Sources/Edge0MLX/),核心逻辑是 ExpertTensorStore 做流式专家张量加载;Android 用 Kotlin 写运行时,通过 JNI 桥接到原生引擎。这些引擎在 2026 年 9 月底开源,但目前各平台分开集成,统一的跨平台推理框架计划在 2026 年第四季度交付。
关键实现走读
Prerouter::predict 接收每个专家"头"的输入,把隐藏态拷贝进特征向量,再把已执行过的专家索引标记为 1。
bool Prerouter::predict(const std::vector<Input> &in, std::vector<Selection> &out_sel,
std::vector<std::vector<double>> *out_logits, std::string &err) {
if (!impl_ || !impl_->ready) { err = "prerouter: not built"; return false; }
const int O = n_heads();
if (static_cast<int>(in.size()) != O) { err = "prerouter: head count does not match input count"; return false; }
const auto ® = impl_->reg;
const int64_t prh = reg.prerouter_hidden, E = reg.num_experts, f_in = impl_->f_in;
std::vector<float> feats(static_cast<size_t>(O) * f_in, 0.f);
for (int i = 0; i < O; i++) {
if (!in[i].hidden) { err = "prerouter: missing header hidden input owner=" + std::to_string(owner(i)); return false; }
float *row = feats.data() + static_cast<size_t>(i) * f_in;
std::memcpy(row, in[i].hidden, sizeof(float) * impl_->hidden);
for (int64_t e : in[i].cur_exec)
if (e >= 0 && e < E) row[impl_->hidden + e] = 1.f;
prerouter 预测"下一层会用到哪些专家"不能只看当前隐藏态,还得知道这一层已走过哪些专家路径。把已执行专家编码成 one-hot 向量拼在隐藏态特征后面,把上下文显式喂给模型。没有这部分信息,预测会退化成静态预测,准确率下降,预取的专家会和实际需要的不匹配,"加载和计算重叠"优化就会打折扣。
ExpertIo::ShardMaps::raw 是专家权重读取的核心,检查某个分片文件是否已被 mmap 过,没有就打开文件、读取大小、建立内存映射。
const uint8_t *ExpertIo::ShardMaps::raw(const std::string &shard_path, uint64_t off,
uint64_t len) {
uint8_t *base = nullptr;
uint64_t total = 0;
{
std::lock_guard<std::mutex> lk(mu_);
auto it = maps_.find(shard_path);
if (it == maps_.end()) {
int fd = ::open(shard_path.c_str(), O_RDONLY);
if (fd < 0) return nullptr;
struct stat sb;
if (fstat(fd, &sb) != 0) { ::close(fd); return nullptr; }
void *m = mmap(nullptr, size_t(sb.st_size), PROT_READ, MAP_SHARED, fd, 0);
if (m == MAP_FAILED) { ::close(fd); return nullptr; }
用 std::lock_guard 加锁防止多线程同时映射同一文件造成重复映射和资源浪费。用 MAP_SHARED 加 PROT_READ 建立只读共享映射,操作系统的页缓存会被多次读取共享复用。这是整个 SSD offload 机制高效工作的底层支撑:mmap 把"按需读取磁盘文件"变成"按需访问虚拟内存地址",操作系统负责页面换入换出。如果不用 mmap,每次专家切换都要走完整的文件 I/O 和内存拷贝,无法利用页缓存自动复用已读过的数据。
动手上手
先装依赖,进入 python 目录建虚拟环境:
cd python
python3.12 -m venv .venv && .venv/bin/pip install -e '.[dev,fetch]'
下载 35B tier 的模型文件:
.venv/bin/python scripts/fetch_models.py --tier edge0-35b --target-dir models
设置环境变量指向下载好的模型目录:
export EDGE0_35B_MODEL=$PWD/models/edge0-35b
跑一次演示推理:
edge0 demo edge0-35b
启动 OpenAI 兼容的服务端:
edge0 serve edge0-35b
应用场景
本地 macOS 大模型推理是最直接场景。下载 tier,edge0 serve 启动服务,用 curl 或任何 OpenAI 兼容客户端调用。限制是只支持 Apple Silicon,需要 MLX 0.30.6 这个具体版本,实际部署时还要给系统进程、tokenizer 和 KV 缓存留出额外内存。
多适配器共享基模型场景值得一提。基模型目录下放多组 LoRA safetensors 文件,切换适配器只需替换文件,不用重新加载整个基模型。前提是适配器的 r、alpha 和 owner 元数据必须和基模型匹配。
跨平台移动推理目前要分别对待。iOS 和 Android 的引擎代码已开源,但各自是独立技术栈,要在手机上跑得分别对接 Swift 或 Kotlin 的原生接口。统一的跨平台框架定在 2026 年第四季度。
独立分析
和传统全量加载 MoE 方案比,Edge0 的核心差异是内存占用模型:传统方案要把全部参数常驻内存,Edge0 的峰值内存只由当前活跃的专家集合决定。在 35B 规模上,2.9 GiB 对比几十 GB 全量加载,量级完全不同。
和标准 4-bit 量化方案比,差异在精度恢复手段。普通 int4 量化后精度损失直接,没有补救。Edge0 多了一步 LoRA 蒸馏训练,平均基准损失压到 3.9 分和 2.8 分。
和同步专家加载方案比,prerouter 预测加载重叠执行是 Edge0 的特点。README 原话是增益随存储延迟和 K 值增长,最高能到 59%。这个数字的前提是存储延迟较高的场景,如果磁盘本身很快(高速 NVMe),这个增益会相应收窄。
这套架构的三个机制组合针对"内存有限但想跑大模型"这个具体场景,对 Apple Silicon 用户确实存在落地空间。但验证范围窄,只在一种硬件、一种后端上跑通,benchmark 数据也只来自单一机型(M4 Pro 24GB),能不能推广到其他配置或芯片代际,现有数据不能回答。
局限与风险
Python 框架目前只支持 macOS 的 Apple Silicon,CUDA 后端还没实现,Linux 和 Windows 上的 GPU 用户现在用不了。
只提供 4-bit 量化的检查点,如果需要更高精度或别的量化位宽,得自己转换,项目没有现成转换工具说明。
文档目前只有 README 和 docs 目录,API 细节和测试用例没有专门说明,想深入用需要直接读源码。
跨平台统一推理框架要等到 2026 年第四季度,现在 iOS、Android、macOS 三个平台各自独立集成,没有通用接口。
适配器文件依赖配套的元数据,如果目录结构不完整或 LoRA 参数和基模型不匹配,加载会失败。
结论卡片
适合:需要在内存受限的设备上跑大规模 MoE 模型的工程师,以及研究稀疏推理、专家调度或 LoRA 适配技术的学术研究者。
不适合:需要生产级文档支撑、需要跨平台统一 API、或者硬件不是 Apple Silicon 的用户。
接下来能做的:等 2026 年第四季度的统一推理框架和 CUDA 后端落地,或者现在就参与某个平台的适配工作。
原文地址:tools.wflynn.cn/aitools/git…
关注公众号「YourTools」,每天推送 GitHub 趋势与开源工具速报: