WebLLM: запуск LLM прямо в браузере с WebGPU

Обзор

WebLLM — высокопроизводительный движок вывода больших языковых моделей (LLM inference engine), работающий прямо в браузере с аппаратным ускорением. Всё выполняется внутри браузера без какой-либо серверной поддержки и ускоряется с помощью WebGPU.

WebLLM полностью совместим с OpenAI API. Это означает, что вы можете использовать тот же OpenAI API для работы с любыми моделями с открытым исходным кодом локально, включая потоковую передачу, режим JSON, вызов функций (в разработке) и многое другое.

Это открывает широкие возможности для создания ИИ-ассистентов, доступных каждому, с соблюдением конфиденциальности и использованием ускорения на GPU.

WebLLM можно подключить как базовый npm-пакет и построить поверх него собственное веб-приложение, следуя приведённым ниже примерам. Проект является компаньоном MLC LLM — системы для универсального развёртывания LLM в различных аппаратных средах.

Ключевые возможности

  • Вывод в браузере: WebLLM — высокопроизводительный движок вывода языковых моделей прямо в браузере, использующий WebGPU для аппаратного ускорения. Мощные операции с LLM выполняются непосредственно в браузере, без обработки на сервере.

  • Полная совместимость с OpenAI API: бесшовная интеграция приложения с WebLLM через OpenAI API с поддержкой потоковой передачи, режима JSON, управления на уровне логитов, задания начального зерна генерации и многого другого.

  • Структурированная генерация JSON: WebLLM поддерживает современный режим структурированной генерации JSON, реализованный в части библиотеки модели на WebAssembly для максимальной производительности. Попробуйте генерацию JSON по произвольной схеме на WebLLM JSON Playground на HuggingFace.

  • Поддержка широкого круга моделей: WebLLM из коробки поддерживает Llama 3, Phi 3, Gemma, Mistral, Qwen (通义千问) и многие другие модели, что делает его универсальным инструментом для различных задач ИИ. Полный список поддерживаемых моделей доступен на MLC Models.

  • Подключение пользовательских моделей: легко интегрируйте и развёртывайте собственные модели в формате MLC, адаптируя WebLLM под конкретные задачи и сценарии использования.

  • Plug-and-Play интеграция: подключайте WebLLM к своим проектам через менеджеры пакетов NPM и Yarn или напрямую через CDN. Доступны подробные примеры и модульная архитектура для подключения к UI-компонентам.

  • Потоковая передача и взаимодействие в реальном времени: поддержка потоковых ответов на запросы чата позволяет генерировать вывод в реальном времени, что особенно полезно для чат-ботов и виртуальных ассистентов.

  • Поддержка Web Worker и Service Worker: оптимизируйте производительность интерфейса и эффективно управляйте жизненным циклом моделей, перенося вычисления в отдельные рабочие потоки или сервис-воркеры (service workers).

  • Поддержка расширений Chrome: расширяйте возможности браузера с помощью пользовательских расширений Chrome на базе WebLLM — доступны примеры как простых, так и продвинутых расширений.

Встроенные модели

Полный список доступных моделей опубликован на MLC Models. WebLLM поддерживает подмножество этих моделей; актуальный список доступен в prebuiltAppConfig.model_list.

Основные семейства поддерживаемых моделей:

  • 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

Если нужной модели нет в списке, создайте запрос через issue или ознакомьтесь с разделом Пользовательские модели, чтобы скомпилировать и подключить собственную модель.

Быстрый старт с примерами

Изучите, как использовать WebLLM для интеграции больших языковых моделей в приложение и генерации ответов в чате, на основе простого примера чат-бота:

Пример более крупного и сложного проекта — WebLLM Chat.

Дополнительные примеры для различных сценариев использования находятся в папке examples.

Начало работы

WebLLM предлагает минималистичный и модульный интерфейс для работы с чат-ботом в браузере. Пакет спроектирован так, чтобы легко подключаться к любым UI-компонентам.

Установка

Через менеджер пакетов

# npm
npm install @mlc-ai/web-llm
# yarn
yarn add @mlc-ai/web-llm
# или pnpm
pnpm install @mlc-ai/web-llm

Затем импортируйте модуль в своём коде:

