WebLLM:用 WebGPU 把 LLM 跑进浏览器,以及几个容易被忽略的实现细节
Browser AI(浏览器内置 AI / 浏览器侧推理):WebLLM 是一个高性能的浏览器内 LLM 推理引擎,把语言模型推理直接放到 Web 浏览器里,靠硬件加速跑,全程不需要服务端支持,加速后端是 WebGPU。
WebLLM 完全兼容 OpenAI API。同一套 OpenAI API 可以直接用在本地开源模型上,支持 streaming、JSON-mode、function-calling(WIP)。
它也可以作为基础 npm 包使用,由你自己搭上层 Web 应用。
- 文档:https://webllm.mlc.ai/docs/
- 在线 Chat demo:https://chat.webllm.ai/
- 模型列表:https://mlc.ai/models
- 代码仓库:https://github.com/mlc-ai/web-llm
主要特性
- 浏览器内推理:基于 WebGPU 做硬件加速,无服务端处理。
- 完整 OpenAI API 兼容:streaming、JSON-mode、logit 级控制、seeding。
- 结构化 JSON 生成:在 WebAssembly 部分实现,以拿到更好的性能。
- 模型覆盖广:Llama 3、Phi 3、Gemma、Mistral、Qwen(通义千问)。
- 自定义模型接入:可接入 MLC 格式的模型。
- 接入方式灵活:NPM/Yarn 或 CDN。
- 流式与实时交互。
- Web Worker 与 Service Worker 支持。
- Chrome 扩展支持。
内置模型
模型清单见 prebuiltAppConfig.model_list:https://github.com/mlc-ai/web-llm/blob/main/src/config.ts#L293
涉及的模型族:
- Llama:Llama 3、Llama 2、Hermes-2-Pro-Llama-3
- Phi:Phi 3、Phi 2、Phi 1.5
- Gemma:Gemma-2B
- Mistral:Mistral-7B-v0.3、Hermes-2-Pro-Mistral-7B、NeuralHermes-2.5-Mistral-7B、OpenHermes-2.5-Mistral-7B
- Qwen(通义千问):Qwen2 0.5B、1.5B、7B
开始使用
安装
包管理器:
npm install @mlc-ai/web-llm
yarn add @mlc-ai/web-llm
pnpm install @mlc-ai/web-llm
import * as webllm from "@mlc-ai/web-llm";
import { CreateMLCEngine } from "@mlc-ai/web-llm";
CDN 引入:
import * as webllm from "https://esm.run/@mlc-ai/web-llm";
// or
const webllm = await import("https://esm.run/@mlc-ai/web-llm");
创建 MLCEngine
import { CreateMLCEngine } from "@mlc-ai/web-llm";
const initProgressCallback = (initProgress) => { console.log(initProgress); };
const selectedModel = "Llama-3.1-8B-Instruct-q4f32_1-MLC";
const engine = await CreateMLCEngine(selectedModel, { initProgressCallback });
也可以分两步:
import { MLCEngine } from "@mlc-ai/web-llm";
const engine = new MLCEngine({ initProgressCallback });
await engine.reload(selectedModel);
Cache Backend 策略
WebLLM 通过 AppConfig.cacheBackend 提供四种缓存后端:
"cache":浏览器 Cache API(默认)"indexeddb":IndexedDB"opfs":Origin Private File System(OPFS)"cross-origin":实验性的 Chrome Cross-Origin Storage API 扩展后端
import { CreateMLCEngine, prebuiltAppConfig } from "@mlc-ai/web-llm";
const appConfig = { ...prebuiltAppConfig, cacheBackend: "cross-origin" };
const engine = await CreateMLCEngine("Llama-3.1-8B-Instruct-q4f32_1-MLC", { appConfig });
几点注意:
- 选了
"opfs"但环境不支持 OPFS 时,缓存操作会失败并报 OPFS availability 错误。 - 使用
"opfs"时,appConfig.opfsAccessMode可取值"auto"/"sync",默认是"async"。 "cross-origin"依赖浏览器扩展,清理由扩展自身管理。
Chat Completion
const messages = [
{ role: "system", content: "You are a helpful AI assistant." },
{ role: "user", content: "Hello!" },
];
const reply = await engine.chat.completions.create({ messages });
console.log(reply.choices[0].message);
console.log(reply.usage);
model 参数不被支持;要换模型请用 CreateMLCEngine(model) 或 engine.reload(model)。
Streaming
const chunks = await engine.chat.completions.create({ messages, temperature: 1, stream: true, stream_options: { include_usage: true } });
let reply = "";
for await (const chunk of chunks) { reply += chunk.choices[0]?.delta.content || ""; if (chunk.usage) console.log(chunk.usage); }
进阶用法
使用 Worker(Dedicated Web Worker)
// worker.ts
import { WebWorkerMLCEngineHandler } from "@mlc-ai/web-llm";
const handler = new WebWorkerMLCEngineHandler();
self.onmessage = (msg: MessageEvent) => { handler.onmessage(msg); };
// main.ts
import { CreateWebWorkerMLCEngine } from "@mlc-ai/web-llm";
const engine = await CreateWebWorkerMLCEngine(new Worker(new URL("./worker.ts", import.meta.url), { type: "module" }), selectedModel, { initProgressCallback });
使用 Service Worker
Service Worker 的生命周期由浏览器管理,随时可能被杀死。ServiceWorkerMLCEngine 会通过周期性心跳事件尽量让 SW 线程保活,但代码里仍然要有错误处理。相关参数是 ServiceWorkerMLCEngine 中的 keepAliveMs 和 missedHeatbeat(见 src/service_worker.ts#L234,https://github.com/mlc-ai/web-llm/blob/main/src/service_worker.ts#L234)。
关键约束:handler 必须在 worker 脚本的顶层实例化,这样消息监听器会在脚本首次求值时注册。不要放在 'activate' 或 'message' 监听器里创建——浏览器可以重启一个已经 active 的 worker 而不再次派发 'activate' 事件。
// sw.ts
import { ServiceWorkerMLCEngineHandler } from "@mlc-ai/web-llm";
new ServiceWorkerMLCEngineHandler();
// main.ts
import { MLCEngineInterface, CreateServiceWorkerMLCEngine } from "@mlc-ai/web-llm";
if ("serviceWorker" in navigator) { navigator.serviceWorker.register(new URL("sw.ts", import.meta.url), { type: "module" }); }
const engine = await CreateServiceWorkerMLCEngine(selectedModel, { initProgressCallback });
Chrome Extension
示例见 examples/chrome-extension 与 examples/chrome-extension-webgpu-service-worker。另有一个完整项目:WebLLM Assistant。
OpenAI 兼容性
- streaming
- json-mode
- seed-to-reproduce
- function-calling(WIP)
Integrity 校验
WebLLM 支持对模型产物做可选的完整性校验,使用 SRI 哈希。当 ModelRecord 上设置了 integrity 字段时,WebLLM 会在加载前用给定哈希校验下载到的 config、WASM、tokenizer 文件。
const appConfig = { model_list: [{
model: "https://huggingface.co/mlc-ai/Llama-3.2-1B-Instruct-q4f16_1-MLC",
model_id: "Llama-3.2-1B-Instruct-q4f16_1-MLC",
model_lib: "https://raw.githubusercontent.com/user/model-libs/main/model.wasm",
integrity: {
config: "sha256-",
model_lib: "sha256-",
tokenizer: { "tokenizer.json": "sha256-" },
onFailure: "error", // "error" (default) throws IntegrityError, "warn" logs and continues
},
}]};
生成 SRI 哈希:
openssl dgst -sha256 -binary | openssl base64 -A | sed 's/^/sha256-/'
openssl dgst -sha384 -binary | openssl base64 -A | sed 's/^/sha384-/'
openssl dgst -sha512 -binary | openssl base64 -A | sed 's/^/sha512-/'
哈希不匹配时会抛 IntegrityError(onFailure: "warn" 时只记录警告并继续)。integrity 下所有字段都是可选的。
自定义模型
接入新模型只需要两个元素:model(模型产物 URL)和 model_lib(wasm 库 URL)。
const appConfig = { "model_list": [{ "model": "/url/to/my/llama", "model_id": "MyLlama-3b-v1-q4f32_0", "model_lib": "/url/to/myllama3b.wasm" }] };
const chatOpts = { "repetition_penalty": 1.01 };
const engine = await CreateMLCEngine("MyLlama-3b-v1-q4f32_0", { appConfig }, chatOpts);
从源码构建 WebLLM 包
npm install
npm run build
把 examples/get-started/package.json 里的 "@mlc-ai/web-llm": "^0.2.85" 改成 ../..,然后 cd examples/get-started、npm install、npm start。
最坏情况下清理重来:rm -rf node_modules dist package-lock.json .parcel-cache。
从源码构建 TVMjs
WebLLM 运行时很大程度依赖 TVMjs(https://github.com/apache/tvm/tree/main/web ;npm 包为 @mlc-ai/web-runtime)。
- 安装 emscripten。注意:直接用最新的 emcc 可能出问题,应使用
./emsdk install 3.1.56而不是./emsdk install latest。否则会报错:Init error, LinkError: WebAssembly.instantiate(): Import #6 module="wasi_snapshot_preview1" function="proc_exit": function import requires a callable - 在
package.json中把"@mlc-ai/web-runtime": "0.18.0-dev2"改成"file:./tvm_home/web"。 ./scripts/prep_deps.sh;git clone https://github.com/mlc-ai/relax 3rdparty/tvm-unity --recursive(--recursive是必需的,否则会找不到dlpack/dlpack.h)。npm run build。