Зачем вообще это нужно? (Серверы устарели?)
Отправлять каждый запрос в облако - это как ездить в соседний город за хлебом. Дорого, медленно, и ты зависишь от пробок на дороге. В 2026 году браузеры научились делать то, что раньше требовало серверов с дорогими GPU.
Запуск LLM в браузере - это не просто технический трюк. Это смена парадигмы. Пользовательские данные никогда не покидают его устройство. Нет счетов за API. Нет задержек на сеть. Модель работает даже в самолете без интернета.
Но есть нюанс: браузерные LLM пока не догоняют по качеству самые большие облачные модели. Мы говорим о моделях до 7-13 миллиардов параметров, а не о монстрах вроде GPT-4.5 или Claude 3.7. Хотя разрыв стремительно сокращается.
Три столпа браузерного инференса
Чтобы модель заработала в браузере, нужно решить три проблемы: вычисления, память и интерфейс. К счастью, в 2026 году для каждой есть готовое решение.
| Проблема | Решение | Что дает |
|---|---|---|
| Медленные вычисления на CPU | WebGPU | Доступ к видеокарте, ускорение в 10-100 раз |
| Большие модели (гигабайты) | Transformers.js + квантование | Модели 1-4 ГБ вместо 10-20 ГБ |
| Удобный ввод/вывод | Chrome Prompt API (экспериментальный) | Нативный интерфейс для промптов |
1WebGPU: разгоняем браузер до предела
WebGPU - это не просто "WebGL 2.0". Это прямой доступ к графическому конвейеру, как в Vulkan или Metal. И да, в Chrome 124+ (актуально на 07.02.2026) он стабильно работает без флагов.
Почему WebGPU, а не WebAssembly? Потому что матричные умножения - это хлеб с маслом нейросетей, а GPU созданы именно для этого. Разница в скорости - как между велосипедом и Ferrari.
// Проверяем поддержку WebGPU
async function checkWebGPU() {
if (!navigator.gpu) {
console.error('WebGPU не поддерживается');
return false;
}
const adapter = await navigator.gpu.requestAdapter();
if (!adapter) {
console.error('Не удалось получить GPU адаптер');
return false;
}
const device = await adapter.requestDevice();
console.log(`GPU: ${adapter.name}, Тип: ${adapter.features}`);
return device;
}
// Важно: запрашиваем compute-возможности
const adapter = await navigator.gpu.requestAdapter({
powerPreference: 'high-performance',
requiredFeatures: ['timestamp-query', 'shader-f16'] // Новые фичи 2025-2026
});2Transformers.js: Hugging Face в браузере
Transformers.js 4.0 (вышел в конце 2025) - это порт знаменитой библиотеки на JavaScript. Но с одной хитрой особенностью: он умеет автоматически определять, использовать ли WebGPU, WebAssembly или чистый JavaScript.
Самое важное - поддержка квантованных моделей. Модель в 7 миллиардов параметров весит ~14 ГБ в fp16. В формате int4 (квантование 4 бита) - всего ~4 ГБ. Разница между "не влезет" и "работает на среднем ноутбуке".
import { pipeline, env } from '@xenova/transformers';
// Форсируем использование WebGPU
env.backends.onnx.wasm.numThreads = 1; // Отключаем многопоточность WASM
env.backends.onnx.webgpu.enabled = true;
// Загружаем квантованную модель
const generator = await pipeline('text-generation',
'Qwen/Qwen2.5-3B-Instruct-GGUF-Q4_K_M', // Квантованная 4-битная версия
{
device: 'webgpu', // Явно указываем устройство
dtype: 'q4', // Используем 4-битное квантование
progress_callback: (info) => {
console.log(`Загрузка: ${info.loaded}/${info.total} байт`);
}
}
);
// Генерация текста
const output = await generator('Объясни квантовую механику простыми словами:', {
max_new_tokens: 256,
temperature: 0.7,
do_sample: true
});Какие модели работают лучше всего в 2026? Qwen2.5-3B, Phi-3-mini, Gemma-2B. Они достаточно маленькие, но дают осмысленные ответы. Llama 3.2 3B тоже отлично себя показывает.
Не пытайтесь загружать модели больше 7B параметров в браузере. Даже с квантованием они съедят всю память и зависнут. Проверено на горьком опыте.
3IndexedDB: кэшируем модели навсегда
Загружать 4 ГБ модели при каждом открытии страницы - это издевательство. Решение: кэширование в IndexedDB. После первой загрузки модель живет в браузере пользователя.
// Сохраняем модель в IndexedDB
async function cacheModel(modelName, modelData) {
const db = await openDB('llm-cache', 2, {
upgrade(db) {
if (!db.objectStoreNames.contains('models')) {
db.createObjectStore('models', { keyPath: 'name' });
}
}
});
await db.put('models', {
name: modelName,
data: modelData,
timestamp: Date.now(),
version: '1.0.0'
});
console.log(`Модель ${modelName} закэширована`);
}
// Загружаем из кэша
async function loadCachedModel(modelName) {
const db = await openDB('llm-cache', 2);
const cached = await db.get('models', modelName);
if (cached && Date.now() - cached.timestamp < 30 * 24 * 60 * 60 * 1000) {
// Кэш актуален (меньше 30 дней)
return cached.data;
}
return null; // Кэш устарел или отсутствует
}Важный трюк: разбивайте модель на чанки по 50-100 МБ. IndexedDB иногда тормозит на очень больших объектах. И всегда проверяйте версию модели - если вы обновили веса, старый кэш нужно инвалидировать.
4Chrome Prompt API: нативный интерфейс
В Chrome 125+ появился экспериментальный Prompt API. Это не просто текстовое поле - это системный уровень для работы с LLM. Пользователь видит знакомый интерфейс, а вы получаете чистый промпт.
// Проверяем доступность API
if ('prompt' in window) {
// Регистрируем обработчик
navigator.prompt.register({
name: 'summarize',
description: 'Суммаризация текста',
contexts: ['selection'], // Срабатывает на выделенный текст
});
// Обрабатываем запрос
navigator.prompt.addEventListener('prompt', async (event) => {
if (event.context === 'summarize') {
const selectedText = event.data.text;
const summary = await generateSummary(selectedText);
event.respondWith({
text: summary,
confidence: 0.8
});
}
});
}
// Альтернатива для других браузеров
const fallbackPrompt = async (text) => {
// Используем обычный textarea
return await model.generate(text);
};Собираем все вместе: рабочее приложение
Теория - это хорошо, но код - лучше. Вот минимальный рабочий пример, который можно запустить сегодня.
Браузерная LLM
Проверяем WebGPU...
Память: -
// app.js
import { pipeline, env } from '@xenova/transformers';
class BrowserLLM {
constructor() {
this.model = null;
this.isReady = false;
this.memoryMonitor = null;
}
async init() {
// 1. Проверяем WebGPU
if (!await this.checkWebGPU()) {
throw new Error('WebGPU не поддерживается');
}
// 2. Настраиваем Transformers.js
env.backends.onnx.webgpu.enabled = true;
env.allowLocalModels = false; // Всегда качаем с Hugging Face
// 3. Пробуем загрузить из кэша
const cached = await this.loadFromCache('qwen2.5-3b-q4');
if (cached) {
console.log('Загружаем из кэша');
// Здесь была бы логика загрузки бинарных данных
} else {
console.log('Качаем с Hugging Face...');
}
// 4. Загружаем модель
this.model = await pipeline('text-generation',
'Qwen/Qwen2.5-3B-Instruct-GGUF-Q4_K_M',
{
device: 'webgpu',
dtype: 'q4',
model_file: 'model.onnx' // WebGPU-оптимизированная версия
}
);
// 5. Запускаем мониторинг памяти
this.startMemoryMonitor();
this.isReady = true;
console.log('Модель готова к работе');
}
async generate(prompt, options = {}) {
if (!this.isReady) {
throw new Error('Модель не инициализирована');
}
const result = await this.model(prompt, {
max_new_tokens: options.maxTokens || 256,
temperature: options.temperature || 0.7,
top_p: options.topP || 0.9,
repetition_penalty: options.repetitionPenalty || 1.1
});
// Кэшируем результат для похожих промптов
await this.cachePrompt(prompt, result[0].generated_text);
return result[0].generated_text;
}
startMemoryMonitor() {
this.memoryMonitor = setInterval(() => {
if (navigator.deviceMemory) {
const used = performance.memory ?
(performance.memory.usedJSHeapSize / 1024 / 1024).toFixed(1) : 'N/A';
document.getElementById('memory').textContent =
`Память: ${used} МБ / ${navigator.deviceMemory} ГБ`;
}
}, 1000);
}
// ... остальные методы
}
// Инициализация при загрузке
const llm = new BrowserLLM();
llm.init().then(() => {
document.getElementById('status').textContent = 'Готов к работе!';
document.getElementById('generate').addEventListener('click', async () => {
const prompt = document.getElementById('input').value;
const output = await llm.generate(prompt);
document.getElementById('output').textContent = output;
});
});Что может пойти не так (и как это починить)
Браузерный инференс - это не магия. Вот типичные проблемы и их решения.
- Модель не загружается: Проверьте CORS на Hugging Face. В 2026 многие модели требуют токен даже для скачивания. Используйте прокси или mirror.
- WebGPU падает с ошибкой: Скорее всего, не хватает памяти. Уменьшайте batch size, используйте более агрессивное квантование (Q2 вместо Q4).
- Генерация тормозит: Включите кэширование ключей (KV cache). В Transformers.js 4.0+ есть experimental_kv_cache.
- Браузер зависает: Добавьте Web Worker. Вычисления в основном потоке блокируют интерфейс.
Самая частая ошибка: забыть про прогресс-бар. Загрузка 4 ГБ модели занимает время. Показывайте прогресс, иначе пользователь закроет вкладку.
А что насчет альтернатив?
Transformers.js - не единственный игрок. MLC WebLLM предлагает другой подход: компиляция моделей в WebAssembly. Он часто быстрее на CPU, но проигрывает WebGPU на GPU.
Еще есть EdgeVec с Ollama - гибридный подход, где браузер общается с локальным сервером. Не совсем "в браузере", но данные не уходят в облако.
Для самых смелых: прямая работа с WebGPU без библиотек. В 10 раз сложнее, но дает полный контроль.
Что будет дальше? (Прогноз на 2027)
К концу 2026 браузерные LLM станут обычным делом. Вот что нас ждет:
- Модели 10B+ параметров будут работать на средних GPU благодаря 2-битному квантованию
- Стандартный Prompt API во всех браузерах (включая Safari)
- Офлайн-fine-tuning прямо в браузере (уже есть эксперименты)
- Мультимодальность: Stable Diffusion 4 в браузере - не фантастика
Самый интересный тренд: браузерные агенты, которые живут в расширении и помогают с повседневными задачами. Без отправки данных куда-либо.
Начните экспериментировать сейчас. Через год это будет обязательным навыком для фронтенд-разработчиков. И да, серверы для простых LLM-запросов действительно устаревают.
P.S. Если ваш проект требует максимальной производительности, посмотрите как упаковать 20B модель в 6 ГБ VRAM. Те же принципы, но для более мощного железа.