AI 探索技术手记

《前端本地 AI 实战:用 Transformers.js 在浏览器里跑 LLM》

Michael Meng· 2026年7月23日· ◷ 5 分钟阅读
《前端本地 AI 实战:用 Transformers.js 在浏览器里跑 LLM》

一、问题引入:为什么要在浏览器跑 AI?

上传身份证照片给 OCR 识别、在浏览器里做情感分析、实现离线翻译——这些场景都有一个共同要求:数据不出用户设备

让模型在浏览器里跑,就能同时满足:
- 隐私:数据不离开用户浏览器
- 低延迟:省去网络往返
- 离线:飞机上也能用

方案 延迟 隐私 成本 模型能力
后端 LLM API 300-2000ms ❌ 数据离开设备 按 Token 付费 强(GPT-4o 级别)
浏览器本地 AI 50-500ms ✅ 数据不出设备 $0(用户 CPU) 中小(1-7B 参数)

二、核心框架选型

框架 定位 推荐场景
Transformers.js HuggingFace 官方,支持 BERT/GPT/T5/Whisper 等 文本处理、翻译、情感分析
TensorFlow.js Google,支持图像识别、姿态检测 计算机视觉
WebLLM 专注 LLM 浏览器推理(Llama/Phi) 聊天机器人
ml5.js 极简 API,适合快速原型 教学/ Demo

本文聚焦 Transformers.js —— 生态最全,使用最广泛。


三、完整可运行代码

3.1 基础使用:情感分析

import { pipeline } from '@xenova/transformers'

// pipeline 会自动下载模型到浏览器缓存
const classifier = await pipeline('sentiment-analysis', 'Xenova/distilbert-base-uncased-finetuned-sst-2-english')

// 一行代码完成推理
const result = await classifier('I love building AI apps in the browser!')
console.log(result)
// [{ label: 'POSITIVE', score: 0.999 }]

3.2 进阶:文本生成(本地 LLM)

import { pipeline } from '@xenova/transformers'

// 加载轻量级对话模型(约 500MB,首次下载后缓存)
const generator = await pipeline(
  'text-generation',
  'Xenova/LaMini-Flan-T5-783M',
  { quantized: true } // ★ 量化:模型体积减小 75%
)

const output = await generator(
  'Explain quantum computing in simple terms:',
  {
    max_new_tokens: 200,
    temperature: 0.7,
  }
)
console.log(output[0].generated_text)

3.3 工程化:WebWorker + IndexedDB 缓存

// worker.ts —— 在独立线程中加载和运行模型
import { pipeline, env } from '@xenova/transformers'

// 配置模型缓存到 IndexedDB(持久化,刷新不丢失)
env.useBrowserCache = true
env.cacheName = 'ai-model-cache'

let generator: any = null

self.onmessage = async (e: MessageEvent) => {
  const { type, payload } = e.data

  if (type === 'load') {
    // 加载模型(异步,只执行一次)
    self.postMessage({ type: 'status', payload: 'loading' })
    try {
      generator = await pipeline('text-generation', payload.model, {
        quantized: true,
      })
      self.postMessage({ type: 'status', payload: 'ready' })
    } catch (err) {
      self.postMessage({ type: 'error', payload: String(err) })
    }
  }

  if (type === 'generate' && generator) {
    self.postMessage({ type: 'status', payload: 'generating' })
    try {
      const result = await generator(payload.prompt, payload.options)
      self.postMessage({ type: 'result', payload: result[0].generated_text })
    } catch (err) {
      self.postMessage({ type: 'error', payload: String(err) })
    }
  }
}
// App.tsx —— 主线程调用 Worker
import { useState, useRef, useEffect } from 'react'