// Импортировать всё
import * as webllm from "@mlc-ai/web-llm";
// Или только то, что нужно
import { CreateMLCEngine } from "@mlc-ai/web-llm";

Через CDN

Благодаря 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");

Создание MLCEngine

Большинство операций в WebLLM выполняется через интерфейс MLCEngine. Создать экземпляр MLCEngine и загрузить модель можно с помощью фабричной функции CreateMLCEngine().

(Обратите внимание: загрузка моделей требует скачивания, что при первом запуске без кэша может занять значительное время. Правильно обрабатывайте этот асинхронный вызов.)

import { CreateMLCEngine } from "@mlc-ai/web-llm";

// Callback-функция для отображения прогресса загрузки модели
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:

Пример:

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" требует установки и активации совместимого браузерного расширения.

  • Бэкенд cross-origin в настоящее время не поддерживает программное удаление тензорного кэша — очистка управляется расширением.

Запрос завершения чата

После успешной инициализации движка можно отправлять запросы на завершение чата через интерфейс engine.chat.completions в стиле OpenAI API. Полный список параметров и их описание смотрите в разделе о совместимости с OpenAI и в справочнике 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 поддерживает потоковую генерацию ответов. Чтобы её включить, достаточно передать stream: true в вызов engine.chat.completions.create.

const messages = [
  { role: "system", content: "You are a helpful AI assistant." },
  { role: "user", content: "Hello!" },
];

// chunks — объект типа AsyncGenerator
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 || "";
  console.log(reply);
  if (chunk.usage) {
    console.log(chunk.usage); // usage есть только в последнем чанке
  }
}

const fullReply = await engine.getMessage();
console.log(fullReply);

Расширенное использование

Использование воркеров

Тяжёлые вычисления можно вынести в воркер-скрипт, чтобы повысить производительность приложения. Для этого нужно:

  1. Создать обработчик в воркер-потоке, который взаимодействует с основным потоком и обрабатывает запросы.

  2. Создать Worker Engine в основном приложении, который под капотом передаёт сообщения обработчику в воркер-потоке.

Подробные реализации различных видов воркеров описаны в следующих разделах.

Выделенный Web Worker

WebLLM поддерживает API для WebWorker — это позволяет перенести процесс генерации в отдельный рабочий поток, чтобы вычисления не мешали работе интерфейса.

Создаём обработчик в воркер-потоке, который взаимодействует с основным потоком:

// worker.ts
import { WebWorkerMLCEngineHandler } from "@mlc-ai/web-llm";

// Обработчик, работающий в воркер-потоке
const handler = new WebWorkerMLCEngineHandler();
self.onmessage = (msg: MessageEvent) => {
  handler.onmessage(msg);
};

В основной логике создаём WebWorkerMLCEngine, реализующий тот же интерфейс MLCEngineInterface. Всё остальное остаётся без изменений:

// main.ts
import { CreateWebWorkerMLCEngine } from "@mlc-ai/web-llm";

async function main() {
  // Используем WebWorkerMLCEngine вместо MLCEngine
  const engine = await CreateWebWorkerMLCEngine(
    new Worker(new URL("./worker.ts", import.meta.url), {
      type: "module",
    }),
    selectedModel,
    { initProgressCallback }, // engineConfig
  );

  // всё остальное — без изменений
}

Использование Service Worker

WebLLM поддерживает API для ServiceWorker (сервис-воркера) — это позволяет перенести процесс генерации в сервис-воркер, избежав повторной загрузки модели при каждом посещении страницы, и улучшить работу приложения в офлайн-режиме.

(Примечание: жизненный цикл Service Worker управляется браузером, и он может быть завершён в любой момент без уведомления веб-приложения. ServiceWorkerMLCEngine будет периодически отправлять heartbeat-события, чтобы не дать сервис-воркеру завершиться, однако приложение также должно содержать соответствующую обработку ошибок. Подробнее о параметрах keepAliveMs и missedHeatbeat читайте в ServiceWorkerMLCEngine.)

Создаём обработчик в воркер-потоке. Инициализируйте его на верхнем уровне скрипта воркера, чтобы слушатель сообщений был зарегистрирован при первоначальном выполнении скрипта. Не создавайте его внутри слушателей activate или message: браузер может перезапустить уже активный воркер без повторной отправки события activate.

