编程 WebLLM:用 WebGPU 把 LLM 跑进浏览器,以及几个容易被忽略的实现细节

2026-10-07 00:04:02

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)。

  1. 安装 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
  2. 在 package.json 中把 "@mlc-ai/web-runtime": "0.18.0-dev2" 改成 "file:./tvm_home/web"。
  3. ./scripts/prep_deps.sh;git clone https://github.com/mlc-ai/relax 3rdparty/tvm-unity --recursive(--recursive 是必需的,否则会找不到 dlpack/dlpack.h)。
  4. npm run build。

推荐文章

程序员茄子在线接单