export default function LocalAIApp() {
  const [output, setOutput] = useState('')
  const [status, setStatus] = useState('idle')
  const workerRef = useRef<Worker | null>(null)

  useEffect(() => {
    // 初始化 Worker
    workerRef.current = new Worker(
      new URL('./worker.ts', import.meta.url),
      { type: 'module' }
    )

    workerRef.current.onmessage = (e) => {
      const { type, payload } = e.data
      if (type === 'status') setStatus(payload)
      if (type === 'result') {
        setOutput(payload)
        setStatus('ready')
      }
      if (type === 'error') {
        setOutput(`Error: ${payload}`)
        setStatus('error')
      }
    }

    // 页面加载时预加载模型
    workerRef.current.postMessage({
      type: 'load',
      payload: { model: 'Xenova/LaMini-Flan-T5-783M' },
    })

    return () => { workerRef.current?.terminate() }
  }, [])

  const handleGenerate = (prompt: string) => {
    workerRef.current?.postMessage({
      type: 'generate',
      payload: {
        prompt,
        options: { max_new_tokens: 200, temperature: 0.7 },
      },
    })
  }

  return (
    <div>
      <h2>浏览器本地 AI</h2>
      <p>状态: {status}</p>
      <button
        onClick={() => handleGenerate('What is WebAssembly?')}
        disabled={status !== 'ready'}
      >
        生成
      </button>
      <pre>{output}</pre>
    </div>
  )
}

四、逐行精讲

4.1 量化:为什么 quantized: true 是关键

const generator = await pipeline('text-generation', model, { quantized: true })

一个 7B 参数的模型原始大小约 14GB(FP16)。开启量化后:

量化方式 模型体积 内存占用 精度损失
FP16(不量化) 14 GB 无法在浏览器运行 0%
8-bit 7 GB 仍太大 <1%
4-bit 3.5 GB 部分浏览器可运行 1-3%

Transformers.js 的 quantized: true 默认使用 ONNX 量化模型,体积缩小 75%,精度损失可忽略。

4.2 为什么 Worker 是必须的

workerRef.current = new Worker(new URL('./worker.ts', import.meta.url))

模型加载是 CPU 密集型操作。在主线程加载会直接冻结 UI 5-30 秒。WebWorker 将加载和推理隔离到独立线程,用户仍可正常交互。

4.3 IndexedDB 缓存 = 第二次秒开

env.useBrowserCache = true

模型文件(几百 MB)首次下载后存入 IndexedDB。第二次访问时直接从缓存读取,加载速度提升 10-100 倍。


五、常见问题

Q1:浏览器内存不够怎么办?
A:1. 选更小的模型(1B 以下,如 Xenova/LaMini-Flan-T5-77M);2. 开启 4-bit 量化;3. 检查剩余内存 performance.memory;4. 降级提示用户换设备。

Q2:加载太慢怎么办?
A:1. 预加载(用户打开页面就开始加载);2. 懒加载(用户触发功能时才加载);3. 显示加载进度条 pipeline(..., { progress_callback: ... });4. CDN 加速模型文件托管。


六、决策框架

你的 AI 场景适合本地还是后端?
├── 数据绝对不能离开设备(身份证/医疗)
│   └── 本地 AI(Transformers.js)
│
├── 需要 GPT-4 级别的推理能力
│   └── 后端 LLM API
│
├── 离线环境(飞机/弱网)
│   └── 本地 AI
│
├── 高并发 + 低延迟(聊天机器人)
│   ├── 轻量对话 → 本地 WebLLM
│   └── 复杂推理 → 后端 LLM
│
└── 混合方案:本地做预处理 + 后端做深度推理
    └── 本地提取实体 → 后端 LLM 全量分析

七、面试速记

Q:浏览器跑 AI 有哪几种方式?
A:1. Transformers.js(文本/NLP);2. TensorFlow.js(视觉);3. WebLLM(聊天 LLM);4. WebAssembly + ONNX Runtime(自定义模型)。

Q:本地 AI 的核心优化手段?
A:量化(4-bit 体积缩小 75%)、WebWorker(不冻 UI)、IndexedDB 缓存(二次秒开)、模型分片加载(并行下载)、WASM 加速推理。


八、总结

  1. Transformers.js 把 HuggingFace 生态搬到了浏览器,一行代码跑 NLP。
  2. quantized: true 是唯一能跑通的生产开关。
  3. WebWorker + IndexedDB 是前端本地 AI 的工程标配。
  4. 本地 AI 不替代后端 LLM——各司其职,混合方案是趋势。

Comments 留言讨论

还没有评论,来抢个沙发,聊聊你的看法~

Michael.Meng

michaelnews@126.com
用 AI 记录,用文字沉淀

© 2026 Michael Meng · 保留所有权利 · Powered by FastAPI + Nuxt