// sw.ts
import { ServiceWorkerMLCEngineHandler } from "@mlc-ai/web-llm";

new ServiceWorkerMLCEngineHandler();
console.log("Service Worker is ready");

В основной логике регистрируем сервис-воркер и создаём движок с помощью функции 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), // скрипт воркера
    { type: "module" },
  );
}

const engine: MLCEngineInterface = await CreateServiceWorkerMLCEngine(
  selectedModel,
  { initProgressCallback }, // engineConfig
);

Полный пример запуска WebLLM в сервис-воркере находится в examples/service-worker.

Расширение Chrome

Примеры создания расширений Chrome с WebLLM доступны в examples/chrome-extension и examples/chrome-extension-webgpu-service-worker. Второй вариант использует сервис-воркер, что обеспечивает постоянную работу расширения в фоне. Также можно изучить полноценный проект расширения Chrome — WebLLM Assistant, построенный на базе WebLLM.

Полная совместимость с OpenAI

WebLLM разработан с полной совместимостью с OpenAI API. Помимо создания простого чат-бота, доступны следующие возможности:

  • streaming (потоковая передача): вывод результата по частям в реальном времени в виде объекта AsyncGenerator;

  • json-mode (режим JSON): гарантированный вывод в формате JSON; подробнее в справочнике OpenAI;

  • seed-to-reproduce: использование начального зерна генерации (поле seed) для воспроизводимых результатов;

  • function-calling (вызов функций, в разработке): вызов функций через поля tools и tool_choice (предварительная поддержка) или ручной вызов функций без tools и tool_choice (максимальная гибкость).

Проверка целостности

WebLLM поддерживает опциональную проверку целостности артефактов модели с помощью хэшей SRI (Subresource Integrity). Если в ModelRecord указано поле integrity, WebLLM будет проверять скачанные файлы конфигурации, WASM и токенизатора по предоставленным хэшам перед загрузкой.

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" (по умолчанию) выбрасывает IntegrityError, "warn" — логирует и продолжает
      },
    },
  ],
};

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-подобной оболочки (macOS/Linux). В Windows запустите openssl через Git Bash или WSL.

Если хэш не совпадает, выбрасывается исключение IntegrityError (или выводится предупреждение при onFailure: "warn"). Все поля в integrity необязательны — проверяются только явно указанные артефакты. Если поле integrity не задано вовсе, WebLLM работает как прежде — без какой-либо проверки.

Полный рабочий пример доступен в examples/integrity-verification.

Пользовательские модели

WebLLM является компаньоном MLC LLM и поддерживает пользовательские модели в формате MLC. Он повторно использует артефакты моделей и процесс сборки MLC LLM. Чтобы скомпилировать и использовать собственные модели с WebLLM, обратитесь к документации MLC LLM — там описана компиляция и развёртывание новых весов моделей и библиотек для WebLLM.

Здесь изложим общую концепцию. В пакете WebLLM есть два элемента, обеспечивающих поддержку новых моделей и вариантов весов:

  • model: URL до артефактов модели — весов и метаданных.

  • model_lib: URL до библиотеки на WebAssembly (wasm-файла) с исполняемым кодом для ускорения вычислений модели.

Оба параметра настраиваются в 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",
      }
    ],
  };
  // Переопределение параметров чата
  const chatOpts = {
    "repetition_penalty": 1.01
  };

  // Загружаем предсобранную модель с переопределением параметров чата
  // и пользовательской конфигурацией приложения.
  // Под капотом модель будет загружена с myLlamaUrl
  // и закэширована в браузерном кэше.
  // Также будет загружена библиотека модели из "/url/to/myllama3b.wasm",
  // при условии её совместимости с моделью по myLlamaUrl.
  const engine = await CreateMLCEngine(
    "MyLlama-3b-v1-q4f32_0",
    { appConfig }, // engineConfig
    chatOpts,
  );
}

Во многих случаях нужно лишь указать вариант весов модели, не меняя саму библиотеку модели (например, NeuralHermes-Mistral может использовать библиотеку Mistral). Примеры совместного использования библиотеки модели разными вариантами весов смотрите в webllm.prebuiltAppConfig.

