一、引言:为什么要在本地运行大模型?
随着大语言模型(LLMs)技术的迅猛发展,我们越来越多地看到 ChatGPT、Claude、Gemini 等服务在各种云平台上大放异彩。然而,这些模型通常依赖强大的服务器支持,无法在离线、隐私敏感或资源受限的环境中运行。
在如下场景中,离线运行 LLM 模型具有强烈的现实意义:
- 🔒 隐私保护:在医疗、法律、政务等场景中,本地部署可避免数据泄露;
- 📴 无网络环境:如边缘设备、远程科研站点、封闭内网等;
- 💻 本地开发/学习:开发者希望快速迭代而不依赖网络;
- 📦 零运维开箱即用:用户不愿意配置复杂的服务器。
👉 是否可以直接在中低端设备上运行大模型?
答案是:可以!本文将分享我如何借助 MLC LLM + Electron + WebGPU,打造了一个完全本地运行的离线聊天应用,成功在 Mac M2/8G 上运行 Qwen3-0.6B 和 GPT2 等模型,支持 WebGPU 离线推理,启动即用,无需联网。
二、MLC-LLM 简介:支持多平台的端侧推理方案
MLC(Machine Learning Compilation)来自 TVM 团队,是一款专注于「端侧设备大模型推理」的框架。其核心特性:
| 特性 | 说明 |
|---|---|
| ✅ WebGPU 支持 | 可直接在浏览器或 Electron 中加载模型执行 |
| ✅ 模型量化(如 q4f16_1) | 将原始模型压缩至原来的 1/4–1/8,保持推理精度 |
| ✅ WASM + ONNX 编译 | 支持将 HuggingFace 上的模型编译为本地可加载格式 |
| ✅ iOS / Android / Desktop / Web 多平台适配 | 尤其适合中低配硬件 |
目前主要部署案例集中于:
- Web 端(官方 demo + WebLLM)
- Android(官方 APK)
桌面端尚无较成熟开源案例,尤其是在打包、离线模型管理等方面存在空白。
三、为何选择 Electron 而非 Tauri
很多人会问,为什么不用 Tauri?原因如下:
| 方案 | WebGPU 支持 | WASM 支持 | 打包灵活性 | 模型加载支持 |
|---|---|---|---|---|
| Electron | ✅ Chromium 原生支持 | ✅ | ✅ 高度可定制 | ✅ 支持 WebGPU 推理 |
| Tauri | ❌ 当前无 WebGPU 支持 | ✅ | ✅ 更轻量 | ❌ 无法直接运行 wasm 模型 |
因此,为了实现真正可用的 桌面 WebGPU 推理系统,Electron 是目前唯一可选项。
🏗️ 技术栈选型与架构
| 组件 | 技术栈 | 用途 |
|---|---|---|
| 前端界面 | React + Ant Design | 聊天 UI、模型选择、状态展示 |
| 桌面壳 | Electron | 跨平台桌面端,支持 WebGPU |
| 模型引擎 | MLC-AI / WebLLM | 执行模型推理(qwen / gpt2) |
| 模型服务 | http.createServer 本地静态服务 | 提供 wasm 和 params 的加载路径 |
💻 应用特点
- ✅ 支持 Qwen3-0.6B / GPT2 模型本地运行(均为量化版本)
- ✅ 无需服务器,可离线使用
- ✅ 支持联网时动态下载新模型(自动解压到缓存)
- ✅ 自动保存聊天历史,可查看多轮会话
- ✅ 支持
stream流式生成(体验类似 ChatGPT)
四、系统架构概览
系统由三部分组成
┌────────────┐ ┌────────────────────┐ ┌────────────────────────┐
│ React UI │ <─> │ Electron 主进程 │ <─> │ 本地模型 HTTP Server │
└────────────┘ └────────────────────┘ └────────────────────────┘
↑
提供 wasm 和模型 config.json、tokenizer
📦 项目结构图(图解式)
project-root/
├── public/ # 模型资源(用于打包)
│ ├── models/ # 存放模型 JSON/tokenizer
│ └── Qwen3-4B-q4f16_1/
│ └── resolve/
│ └── main/
│ ├── config.json
│ ├── tokenizer.json
│ ├── params_shard_0.bin
│ ├── ...
│ └── libs/ # 存放编译后的 .wasm
├── model-zips/ # 模型压缩包(打包前存放)
├── extracted-models/ # 解压后的模型目录(运行时生成)
├── src/
│ ├── main.ts # Electron 主进程
│ ├── preload.ts # contextBridge API 注入
│ └── renderer/ # React UI 界面
├── dist/ # 打包后目录
├── package.json
└── electron-builder.config.js
五、关键实现细节
5.1 动态加载模型(MLC Engine)
const appConfig = {
model_list: [
{
model: `http://localhost:${port}/models/Qwen3-0.6B-q4f16_1`,
model_id: "Qwen3-0.6B-q4f16_1",
model_lib: `http://localhost:${port}/libs/Qwen3-0.6B-q4f16_1-ctx4k_cs1k-webgpu.wasm`,
required_features: ["shader-f16"],
},
{
model: `http://localhost:${port}/models/gpt2-q0f16`,
model_id: "gpt2-q0f16",
model_lib: `http://localhost:${port}/libs/gpt2-q0f16-ctx1k_cs1k-webgpu.wasm`,
required_features: ["shader-f16"],
},
],
};
const initEngine = async () => {
const engine = await CreateMLCEngine(selectedModel, {
appConfig,
initProgressCallback: (progress) => {
document.getElementById("download-status").textContent = progress.text;
}
});
setEngine(engine);
};
5.2 自动启动本地 HTTP 静态服务器
由于 WebLLM 使用 fetch 请求加载 .wasm 和 config.json,不能使用 file://,我们用 Node 启动 HTTP 服务器:
function startStaticServer() {
http
.createServer((req, res) => {
const reqPath = decodeURIComponent(req.url || "/");
const filePath = path.join(PUBLIC_DIR, reqPath);
// 安全限制:防止路径穿越
if (!filePath.startsWith(PUBLIC_DIR)) {
res.writeHead(403);
return res.end("Forbidden");
}
fs.readFile(filePath, (err, data) => {
if (err) {
res.writeHead(404);
return res.end("Not Found");
}
const contentType = filePath.endsWith(".wasm")
? "application/wasm"
: filePath.endsWith(".json")
? "application/json"
: "application/octet-stream";
res.writeHead(200, { "Content-Type": contentType });
res.end(data);
});
})
.listen(PORT, () => {
console.log(`🚀 Local model server running: http://localhost:${PORT}`);
});
}
app.whenReady().then(() => {
startStaticServer()
createWindow()
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) createWindow();
});
})
Electron 中通过 http.createServer 启动静态服务,将路径映射到本地模型资源。
5.3 动态选择端口
import getPort from 'get-port';
const port = await getPort({ port: getPort.makeRange(3999, 4100) });
六、运行效果与性能
在 Mac mini M2 / 8G 设备上实测:
| 模型 | 参数量 | 推理速度 | 支持语言 | 占用内存 |
|---|---|---|---|---|
| GPT2-q0f16 | 345M | ~15 tokens/s | 英文,中文差 | ~800MB |
| Qwen3-0.6B-q4f16_1 | 0.6B | ✅ 10 tokens/s | ✅ 中文表现优秀 | ~1.5GB |
⚠️ GPT2 不建议用于中文任务,建议直接使用 Qwen / Mistral 量化版本。
界面展示如下:
界面视频如下: jvideo
七、对比其他方案
| 方案 | 优点 | 缺点 |
|---|---|---|
| WebLLM+Electron | 零服务器依赖、开箱即用、跨平台 | 初次加载略慢、模型容量有限 |
| Ollama + UI | 模型选择丰富、运行快 | 需安装 server、打包复杂 |
| llama-cpp + binding | 性能好、支持 GPU | 构建复杂、UI 不统一 |
| Tauri + WebLLM | 更轻量级尝试 | ❌ 不支持 WebGPU,无法运行 wasm |
八、结语
本文展示了一个完整、可打包、可扩展的 MLC-LLM + Electron 桌面聊天助手实现流程。通过本文项目实践,你可以在普通 Mac 或 PC 上:
- ✳️ 运行自己的大语言模型
- ✳️ 完全脱离云端,保护隐私
- ✳️ 实现多模型加载、多轮对话、离线推理
未来,我将尝试:
- 🧠 更丰富的本地多模型支持(Mixtral、Gemma 等)
- 🎙️ 音视频输入(Multimodal)能力
- 🧊 支持模型压缩、合并、混合精度
- 📚 离线知识库、RAG 检索集成
- 🤖 构建 LLM 本地 Agent 框架
- 🌐 实现 Web 与 App 多端统一体验
欢迎点赞收藏,也欢迎一起共建!如果你对离线大模型部署、MLC-AI 推理、WebGPU Electron 有兴趣,欢迎交流,一起打造本地化 AI 助手!