WebLLM:在浏览器中运行大模型推理
开源项目,用 WebGPU 让浏览器本地运行 LLM 推理,无需后端,实现真正的端侧计算。
开源项目,用 WebGPU 让浏览器本地运行 LLM 推理,无需后端,实现真正的端侧计算。
高性能浏览器内 LLM 推理引擎
文档 | 博文 | 论文 | 示例
WebLLM 是一款高性能的浏览器内 LLM 推理引擎,可将语言模型推理直接带到网页浏览器中,并配有硬件加速支持。所有操作都在浏览器内运行,无需服务器支持,并通过 WebGPU 实现加速。
WebLLM 完全兼容 OpenAI API。也就是说,你可以在任何开源模型上本地使用相同的 OpenAI API,包括流式输出、JSON 模式、函数调用(进行中)等功能。
我们可以为每个人构建 AI 智能体的许多有趣机会,同时在享受 GPU 加速的同时实现隐私保护。
你可以将 WebLLM 用作基础 npm 包,并按照下面的示例在其基础上构建自己的网页应用程序。该项目是 MLC LLM 的配套项目,MLC LLM 支持在各种硬件环境中通用部署 LLM。
查看 WebLLM Chat 来体验它!
浏览器内推理:WebLLM 是一款高性能的浏览器内语言模型推理引擎,利用 WebGPU 实现硬件加速,能够在网页浏览器内直接执行强大的 LLM 操作,无需服务器端处理。
浏览器内推理:WebLLM 是一款高性能的浏览器内语言模型推理引擎,利用 WebGPU 实现硬件加速,能够在网页浏览器内直接执行强大的 LLM 操作,无需服务器端处理。
完全 OpenAI API 兼容性:使用 OpenAI API 无缝集成你的应用与 WebLLM,支持流式输出、JSON 模式、logit 级控制、种子设置等功能。
完全 OpenAI API 兼容性:使用 OpenAI API 无缝集成你的应用与 WebLLM,支持流式输出、JSON 模式、logit 级控制、种子设置等功能。
结构化 JSON 生成:WebLLM 支持最先进的 JSON 模式结构化生成,在模型库的 WebAssembly 部分中实现,可实现最优性能。查看 HuggingFace 上的 WebLLM JSON Playground 来尝试使用自定义 JSON 模式生成 JSON 输出。
结构化 JSON 生成:WebLLM 支持最先进的 JSON 模式结构化生成,在模型库的 WebAssembly 部分中实现,可实现最优性能。查看 HuggingFace 上的 WebLLM JSON Playground 来尝试使用自定义 JSON 模式生成 JSON 输出。
广泛的模型支持:WebLLM 原生支持一系列模型,包括 Llama 3、Phi 3、Gemma、Mistral、Qwen(通义千问)等,适用于各种 AI 任务。完整的支持模型列表请查看 MLC Models。
广泛的模型支持:WebLLM 原生支持一系列模型,包括 Llama 3、Phi 3、Gemma、Mistral、Qwen(通义千问)等,适用于各种 AI 任务。完整的支持模型列表请查看 MLC Models。
自定义模型集成:可轻松集成和部署 MLC 格式的自定义模型,使你能够根据特定需求和场景调整 WebLLM,增强模型部署的灵活性。
自定义模型集成:可轻松集成和部署 MLC 格式的自定义模型,使你能够根据特定需求和场景调整 WebLLM,增强模型部署的灵活性。
即插即用集成:使用 NPM 和 Yarn 等包管理器或直接通过 CDN 轻松集成 WebLLM 到你的项目中,提供全面的示例和模块化设计,便于与 UI 组件连接。
即插即用集成:使用 NPM 和 Yarn 等包管理器或直接通过 CDN 轻松集成 WebLLM 到你的项目中,提供全面的示例和模块化设计,便于与 UI 组件连接。
流式传输和实时交互:支持流式聊天补全,允许实时生成输出,增强了聊天机器人和虚拟助手等交互应用。
流式传输和实时交互:支持流式聊天补全,允许实时生成输出,增强了聊天机器人和虚拟助手等交互应用。
Web Worker 和 Service Worker 支持:通过将计算卸载到单独的工作线程或 Service Worker 来优化 UI 性能,并高效地管理模型生命周期。
Web Worker 和 Service Worker 支持:通过将计算卸载到单独的工作线程或 Service Worker 来优化 UI 性能,并高效地管理模型生命周期。
Chrome 扩展支持:通过使用 WebLLM 的自定义 Chrome 扩展来扩展网络浏览器的功能,并提供构建基础和高级扩展的示例。
Chrome 扩展支持:通过使用 WebLLM 的自定义 Chrome 扩展来扩展网络浏览器的功能,并提供构建基础和高级扩展的示例。
查看 MLC Models 上的完整可用模型列表。WebLLM 支持这些可用模型的一个子集,列表可以在 prebuiltAppConfig.model_list 中访问。
以下是目前支持的主要模型家族:
Llama:Llama 3、Llama 2、Hermes-2-Pro-Llama-3
Phi:Phi 3、Phi 2、Phi 1.5
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
如果你需要更多模型,可以通过提交 issue 来请求新模型,或查看自定义模型部分以了解如何使用 WebLLM 编译和使用自己的模型。
通过这个简单的聊天机器人示例,学习如何使用 WebLLM 将大型语言模型集成到你的应用中并生成聊天补全:
有关更大、更复杂的项目的高级示例,请查看 WebLLM Chat。
examples 文件夹中提供了针对不同用例的更多示例。
WebLLM 提供了一个最小化和模块化的接口来访问浏览器中的聊天机器人。该包设计成模块化的方式,可以连接到任何 UI 组件。
# npm
npm install @mlc-ai/web-llm
# yarn
yarn add @mlc-ai/web-llm
# or pnpm
pnpm install @mlc-ai/web-llm
然后在你的代码中导入该模块。
// 导入所有内容
import * as webllm from "@mlc-ai/web-llm";
// 或仅导入你需要的内容
import { CreateMLCEngine } from "@mlc-ai/web-llm";
得益于 jsdelivr.com,WebLLM 可以直接通过 URL 导入,并在 jsfiddle.net、Codepen.io 和 Scribbler 等云开发平台上开箱即用:
import * as webllm from "https://esm.run/@mlc-ai/web-llm";
它也可以动态导入为:
const webllm = await import("https://esm.run/@mlc-ai/web-llm");
WebLLM 中的大多数操作都是通过 MLCEngine 接口调用的。你可以创建一个 MLCEngine 实例并通过调用 CreateMLCEngine() 工厂函数来加载模型。
(注意加载模型需要下载,第一次运行没有缓存可能需要相当长的时间。你应该正确处理这个异步调用。)
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: initProgressCallback }, // engineConfig
);
在幕后,这个工厂函数为首次创建引擎实例(同步)和加载模型(异步)执行以下步骤。你也可以在应用中分别进行。
import { MLCEngine } from "@mlc-ai/web-llm";
// 这是一个同步调用,立即返回
const engine = new MLCEngine({
initProgressCallback: initProgressCallback,
});
// 这是一个异步调用,可能需要很长时间才能完成
await engine.reload(selectedModel);
WebLLM 通过 AppConfig.cacheBackend 支持四个缓存后端:
"cache":浏览器 Cache API(默认)。
"indexeddb":浏览器 IndexedDB。
"opfs":浏览器原点私有文件系统(OPFS)。
"cross-origin":实验性 Chrome 跨域存储 API 扩展后端。安装跨域存储扩展来使用它。(如果扩展未安装,WebLLM 会自动回退到默认缓存。)
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 可用性错误。
使用"opfs"时,appConfig.opfsAccessMode 可以设置为"auto"以在支持的地方使用 OPFS 同步访问句柄,或设置为"sync"以要求同步访问句柄。默认值为"async"。
"cross-origin"后端需要安装并启用兼容的浏览器扩展。
跨域后端目前不支持以编程方式删除张量缓存;缓存清除由扩展管理。
成功初始化引擎后,你现在可以通过 engine.chat.completions 接口使用 OpenAI 风格的聊天 API 调用聊天完成。关于完整的参数列表及其说明,请查看下面的部分和 OpenAI API 参考文档。
(注意:这里不支持 model 参数,它会被忽略。请改为调用 CreateMLCEngine(model) 或 engine.reload(model),如上面的创建 MLCEngine 部分所示。)
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);
WebLLM 也支持流式聊天完成生成。要使用它,只需在 engine.chat.completions.create 调用中传入 stream: true。
const messages = [
{ role: "system", content: "You are a helpful AI assistant." },
{ role: "user", content: "Hello!" },
];
// Chunks is an AsyncGenerator object
const chunks = await engine.chat.completions.create({
messages,
temperature: 1,
stream: true, // <-- Enable streaming
stream_options: { include_usage: true },
});
let reply = "";
for await (const chunk of chunks) {
reply += chunk.choices[0]?.delta.content || "";
console.log(reply);
if (chunk.usage) {
console.log(chunk.usage); // only last chunk has usage
}
}
const fullReply = await engine.getMessage();
console.log(fullReply);
你可以将繁重的计算放在 worker 脚本中来优化应用性能。要这样做,你需要:
在 worker 线程中创建一个处理程序,该程序与前端通信并处理请求。
在主应用程序中创建一个 Worker Engine,它在底层向 worker 线程中的处理程序发送消息。
关于不同类型 Worker 的详细实现,请查看下面的部分。
WebLLM 提供了 WebWorker 的 API 支持,你可以将生成过程挂接到单独的 worker 线程,这样 worker 线程中的计算就不会中断 UI。
我们在 worker 线程中创建一个与前端通信并处理请求的处理程序。
// worker.ts
import { WebWorkerMLCEngineHandler } from "@mlc-ai/web-llm";
// A handler that resides in the worker thread
const handler = new WebWorkerMLCEngineHandler();
self.onmessage = (msg: MessageEvent) => {
handler.onmessage(msg);
};
在主逻辑中,我们创建一个实现相同 MLCEngineInterface 的 WebWorkerMLCEngine。其余逻辑保持不变。
// main.ts
import { CreateWebWorkerMLCEngine } from "@mlc-ai/web-llm";
async function main() {
// Use a WebWorkerMLCEngine instead of MLCEngine here
const engine = await CreateWebWorkerMLCEngine(
new Worker(new URL("./worker.ts", import.meta.url), {
type: "module",
}),
selectedModel,
{ initProgressCallback }, // engineConfig
);
// everything else remains the same
}
WebLLM 提供了 ServiceWorker 的 API 支持,你可以将生成过程挂接到 service worker,避免在每次页面访问时重新加载模型,并优化应用的离线体验。
(注意:Service Worker 的生命周期由浏览器管理,可能在任何时候被终止而不通知 webapp。ServiceWorkerMLCEngine 会通过定期发送心跳事件来尝试保持 service worker 线程活动,但你的应用程序也应该包含适当的错误处理。详情请查看 ServiceWorkerMLCEngine 中的 keepAliveMs 和 missedHeatbeat。)
我们在 worker 线程中创建一个与前端通信并处理请求的处理程序。
// sw.ts
import { ServiceWorkerMLCEngineHandler } from "@mlc-ai/web-llm";
let handler: ServiceWorkerMLCEngineHandler;
self.addEventListener("activate", function (event) {
handler = new ServiceWorkerMLCEngineHandler();
console.log("Service Worker is ready");
});
然后在主逻辑中,我们注册 service worker 并使用 CreateServiceWorkerMLCEngine 函数创建引擎。其余逻辑保持不变。
// main.ts
import {
MLCEngineInterface,
CreateServiceWorkerMLCEngine,
} from "@mlc-ai/web-llm";
if ("serviceWorker" in navigator) {
navigator.serviceWorker.register(
new URL("sw.ts", import.meta.url), // worker script
{ type: "module" },
);
}
const engine: MLCEngineInterface = await CreateServiceWorkerMLCEngine(
selectedModel,
{ initProgressCallback }, // engineConfig
);
你可以在 examples/service-worker 中找到如何在 service worker 中运行 WebLLM 的完整示例。
你也可以在 examples/chrome-extension 和 examples/chrome-extension-webgpu-service-worker 中找到用 WebLLM 构建 Chrome 扩展的示例。后者利用 service worker,所以扩展在后台持久化运行。此外,你还可以探索另一个完整的 Chrome 扩展项目 WebLLM Assistant,它也是使用 WebLLM 构建的。
WebLLM 被设计为与 OpenAI API 完全兼容。因此,除了构建简单的聊天机器人外,你还可以用 WebLLM 实现以下功能:
streaming:以 AsyncGenerator 的形式实时返回输出块
json-mode:有效地确保输出为 JSON 格式,详见 OpenAI 参考文档。
seed-to-reproduce:使用 seeding 来确保可重现的输出,通过 seed 字段。
function-calling(开发中):通过 tools 和 tool_choice 字段调用函数(初步支持);或不使用 tools 或 tool_choice 的手动函数调用(保持最大灵活性)。
WebLLM 支持使用 SRI(Subresource Integrity)哈希对模型制品进行可选的完整性验证。当在 ModelRecord 上设置 integrity 字段时,WebLLM 将在加载之前验证下载的配置、WASM 和 tokenizer 文件是否与提供的哈希匹配。
import { CreateMLCEngine } from "@mlc-ai/web-llm";
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-<base64-hash-of-mlc-chat-config.json>",
model_lib: "sha256-<base64-hash-of-wasm-file>",
tokenizer: {
"tokenizer.json": "sha256-<base64-hash-of-tokenizer.json>",
},
onFailure: "error", // "error" (default) throws IntegrityError, "warn" logs and continues
},
},
],
};
const engine = await CreateMLCEngine("Llama-3.2-1B-Instruct-q4f16_1-MLC", {
appConfig,
});
你可以使用以下方式为模型文件生成 SRI 哈希:
# SHA-256
openssl dgst -sha256 -binary <file> | openssl base64 -A | sed 's/^/sha256-/'
# SHA-384
openssl dgst -sha384 -binary <file> | openssl base64 -A | sed 's/^/sha384-/'
# SHA-512
openssl dgst -sha512 -binary <file> | openssl base64 -A | sed 's/^/sha512-/'
openssl 命令需要类 Unix shell(macOS/Linux)。在 Windows 上,通过 Git Bash 或 WSL 运行 openssl。
如果哈希不匹配,会抛出 IntegrityError(或当 onFailure: "warn" 时记录警告)。integrity 中的所有字段都是可选的——只有指定的制品会被验证。完全省略 integrity 字段时,WebLLM 的行为与之前完全相同(无验证)。
详细的完整工作示例,请参考 integrity-verification 示例。
WebLLM 是 MLC LLM 的配套项目,支持 MLC 格式的自定义模型。它重用模型制品并构建 MLC LLM 的流程。要使用 WebLLM 编译和使用你自己的模型,请查看 MLC LLM 文档,了解如何编译和部署新的模型权重和库到 WebLLM。
这里我们概述高层理念。WebLLM 包中有两个元素使得新模型和权重变体成为可能。
model:包含指向模型制品(如权重和元数据)的 URL。
model_lib:指向包含加速模型计算的可执行文件的 Web Assembly 库(即 wasm 文件)的 URL。
两者在 WebLLM 中都是可自定义的。
import { CreateMLCEngine } from "@mlc-ai/web-llm";
async main() {
const appConfig = {
"model_list": [
{
"model": "/url/to/my/llama",
"model_id": "MyLlama-3b-v1-q4f32_0",
"model_lib": "/url/to/myllama3b.wasm",
}
],
};
// override default
const chatOpts = {
"repetition_penalty": 1.01
};
// load a prebuilt model
// with a chat option override and app config
// under the hood, it will load the model from myLlamaUrl
// and cache it in the browser cache
// The chat will also load the model library from "/url/to/myllama3b.wasm",
// assuming that it is compatible to the model in myLlamaUrl.
const engine = await CreateMLCEngine(
"MyLlama-3b-v1-q4f32_0",
{ appConfig }, // engineConfig
chatOpts,
);
}
许多情况下,我们只需要提供模型权重变体,但不一定需要新的模型(例如 NeuralHermes-Mistral 可以复用 Mistral 的模型库)。关于模型库如何被不同模型变体共享的例子,请参看 webllm.prebuiltAppConfig。
注意: 除非你想修改 WebLLM 软件包,否则不需要从源码构建。要使用 npm,只需按照《开始使用》或任何示例进行操作。
要从源码构建,只需运行:
npm install
npm run build
然后,要在示例中测试你代码改动的效果,在 examples/get-started/package.json 中,将 "@mlc-ai/web-llm": "^0.2.84" 改为 "@mlc-ai/web-llm": ../...
cd examples/get-started
npm install
npm start
注意有时你需要在 file:../.. 和 ../.. 之间切换,以触发 npm 识别新的改动。最坏的情况下,你可以运行:
cd examples/get-started
rm -rf node_modules dist package-lock.json .parcel-cache
npm install
npm start
WebLLM 的运行时很大程度上依赖于 TVMjs:https://github.com/apache/tvm/tree/main/web
虽然它也可作为 npm 软件包使用:https://www.npmjs.com/package/@mlc-ai/web-runtime,但如果需要,你可以按照下面的步骤从源码构建它。
安装 emscripten。 Emscripten 是一个基于 LLVM 的编译器,用于将 C/C++ 源代码编译为 WebAssembly。按照安装说明安装最新的 emsdk。通过 source path/to/emsdk_env.sh 来 source emsdk_env.sh,使得 emcc 可以从 PATH 中访问,且 emcc 命令可用。我们可以通过在终端尝试 emcc 来验证安装是否成功。注意:我们最近发现使用最新的 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" 改为 "@mlc-ai/web-runtime": "file:./tvm_home/web"。
设置必要的环境
为 web 构建准备所有必要的依赖项:
./scripts/prep_deps.sh
在此步骤中,如果环境中未定义 $TVM_SOURCE_DIR,我们将执行以下命令来构建 tvmjs 依赖项:
git clone https://github.com/mlc-ai/relax 3rdparty/tvm-unity --recursive
这会克隆 mlc-ai/relax 的当前 HEAD。但是,这不一定是正确的分支或提交来克隆。要从源码构建特定的 npm 版本,请参考版本更新 PR,其中说明当前 WebLLM 版本依赖的分支(即 mlc-ai/relax 或 apache/tvm)和提交。例如,版本 0.2.52 根据其版本更新 PR #521,是通过检出 apache/tvm 中的以下提交 https://github.com/apache/tvm/commit/e6476847753c80e054719ac47bc2091c888418b6 来构建的,而不是 mlc-ai/relax 的 HEAD。
此外,--recursive 是必要和重要的。否则,你可能遇到错误如 fatal error: 'dlpack/dlpack.h' file not found。
构建 WebLLM 软件包
npm run build
验证一些子软件包
你可以然后进入 examples 中的子文件夹来验证一些子软件包。我们使用 Parcel v2 进行打包。虽然 Parcel 有时不太善于跟踪父目录的改动。当你在 WebLLM 软件包中做出改动时,尝试编辑子文件夹的 package.json 并保存它,这将触发 Parcel 重新构建。
如果你想在本地运行时运行 LLM,请查看 MLC-LLM。
你可能也对 Web Stable Diffusion 感兴趣。
本项目由来自 CMU Catalyst、UW SAMPL、SJTU、OctoML 和 ML 的成员发起。