Сборка пакета WebLLM из исходников

ПРИМЕЧАНИЕ: сборка из исходников нужна только если вы хотите изменить пакет WebLLM. Для использования npm-пакета просто следуйте разделу Начало работы или любому из примеров.

Чтобы собрать из исходников, выполните:

npm install
npm run build

Затем, чтобы проверить изменения на примере, откройте examples/get-started/package.json и замените "@mlc-ai/web-llm": "^0.2.85" на "@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

Если нужно собрать TVMjs из исходников

Среда выполнения WebLLM в значительной мере опирается на TVMjs: https://github.com/apache/tvm/tree/main/web

Он также доступен как npm-пакет: https://www.npmjs.com/package/@mlc-ai/web-runtime, однако при необходимости его можно собрать из исходников, следуя шагам ниже.

  1. Установите emscripten — компилятор на базе LLVM, преобразующий исходный код C/C++ в WebAssembly.

    • Следуйте инструкции по установке для установки последней версии emsdk.

    • Подключите emsdk_env.sh командой source path/to/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
  2. В ./package.json замените "@mlc-ai/web-runtime": "0.18.0-dev2", на "@mlc-ai/web-runtime": "file:./tvm_home/web",.

  3. Подготовьте необходимое окружение.

    Установите все зависимости для веб-сборки:

    ./scripts/prep_deps.sh

    На этом шаге, если переменная $TVM_SOURCE_DIR не задана в окружении, будет выполнена следующая команда для сборки зависимости tvmjs:

    git clone https://github.com/mlc-ai/relax 3rdparty/tvm-unity --recursive

    Это клонирует текущий HEAD репозитория mlc-ai/relax, однако он не всегда будет нужной веткой или коммитом. Чтобы собрать конкретную npm-версию из исходников, обратитесь к PR с обновлением версии — там указано, из какой ветки (mlc-ai/relax или apache/tvm) и какого коммита собирается данная версия WebLLM. Например, версия 0.2.52 согласно соответствующему PR собирается из коммита e6476847 репозитория apache/tvm, а не из HEAD mlc-ai/relax.

    Флаг --recursive обязателен. Без него могут возникать ошибки вида fatal error: 'dlpack/dlpack.h' file not found.

  4. Соберите пакет WebLLM:

    npm run build
  5. Проверьте некоторые подпакеты.

    Перейдите в подпапки в examples и проверьте работу подпакетов. Для бандлинга используется Parcel v2. Parcel иногда плохо отслеживает изменения в родительских директориях. При изменении пакета WebLLM попробуйте отредактировать и сохранить package.json соответствующей подпапки — это заставит Parcel пересобрать проект.

Ссылки

Благодарности

Проект инициирован участниками CMU Catalyst, UW SAMPL, SJTU, OctoML и сообщества MLC. Мы намерены продолжать его развитие и поддержку в интересах сообщества открытого машинного обучения.

Проект стал возможным благодаря экосистеме открытого программного обеспечения, на которую мы опираемся. Мы благодарим сообщество Apache TVM и разработчиков TVM Unity. Участники сообщества открытого машинного обучения сделали эти модели общедоступными. PyTorch и Hugging Face сделали их удобными в использовании. Отдельная благодарность командам, стоящим за Vicuna, SentencePiece, LLaMA и Alpaca. Также хотим поблагодарить сообщества WebAssembly, Emscripten и WebGPU и, наконец, разработчиков Dawn и WebGPU.

Цитирование

Если проект оказался вам полезен, пожалуйста, укажите следующую ссылку:

@misc{ruan2026webllmhighperformanceinbrowserllm,
      title={WebLLM: A High-Performance In-Browser LLM Inference Engine},
      author={Charlie F. Ruan and Yucheng Qin and Akaash R. Parthasarathy and Xun Zhou and Ruihang Lai and Hongyi Jin and Yixin Dong and Bohan Hou and Meng-Shiun Yu and Yiyan Zhai and Sudeep Agarwal and Hangrui Cao and Siyuan Feng and Tianqi Chen},
      year={2026},
      eprint={2412.15803},
      archivePrefix={arXiv},
      primaryClass={cs.LG},
      url={https://arxiv.org/abs/2412.15803},
}
© 2026 meganuke